### Install Build Tools for Ubuntu Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Use this command to install essential build tools on Ubuntu if you encounter CGO unavailability. ```bash sudo apt-get update && sudo apt-get install build-essential ``` -------------------------------- ### Install go-duckdb Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Use this command to get the latest version of the go-duckdb package. ```sh go get github.com/marcboeker/go-duckdb/v2 ``` -------------------------------- ### Use NewConnector with Initialization Callback Source: https://context7.com/marcboeker/go-duckdb/llms.txt Creates a connector with custom initialization logic executed on each new connection. Useful for setting session-level configurations or running setup queries. ```go package main import ( "context" "database/sql" "database/sql/driver" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) func main() { connector, err := duckdb.NewConnector("mydb.db", func(execer driver.ExecerContext) error { // Run initialization queries on each new connection bootQueries := []string{ `SET schema=main`, `SET search_path=main`, } for _, query := range bootQueries { _, err := execer.ExecContext(context.Background(), query, nil) if err != nil { return err } } return nil }) if err != nil { log.Fatal(err) } defer connector.Close() // Use the connector with sql.OpenDB db := sql.OpenDB(connector) defer db.Close() // Create and query a table _, err = db.ExecContext(context.Background(), `CREATE TABLE test (id INTEGER, name VARCHAR)`) if err != nil { log.Fatal(err) } _, err = db.ExecContext(context.Background(), `INSERT INTO test VALUES (1, 'Alice'), (2, 'Bob')`) if err != nil { log.Fatal(err) } rows, err := db.QueryContext(context.Background(), `SELECT id, name FROM test`) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var name string if err := rows.Scan(&id, &name); err != nil { log.Fatal(err) } fmt.Printf("ID: %d, Name: %s\n", id, name) } // Output: // ID: 1, Name: Alice // ID: 2, Name: Bob } ``` -------------------------------- ### Enable and Retrieve DuckDB Profiling Information Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md This example demonstrates how to enable profiling for a connection, execute a query, retrieve profiling information, and then disable profiling. ```go db, err := sql.Open("duckdb", "") deferr db.Close() conn, err := db.Conn(context.Background()) deferr conn.Close() _, err = conn.ExecContext(context.Background(), `PRAGMA enable_profiling = 'no_output'`) _, err = conn.ExecContext(context.Background(), `PRAGMA profiling_mode = 'detailed'`) res, err := conn.QueryContext(context.Background(), `SELECT 42`) deferr res.Close() info, err := GetProfilingInfo(conn) _, err = conn.ExecContext(context.Background(), `PRAGMA disable_profiling`) ``` -------------------------------- ### Install GCC on Windows with msys64 Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Follow these steps to install the necessary GCC compiler and runtime libraries on Windows using msys64 for compiling the go-duckdb package. ```sh pacman -S mingw-w64-ucrt-x86_64-gcc ``` -------------------------------- ### Go DuckDB Arrow Interface Example Source: https://context7.com/marcboeker/go-duckdb/llms.txt Demonstrates using DuckDB's Arrow interface for efficient columnar data processing in Go. Requires building with the 'duckdb_arrow' tag. ```go // Build with: go build -tags="duckdb_arrow" package main import ( "context" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) func main() { connector, err := duckdb.NewConnector("", nil) if err != nil { log.Fatal(err) } defer connector.Close() conn, err := connector.Connect(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() // Create Arrow interface from connection arrow, err := duckdb.NewArrowFromConn(conn) if err != nil { log.Fatal(err) } // Execute query and get Arrow RecordReader reader, err := arrow.QueryContext( context.Background(), "SELECT i, i*2 as doubled FROM generate_series(1, 1000) t(i)", ) if err != nil { log.Fatal(err) } defer reader.Release() // Print schema schema := reader.Schema() fmt.Printf("Schema: %v\n", schema) // Process records totalRows := int64(0) for reader.Next() { record := reader.Record() totalRows += record.NumRows() // Access columnar data col0 := record.Column(0) col1 := record.Column(1) fmt.Printf("Batch: %d rows, columns: %s, %s\n", record.NumRows(), col0.DataType(), col1.DataType()) } if err := reader.Err(); err != nil { log.Fatal(err) } fmt.Printf("Total rows processed: %d\n", totalRows) } ``` -------------------------------- ### Configure DuckDB with DSN Parameters Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Configure DuckDB options by appending them as query parameters to the DSN. For example, set read-only access and specify the number of threads. ```go db, err := sql.Open("duckdb", "/path/to/foo.db?access_mode=read_only&threads=4") def db.Close() ``` -------------------------------- ### Go DuckDB Replacement Scan Example Source: https://context7.com/marcboeker/go-duckdb/llms.txt Registers and uses a custom replacement scan to intercept table references and redirect them to custom data sources. This example redirects 'csv_' prefixed tables to the 'read_csv' function. ```go package main import ( "context" "database/sql" "fmt" "log" "strings" "github.com/marcboeker/go-duckdb/v2" ) func main() { connector, err := duckdb.NewConnector("", nil) if err != nil { log.Fatal(err) } defer connector.Close() // Register a replacement scan that handles "csv_" prefixed table names duckdb.RegisterReplacementScan(connector, func(tableName string) (string, []any, error) { if strings.HasPrefix(tableName, "csv_") { // Convert csv_filename to read_csv('filename.csv') filename := strings.TrimPrefix(tableName, "csv_") + ".csv" return "read_csv", []any{filename}, nil } // Return empty string to indicate no replacement return "", nil, nil }) db := sql.OpenDB(connector) defer db.Close() // Create a test CSV file (in real usage, this would be an actual file) _, err = db.ExecContext(context.Background(), " COPY (SELECT 1 as id, 'Alice' as name UNION ALL SELECT 2, 'Bob') TO 'users.csv' (HEADER, DELIMITER ',') ") if err != nil { log.Fatal(err) } // Query using the replacement scan - "csv_users" becomes read_csv('users.csv') rows, err := db.QueryContext(context.Background(), `SELECT * FROM csv_users`) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var name string if err := rows.Scan(&id, &name); err != nil { log.Fatal(err) } fmt.Printf("ID: %d, Name: %s\n", id, name) } // Output: // ID: 1, Name: Alice // ID: 2, Name: Bob } ``` -------------------------------- ### Registering Scalar UDFs in Go for DuckDB Source: https://context7.com/marcboeker/go-duckdb/llms.txt This Go program demonstrates how to define and register custom scalar user-defined functions (UDFs) with DuckDB. It includes an example of a simple string transformation function (`my_upper`) and a variadic function for summing integers (`sum_ints`). Ensure the `go-duckdb` library is imported. ```go package main import ( "context" "database/sql" "database/sql/driver" "fmt" "log" "strings" "github.com/marcboeker/go-duckdb/v2" ) // Define a custom scalar function type upperFunc struct{} func (*upperFunc) Config() duckdb.ScalarFuncConfig { inputType, _ := duckdb.NewTypeInfo(duckdb.TYPE_VARCHAR) resultType, _ := duckdb.NewTypeInfo(duckdb.TYPE_VARCHAR) return duckdb.ScalarFuncConfig{ InputTypeInfos: []duckdb.TypeInfo{inputType}, ResultTypeInfo: resultType, } } func (*upperFunc) Executor() duckdb.ScalarFuncExecutor { return duckdb.ScalarFuncExecutor{ RowExecutor: func(values []driver.Value) (any, error) { str := values[0].(string) return strings.ToUpper(str), nil }, } } // Define a variadic sum function type sumInts struct{} func (*sumInts) Config() duckdb.ScalarFuncConfig { intType, _ := duckdb.NewTypeInfo(duckdb.TYPE_INTEGER) return duckdb.ScalarFuncConfig{ InputTypeInfos: []duckdb.TypeInfo{}, // No fixed parameters ResultTypeInfo: intType, VariadicTypeInfo: intType, // Accept any number of integers } } func (*sumInts) Executor() duckdb.ScalarFuncExecutor { return duckdb.ScalarFuncExecutor{ RowExecutor: func(values []driver.Value) (any, error) { sum := int32(0) for _, v := range values { sum += v.(int32) } return sum, nil }, } } func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() // Register the upper function var upperUDF *upperFunc if err := duckdb.RegisterScalarUDF(conn, "my_upper", upperUDF); err != nil { log.Fatal(err) } // Register the sum function var sumUDF *sumInts if err := duckdb.RegisterScalarUDF(conn, "sum_ints", sumUDF); err != nil { log.Fatal(err) } // Use the custom functions var result string row := db.QueryRow(`SELECT my_upper('hello world')`) if err := row.Scan(&result); err != nil { log.Fatal(err) } fmt.Printf("Upper: %s\n", result) var sum int32 row = db.QueryRow(`SELECT sum_ints(1, 2, 3, 4, 5)`) if err := row.Scan(&sum); err != nil { log.Fatal(err) } fmt.Printf("Sum: %d\n", sum) // Output: // Upper: HELLO WORLD // Sum: 15 } ``` -------------------------------- ### Extract Table Names from SQL Query Source: https://context7.com/marcboeker/go-duckdb/llms.txt Use `GetTableNames` to extract table names from a SQL query. Set the `qualified` parameter to `true` to get fully qualified names (including schema and database). ```go package main import ( "context" "database/sql" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() // Create some tables db.Exec(`CREATE TABLE users (id INTEGER, name VARCHAR)`) db.Exec(`CREATE TABLE orders (id INTEGER, user_id INTEGER, amount DECIMAL)`) db.Exec(`CREATE SCHEMA sales`) db.Exec(`CREATE TABLE sales.products (id INTEGER, name VARCHAR)`) conn, err := db.Conn(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() // Get table names from a query (not qualified) query := ` SELECT u.name, o.amount, p.name FROM users u JOIN orders o ON u.id = o.user_id JOIN sales.products p ON o.id = p.id ` tables, err := duckdb.GetTableNames(conn, query, false) if err != nil { log.Fatal(err) } fmt.Println("Table names (unqualified):") for _, t := range tables { fmt.Printf(" - %s\n", t) } // Get fully qualified table names tablesQualified, err := duckdb.GetTableNames(conn, query, true) if err != nil { log.Fatal(err) } fmt.Println("Table names (qualified):") for _, t := range tablesQualified { fmt.Printf(" - %s\n", t) } // Output: // Table names (unqualified): // - users // - orders // - products // Table names (qualified): // - memory.main.users // - memory.main.orders // - memory.sales.products } ``` -------------------------------- ### Initialize DuckDB with sql.OpenDB and Callbacks Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Use sql.OpenDB for more control, allowing initialization steps like setting schema and search path via a callback function before the database is opened. ```go c, err := duckdb.NewConnector("/path/to/foo.db?access_mode=read_only&threads=4", func(execer driver.ExecerContext) error { bootQueries := []string{ `SET schema=main`, `SET search_path=main`, } for _, query := range bootQueries { _, err = execer.ExecContext(context.Background(), query, nil) if err != nil { return err } } return nil }) def c.Close() db := sql.OpenDB(c) def db.Close() ``` -------------------------------- ### Build with Static DuckDB Library (Darwin ARM64) Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Builds a Go module linking against a custom static DuckDB library. Ensure CGO is enabled and specify necessary flags and paths. ```sh CGO_ENABLED=1 CPPFLAGS="-DDUCKDB_STATIC_BUILD" CGO_LDFLAGS="-lduckdb_bundle -lc++ -L/path/to/libs" go build -tags=duckdb_use_static_lib ``` -------------------------------- ### Manage Database Transactions in Go with DuckDB Source: https://context7.com/marcboeker/go-duckdb/llms.txt Demonstrates how to use `Begin`, `Commit`, and `Rollback` to manage ACID transactions in DuckDB using Go. Errors during transaction operations should trigger a rollback. ```go package main import ( "context" "database/sql" "fmt" "log" _ "github.com/marcboeker/go-duckdb/v2" ) func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() ctx := context.Background() // Create table _, err = db.ExecContext(ctx, "CREATE TABLE accounts(id INTEGER, balance DECIMAL(10,2))") if err != nil { log.Fatal(err) } _, err = db.ExecContext(ctx, "INSERT INTO accounts VALUES (1, 1000.00), (2, 500.00)") if err != nil { log.Fatal(err) } // Start a transaction tx, err := db.Begin() if err != nil { log.Fatal(err) } // Transfer money between accounts _, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - 100 WHERE id = 1") if err != nil { tx.Rollback() log.Fatal(err) } _, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance + 100 WHERE id = 2") if err != nil { tx.Rollback() log.Fatal(err) } // Verify within transaction var balance float64 row := tx.QueryRowContext(ctx, "SELECT balance FROM accounts WHERE id = 2") if err := row.Scan(&balance); err != nil { tx.Rollback() log.Fatal(err) } fmt.Printf("Balance in transaction: %.2f\n", balance) // Commit the transaction if err := tx.Commit(); err != nil { log.Fatal(err) } // Verify after commit row = db.QueryRowContext(ctx, "SELECT balance FROM accounts WHERE id = 2") if err := row.Scan(&balance); err != nil { log.Fatal(err) } fmt.Printf("Balance after commit: %.2f\n", balance) // Output: // Balance in transaction: 600.00 // Balance after commit: 600.00 } ``` -------------------------------- ### Enable and Use Query Profiling in Go Source: https://context7.com/marcboeker/go-duckdb/llms.txt This Go code demonstrates how to enable DuckDB's query profiling, set it to detailed mode, execute a query, retrieve profiling information, and then disable profiling. Ensure the 'duckdb' package is imported. ```go package main import ( "context" "database/sql" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() ctx := context.Background() // Create test data conn.ExecContext(ctx, `CREATE TABLE test AS SELECT i, i*2 as doubled FROM range(10000) t(i)`) // Enable profiling _, err = conn.ExecContext(ctx, `PRAGMA enable_profiling = 'no_output'`) if err != nil { log.Fatal(err) } _, err = conn.ExecContext(ctx, `PRAGMA profiling_mode = 'detailed'`) if err != nil { log.Fatal(err) } // Execute query to profile rows, err := conn.QueryContext(ctx, ` SELECT i, doubled FROM test WHERE i > 5000 ORDER BY doubled DESC LIMIT 10 `) if err != nil { log.Fatal(err) } rows.Close() // Get profiling information info, err := duckdb.GetProfilingInfo(conn) if err != nil { log.Fatal(err) } // Print top-level metrics fmt.Println("Query Metrics:") for key, value := range info.Metrics { fmt.Printf(" %s: %s\n", key, value) } // Print operator metrics fmt.Println("\nOperator Tree:") printOperators(info.Children, 0) // Disable profiling conn.ExecContext(ctx, `PRAGMA disable_profiling`) } func printOperators(children []duckdb.ProfilingInfo, depth int) { indent := "" for i := 0; i < depth; i++ { indent += " " } for _, child := range children { if name, ok := child.Metrics["OPERATOR_TYPE"]; ok { fmt.Printf("%s- %s\n", indent, name) } printOperators(child.Children, depth+1) } } ``` -------------------------------- ### Build with Dynamic DuckDB Library (Linux) Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Dynamically links to libduckdb on Linux. Ensure libduckdb is available and specify the library path. The executable must be run with LD_LIBRARY_PATH set. ```sh # On Linux. CGO_ENABLED=1 CGO_LDFLAGS="-lduckdb -L/path/to/libs" go build -tags=duckdb_use_lib main.go LD_LIBRARY_PATH=/path/to/libs ./main ``` -------------------------------- ### Execute Parameterized Queries with Prepared Statements in Go Source: https://context7.com/marcboeker/go-duckdb/llms.txt Use prepared statements with `?` placeholders for safe and efficient parameterized queries. Ensure to close the statement and rows when done. ```go package main import ( "context" "database/sql" "fmt" "log" "time" _ "github.com/marcboeker/go-duckdb/v2" ) type User struct { Name string Age int Height float32 Awesome bool Bday time.Time } func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() ctx := context.Background() // Create table _, err = db.ExecContext(ctx, " CREATE TABLE users( name VARCHAR, age INTEGER, height FLOAT, awesome BOOLEAN, bday DATE ) ") if err != nil { log.Fatal(err) } // Prepare statement for bulk inserts stmt, err := db.PrepareContext(ctx, "INSERT INTO users VALUES(?, ?, ?, ?, ?)") if err != nil { log.Fatal(err) } defer stmt.Close() // Insert multiple rows users := []User{ {"Alice", 30, 1.65, true, time.Date(1994, 5, 15, 0, 0, 0, 0, time.UTC)}, {"Bob", 25, 1.80, true, time.Date(1999, 8, 22, 0, 0, 0, 0, time.UTC)}, {"Charlie", 35, 1.75, false, time.Date(1989, 3, 10, 0, 0, 0, 0, time.UTC)}, } for _, u := range users { _, err = stmt.ExecContext(ctx, u.Name, u.Age, u.Height, u.Awesome, u.Bday.Format("2006-01-02")) if err != nil { log.Fatal(err) } } // Query with parameters rows, err := db.QueryContext(ctx, " SELECT name, age, height, awesome, bday FROM users WHERE age > ? AND awesome = ? ", 20, true) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var u User if err := rows.Scan(&u.Name, &u.Age, &u.Height, &u.Awesome, &u.Bday); err != nil { log.Fatal(err) } fmt.Printf("%s is %d years old, %.2fm tall, bday: %s\n", u.Name, u.Age, u.Height, u.Bday.Format("2006-01-02")) } // Output: // Alice is 30 years old, 1.65m tall, bday: 1994-05-15 // Bob is 25 years old, 1.80m tall, bday: 1999-08-22 } ``` -------------------------------- ### Build with Dynamic DuckDB Library (macOS) Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Dynamically links to libduckdb on macOS. Ensure libduckdb is available and specify the library path. The executable must be run with DYLD_LIBRARY_PATH set. ```sh # On MacOS. CGO_ENABLED=1 CGO_LDFLAGS="-lduckdb -L/path/to/libs" go build -tags=duckdb_use_lib main.go DYLD_LIBRARY_PATH=/path/to/libs ./main ``` -------------------------------- ### Open In-Memory DuckDB Database Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Use this to create an in-memory instance of DuckDB. Ensure to close the database connection when done. ```go db, err := sql.Open("duckdb", "") def db.Close() ``` -------------------------------- ### Enable CGO for Cross-Compilation Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md When cross-compiling, explicitly enable CGO and specify the C cross-compiler to avoid build errors. ```bash CC={C cross compiler} CGO_ENABLED=1 {command} ``` -------------------------------- ### Enable DuckDB Arrow Build Tag Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Pass the `duckdb_arrow` tag to `go build` to enable the DuckDB Arrow Interface. This is required for using Arrow-related functionalities. ```sh go build -tags="duckdb_arrow" ``` -------------------------------- ### Creating DuckDB Type Information in Go Source: https://context7.com/marcboeker/go-duckdb/llms.txt Shows how to create TypeInfo objects for various DuckDB data types including primitives, DECIMAL, ENUM, LIST, STRUCT, MAP, ARRAY, and UNION. These are useful for UDFs or other advanced contexts. ```go package main import ( "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) func main() { // Primitive types varcharType, err := duckdb.NewTypeInfo(duckdb.TYPE_VARCHAR) if err != nil { log.Fatal(err) } fmt.Printf("VARCHAR type: %v\n", varcharType.InternalType()) intType, _ := duckdb.NewTypeInfo(duckdb.TYPE_INTEGER) bigintType, _ := duckdb.NewTypeInfo(duckdb.TYPE_BIGINT) boolType, _ := duckdb.NewTypeInfo(duckdb.TYPE_BOOLEAN) doubleType, _ := duckdb.NewTypeInfo(duckdb.TYPE_DOUBLE) timestampType, _ := duckdb.NewTypeInfo(duckdb.TYPE_TIMESTAMP) // DECIMAL type with precision and scale decimalType, err := duckdb.NewDecimalInfo(18, 4) // DECIMAL(18,4) if err != nil { log.Fatal(err) } fmt.Printf("DECIMAL type created\n") // ENUM type enumType, err := duckdb.NewEnumInfo("small", "medium", "large") if err != nil { log.Fatal(err) } fmt.Printf("ENUM type: %v\n", enumType.InternalType()) // LIST type listType, err := duckdb.NewListInfo(intType) if err != nil { log.Fatal(err) } fmt.Printf("LIST type: %v\n", listType.InternalType()) // STRUCT type nameEntry, _ := duckdb.NewStructEntry(varcharType, "name") ageEntry, _ := duckdb.NewStructEntry(intType, "age") structType, err := duckdb.NewStructInfo(nameEntry, ageEntry) if err != nil { log.Fatal(err) } fmt.Printf("STRUCT type: %v\n", structType.InternalType()) // MAP type mapType, err := duckdb.NewMapInfo(varcharType, intType) if err != nil { log.Fatal(err) } fmt.Printf("MAP type: %v\n", mapType.InternalType()) // ARRAY type (fixed size) arrayType, err := duckdb.NewArrayInfo(doubleType, 3) if err != nil { log.Fatal(err) } fmt.Printf("ARRAY type: %v\n", arrayType.InternalType()) // UNION type unionType, err := duckdb.NewUnionInfo( []duckdb.TypeInfo{intType, varcharType}, []string{"int_val", "str_val"}, ) if err != nil { log.Fatal(err) } fmt.Printf("UNION type: %v\n", unionType.InternalType()) // Use in UDF or other contexts _ = []duckdb.TypeInfo{varcharType, intType, bigintType, boolType, doubleType, timestampType, decimalType, enumType, listType, structType, mapType, arrayType, unionType} } ``` -------------------------------- ### Create and Use DuckDB Appender Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Obtain an Appender from a DuckDB connection to efficiently append rows to a specified table. Ensure the table exists before creating the appender. ```go c, err := duckdb.NewConnector("test.db", nil) deferr c.Close() conn, err := c.Connect(context.Background()) deferr conn.Close() // Obtain an appender from the connection. // NOTE: The table 'test_tbl' must exist in test.db. appender, err := NewAppenderFromConn(conn, "", "test_tbl") deferr appender.Close() err = appender.AppendRow(...) ``` -------------------------------- ### Bulk Updates with Query Appender in Go Source: https://context7.com/marcboeker/go-duckdb/llms.txt Use NewQueryAppender for bulk operations like UPDATE, DELETE, or MERGE INTO. It treats batched rows as a temporary table, enabling complex data modifications. Ensure correct type information and column names are provided for the appended data. ```go package main import ( "context" "database/sql" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" "math/big" ) func main() { connector, err := duckdb.NewConnector("", nil) if err != nil { log.Fatal(err) } defer connector.Close() conn, err := connector.Connect(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() db := sql.OpenDB(connector) defer db.Close() // Create and populate a table _, err = db.Exec(`CREATE TABLE products (id INTEGER PRIMARY KEY, name VARCHAR, price DECIMAL(10,2))`) if err != nil { log.Fatal(err) } _, err = db.Exec(`INSERT INTO products VALUES (1, 'Widget', 9.99), (2, 'Gadget', 19.99)`) if err != nil { log.Fatal(err) } // Create type info for the temporary table columns intType, _ := duckdb.NewTypeInfo(duckdb.TYPE_INTEGER) decimalType, _ := duckdb.NewDecimalInfo(10, 2) // Create query appender for bulk updates appender, err := duckdb.NewQueryAppender( conn, `UPDATE products SET price = appended_data.new_price FROM appended_data WHERE products.id = appended_data.product_id`, "appended_data", []duckdb.TypeInfo{intType, decimalType}, []string{"product_id", "new_price"}, ) if err != nil { log.Fatal(err) } // Batch price updates appender.AppendRow(int32(1), duckdb.Decimal{Width: 10, Scale: 2, Value: big.NewInt(1299)}) // 12.99 appender.AppendRow(int32(2), duckdb.Decimal{Width: 10, Scale: 2, Value: big.NewInt(2499)}) // 24.99 if err := appender.Close(); err != nil { log.Fatal(err) } // Verify updates rows, _ := db.Query(`SELECT id, name, price FROM products ORDER BY id`) defer rows.Close() for rows.Next() { var id int var name string var price float64 rows.Scan(&id, &name, &price) fmt.Printf("Product %d: %s - $%.2f\n", id, name, price) } // Output: // Product 1: Widget - $12.99 // Product 2: Gadget - $24.99 } ``` -------------------------------- ### Bulk Insert with Appender API in Go Source: https://context7.com/marcboeker/go-duckdb/llms.txt Use the Appender API for efficient bulk data insertion by batching rows. This is significantly faster than individual INSERT statements for large datasets. Ensure the appender is closed to flush remaining data and release resources. ```go package main import ( "context" "database/sql" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) func main() { // Create connector for direct connection access connector, err := duckdb.NewConnector("", nil) if err != nil { log.Fatal(err) } defer connector.Close() // Get a raw connection for the appender conn, err := connector.Connect(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() // Use sql.OpenDB for DDL operations db := sql.OpenDB(connector) defer db.Close() // Create table _, err = db.Exec(`CREATE TABLE users (id INTEGER, name VARCHAR, score DOUBLE)`) if err != nil { log.Fatal(err) } // Create appender for bulk inserts appender, err := duckdb.NewAppenderFromConn(conn, "", "users") if err != nil { log.Fatal(err) } // Append rows efficiently for i := 0; i < 1000; i++ { err := appender.AppendRow(int32(i), fmt.Sprintf("user_%d", i), float64(i)*1.5) if err != nil { log.Fatal(err) } } // Close flushes remaining data and releases resources if err := appender.Close(); err != nil { log.Fatal(err) } // Verify data var count int64 row := db.QueryRow(`SELECT COUNT(*) FROM users`) if err := row.Scan(&count); err != nil { log.Fatal(err) } fmt.Printf("Inserted %d rows\n", count) var avgScore float64 row = db.QueryRow(`SELECT AVG(score) FROM users`) if err := row.Scan(&avgScore); err != nil { log.Fatal(err) } fmt.Printf("Average score: %.2f\n", avgScore) // Output: // Inserted 1000 rows // Average score: 749.25 } ``` -------------------------------- ### Open DuckDB Database Connection Source: https://context7.com/marcboeker/go-duckdb/llms.txt Opens a connection to DuckDB using the standard database/sql interface. Supports in-memory and file-backed databases with configuration options. ```go package main import ( "context" "database/sql" "fmt" "log" _ "github.com/marcboeker/go-duckdb/v2" ) func main() { // In-memory database db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() // Verify connection if err := db.Ping(); err != nil { log.Fatal(err) } // File-backed database with configuration options dbPersistent, err := sql.Open("duckdb", "/path/to/mydb.db?access_mode=READ_WRITE&threads=4") if err != nil { log.Fatal(err) } defer dbPersistent.Close() // Query current settings var accessMode string row := db.QueryRowContext(context.Background(), "SELECT current_setting('access_mode')") if err := row.Scan(&accessMode); err != nil { log.Fatal(err) } fmt.Printf("Access mode: %s\n", accessMode) // Output: Access mode: automatic } ``` -------------------------------- ### Tagging a New Release Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Create a new Git tag and push it to the origin to mark a new release version. This is part of the process for releasing new DuckDB versions. ```git git tag git push origin ``` -------------------------------- ### Handle Special DuckDB Types: UUID, Interval, Decimal Source: https://context7.com/marcboeker/go-duckdb/llms.txt Demonstrates how to work with DuckDB's specialized types: UUID for unique identifiers, Interval for time durations, and Decimal for arbitrary-precision numbers. Requires importing the `math/big` package for programmatic Decimal creation. ```go package main import ( "database/sql" "fmt" "log" "math/big" "github.com/marcboeker/go-duckdb/v2" ) func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() // UUID type var uuid duckdb.UUID row := db.QueryRow(`SELECT uuid()`) if err := row.Scan(&uuid); err != nil { log.Fatal(err) } fmt.Printf("Generated UUID: %s\n", uuid.String()) // Interval type db.Exec(`CREATE TABLE events (name VARCHAR, duration INTERVAL)`) db.Exec(`INSERT INTO events VALUES ('meeting', INTERVAL '1 hour 30 minutes')`) db.Exec(`INSERT INTO events VALUES ('break', INTERVAL '15 minutes')`) rows, _ := db.Query(`SELECT name, duration FROM events`) defer rows.Close() for rows.Next() { var name string var interval duckdb.Interval rows.Scan(&name, &interval) fmt.Printf("Event: %s, Duration: %d months, %d days, %d micros\n", name, interval.Months, interval.Days, interval.Micros) } // Decimal type db.Exec(`CREATE TABLE prices (item VARCHAR, price DECIMAL(10, 2))`) db.Exec(`INSERT INTO prices VALUES ('Widget', 19.99), ('Gadget', 149.95)`) rows, _ = db.Query(`SELECT item, price FROM prices`) defer rows.Close() for rows.Next() { var item string var price duckdb.Decimal rows.Scan(&item, &price) fmt.Printf("Item: %s, Price: %s (float: %.2f)\n", item, price.String(), price.Float64()) } // Creating Decimal values programmatically decimal := duckdb.Decimal{ Width: 10, Scale: 2, Value: big.NewInt(9999), // Represents 99.99 } fmt.Printf("Custom decimal: %s\n", decimal.String()) // Output example: // Generated UUID: 550e8400-e29b-41d4-a716-446655440000 // Event: meeting, Duration: 0 months, 0 days, 5400000000 micros // Event: break, Duration: 0 months, 0 days, 900000000 micros // Item: Widget, Price: 19.99 (float: 19.99) // Item: Gadget, Price: 149.95 (float: 149.95) // Custom decimal: 99.99 } ``` -------------------------------- ### Add GCC to Windows PATH Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md This command adds the GCC bin directory to your system's PATH environment variable in PowerShell, allowing you to compile Go programs that depend on it. ```powershell $env:PATH = "C:\msys64\ucrt64\bin:$env:PATH" ``` -------------------------------- ### Close Database and Connection Resources Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Ensure all database and connection resources are properly closed to prevent memory leaks and unexpected state persistence. This includes the database handle, connections, rows, appenders, and connectors. ```go db, err := sql.Open("duckdb", "") deferr db.Close() conn, err := db.Conn(context.Background()) deferr conn.Close() rows, err := conn.QueryContext(context.Background(), "SELECT 42") // Alternatively, rows.Next() has to return false. rows.Close() appender, err := duckdb.NewAppenderFromConn(conn, "", "test") deferr appender.Close() c, err := duckdb.NewConnector("", nil) // Optional, if passed to sql.OpenDB. deferr c.Close() ``` -------------------------------- ### Open Persistent DuckDB Database Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Opens a persistent DuckDB database file. If the file does not exist, it will be created. Remember to close the connection. ```go db, err := sql.Open("duckdb", "/path/to/foo.db") def db.Close() ``` -------------------------------- ### Working with Complex Types in Go DuckDB Source: https://context7.com/marcboeker/go-duckdb/llms.txt Demonstrates scanning JSON arrays, JSON objects, STRUCTs, LISTs, and MAPs into Go variables using duckdb.Composite and duckdb.Map. Ensure the Go types match the expected DuckDB structure. ```go package main import ( "database/sql" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() // Working with JSON arrays var jsonArray duckdb.Composite[[]any] row := db.QueryRow(`SELECT json_array('apple', 'banana', 'cherry')`) if err := row.Scan(&jsonArray); err != nil { log.Fatal(err) } fmt.Printf("JSON Array: %v\n", jsonArray.Get()) // Working with JSON objects var jsonMap duckdb.Composite[map[string]any] row = db.QueryRow(`SELECT '{"name": "John", "age": 30, "skills": ["Go", "SQL"]}'::JSON`) if err := row.Scan(&jsonMap); err != nil { log.Fatal(err) } fmt.Printf("Name: %s\n", jsonMap.Get()["name"]) fmt.Printf("Skills: %v\n", jsonMap.Get()["skills"]) // Working with STRUCT types type Person struct { Name string `mapstructure:"name"` Age int `mapstructure:"age"` } var person duckdb.Composite[Person] row = db.QueryRow(`SELECT {'name': 'Alice', 'age': 25}::STRUCT(name VARCHAR, age INTEGER)`) if err := row.Scan(&person); err != nil { log.Fatal(err) } fmt.Printf("Person: %+v\n", person.Get()) // Working with LIST types var numbers duckdb.Composite[[]int64] row = db.QueryRow(`SELECT [1, 2, 3, 4, 5]`) if err := row.Scan(&numbers); err != nil { log.Fatal(err) } fmt.Printf("Numbers: %v\n", numbers.Get()) // Working with MAP types var mapData duckdb.Map row = db.QueryRow(`SELECT MAP {'a': 1, 'b': 2, 'c': 3}`) if err := row.Scan(&mapData); err != nil { log.Fatal(err) } fmt.Printf("Map: %v\n", mapData) // Output: // JSON Array: [apple banana cherry] // Name: John // Skills: [Go SQL] // Person: {Name:Alice Age:25} // Numbers: [1 2 3 4 5] // Map: map[a:1 b:2 c:3] } ``` -------------------------------- ### Obtain and Use DuckDB Arrow Interface Source: https://github.com/marcboeker/go-duckdb/blob/main/README.md Obtain an Arrow interface from a DuckDB connection using `NewArrowFromConn()`. Note that Arrow connections are not safe for concurrent use and do not support connection pooling. ```go c, err := duckdb.NewConnector("", nil) defer c.Close() conn, err := c.Connect(context.Background()) deferr conn.Close() // Obtain the Arrow from the connection. arrow, err := duckdb.NewArrowFromConn(conn) rdr, err := arrow.QueryContext(context.Background(), "SELECT * FROM generate_series(1, 10)") deferr rdr.Release() for rdr.Next() { // Process each record. } ``` -------------------------------- ### Register Custom Table Function in Go Source: https://context7.com/marcboeker/go-duckdb/llms.txt Implement and register a custom table function that generates a sequence of numbers. This function can be used in FROM clauses of SQL queries. Ensure the 'duckdb' package is imported. ```go package main import ( "context" "database/sql" "fmt" "log" "github.com/marcboeker/go-duckdb/v2" ) // Table source that generates a sequence of numbers type sequenceSource struct { start int64 end int64 current int64 } func (s *sequenceSource) ColumnInfos() []duckdb.ColumnInfo { t, _ := duckdb.NewTypeInfo(duckdb.TYPE_BIGINT) return []duckdb.ColumnInfo{{Name: "value", T: t}} } func (s *sequenceSource) Init() {} func (s *sequenceSource) FillRow(row duckdb.Row) (bool, error) { if s.current > s.end { return false, nil } err := duckdb.SetRowValue(row, 0, s.current) s.current++ return true, err } func (s *sequenceSource) Cardinality() *duckdb.CardinalityInfo { return &duckdb.CardinalityInfo{ Cardinality: uint(s.end - s.start + 1), Exact: true, } } func main() { db, err := sql.Open("duckdb", "") if err != nil { log.Fatal(err) } defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() // Define type info for function parameters intType, _ := duckdb.NewTypeInfo(duckdb.TYPE_BIGINT) // Create the table function tableFunc := duckdb.RowTableFunction{ Config: duckdb.TableFunctionConfig{ Arguments: []duckdb.TypeInfo{intType, intType}, // start, end }, BindArguments: func(named map[string]any, args ...any) (duckdb.RowTableSource, error) { return &sequenceSource{ start: args[0].(int64), end: args[1].(int64), current: args[0].(int64), }, nil }, } // Register the function if err := duckdb.RegisterTableUDF(conn, "sequence", tableFunc); err != nil { log.Fatal(err) } // Use the table function in a query rows, err := db.QueryContext(context.Background(), ` SELECT value, value * value AS square FROM sequence(1, 5) `) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var value, square int64 if err := rows.Scan(&value, &square); err != nil { log.Fatal(err) } fmt.Printf("Value: %d, Square: %d\n", value, square) } // Output: // Value: 1, Square: 1 // Value: 2, Square: 4 // Value: 3, Square: 9 // Value: 4, Square: 16 // Value: 5, Square: 25 } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.