### Install go-hdb Driver Source: https://github.com/sap/go-hdb/blob/main/README.md Use this command to install the go-hdb driver. It ensures you get the latest version. ```bash go get -u github.com/SAP/go-hdb/driver ``` -------------------------------- ### Benchmark Example Output Source: https://github.com/sap/go-hdb/blob/main/cmd/bulkbench/README.md This output demonstrates the results of running the bulk benchmarking tool with specific flags. It includes runtime information, system specifications, and detailed performance metrics for various benchmark configurations. ```text ./bulkbench.test -test.bench . -test.benchtime 10x -wait 5 2024/01/21 19:52:15 Runtime Info - GOMAXPROCS: 32 NumCPU: 32 DriverVersion 1.7.5 HDBVersion: 2.00.072.00.1690304772 goos: linux goarch: amd64 pkg: github.com/SAP/go-hdb/cmd/bulkbench cpu: AMD Ryzen 9 7950X 16-Core Processor Benchmark/sequential-1x100000-32 10 5285818357 ns/op 0.09177 avgsec/op 0.09784 maxsec/op 0.09167 medsec/op 0.08464 minsec/op Benchmark/sequential-10x10000-32 10 5277063412 ns/op 0.08814 avgsec/op 0.09130 maxsec/op 0.08851 medsec/op 0.08482 minsec/op Benchmark/sequential-100x1000-32 10 5411144659 ns/op 0.2250 avgsec/op 0.2429 maxsec/op 0.2240 medsec/op 0.2078 minsec/op Benchmark/sequential-1x1000000-32 10 6060119948 ns/op 0.8404 avgsec/op 0.9543 maxsec/op 0.8295 medsec/op 0.7983 minsec/op Benchmark/sequential-10x100000-32 10 5829117992 ns/op 0.6361 avgsec/op 0.6495 maxsec/op 0.6376 medsec/op 0.6168 minsec/op Benchmark/sequential-100x10000-32 10 6008048710 ns/op 0.8170 avgsec/op 0.8402 maxsec/op 0.8165 medsec/op 0.7955 minsec/op Benchmark/concurrent-1x100000-32 10 5273034242 ns/op 0.08714 avgsec/op 0.09937 maxsec/op 0.08703 medsec/op 0.07726 minsec/op Benchmark/concurrent-10x10000-32 10 5262679752 ns/op 0.06510 avgsec/op 0.07544 maxsec/op 0.06520 medsec/op 0.05459 minsec/op Benchmark/concurrent-100x1000-32 10 5347686066 ns/op 0.09944 avgsec/op 0.1087 maxsec/op 0.09856 medsec/op 0.09213 minsec/op Benchmark/concurrent-1x1000000-32 10 6039928922 ns/op 0.8263 avgsec/op 0.8493 maxsec/op 0.8222 medsec/op 0.7998 minsec/op Benchmark/concurrent-10x100000-32 10 5702794921 ns/op 0.5025 avgsec/op 0.5368 maxsec/op 0.5168 medsec/op 0.4340 minsec/op Benchmark/concurrent-100x10000-32 10 5663472687 ns/op 0.4123 avgsec/op 0.4419 maxsec/op 0.4131 medsec/op 0.3869 minsec/op PASS ``` -------------------------------- ### Opening a Connection via DSN (sql.Open) Source: https://context7.com/sap/go-hdb/llms.txt Demonstrates how to register the 'hdb' driver and open a database connection using a Data Source Name (DSN) string with sql.Open. Includes examples for both on-premise and HANA Cloud connections. ```APIDOC ## Opening a Connection via DSN (`sql.Open`) Register the `"hdb"` driver by importing the package and open a connection with a DSN URL. ```go package main import ( "database/sql" "log" _ "github.com/SAP/go-hdb/driver" // registers "hdb" driver ) func main() { // DSN format: hdb://:@: // Optional query parameters: databaseName, defaultSchema, timeout, pingInterval // TLS parameters: TLSRootCAFile, TLSServerName, TLSInsecureSkipVerify db, err := sql.Open("hdb", "hdb://myUser:myPassword@localhost:30015?databaseName=myTenant") if err != nil { log.Fatal(err) } defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } // HANA Cloud connection (TLS required) dbCloud, err := sql.Open("hdb", "hdb://myUser:myPassword@myinstance.hanacloud.ondemand.com:443?TLSServerName=myinstance.hanacloud.ondemand.com", ) if err != nil { log.Fatal(err) } defer dbCloud.Close() } ``` ``` -------------------------------- ### Handle HANA `DECIMAL` with `driver.Decimal` Source: https://context7.com/sap/go-hdb/llms.txt Use `driver.Decimal` to map HANA `DECIMAL` types to Go's `math/big.Rat` for arbitrary-precision rational arithmetic. This example shows writing and reading decimal values, including nullable ones. ```go package main import ( "database/sql" "fmt" "log" "math/big" "github.com/SAP/go-hdb/driver" ) func main() { db, _ := sql.Open("hdb", "hdb://user:password@host:30015") defer db.Close() db.Exec("create table decimal_demo (val decimal(28,10))") // Write: cast *big.Rat to *driver.Decimal in := (*driver.Decimal)(new(big.Rat).SetFrac( big.NewInt(355), big.NewInt(113), // pi approximation )) db.Exec("insert into decimal_demo values(?)", in) // Read: scan into driver.Decimal, then cast to *big.Rat var out driver.Decimal if err := db.QueryRow("select * from decimal_demo").Scan(&out); err != nil { log.Fatal(err) } fmt.Printf("val: %s\n", (*big.Rat)(&out).FloatString(10)) // val: 3.1415929204 // Nullable decimal var nullOut driver.NullDecimal db.QueryRow("select * from decimal_demo").Scan(&nullOut) if nullOut.Valid { fmt.Println((*big.Rat)(nullOut.Decimal).RatString()) } } ``` -------------------------------- ### Start Docker Containers for LDAP Tests Source: https://github.com/sap/go-hdb/blob/main/tests/ldap/README.md Execute this command from the tests/ldap/ directory to start the OpenLDAP and HANA Express containers in detached mode. ```bash cd tests/ldap docker compose up -d ``` -------------------------------- ### Configure LDAP Server Source: https://github.com/sap/go-hdb/blob/main/tests/ldap/README.md Run the LDAP configuration script inside the 'ldap' container after the services have started. ```bash docker compose exec ldap /ldapConfig.sh ``` -------------------------------- ### Set DSN Environment Variable Source: https://github.com/sap/go-hdb/blob/main/tests/x509/README.md Configure the GOHDBDSN environment variable to point to your HANA instance with an admin user for X.509 authentication setup. ```bash export GOHDBDSN="hdb://user:password@host:port" ``` -------------------------------- ### Custom LOB Scanning with `ScanLobBytes` Source: https://context7.com/sap/go-hdb/llms.txt Implement `sql.Scanner` for custom types like `BytesLob` to scan LOB data directly without using `*driver.Lob`. This example shows scanning into a `[]byte` wrapper. ```go package main import ( "database/sql" "fmt" "log" "github.com/SAP/go-hdb/driver" ) // BytesLob wraps []byte and implements sql.Scanner for BLOB columns. type BytesLob []byte func (b *BytesLob) Scan(src any) error { return driver.ScanLobBytes(src, (*[]byte)(b)) } func main() { db, _ := sql.Open("hdb", "hdb://user:password@host:30015") defer db.Close() db.Exec("create table scan_lob_demo (b1 blob, b2 blob)") tx, _ := db.Begin() tx.Exec("insert into scan_lob_demo values (?, ?)", []byte("hello"), []byte("world")) tx.Commit() stmt, _ := db.Prepare("select * from scan_lob_demo") defer stmt.Close() var b BytesLob var nb sql.Null[BytesLob] // nullable variant using generics if err := stmt.QueryRow().Scan(&b, &nb); err != nil { log.Fatal(err) } fmt.Println(string(b)) // hello fmt.Println(string(nb.V)) // world } ``` -------------------------------- ### Scan Rows into Go Structs with StructScanner Source: https://context7.com/sap/go-hdb/llms.txt Use `StructScanner` to map `sql.Rows` column names to exported struct fields. Fields can be mapped using `sql` struct tags or by implementing a `Tagger` interface. This example demonstrates scanning multiple rows and a single row. ```go package main import ( "database/sql" "fmt" "log" "github.com/SAP/go-hdb/driver" ) type Employee struct { ID int `sql:"EMP_ID"` FirstName string `sql:"FIRST_NAME"` LastName string `sql:"LAST_NAME"` Department string // column name = "DEPARTMENT" Ignored string `sql:"-"` // explicitly excluded } func main() { db, _ := sql.Open("hdb", "hdb://user:password@host:30015") defer db.Close() db.Exec("create table employees (emp_id integer, first_name nvarchar(50), last_name nvarchar(50), department nvarchar(50))") db.Exec("insert into employees values (?,?,?,?)", 1, "Ada", "Lovelace", "Engineering") db.Exec("insert into employees values (?,?,?,?)", 2, "Grace", "Hopper", "Computing") scanner, err := driver.NewStructScanner[Employee]() if err != nil { log.Fatal(err) } // Scan multiple rows rows, _ := db.Query("select * from employees") defer rows.Close() for rows.Next() { var emp Employee if err := scanner.Scan(rows, &emp); err != nil { log.Fatal(err) } fmt.Printf("ID=%d Name=%s %s Dept=%s\n", emp.ID, emp.FirstName, emp.LastName, emp.Department) } // ID=1 Name=Ada Lovelace Dept=Engineering // ID=2 Name=Grace Hopper Dept=Computing // Scan a single row (closes rows automatically) rows2, _ := db.Query("select * from employees where emp_id = 1") var emp Employee if err := scanner.ScanRow(rows2, &emp); err != nil { log.Fatal(err) } fmt.Printf("Single: %s\n", emp.FirstName) // Single: Ada } ``` -------------------------------- ### Run X.509 Tests Source: https://github.com/sap/go-hdb/blob/main/tests/x509/README.md Execute the Go tests for X.509 authentication by enabling the 'x509' build tag. ```go go test --tags x509 ``` -------------------------------- ### HANA Cloud Connection DSN Source: https://github.com/sap/go-hdb/blob/main/README.md Example of a Data Source Name (DSN) for connecting to HANA Cloud. It requires TLS and specifies the server name for SNI. ```plaintext "hdb://:@something.hanacloud.ondemand.com:443?TLSServerName=something.hanacloud.ondemand.com" ``` -------------------------------- ### Raw Connection Access with HDBVersion and DBConnectInfo Source: https://context7.com/sap/go-hdb/llms.txt Demonstrates how to access the underlying `driver.Conn` via `sql.Conn.Raw` to call go-hdb-specific methods like `HDBVersion()` and `DBConnectInfo()`. ```APIDOC ## `Conn.HDBVersion` and `Conn.DBConnectInfo` — Raw Connection Access Access the underlying `driver.Conn` via `sql.Conn.Raw` to call go-hdb-specific methods: `HDBVersion()` returns the connected HANA server version, and `DBConnectInfo` returns host/port routing information for a tenant database. ### Usage Example ```go package main import ( "context" "database/sql" "fmt" "log" "github.com/SAP/go-hdb/driver" ) func main() { db, _ := sql.Open("hdb", "hdb://user:password@host:30015") defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() if err := conn.Raw(func(driverConn any) error { hdbConn := driverConn.(driver.Conn) // HANA server version (e.g. "2.00.065.00") ver := hdbConn.HDBVersion() fmt.Printf("HANA version: %s\n", ver) fmt.Printf("Major: %d, Minor: %d, SPS: %d, Revision: %d\n", ver.Major(), ver.Minor(), ver.SPS(), ver.Revision()) // Database connection routing info dbName := hdbConn.DatabaseName() ci, err := hdbConn.DBConnectInfo(context.Background(), dbName) if err != nil { return err } fmt.Printf("Database: %s, IsConnected: %v\n", dbName, ci.IsConnected) return nil }); err != nil { log.Fatal(err) } } ``` ``` -------------------------------- ### Write and Read LOBs with `driver.Lob` Source: https://context7.com/sap/go-hdb/llms.txt Demonstrates writing LOB data from an `io.Reader` and reading LOB data into a `bytes.Buffer` or a file. LOB writes require an active transaction. ```go package main import ( "bytes" "database/sql" "log" "os" "strings" "github.com/SAP/go-hdb/driver" ) func main() { db, _ := sql.Open("hdb", "hdb://user:password@host:30015") defer db.Close() // Create test table db.Exec("create table lob_demo (data nclob)") // --- Write LOB from io.Reader --- tx, err := db.Begin() // LOBs require a transaction (no auto-commit) if err != nil { log.Fatal(err) } stmt, _ := tx.Prepare("insert into lob_demo values(?)") defer stmt.Close() content := strings.NewReader("Hello, HANA LOB world!") writeLob := driver.NewLob(content, nil) // reader=content, writer=nil if _, err := stmt.Exec(writeLob); err != nil { log.Fatal(err) } tx.Commit() // --- Read LOB into bytes.Buffer --- buf := new(bytes.Buffer) readLob := new(driver.Lob) readLob.SetWriter(buf) if err := db.QueryRow("select * from lob_demo").Scan(readLob); err != nil { log.Fatal(err) } // buf.String() == "Hello, HANA LOB world!" // --- Read LOB into a file --- outFile, _ := os.Create("output.txt") defer outFile.Close() fileLob := driver.NewLob(nil, outFile) db.QueryRow("select * from lob_demo").Scan(fileLob) } ``` -------------------------------- ### Connect to Tenant Database via System DB Redirect Source: https://context7.com/sap/go-hdb/llms.txt Use `Connector.WithDatabase` to create a new `*Connector` that connects to a named HANA tenant database. This method uses the system database redirect mechanism to resolve the correct host and port. Connect to the system DB first, then redirect to the tenant. ```go package main import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) func main() { // Connect to system DB first, then redirect to tenant base := driver.NewBasicAuthConnector("sysdb-host:30013", "SYSTEM", "password") tenantConnector := base.WithDatabase("MY_TENANT_DB") db := sql.OpenDB(tenantConnector) defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Execute Prepared Statement with Different User Credentials Source: https://context7.com/sap/go-hdb/llms.txt Use `WithUserSwitch` to inject a `SessionUser` into the context. This allows a prepared statement to be executed with different credentials within the same pooled connection, avoiding the need to open a new connection. ```go package main import ( "context" "database/sql" "log" "github.com/SAP/go-hdb/driver" ) func main() { connector := driver.NewBasicAuthConnector("host:30015", "admin", "adminPass") db := sql.OpenDB(connector) defer db.Close() db.Exec("create table audit_log (action nvarchar(100), performed_by nvarchar(50))") // Switch to a different user for audited inserts auditUser := &driver.SessionUser{Username: "audit_user", Password: "auditPass"} ctx := driver.WithUserSwitch(context.Background(), auditUser) stmt, err := db.PrepareContext(ctx, "insert into audit_log values (?, ?)") if err != nil { log.Fatal(err) } defer stmt.Close() if _, err := stmt.ExecContext(ctx, "DATA_EXPORT", "audit_user"); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Compile Benchmark Source: https://github.com/sap/go-hdb/blob/main/cmd/bulkbench/README.md Compiles the bulkbench benchmark executable. Ensure the GOHDBDSN environment variable is set to your database connection string. ```bash export GOHDBDSN="hdb://MyUser:MyPassword@host:port" go test -c ``` -------------------------------- ### Run Go LDAP Authentication Tests Source: https://github.com/sap/go-hdb/blob/main/tests/ldap/README.md Execute the Go tests with the 'ldap' build tag enabled. Ensure you are in the tests/ldap/ directory. ```go go test --tags ldap ``` -------------------------------- ### Bulk Insert Methods Source: https://context7.com/sap/go-hdb/llms.txt Demonstrates various methods for performing high-throughput bulk inserts using the `stmt.Exec` function with different argument types. ```APIDOC ## Bulk Insert — Extended Argument List and Iterator The driver supports high-throughput bulk inserts by passing a flat extended argument list, a callback function returning `driver.ErrEndOfRows`, or a Go `iter.Seq[[]any]` iterator to `stmt.Exec`. ### Method 1: Flat Extended Argument List This method involves creating a flat slice of `any` containing all the row data and passing it to `stmt.Exec`. ### Method 2: Callback Function A callback function can be provided to `stmt.Exec`. This function is called repeatedly to fetch rows until it returns `driver.ErrEndOfRows`. ### Method 3: `iter.Seq[[]any]` Iterator This method utilizes Go's standard iterator interface (`iter.Seq`) to provide rows to `stmt.Exec`. ### Method 4: `slices.Chunk` Iterator This method uses the `slices.Chunk` utility to create an iterator from a slice, suitable for bulk inserts. ``` -------------------------------- ### Open Connection via DSN Source: https://context7.com/sap/go-hdb/llms.txt Register the "hdb" driver by importing the package and open a connection with a DSN URL. Supports basic, X.509, JWT, and LDAP authentication. TLS parameters can be configured via query parameters. ```go package main import ( "database/sql" "log" _ "github.com/SAP/go-hdb/driver" // registers "hdb" driver ) func main() { // DSN format: hdb://:@: // Optional query parameters: databaseName, defaultSchema, timeout, pingInterval // TLS parameters: TLSRootCAFile, TLSServerName, TLSInsecureSkipVerify db, err := sql.Open("hdb", "hdb://myUser:myPassword@localhost:30015?databaseName=myTenant") if err != nil { log.Fatal(err) } defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } // HANA Cloud connection (TLS required) dbCloud, err := sql.Open("hdb", "hdb://myUser:myPassword@myinstance.hanacloud.ondemand.com:443?TLSServerName=myinstance.hanacloud.ondemand.com", ) if err != nil { log.Fatal(err) } defer dbCloud.Close() } ``` -------------------------------- ### Execute Benchmark with Options Source: https://github.com/sap/go-hdb/blob/main/cmd/bulkbench/README.md Runs all defined benchmarks multiple times, specifying the number of runs and filtering by specific BatchCount/BatchSize parameters. The test table is dropped and recreated before each benchmark. ```bash ./bulkbench.test -test.bench . -test.benchtime 10x -parameters "10x10000" ``` -------------------------------- ### Bulk Insert with Extended Argument List and Iterator in Go Source: https://context7.com/sap/go-hdb/llms.txt Demonstrates high-throughput bulk inserts using a flat extended argument list, a callback function, or a Go iter.Seq iterator with `stmt.Exec`. Ensure the driver is imported. ```go package main import ( "context" "database/sql" "fmt" "iter" "log" "slices" "github.com/SAP/go-hdb/driver" ) func main() { connector, _ := driver.NewDSNConnector("hdb://user:password@host:30015") db := sql.OpenDB(connector) defer db.Close() if _, err := db.Exec("create table bulk_demo (i integer, f double)"); err != nil { log.Fatal(err) } defer db.Exec("drop table bulk_demo") stmt, err := db.PrepareContext(context.Background(), "insert into bulk_demo values (?, ?)") if err != nil { log.Fatal(err) } defer stmt.Close() // Method 1: flat extended argument list (1000 rows at once) const n = 1000 args := make([]any, n*2) for i := range n { args[i*2], args[i*2+1] = i, float64(i) } if _, err := stmt.Exec(args...); err != nil { log.Fatal(err) } // Method 2: callback function i := 0 if _, err := stmt.Exec(func(args []any) error { if i >= n { return driver.ErrEndOfRows // signals end of bulk input } args[0], args[1] = i, float64(i) i++ return nil }); err != nil { log.Fatal(err) } // Method 3: iter.Seq[[]any] (standard Go iterator) var myIter iter.Seq[[]any] = func(yield func([]any) bool) { for j := range n { if !yield([]any{j, float64(j)}) { return } } } if _, err := stmt.Exec(myIter); err != nil { log.Fatal(err) } // Method 4: slices.Chunk iterator if _, err := stmt.Exec(slices.Chunk(args, 2)); err != nil { log.Fatal(err) } var count int db.QueryRow("select count(*) from bulk_demo").Scan(&count) fmt.Println(count) // 4000 } ``` -------------------------------- ### Run Unit Tests Source: https://github.com/sap/go-hdb/blob/main/README.md Execute only the driver's unit tests by using the 'unit' Go build tag. This does not require a HANA Database server. ```bash go test --tags unit ``` -------------------------------- ### Run All Benchmarks Source: https://github.com/sap/go-hdb/blob/main/cmd/bulkbench/README.md Executes all available benchmarks defined in the test suite, running each benchmark ten times. This provides a comprehensive performance overview across different configurations. ```bash ./bulkbench.test -test.bench . -test.benchtime 10x ``` -------------------------------- ### Programmatic Basic Auth Connector Source: https://context7.com/sap/go-hdb/llms.txt Create a *Connector for username/password authentication with full programmatic control over connection parameters. Supports setting timeout, ping interval, default schema, bulk size, fetch size, and TLS configuration. ```go package main import ( "database/sql" "log" "time" "github.com/SAP/go-hdb/driver" ) func main() { connector := driver.NewBasicAuthConnector("myhost:30015", "myUser", "myPassword") // Configure connection parameters connector.SetTimeout(60 * time.Second) connector.SetPingInterval(30 * time.Second) connector.SetDefaultSchema("MY_SCHEMA") connector.SetBulkSize(5000) connector.SetFetchSize(256) // Optional: TLS with custom root CA if err := connector.SetTLS("myhost", false, "/path/to/root.pem"); err != nil { log.Fatal(err) } // Optional: password refresh callback for rotation scenarios connector.SetRefreshPassword(func() (string, bool) { newPassword := fetchNewPasswordFromVault() return newPassword, true }) db := sql.OpenDB(connector) defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } func fetchNewPasswordFromVault() string { return "rotated-password" } ``` -------------------------------- ### Stored Procedure Calls with Output Parameters in Go Source: https://context7.com/sap/go-hdb/llms.txt Shows how to call stored procedures with scalar and table output parameters using `sql.Named` and `sql.Out`. Procedures with table output parameters require `db.Prepare` and keeping the statement open while iterating `sql.Rows`. ```go package main import ( "database/sql" "fmt" "log" "github.com/SAP/go-hdb/driver" ) func main() { connector, _ := driver.NewDSNConnector("hdb://user:password@host:30015") db := sql.OpenDB(connector) defer db.Close() // --- Scalar output parameter --- db.Exec(`create procedure greet_proc (out message nvarchar(1024)) language SQLSCRIPT as begin message := 'Hello World!'; end`) var msg string db.Exec("call greet_proc(?)", sql.Named("MESSAGE", sql.Out{Dest: &msg})) fmt.Println(msg) // Hello World! // --- Table output parameter (must use Prepare) --- db.Exec("create type StrTableType as table (x nvarchar(256))") db.Exec(`create procedure str_table_proc (out t StrTableType) language SQLSCRIPT as begin create local temporary table #t like StrTableType; insert into #t values('row one'); insert into #t values('row two'); t = select * from #t; drop table #t; end`) stmt, err := db.Prepare("call str_table_proc(?)") if err != nil { log.Fatal(err) } defer stmt.Close() var tableRows sql.Rows if _, err := stmt.Exec(sql.Named("T", sql.Out{Dest: &tableRows})); err != nil { log.Fatal(err) } for tableRows.Next() { var x string tableRows.Scan(&x) fmt.Println(x) // row one, row two } } ``` -------------------------------- ### Run Integration Tests Source: https://github.com/sap/go-hdb/blob/main/README.md Run all driver integration tests. Ensure the GOHDBDSN environment variable is set with valid connection details. ```bash go test ``` -------------------------------- ### Generate X.509 Certificates Source: https://github.com/sap/go-hdb/blob/main/tests/x509/README.md Run this script to generate a self-signed root CA and sign client certificates for testing X.509 authentication. ```bash cd tests/x509 bash x509Config.sh ``` -------------------------------- ### Lob Streaming (Read and Write) Source: https://context7.com/sap/go-hdb/llms.txt Demonstrates how to stream large object data (BLOB/CLOB/NCLOB) between Go io.Reader/io.Writer and HANA. LOB writes must be performed within a transaction. ```APIDOC ## Lob — Large Object Streaming (Read and Write) `Lob` streams large object data between Go `io.Reader`/`io.Writer` and HANA `BLOB`/`CLOB`/`NCLOB` fields. LOB writes must always be inside a transaction. ### Usage **Writing LOB from io.Reader:** ```go content := strings.NewReader("Hello, HANA LOB world!") writeLob := driver.NewLob(content, nil) // reader=content, writer=nil // Use stmt.Exec(writeLob) within a transaction ``` **Reading LOB into bytes.Buffer:** ```go buf := new(bytes.Buffer) readLob := new(driver.Lob) readLob.SetWriter(buf) // Use db.QueryRow(...).Scan(readLob) ``` **Reading LOB into a file:** ```go outFile, _ := os.Create("output.txt") fileLob := driver.NewLob(nil, outFile) // Use db.QueryRow(...).Scan(fileLob) ``` ``` -------------------------------- ### Execute Benchmark with Wait Time Source: https://github.com/sap/go-hdb/blob/main/cmd/bulkbench/README.md Executes benchmarks with a specified delay before each run to reduce database pressure. This is useful for mitigating load on the database during testing. ```bash ./bulkbench.test -test.bench . -test.benchtime 10x -wait 5 ``` -------------------------------- ### Create Test Table Source: https://github.com/sap/go-hdb/blob/main/cmd/bulkbench/README.md Defines a column table with integer and double columns for testing purposes. ```sql create column table (id integer, field double) ``` -------------------------------- ### Enable Extended Driver Statistics with Prometheus Source: https://context7.com/sap/go-hdb/llms.txt Utilize `driver.OpenDB` and `DB.ExStats()` to track per-database connection statistics. Integrate with Prometheus collectors to expose HANA-specific metrics, including standard SQL DB pool statistics, driver-level stats, and per-statement SQL timing histograms. ```go package main import ( "fmt" "log" "net/http" "time" "github.com/SAP/go-hdb/driver" drivercollectors "github.com/SAP/go-hdb/prometheus/collectors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { connector, err := driver.NewDSNConnector("hdb://user:password@host:30015") if err != nil { log.Fatal(err) } // Use driver.OpenDB (not sql.OpenDB) for extended statistics db := driver.OpenDB(connector) defer db.Close() const dbLabel = "production" // 1. Standard sql.DB pool statistics prometheus.MustRegister(collectors.NewDBStatsCollector(db.DB, dbLabel)) // 2. go-hdb driver-level stats (auth time, read/write bytes, etc.) prometheus.MustRegister(drivercollectors.NewDriverStatsCollector(connector.NativeDriver(), dbLabel)) // 3. Extended per-DB stats (per-statement SQL timing histograms) prometheus.MustRegister(drivercollectors.NewDBExStatsCollector(db, dbLabel)) // Expose metrics endpoint http.Handle("/metrics", promhttp.Handler()) go http.ListenAndServe(":9090", nil) // Inspect stats directly stats := db.ExStats() fmt.Printf("Open connections: %d\n", stats.OpenConnections) fmt.Printf("Open transactions: %d\n", stats.OpenTransactions) fmt.Printf("Bytes read: %d\n", stats.ReadBytes) fmt.Printf("Time unit: %s\n", stats.TimeUnit) time.Sleep(30 * time.Second) // metrics accessible at http://localhost:9090/metrics } ``` -------------------------------- ### SQL Script Scanning with sqlscript.ScanFunc Source: https://context7.com/sap/go-hdb/llms.txt Demonstrates how to use the `sqlscript.ScanFunc` to tokenize multi-statement SQL scripts separated by semicolons. This is useful for executing DDL scripts statement by statement. ```APIDOC ## `sqlscript` Package — SQL Script Scanning The `sqlscript` package provides a `bufio.SplitFunc` that tokenizes multi-statement SQL scripts separated by semicolons, suitable for executing DDL scripts statement by statement. ### Usage Example ```go package main import ( "bufio" "database/sql" "log" "strings" "github.com/SAP/go-hdb/driver" "github.com/SAP/go-hdb/sqlscript" ) func main() { ddl := ` -- Create schema objects CREATE LOCAL TEMPORARY TABLE #staging (id INTEGER, name VARCHAR(100)); INSERT INTO #STAGING VALUES (1, 'Alice'); INSERT INTO #STAGING VALUES (2, 'Bob'); DROP TABLE #staging ` connector, _ := driver.NewDSNConnector("hdb://user:password@host:30015") db := sql.OpenDB(connector) defer db.Close() scanner := bufio.NewScanner(strings.NewReader(ddl)) // ScanFunc splits on DefaultSeparator (";"), second arg=true preserves comments scanner.Split(sqlscript.ScanFunc(sqlscript.DefaultSeparator, true)) for scanner.Scan() { stmt := scanner.Text() if _, err := db.Exec(stmt); err != nil { log.Fatalf("failed to execute: %q\nerror: %v", stmt, err) } } if err := scanner.Err(); err != nil { log.Fatal(err) } } ``` ``` -------------------------------- ### Stored Procedure Calls Source: https://context7.com/sap/go-hdb/llms.txt Explains how to call stored procedures with scalar and table output parameters using `sql.Named` and `sql.Out`. ```APIDOC ## Stored Procedure Calls — Scalar and Table Output Parameters Use `sql.Named` with `sql.Out` to bind output parameters. Procedures with table output parameters must use `db.Prepare` and keep the statement open while iterating `sql.Rows`. ### Scalar Output Parameter To handle scalar output parameters, use `db.Exec` with `sql.Named` and `sql.Out` to specify the destination variable. ### Table Output Parameter For procedures with table output parameters, `db.Prepare` must be used. The returned `sql.Rows` should be kept open and iterated over to retrieve the table data. ``` -------------------------------- ### Connector.WithDatabase Source: https://context7.com/sap/go-hdb/llms.txt Creates a new Connector that automatically resolves the correct host/port for a named HANA tenant database using the system database redirect mechanism. ```APIDOC ## Connector.WithDatabase — Tenant Database Connection `WithDatabase` creates a new `*Connector` that automatically resolves the correct host/port for a named HANA tenant database via the system database redirect mechanism. ```go package main import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) func main() { // Connect to system DB first, then redirect to tenant base := driver.NewBasicAuthConnector("sysdb-host:30013", "SYSTEM", "password") tenantConnector := base.WithDatabase("MY_TENANT_DB") db := sql.OpenDB(tenantConnector) defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } ``` ``` -------------------------------- ### Tokenize SQL Scripts with sqlscript.ScanFunc Source: https://context7.com/sap/go-hdb/llms.txt Use sqlscript.ScanFunc to split multi-statement SQL scripts by semicolons. The second argument 'true' preserves comments. ```go package main import ( "bufio" "database/sql" "log" "strings" "github.com/SAP/go-hdb/driver" "github.com/SAP/go-hdb/sqlscript" ) func main() { ddl := ` -- Create schema objects CREATE LOCAL TEMPORARY TABLE #staging (id INTEGER, name VARCHAR(100)); INSERT INTO #STAGING VALUES (1, 'Alice'); INSERT INTO #STAGING VALUES (2, 'Bob'); DROP TABLE #staging ` connector, _ := driver.NewDSNConnector("hdb://user:password@host:30015") db := sql.OpenDB(connector) defer db.Close() scanner := bufio.NewScanner(strings.NewReader(ddl)) // ScanFunc splits on DefaultSeparator (";"), second arg=true preserves comments scanner.Split(sqlscript.ScanFunc(sqlscript.DefaultSeparator, true)) for scanner.Scan() { stmt := scanner.Text() if _, err := db.Exec(stmt); err != nil { log.Fatalf("failed to execute: %q\nerror: %v", stmt, err) } } if err := scanner.Err(); err != nil { log.Fatal(err) } } ``` -------------------------------- ### NewX509AuthConnectorByFiles Source: https://context7.com/sap/go-hdb/llms.txt Creates a Connector for mutual TLS (client certificate) authentication using PEM files on disk. The driver automatically reloads rotated certificates on authentication failure. ```APIDOC ## NewX509AuthConnectorByFiles — X.509 Client Certificate Authentication `NewX509AuthConnectorByFiles` creates a `*Connector` for mutual TLS (client certificate) authentication using PEM files on disk. The driver automatically reloads rotated certificates on authentication failure. ```go package main import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) func main() { connector, err := driver.NewX509AuthConnectorByFiles( "myhost:39013", "/etc/certs/client.crt.pem", "/etc/certs/client.key.pem", // not password-encrypted ) if err != nil { log.Fatal(err) } // TLS server certificate verification if err := connector.SetTLS("myhost", false, "/etc/certs/rootCA.pem"); err != nil { log.Fatal(err) } db := sql.OpenDB(connector) defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } ``` ``` -------------------------------- ### Access Raw Connection Info with Conn.Raw Source: https://context7.com/sap/go-hdb/llms.txt Access the underlying driver.Conn via sql.Conn.Raw to call go-hdb-specific methods like HDBVersion() and DBConnectInfo(). ```go package main import ( "context" "database/sql" "fmt" "log" "github.com/SAP/go-hdb/driver" ) func main() { db, _ := sql.Open("hdb", "hdb://user:password@host:30015") defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { log.Fatal(err) } defer conn.Close() if err := conn.Raw(func(driverConn any) error { hdbConn := driverConn.(driver.Conn) // HANA server version (e.g. "2.00.065.00") ver := hdbConn.HDBVersion() fmt.Printf("HANA version: %s\n", ver) fmt.Printf("Major: %d, Minor: %d, SPS: %d, Revision: %d\n", ver.Major(), ver.Minor(), ver.SPS(), ver.Revision()) // Database connection routing info dbName := hdbConn.DatabaseName() ci, err := hdbConn.DBConnectInfo(context.Background(), dbName) if err != nil { return err } fmt.Printf("Database: %s, IsConnected: %v\n", dbName, ci.IsConnected) return nil }); err != nil { log.Fatal(err) } } ``` -------------------------------- ### HANA DECIMAL Type Binding Source: https://context7.com/sap/go-hdb/llms.txt Explains the binding of HANA's `DECIMAL` type to Go's `math/big.Rat` for arbitrary-precision rational arithmetic, allowing seamless casting between `*driver.Decimal` and `*big.Rat`. ```APIDOC ## Decimal / NullDecimal — HANA DECIMAL Type `Decimal` maps the HANA `DECIMAL` database type to Go's `math/big.Rat` for arbitrary-precision rational arithmetic. Cast between `*driver.Decimal` and `*big.Rat` freely. ### Usage **Writing DECIMAL:** ```go // Cast *big.Rat to *driver.Decimal for insertion in := (*driver.Decimal)(new(big.Rat).SetFrac(big.NewInt(355), big.NewInt(113))) // Use db.Exec("insert into ... values(?)", in) ``` **Reading DECIMAL:** ```go var out driver.Decimal // Use db.QueryRow(...).Scan(&out) // Cast back to *big.Rat: (*big.Rat)(&out) ``` **Nullable DECIMAL:** ```go var nullOut driver.NullDecimal // Use db.QueryRow(...).Scan(&nullOut) // Access value via nullOut.Decimal if nullOut.Valid is true ``` ``` -------------------------------- ### Capture Statement Metadata with WithStmtMetadata Source: https://context7.com/sap/go-hdb/llms.txt Use `WithStmtMetadata` to inject a metadata capture value into a context for `PrepareContext`. This populates `StmtMetadata` with parameter and result column types after preparation, allowing dynamic argument building. ```go package main import ( "context" "database/sql" "fmt" "log" "reflect" "github.com/SAP/go-hdb/driver" ) func main() { db, _ := sql.Open("hdb", "hdb://user:password@host:30015") defer db.Close() db.Exec(`create procedure meta_proc (out message nvarchar(1024)) language SQLSCRIPT as begin message := 'Hello World!'; end`) // Attach metadata capture to context var stmtMeta driver.StmtMetadata ctx := driver.WithStmtMetadata(context.Background(), &stmtMeta) stmt, err := db.PrepareContext(ctx, "call meta_proc(?)") if err != nil { log.Fatal(err) } defer stmt.Close() // Build exec args dynamically from column metadata columnTypes := stmtMeta.ColumnTypes() args := make([]any, len(columnTypes)) for i, ct := range columnTypes { dest := reflect.New(ct.ScanType()).Interface() args[i] = sql.Named(ct.Name(), sql.Out{Dest: dest}) fmt.Printf("Column: name=%s type=%s scanType=%s\n", ct.Name(), ct.DatabaseTypeName(), ct.ScanType()) } // Column: name=MESSAGE type=NVARCHAR scanType=sql.NullString if _, err := stmt.Exec(args...); err != nil { log.Fatal(err) } namedArg := args[0].(sql.NamedArg) out := namedArg.Value.(sql.Out).Dest.(*sql.NullString) fmt.Println(out.String) // Hello World! } ``` -------------------------------- ### X.509 Client Certificate Authentication Source: https://context7.com/sap/go-hdb/llms.txt Use `NewX509AuthConnectorByFiles` for mutual TLS authentication with PEM files. The driver automatically reloads rotated certificates on authentication failure. Configure TLS server certificate verification using `SetTLS`. ```go package main import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) func main() { connector, err := driver.NewX509AuthConnectorByFiles( "myhost:39013", "/etc/certs/client.crt.pem", "/etc/certs/client.key.pem", // not password-encrypted ) if err != nil { log.Fatal(err) } // TLS server certificate verification if err := connector.SetTLS("myhost", false, "/etc/certs/rootCA.pem"); err != nil { log.Fatal(err) } db := sql.OpenDB(connector) defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } ``` -------------------------------- ### NewBasicAuthConnector — Programmatic Basic Auth Connector Source: https://context7.com/sap/go-hdb/llms.txt Creates a `*Connector` for username/password authentication, offering programmatic control over connection parameters without needing to construct a DSN string. ```APIDOC ## `NewBasicAuthConnector` — Programmatic Basic Auth Connector `NewBasicAuthConnector` creates a `*Connector` for username/password authentication, providing full programmatic control over all connection parameters without constructing a DSN string. ```go package main import ( "database/sql" "log" "time" "github.com/SAP/go-hdb/driver" ) func main() { connector := driver.NewBasicAuthConnector("myhost:30015", "myUser", "myPassword") // Configure connection parameters connector.SetTimeout(60 * time.Second) connector.SetPingInterval(30 * time.Second) connector.SetDefaultSchema("MY_SCHEMA") connector.SetBulkSize(5000) connector.SetFetchSize(256) // Optional: TLS with custom root CA if err := connector.SetTLS("myhost", false, "/path/to/root.pem"); err != nil { log.Fatal(err) } // Optional: password refresh callback for rotation scenarios connector.SetRefreshPassword(func() (string, bool) { newPassword := fetchNewPasswordFromVault() return newPassword, true }) db := sql.OpenDB(connector) defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } func fetchNewPasswordFromVault() string { return "rotated-password" } ``` ```