Files
2026-07-13 12:32:25 +08:00

121 lines
3.3 KiB
Go

// Copyright 2025 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pg_extension
import (
"fmt"
"sync"
)
// Library is a fully-loaded extension library.
type Library struct {
Magic PgMagicStruct
Funcs map[string]Function
Version Version
internal InternalLoadedLibrary
}
// InternalLoadedLibrary is an interface that is implemented by the specific platform to handle library operations.
type InternalLoadedLibrary interface {
Lookup(sym string) (uintptr, error)
Close() error
}
// Function represents an internal library function.
type Function struct {
Name string
Ptr uintptr
Args []int
APIVersion int
// TODO: return type?
}
// PgFunctionInfo is a stand-in for the C struct that reports the function information.
type PgFunctionInfo struct {
APIVersion int32
}
// PgMagicStruct is a stand-in for the C struct that reports the information of the library.
type PgMagicStruct struct {
Len int32
Version int32
FuncMaxArgs int32
IndexMaxKeys int32
NameDataLen int32
Float4ByVal int32
Float8ByVal int32
}
var (
// loadedLibraries contains all of the loaded libraries.
// TODO: need to close all of these before the program ends
loadedLibraries = make(map[string]*Library)
// loadedLibrariesMutex gates access to the cached libraries.
loadedLibrariesMutex = &sync.Mutex{}
)
// LoadLibrary loads the library of the extension, along with preloading all of the functions given.
func LoadLibrary(path string, funcNames []string) (*Library, error) {
loadedLibrariesMutex.Lock()
defer loadedLibrariesMutex.Unlock()
if lib, ok := loadedLibraries[path]; ok {
return lib, nil
}
internalLib, err := loadLibraryInternal(path)
if err != nil {
return nil, err
}
magicPtr, err := internalLib.Lookup("Pg_magic_func")
if err != nil {
return nil, err
}
// We don't free the magic struct since it's a pointer to static memory
magicStructDatum, isNotNull := CallFmgrFunction(magicPtr)
if !isNotNull {
return nil, fmt.Errorf("unable to find magic function for `%s`", path)
}
magicStruct := *(FromDatum[PgMagicStruct](magicStructDatum))
lib := &Library{
Magic: magicStruct,
Funcs: make(map[string]Function),
internal: internalLib,
}
for _, funcName := range funcNames {
finfoPtr, err := internalLib.Lookup(fmt.Sprintf("pg_finfo_%s", funcName))
if err != nil {
return nil, err
}
// We don't free finfo since it's a pointer to static memory
finfoDatum, isNotNull := CallFmgrFunction(finfoPtr)
apiVersion := 0
if isNotNull {
apiVersion = int(FromDatum[PgFunctionInfo](finfoDatum).APIVersion)
}
funcPtr, err := internalLib.Lookup(funcName)
if err != nil {
return nil, err
}
lib.Funcs[funcName] = Function{
Name: funcName,
Ptr: funcPtr,
Args: nil,
APIVersion: apiVersion,
}
}
loadedLibraries[path] = lib
return lib, nil
}