-
Notifications
You must be signed in to change notification settings - Fork 17
Open
Description
I tried wanted to see if I could get a basic echo scalar string function working, I got as far as being able to load the extension, but when running the function.
D load './string_extension.duckdb_extension';
Initializing custom DuckDB extension
Scalar function registered successfully
D SELECT string_function('Hello, DuckDB!');
Segmentation fault (core dumped)CoPilot suggested something about a C-compatible function pointer, I tried that but got exactly the same results. Any hints on where to go from here? My C is very rusty (no pun intended)...
package main
/*
#include <duckdb.h>
*/
import "C"
import (
"fmt"
"unsafe"
"github.com/duckdb/duckdb-go-bindings"
)
//export string_extension_init_c_api
func string_extension_init_c_api(db *C.duckdb_database) bool{
fmt.Println("Initializing custom DuckDB extension")
// Wrap the database pointer in the duckdb-go-bindings Database struct
database := duckdb_go_bindings.Database{Ptr: unsafe.Pointer(db)}
// Create a connection to the database
var conn duckdb_go_bindings.Connection
if duckdb_go_bindings.Connect(database, &conn) != duckdb_go_bindings.StateSuccess {
fmt.Println("Failed to connect to DuckDB")
return false
}
defer duckdb_go_bindings.Disconnect(&conn)
// Register the scalar function
if err := registerScalarFunction(conn, "string_function", stringFunction); err != nil {
fmt.Printf("Failed to register scalar function: %v\n", err)
return false
}
fmt.Println("Scalar function registered successfully")
return true
}
//export string_function_c
func string_function_c(info *C.duckdb_function_info, input *C.duckdb_data_chunk, output *C.duckdb_data_chunk) {
// Extract the input string
inputChunk := duckdb_go_bindings.DataChunk{Ptr: unsafe.Pointer(input)}
outputChunk := duckdb_go_bindings.DataChunk{Ptr: unsafe.Pointer(output)}
inputVector := inputChunk.GetVector(0)
inputValue := inputVector.GetValue(0)
inputString := duckdb_go_bindings.GetVarchar(inputValue)
// Process the input
result := fmt.Sprintf("Received: %s", inputString)
// Set the output value
outputVector := outputChunk.GetVector(0)
outputVector.SetValue(0, duckdb_go_bindings.CreateVarchar(result))
}
// stringFunction is the implementation of the scalar function
func stringFunction(args []duckdb_go_bindings.Value) (duckdb_go_bindings.Value, error) {
// Ensure the input is a string
if len(args) != 1 {
return duckdb_go_bindings.CreateNullValue(), fmt.Errorf("string_function expects exactly one argument")
}
input := duckdb_go_bindings.GetVarchar(args[0])
if input == "" {
return duckdb_go_bindings.CreateNullValue(), fmt.Errorf("string_function expects a non-empty string argument")
}
// Return the input string with a custom message
result := fmt.Sprintf("Received: %s", input)
return duckdb_go_bindings.CreateVarchar(result), nil
}
// registerScalarFunction registers a scalar function with DuckDB
func registerScalarFunction(conn duckdb_go_bindings.Connection, name string, fn func([]duckdb_go_bindings.Value) (duckdb_go_bindings.Value, error)) error {
// Create a new scalar function
scalarFunc := duckdb_go_bindings.CreateScalarFunction()
// Set the function name
duckdb_go_bindings.ScalarFunctionSetName(scalarFunc, name)
// Define the input parameter type (VARCHAR)
inputType := duckdb_go_bindings.CreateLogicalType(duckdb_go_bindings.TypeVarchar)
duckdb_go_bindings.ScalarFunctionAddParameter(scalarFunc, inputType)
duckdb_go_bindings.DestroyLogicalType(&inputType)
// Define the return type (VARCHAR)
returnType := duckdb_go_bindings.CreateLogicalType(duckdb_go_bindings.TypeVarchar)
duckdb_go_bindings.ScalarFunctionSetReturnType(scalarFunc, returnType)
duckdb_go_bindings.DestroyLogicalType(&returnType)
// Set the function implementation
duckdb_go_bindings.ScalarFunctionSetFunction(scalarFunc, unsafe.Pointer(C.string_function_c))
// duckdb_go_bindings.ScalarFunctionSetFunction(scalarFunc, unsafe.Pointer(&fn))
// Register the function with the connection
if duckdb_go_bindings.RegisterScalarFunction(conn, scalarFunc) != duckdb_go_bindings.StateSuccess {
return fmt.Errorf("failed to register scalar function: %s", name)
}
duckdb_go_bindings.DestroyScalarFunction(&scalarFunc)
return nil
}
func main() {
// This is the entry point for the extension
// Typically, DuckDB will call `duckdb_extension_init` automatically
}build script
# Set paths and variables
LIBRARY_FILE="string_extension.so"
OUTPUT_FILE="string_extension.duckdb_extension"
DUCKDB_PLATFORM="linux_amd64" # Adjust this to your platform
DUCKDB_VERSION="v1.2.0" # Replace with your DuckDB version
EXTENSION_VERSION="0.0.1" # Replace with your extension version
# Build the shared library
echo "Building the shared library..."
CGO_ENABLED=1 CPPFLAGS="-DDUCKDB_STATIC_BUILD" CGO_LDFLAGS="-lduckdb -lstdc++ -lm -ldl -L/path/to/duckdb-go-bindings/linux-amd64" \
go build -tags=duckdb_use_static_lib -buildmode=c-shared -o $LIBRARY_FILE main.go
# Append metadata to the shared library
echo "Appending DuckDB extension metadata..."
python3 append_extension_metadata.py \
--library-file $LIBRARY_FILE \
--extension-name "string_extension" \
--duckdb-platform $DUCKDB_PLATFORM \
--duckdb-version $DUCKDB_VERSION \
--extension-version $EXTENSION_VERSION \
--out-file $OUTPUT_FILE
echo "Build complete. Output file: $OUTPUT_FILE"Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels