driver

package
v1.12.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 10, 2024 License: Apache-2.0 Imports: 40 Imported by: 97

Documentation

Overview

Package driver is a native Go SAP HANA driver implementation for the database/sql package. For the SAP HANA SQL Command Network Protocol Reference please see: https://help.sap.com/viewer/7e4aba181371442d9e4395e7ff71b777/2.0.03/en-US/9b9d8c894343424fac157c96dcb0a592.html

Example
package main

import (
	"database/sql"
	"log"

	// Register hdb driver.
	_ "github.com/SAP/go-hdb/driver"
)

const (
	driverName = "hdb"
	hdbDsn     = "hdb://user:password@host:port"
)

func main() {
	db, err := sql.Open(driverName, hdbDsn)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
}
Output:

Example (BulkInsert)

ExampleBulkInsert inserts 2000 rows into a database table:

1000 rows are inserted via an extended argument list and
1000 rows are inserted with the help of a argument function
package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	const numRow = 1000 // Number of rows to be inserted into table.

	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	tableName := driver.RandomIdentifier("table_")

	// Create table.
	if _, err := db.Exec(fmt.Sprintf("create table %s (i integer, f double)", tableName)); err != nil {
		log.Fatal(err)
	}

	// Prepare statement.
	stmt, err := db.PrepareContext(context.Background(), fmt.Sprintf("insert into %s values (?, ?)", tableName))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	// Bulk insert via 'extended' argument list.
	args := make([]any, numRow*2)
	for i := range numRow {
		args[i*2], args[i*2+1] = i, float64(i)
	}
	if _, err := stmt.Exec(args...); err != nil {
		log.Fatal(err)
	}

	// Bulk insert via function.
	i := 0
	if _, err := stmt.Exec(func(args []any) error {
		if i >= numRow {
			return driver.ErrEndOfRows
		}
		args[0], args[1] = i, float64(i)
		i++
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	// Select number of inserted rows.
	var count int
	if err := db.QueryRow(fmt.Sprintf("select count(*) from %s", tableName)).Scan(&count); err != nil {
		log.Fatal(err)
	}
	fmt.Print(count)

	// Drop table.
	if _, err := db.Exec(fmt.Sprintf("drop table %s", tableName)); err != nil {
		log.Fatal(err)
	}

}
Output:

2000
Example (BulkInsertViaIterator)

ExampleBulkInsert inserts 3000 rows into a database table:

1000 rows are inserted via a slices chunc iterator
1000 rows are inserted via a custom iterator
package main

import (
	"context"
	"database/sql"
	"fmt"
	"iter"
	"log"
	"slices"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	const numRow = 1000 // Number of rows to be inserted into table.

	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	tableName := driver.RandomIdentifier("table_")

	// Create table.
	if _, err := db.Exec(fmt.Sprintf("create table %s (i integer, f double)", tableName)); err != nil {
		log.Fatal(err)
	}

	// Prepare statement.
	stmt, err := db.PrepareContext(context.Background(), fmt.Sprintf("insert into %s values (?, ?)", tableName))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	// Bulk insert via slices chunc iterator.
	args := make([]any, numRow*2)
	for i := range numRow {
		args[i*2], args[i*2+1] = i, float64(i)
	}

	if _, err := stmt.Exec(slices.Chunk(args, 2)); err != nil {
		log.Fatal(err)
	}

	// Bulk insert via custom iterator.
	var myIter iter.Seq[[]any] = func(yield func([]any) bool) {
		for i := range numRow {
			if !yield([]any{i, float64(i)}) {
				return
			}
		}
	}

	if _, err := stmt.Exec(myIter); err != nil {
		log.Fatal(err)
	}

	// Select number of inserted rows.
	var count int
	if err := db.QueryRow(fmt.Sprintf("select count(*) from %s", tableName)).Scan(&count); err != nil {
		log.Fatal(err)
	}
	fmt.Print(count)

	// Drop table.
	if _, err := db.Exec(fmt.Sprintf("drop table %s", tableName)); err != nil {
		log.Fatal(err)
	}

}
Output:

2000
Example (CallProcedure)
//go:build !unit

package main

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

/*
callProcedureSimpleOut creates a stored procedure with one output parameter and executes it.
*/
func callProcedureSimpleOut() string {
	const procedureOut = `create procedure %s (out message nvarchar(1024))
language SQLSCRIPT as
begin
    message := 'Hello World!';
end
`

	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	procedureName := driver.RandomIdentifier("procOut_")

	if _, err := db.Exec(fmt.Sprintf(procedureOut, procedureName)); err != nil { // Create stored procedure.
		log.Fatal(err)
	}

	var out string
	if _, err := db.Exec(fmt.Sprintf("call %s(?)", procedureName), sql.Named("MESSAGE", sql.Out{Dest: &out})); err != nil {
		log.Fatal(err)
	}
	return out
}

/*
callProcedureTableOut creates a stored procedure with one table output parameter and executes it
making use of sql.Rows scan parameters.
Stored procedures with table output parameters must be prepared by sql.Prepare as the statement needs to
be kept open until the output table values are retrieved.
*/
func callProcedureTableOut() (output []string) {
	const procedureTable = `create procedure %[1]s (out t %[2]s)
language SQLSCRIPT as
begin
  create local temporary table #test like %[2]s;
  insert into #test values('Hello, 世界');
  insert into #test values('SAP HANA');
  insert into #test values('Go driver');
  t = select * from #test;
  drop table #test;
end
`
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	tableType := driver.RandomIdentifier("TableType_")
	procedureName := driver.RandomIdentifier("ProcTable_")

	if _, err := db.Exec(fmt.Sprintf("create type %s as table (x nvarchar(256))", tableType)); err != nil { // Create table type.
		log.Fatal(err)
	}

	if _, err := db.Exec(fmt.Sprintf(procedureTable, procedureName, tableType)); err != nil { // Create stored procedure.
		log.Fatal(err)
	}

	var tableRows sql.Rows // Scan variable of table output parameter.

	// Call stored procedure via prepare.
	stmt, err := db.Prepare(fmt.Sprintf("call %s(?)", procedureName))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	if _, err := stmt.Exec(sql.Named("T", sql.Out{Dest: &tableRows})); err != nil {
		log.Fatal(err)
	}

	for tableRows.Next() {
		var x string
		if err := tableRows.Scan(&x); err != nil {
			log.Fatal(err)
		}
		output = append(output, x)
	}
	if err := tableRows.Err(); err != nil {
		log.Fatal(err)
	}
	return output
}

/*
callProcedureTableIn creates a stored procedure with one table input and one table output parameter
and executes it making use of sql.Rows scan parameters.
Stored procedure input parameters need to refer by name to an existing database table or temporary table.
Stored procedures with table output parameters must be prepared by sql.Prepare as the statement needs to
be kept open until the output table values are retrieved.
*/
func callProcedureTableIn() (output []string) {
	const procedureTable = `create procedure %[1]s (in t1 %[2]s, out t2 %[2]s)
language SQLSCRIPT as
begin
  t2 = select * from :t1;
end
`
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	tableType := driver.RandomIdentifier("TableType_")
	tableName := driver.RandomIdentifier("#TableIn_") // local temp table needs to start with "#"
	procedureName := driver.RandomIdentifier("ProcTable_")

	if _, err := db.Exec(fmt.Sprintf("create type %s as table (x nvarchar(256))", tableType)); err != nil { // Create table type.
		log.Fatal(err)
	}

	if _, err := db.Exec(fmt.Sprintf(procedureTable, procedureName, tableType)); err != nil { // Create stored procedure.
		log.Fatal(err)
	}

	if _, err := db.Exec(fmt.Sprintf("create local temporary table %s like %s", tableName, tableType)); err != nil {
		log.Fatal(err)
	}

	if _, err := db.Exec(fmt.Sprintf("insert into %s values (?)", tableName), "Hello, 世界", "SAP HANA", "Go driver"); err != nil {
		log.Fatal(err)
	}

	var tableRows sql.Rows // Scan variable of table output parameter.

	// Call stored procedure via prepare.
	stmt, err := db.Prepare(fmt.Sprintf("call %s(%s, ?)", procedureName, tableName))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	if _, err := stmt.Exec(sql.Named("T", sql.Out{Dest: &tableRows})); err != nil {
		log.Fatal(err)
	}

	for tableRows.Next() {
		var x string
		if err := tableRows.Scan(&x); err != nil {
			log.Fatal(err)
		}
		output = append(output, x)
	}
	if err := tableRows.Err(); err != nil {
		log.Fatal(err)
	}
	return output
}

func main() {
	fmt.Println(callProcedureSimpleOut())
	for _, s := range callProcedureTableOut() {
		fmt.Println(s)
	}
	for _, s := range callProcedureTableIn() {
		fmt.Println(s)
	}

}
Output:

Hello World!
Hello, 世界
SAP HANA
Go driver
Hello, 世界
SAP HANA
Go driver

Index

Examples

Constants

View Source
const (
	DSNDatabaseName  = "databaseName"  // Tenant database name.
	DSNDefaultSchema = "defaultSchema" // Database default schema.
	DSNTimeout       = "timeout"       // Driver side connection timeout in seconds.
	DSNPingInterval  = "pingInterval"  // Connection ping interval in seconds.
)

DSN parameters.

View Source
const (
	DSNTLSRootCAFile         = "TLSRootCAFile"         // Path,- filename to root certificate(s).
	DSNTLSServerName         = "TLSServerName"         // ServerName to verify the hostname.
	DSNTLSInsecureSkipVerify = "TLSInsecureSkipVerify" // Controls whether a client verifies the server's certificate chain and host name.
)

DSN TLS parameters. For more information please see https://golang.org/pkg/crypto/tls/#Config. For more flexibility in TLS configuration please see driver.Connector.

View Source
const (
	HdbWarning    = 0
	HdbError      = 1
	HdbFatalError = 2
)

HDB error levels.

View Source
const DriverName = "hdb"

DriverName is the driver name to use with sql.Open for hdb databases.

View Source
const DriverVersion = "1.12.7"

DriverVersion is the version number of the hdb driver.

Variables

View Source
var ErrEndOfRows = errors.New("end of rows")

ErrEndOfRows is the error to be returned using a function based bulk exec to indicate the end of rows.

View Source
var ErrNestedQuery = errors.New("nested sql queries are not supported")

ErrNestedQuery is the error raised if a new sql statement is sent to the database server before the resultset processing of a previous sql query statement is finalized. Currently this only can happen if connections are used concurrently and if stream enabled fields (LOBs) are part of the resultset. This error can be avoided in whether using a transaction or a dedicated connection (sql.Tx or sql.Conn).

View Source
var ErrNestedTransaction = errors.New("nested transactions are not supported")

ErrNestedTransaction is the error raised if a transaction is created within a transaction as this is not supported by hdb.

View Source
var ErrScanOnClosedResultset = errors.New("scan on closed resultset")

ErrScanOnClosedResultset is the error raised in case a scan is executed on a closed resultset.

View Source
var ErrSwitchUser = errors.New("switch user inside transaction or in statement scope (prepared query) is not allowed")

ErrSwitchUser is the error raised if a switch user is requested in a not allowed context.

View Source
var ErrUnsupportedIsolationLevel = errors.New("unsupported isolation level")

ErrUnsupportedIsolationLevel is the error raised if a transaction is started with a not supported isolation level.

Functions

func SQLTrace added in v1.2.0

func SQLTrace() bool

SQLTrace returns true if sql tracing output is active, false otherwise.

func ScanLobBytes added in v1.0.0

func ScanLobBytes(src any, b *[]byte) error

ScanLobBytes supports scanning Lob data into a byte slice. This enables using []byte based custom types for scanning Lobs instead of using a Lob object. For usage please refer to the example.

Example

ExampleScanLobBytes demontrates how to read Lob data using a []byte based data type.

//go:build !unit

package main

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

// BytesLob defines a []byte based data type for scanning Lobs.
type BytesLob []byte

// Scan implements the database.sql.Scanner interface.
func (b *BytesLob) Scan(arg any) error { return driver.ScanLobBytes(arg, (*[]byte)(b)) }

// ExampleScanLobBytes demontrates how to read Lob data using a []byte based data type.
func main() {
	// Open Test database.
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	table := driver.RandomIdentifier("lob_")

	if _, err := db.Exec(fmt.Sprintf("create table %s (b1 blob, b2 blob)", table)); err != nil {
		log.Fatalf("create table failed: %s", err)
	}

	tx, err := db.Begin() // Start Transaction to avoid database error: SQL Error 596 - LOB streaming is not permitted in auto-commit mode.
	if err != nil {
		log.Fatal(err)
	}

	// Lob content can be written using a byte slice.
	content := []byte("scan lob bytes")
	_, err = tx.Exec(fmt.Sprintf("insert into %s values (?, ?)", table), content, content)
	if err != nil {
		log.Fatal(err)
	}

	if err := tx.Commit(); err != nil {
		log.Fatal(err)
	}

	// Select.
	stmt, err := db.Prepare(fmt.Sprintf("select * from %s", table))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	// Scan into BytesLob and sql.Null[BytesLob].
	var b BytesLob
	var nb sql.Null[BytesLob]
	if err := stmt.QueryRow().Scan(&b, &nb); err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(b))
	fmt.Println(string(nb.V))

}
Output:

scan lob bytes
scan lob bytes

func ScanLobString added in v1.0.0

func ScanLobString(src any, s *string) error

ScanLobString supports scanning Lob data into a string. This enables using string based custom types for scanning Lobs instead of using a Lob object. For usage please refer to the example.

Example

ExampleScanLobString demontrates how to read Lob data using a string based data type.

//go:build !unit

package main

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

// StringLob defines a string based data type for scanning Lobs.
type StringLob string

// Scan implements the database.sql.Scanner interface.
func (s *StringLob) Scan(arg any) error { return driver.ScanLobString(arg, (*string)(s)) }

// ExampleScanLobString demontrates how to read Lob data using a string based data type.
func main() {
	// Open Test database.
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	table := driver.RandomIdentifier("lob_")

	if _, err := db.Exec(fmt.Sprintf("create table %s (n1 nclob, n2 nclob)", table)); err != nil {
		log.Fatalf("create table failed: %s", err)
	}

	tx, err := db.Begin() // Start Transaction to avoid database error: SQL Error 596 - LOB streaming is not permitted in auto-commit mode.
	if err != nil {
		log.Fatal(err)
	}

	// Lob content can be written using a string.
	content := "scan lob string"
	_, err = tx.Exec(fmt.Sprintf("insert into %s values (?, ?)", table), content, content)
	if err != nil {
		log.Fatal(err)
	}

	if err := tx.Commit(); err != nil {
		log.Fatal(err)
	}

	// Select.
	stmt, err := db.Prepare(fmt.Sprintf("select * from %s", table))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	// Scan into StringLob and sql.Null[StringLob].
	var s StringLob
	var ns sql.Null[StringLob]
	if err := stmt.QueryRow().Scan(&s, &ns); err != nil {
		log.Fatal(err)
	}
	fmt.Println(s)
	fmt.Println(ns.V)

}
Output:

scan lob string
scan lob string

func ScanLobWriter added in v1.0.0

func ScanLobWriter(src any, wr io.Writer) error

ScanLobWriter supports scanning Lob data into a io.Writer object. This enables using io.Writer based custom types for scanning Lobs instead of using a Lob object. For usage please refer to the example.

Example

ExampleScanLobWriter demontrates how to read Lob data using a io.Writer based data type.

//go:build !unit

package main

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

// WriterLob defines a io.Writer based data type for scanning Lobs.
type WriterLob []byte

// Write implements the io.Writer interface.
func (b *WriterLob) Write(p []byte) (n int, err error) {
	*b = append(*b, p...)
	return len(p), nil
}

// Scan implements the database.sql.Scanner interface.
func (b *WriterLob) Scan(arg any) error { return driver.ScanLobWriter(arg, b) }

// ExampleScanLobWriter demontrates how to read Lob data using a io.Writer based data type.
func main() {
	// Open Test database.
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	table := driver.RandomIdentifier("lob_")

	if _, err := db.Exec(fmt.Sprintf("create table %s (n1 nclob, n2 nclob)", table)); err != nil {
		log.Fatalf("create table failed: %s", err)
	}

	tx, err := db.Begin() // Start Transaction to avoid database error: SQL Error 596 - LOB streaming is not permitted in auto-commit mode.
	if err != nil {
		log.Fatal(err)
	}

	// Lob content can be written using a string.
	content := "scan lob writer"
	_, err = tx.Exec(fmt.Sprintf("insert into %s values (?, ?)", table), content, content)
	if err != nil {
		log.Fatal(err)
	}

	if err := tx.Commit(); err != nil {
		log.Fatal(err)
	}

	// Select.
	stmt, err := db.Prepare(fmt.Sprintf("select * from %s", table))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	// Scan into WriterLob and sql.Null[WriterLob].
	var w WriterLob
	var nw sql.Null[WriterLob]
	if err := stmt.QueryRow().Scan(&w, &nw); err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(w))
	fmt.Println(string(nw.V))

}
Output:

scan lob writer
scan lob writer

func SetSQLTrace added in v1.2.0

func SetSQLTrace(on bool)

SetSQLTrace sets sql tracing output active or inactive.

func Unregister added in v1.8.14

func Unregister() error

Unregister unregisters the go-hdb driver and frees all allocated ressources. After calling any go-hdb access might panic.

func WithStmtMetadata added in v1.8.21

func WithStmtMetadata(ctx context.Context, stmtMetadata *StmtMetadata) context.Context

WithStmtMetadata can be used to add a statement metadata reference to the context used for a Prepare call. The Prepare call will set the stmtMetadata reference on successful preparation.

Example

ExampleWithStmtMetadata demonstrates the use of statement metadata provided by PrepareContext.

package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"reflect"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	const procOut = `create procedure %s (out message nvarchar(1024))
language SQLSCRIPT as
begin
    message := 'Hello World!';
end
`

	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	procedure := driver.RandomIdentifier("procOut_")

	if _, err := db.Exec(fmt.Sprintf(procOut, procedure)); err != nil { // Create stored procedure.
		log.Fatal(err)
	}

	// Call PrepareContext with statement metadata context value.
	var stmtMetadata driver.StmtMetadata
	ctx := driver.WithStmtMetadata(context.Background(), &stmtMetadata)

	stmt, err := db.PrepareContext(ctx, fmt.Sprintf("call %s(?)", procedure))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	// Create Exec args based on statement metadata columns...
	columnTypes := stmtMetadata.ColumnTypes()
	numColumnType := len(columnTypes)
	args := make([]any, numColumnType)

	for i, columnType := range columnTypes {
		out := reflect.New(columnType.ScanType()).Interface()
		args[i] = sql.Named(columnType.Name(), sql.Out{Dest: out})
	}

	// .. and execute Exec.
	if _, err := stmt.Exec(args...); err != nil {
		log.Fatal(err)
	}

	// Finally print the values.
	for _, arg := range args {
		namedArg := arg.(sql.NamedArg)
		sqlOut := namedArg.Value.(sql.Out)
		dest := sqlOut.Dest.(*sql.NullString)
		fmt.Printf("%s: %s", namedArg.Name, dest.String)
	}

}
Output:

MESSAGE: Hello World!

func WithUserSwitch added in v1.10.0

func WithUserSwitch(ctx context.Context, u *SessionUser) context.Context

WithUserSwitch can be used to switch a user on a new or an existing connection (see https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/connect-statement-session-management).

Example

ExampleWithUserSwitch demonstrates switching users on new or existing connections.

package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	ctr := driver.MT.Connector()
	db := sql.OpenDB(ctr)
	defer db.Close()

	tableName := driver.RandomIdentifier("table_")

	sessionUser := &driver.SessionUser{Username: ctr.Username(), Password: ctr.Password()}
	ctx := driver.WithUserSwitch(context.Background(), sessionUser)

	// Create table.
	if _, err := db.ExecContext(context.Background(), fmt.Sprintf("create table %s (i integer)", tableName)); err != nil {
		log.Fatal(err)
	}

	// Switch user via context.
	stmt, err := db.PrepareContext(ctx, fmt.Sprintf("insert into %s values (?)", tableName))
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()
	// Switch to different user not possible in context of statement and transactions, but
	// former context can be used as long as the session user data are not changed.
	if _, err := stmt.ExecContext(ctx, 42); err != nil {
		log.Fatal(err)
	}

	// Drop table.
	if _, err := db.ExecContext(context.Background(), fmt.Sprintf("drop table %s", tableName)); err != nil {
		log.Fatal(err)
	}

}
Output:

Types

type ColumnType added in v1.8.21

type ColumnType interface {
	DatabaseTypeName() string
	DecimalSize() (precision, scale int64, ok bool)
	Length() (length int64, ok bool)
	Name() string
	Nullable() (nullable bool, ok bool)
	ScanType() reflect.Type
}

ColumnType equals sql.ColumnType.

type Conn added in v0.103.0

type Conn interface {
	HDBVersion() *Version
	DatabaseName() string
	DBConnectInfo(ctx context.Context, databaseName string) (*DBConnectInfo, error)
}

Conn enhances a connection with go-hdb specific connection functions.

type Connector added in v0.10.0

type Connector struct {
	// contains filtered or unexported fields
}

A Connector represents a hdb driver in a fixed configuration. A Connector can be passed to sql.OpenDB allowing users to bypass a string based data source name.

func NewBasicAuthConnector added in v0.10.0

func NewBasicAuthConnector(host, username, password string) *Connector

NewBasicAuthConnector creates a connector for basic authentication.

Example

ExampleNewBasicAuthConnector shows how to open a database with the help of a connector using basic authentication.

package main

import (
	"database/sql"
	"log"
	"os"
	"strconv"

	"github.com/SAP/go-hdb/driver"
)

func lookupTLS() (string, bool, string, bool) {
	const (
		envServerName         = "GOHDBTLSSERVERNAME"
		envInsecureSkipVerify = "GOHDBINSECURESKIPVERIFY"
		envRootCAFile         = "GOHDBROOTCAFILE"
	)

	set := false

	serverName, ok := os.LookupEnv(envServerName)
	if ok {
		set = true
	}
	insecureSkipVerify := false
	if b, ok := os.LookupEnv(envInsecureSkipVerify); ok {
		var err error
		if insecureSkipVerify, err = strconv.ParseBool(b); err != nil {
			log.Fatal(err)
		}
		set = true
	}
	rootCAFile, ok := os.LookupEnv(envRootCAFile)
	if ok {
		set = true
	}
	return serverName, insecureSkipVerify, rootCAFile, set
}

func main() {
	const (
		envHost     = "GOHDBHOST"
		envUsername = "GOHDBUSERNAME"
		envPassword = "GOHDBPASSWORD"
		envDatabase = "GOHDBDATABASE"
	)

	host, ok := os.LookupEnv(envHost)
	if !ok {
		return
	}
	username, ok := os.LookupEnv(envUsername)
	if !ok {
		return
	}
	password, ok := os.LookupEnv(envPassword)
	if !ok {
		return
	}
	database, ok := os.LookupEnv(envDatabase)
	if !ok {
		return
	}

	connector := driver.NewBasicAuthConnector(host, username, password)
	if serverName, insecureSkipVerify, rootCAFile, ok := lookupTLS(); ok {
		if err := connector.SetTLS(serverName, insecureSkipVerify, rootCAFile); err != nil {
			log.Fatal(err)
		}
	}
	if database != "" {
		connector = connector.WithDatabase(database)
	}

	db := sql.OpenDB(connector)
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
}
Output:

func NewConnector added in v0.103.0

func NewConnector() *Connector

NewConnector returns a new Connector instance with default values.

func NewDSNConnector added in v0.10.0

func NewDSNConnector(dsnStr string) (*Connector, error)

NewDSNConnector creates a connector from a data source name.

Example

ExampleNewDSNConnector shows how to open a database with the help of a connector using DSN.

package main

import (
	"database/sql"
	"log"
	"os"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	const (
		envDSN = "GOHDBDSN"
	)

	dsn, ok := os.LookupEnv(envDSN)
	if !ok {
		return
	}

	connector, err := driver.NewDSNConnector(dsn)
	if err != nil {
		log.Fatal(err)
	}
	db := sql.OpenDB(connector)
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
}
Output:

func NewJWTAuthConnector added in v0.107.0

func NewJWTAuthConnector(host, token string) *Connector

NewJWTAuthConnector creates a connector for token (JWT) based authentication.

Example

ExampleNewJWTAuthConnector shows how to open a database with the help of a connector using JWT authentication.

package main

import (
	"database/sql"
	"log"
	"os"
	"strconv"

	"github.com/SAP/go-hdb/driver"
)

func lookupTLS() (string, bool, string, bool) {
	const (
		envServerName         = "GOHDBTLSSERVERNAME"
		envInsecureSkipVerify = "GOHDBINSECURESKIPVERIFY"
		envRootCAFile         = "GOHDBROOTCAFILE"
	)

	set := false

	serverName, ok := os.LookupEnv(envServerName)
	if ok {
		set = true
	}
	insecureSkipVerify := false
	if b, ok := os.LookupEnv(envInsecureSkipVerify); ok {
		var err error
		if insecureSkipVerify, err = strconv.ParseBool(b); err != nil {
			log.Fatal(err)
		}
		set = true
	}
	rootCAFile, ok := os.LookupEnv(envRootCAFile)
	if ok {
		set = true
	}
	return serverName, insecureSkipVerify, rootCAFile, set
}

func main() {
	const (
		envHost  = "GOHDBHOST"
		envToken = "GOHDBTOKEN"
	)

	host, ok := os.LookupEnv(envHost)
	if !ok {
		return
	}
	token, ok := os.LookupEnv(envToken)
	if !ok {
		return
	}

	const invalidToken = "ey"

	connector := driver.NewJWTAuthConnector(host, invalidToken)
	if serverName, insecureSkipVerify, rootCAFile, ok := lookupTLS(); ok {
		if err := connector.SetTLS(serverName, insecureSkipVerify, rootCAFile); err != nil {
			log.Fatal(err)
		}
	}
	// in case JWT authentication fails provide a (new) valid token.
	connector.SetRefreshToken(func() (string, bool) { return token, true })

	db := sql.OpenDB(connector)
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
}
Output:

func NewX509AuthConnector added in v0.107.0

func NewX509AuthConnector(host string, clientCert, clientKey []byte) (*Connector, error)

NewX509AuthConnector creates a connector for X509 (client certificate) authentication. Parameters clientCert and clientKey in PEM format, clientKey not password encryped.

func NewX509AuthConnectorByFiles added in v0.107.0

func NewX509AuthConnectorByFiles(host, clientCertFile, clientKeyFile string) (*Connector, error)

NewX509AuthConnectorByFiles creates a connector for X509 (client certificate) authentication based on client certificate and client key files. Parameters clientCertFile and clientKeyFile in PEM format, clientKeyFile not password encryped.

Example

ExampleNewX509AuthConnectorByFiles shows how to open a database with the help of a connector using x509 (client certificate) authentication and providing client certificate and client key by file.

package main

import (
	"database/sql"
	"log"
	"os"
	"strconv"

	"github.com/SAP/go-hdb/driver"
)

func lookupTLS() (string, bool, string, bool) {
	const (
		envServerName         = "GOHDBTLSSERVERNAME"
		envInsecureSkipVerify = "GOHDBINSECURESKIPVERIFY"
		envRootCAFile         = "GOHDBROOTCAFILE"
	)

	set := false

	serverName, ok := os.LookupEnv(envServerName)
	if ok {
		set = true
	}
	insecureSkipVerify := false
	if b, ok := os.LookupEnv(envInsecureSkipVerify); ok {
		var err error
		if insecureSkipVerify, err = strconv.ParseBool(b); err != nil {
			log.Fatal(err)
		}
		set = true
	}
	rootCAFile, ok := os.LookupEnv(envRootCAFile)
	if ok {
		set = true
	}
	return serverName, insecureSkipVerify, rootCAFile, set
}

func main() {
	const (
		envHost           = "GOHDBHOST"
		envClientCertFile = "GOHDBCLIENTCERTFILE"
		envClientKeyFile  = "GOHDBCLIENTKEYFILE"
	)

	host, ok := os.LookupEnv(envHost)
	if !ok {
		return
	}
	clientCertFile, ok := os.LookupEnv(envClientCertFile)
	if !ok {
		return
	}
	clientKeyFile, ok := os.LookupEnv(envClientKeyFile)
	if !ok {
		return
	}

	connector, err := driver.NewX509AuthConnectorByFiles(host, clientCertFile, clientKeyFile)
	if err != nil {
		log.Fatal(err)
	}
	if serverName, insecureSkipVerify, rootCAFile, ok := lookupTLS(); ok {
		if err := connector.SetTLS(serverName, insecureSkipVerify, rootCAFile); err != nil {
			log.Fatal(err)
		}
	}
	db := sql.OpenDB(connector)
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
}
Output:

func (Connector) ApplicationName added in v0.103.0

func (c Connector) ApplicationName() string

ApplicationName returns the application name of the connector.

func (Connector) BufferSize added in v0.99.0

func (c Connector) BufferSize() int

BufferSize returns the bufferSize of the connector.

func (Connector) BulkSize added in v0.99.0

func (c Connector) BulkSize() int

BulkSize returns the bulkSize of the connector.

func (Connector) CESU8Decoder added in v0.105.1

func (c Connector) CESU8Decoder() func() transform.Transformer

CESU8Decoder returns the CESU-8 decoder of the connector.

func (Connector) CESU8Encoder added in v0.105.1

func (c Connector) CESU8Encoder() func() transform.Transformer

CESU8Encoder returns the CESU-8 encoder of the connector.

func (Connector) ClientCert added in v0.107.0

func (c Connector) ClientCert() (clientCert, clientKey []byte)

ClientCert returns the X509 authentication client certificate and key of the connector.

func (*Connector) Connect added in v0.10.0

func (c *Connector) Connect(ctx context.Context) (driver.Conn, error)

Connect implements the database/sql/driver/Connector interface.

func (*Connector) DatabaseName added in v1.5.0

func (c *Connector) DatabaseName() string

DatabaseName returns the tenant database name of the connector.

func (Connector) DefaultSchema added in v0.99.0

func (c Connector) DefaultSchema() string

DefaultSchema returns the database default schema of the connector.

func (Connector) Dfv added in v0.99.0

func (c Connector) Dfv() int

Dfv returns the client data format version of the connector.

func (Connector) Dialer added in v0.100.9

func (c Connector) Dialer() dial.Dialer

Dialer returns the dialer object of the connector.

func (*Connector) Driver added in v0.10.0

func (c *Connector) Driver() driver.Driver

Driver implements the database/sql/driver/Connector interface.

func (Connector) EmptyDateAsNull added in v1.1.7

func (c Connector) EmptyDateAsNull() bool

EmptyDateAsNull returns NULL for empty dates ('0000-00-00') if true, otherwise:

For data format version 1 the backend does return the NULL indicator for empty date fields. For data format version non equal 1 (field type daydate) the NULL indicator is not set and the return value is 0. As value 1 represents '0001-01-01' (the minimal valid date) without setting EmptyDateAsNull '0000-12-31' is returned, so that NULL, empty and valid dates can be distinguished.

https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/3f81ccc7e35d44cbbc595c7d552c202a.html?locale=en-US

func (Connector) FetchSize added in v0.10.0

func (c Connector) FetchSize() int

FetchSize returns the fetchSize of the connector.

func (*Connector) Host added in v0.10.0

func (c *Connector) Host() string

Host returns the host of the connector.

func (Connector) LobChunkSize added in v0.99.0

func (c Connector) LobChunkSize() int

LobChunkSize returns the lobChunkSize of the connector.

func (Connector) Locale added in v0.10.0

func (c Connector) Locale() string

Locale returns the locale of the connector.

func (Connector) Logger added in v1.3.2

func (c Connector) Logger() *slog.Logger

Logger returns the Logger instance of the connector.

func (*Connector) NativeDriver added in v0.107.0

func (c *Connector) NativeDriver() Driver

NativeDriver returns the concrete underlying Driver of the Connector.

func (Connector) Password added in v0.10.0

func (c Connector) Password() string

Password returns the basic authentication password of the connector.

func (Connector) PingInterval added in v0.100.9

func (c Connector) PingInterval() time.Duration

PingInterval returns the connection ping interval of the connector.

func (Connector) RefreshClientCert added in v0.107.0

func (c Connector) RefreshClientCert() func() (clientCert, clientKey []byte, ok bool)

RefreshClientCert returns the callback function for X509 authentication client certificate and key refresh.

func (Connector) RefreshPassword added in v0.107.0

func (c Connector) RefreshPassword() func() (password string, ok bool)

RefreshPassword returns the callback function for basic authentication password refresh.

func (Connector) RefreshToken added in v0.107.0

func (c Connector) RefreshToken() func() (token string, ok bool)

RefreshToken returns the callback function for JWT authentication token refresh.

func (Connector) SessionVariables added in v0.14.0

func (c Connector) SessionVariables() SessionVariables

SessionVariables returns the session variables stored in connector.

func (Connector) SetApplicationName added in v0.103.0

func (c Connector) SetApplicationName(name string)

SetApplicationName sets the application name of the connector.

func (Connector) SetBufferSize added in v0.103.0

func (c Connector) SetBufferSize(bufferSize int)

SetBufferSize sets the bufferSize of the connector.

func (Connector) SetBulkSize added in v0.99.0

func (c Connector) SetBulkSize(bulkSize int)

SetBulkSize sets the bulkSize of the connector.

func (Connector) SetCESU8Decoder added in v0.105.1

func (c Connector) SetCESU8Decoder(cesu8Decoder func() transform.Transformer)

SetCESU8Decoder sets the CESU-8 decoder of the connector.

func (Connector) SetCESU8Encoder added in v0.105.1

func (c Connector) SetCESU8Encoder(cesu8Encoder func() transform.Transformer)

SetCESU8Encoder sets the CESU-8 encoder of the connector.

func (Connector) SetDefaultSchema added in v0.99.0

func (c Connector) SetDefaultSchema(schema string)

SetDefaultSchema sets the database default schema of the connector.

func (Connector) SetDfv added in v0.99.0

func (c Connector) SetDfv(dfv int)

SetDfv sets the client data format version of the connector.

func (Connector) SetDialer added in v0.100.9

func (c Connector) SetDialer(dialer dial.Dialer)

SetDialer sets the dialer object of the connector.

func (Connector) SetEmptyDateAsNull added in v1.1.7

func (c Connector) SetEmptyDateAsNull(emptyDateAsNull bool)

SetEmptyDateAsNull sets the EmptyDateAsNull flag of the connector.

func (Connector) SetFetchSize added in v0.10.0

func (c Connector) SetFetchSize(fetchSize int)

SetFetchSize sets the fetchSize of the connector.

For more information please see DSNFetchSize.

func (Connector) SetLobChunkSize added in v0.105.1

func (c Connector) SetLobChunkSize(lobChunkSize int)

SetLobChunkSize sets the lobChunkSize of the connector.

func (Connector) SetLocale added in v0.10.0

func (c Connector) SetLocale(locale string)

SetLocale sets the locale of the connector.

For more information please see http://help.sap.com/hana/SAP_HANA_SQL_Command_Network_Protocol_Reference_en.pdf.

func (Connector) SetLogger added in v1.3.2

func (c Connector) SetLogger(logger *slog.Logger)

SetLogger sets the Logger instance of the connector.

func (Connector) SetPassword added in v0.108.0

func (c Connector) SetPassword(password string)

SetPassword sets the basic authentication password of the connector.

func (Connector) SetPingInterval added in v0.100.9

func (c Connector) SetPingInterval(d time.Duration)

SetPingInterval sets the connection ping interval value of the connector.

Using a ping interval supports detecting broken connections. In case the ping is not successful a new or another connection out of the connection pool would be used automatically instead of retuning an error.

Parameter d defines the time between the pings as duration. If d is zero no ping is executed. If d is not zero a database ping is executed if an idle connection out of the connection pool is reused and the time since the last connection access is greater or equal than d.

func (Connector) SetRefreshClientCert added in v0.107.0

func (c Connector) SetRefreshClientCert(refreshClientCert func() (clientCert, clientKey []byte, ok bool))

SetRefreshClientCert sets the callback function for X509 authentication client certificate and key refresh. The callback function might be called simultaneously from multiple goroutines only if registered for more than one Connector.

func (Connector) SetRefreshPassword added in v0.107.0

func (c Connector) SetRefreshPassword(refreshPassword func() (password string, ok bool))

SetRefreshPassword sets the callback function for basic authentication password refresh. The callback function might be called simultaneously from multiple goroutines only if registered for more than one Connector.

func (Connector) SetRefreshToken added in v0.107.0

func (c Connector) SetRefreshToken(refreshToken func() (token string, ok bool))

SetRefreshToken sets the callback function for JWT authentication token refresh. The callback function might be called simultaneously from multiple goroutines only if registered for more than one Connector.

func (Connector) SetSessionVariables added in v0.14.0

func (c Connector) SetSessionVariables(sessionVariables SessionVariables)

SetSessionVariables sets the session varibles of the connector.

func (Connector) SetTCPKeepAlive added in v0.100.8

func (c Connector) SetTCPKeepAlive(tcpKeepAlive time.Duration)

SetTCPKeepAlive sets the tcp keep-alive value of the connector.

For more information please see net.Dialer structure.

func (Connector) SetTCPKeepAliveConfig added in v1.12.0

func (c Connector) SetTCPKeepAliveConfig(tcpKeepAliveConfig net.KeepAliveConfig)

SetTCPKeepAliveConfig sets the tcp keep-alive config value of the connector.

For more information please see net.Dialer structure.

func (Connector) SetTLS added in v0.107.0

func (c Connector) SetTLS(serverName string, insecureSkipVerify bool, rootCAFiles ...string) error

SetTLS sets the TLS configuration of the connector with given parameters. An existing connector TLS configuration is replaced.

func (Connector) SetTLSConfig added in v0.11.0

func (c Connector) SetTLSConfig(tlsConfig *tls.Config)

SetTLSConfig sets the TLS configuration of the connector.

func (Connector) SetTimeout added in v0.10.0

func (c Connector) SetTimeout(timeout time.Duration)

SetTimeout sets the timeout of the connector.

For more information please see DSNTimeout.

func (Connector) TCPKeepAlive added in v0.100.8

func (c Connector) TCPKeepAlive() time.Duration

TCPKeepAlive returns the tcp keep-alive value of the connector.

func (Connector) TCPKeepAliveConfig added in v1.12.0

func (c Connector) TCPKeepAliveConfig() net.KeepAliveConfig

TCPKeepAliveConfig returns the tcp keep-alive config value of the connector.

func (Connector) TLSConfig added in v0.11.0

func (c Connector) TLSConfig() *tls.Config

TLSConfig returns the TLS configuration of the connector.

func (Connector) Timeout added in v0.10.0

func (c Connector) Timeout() time.Duration

Timeout returns the timeout of the connector.

func (Connector) Token added in v0.107.0

func (c Connector) Token() string

Token returns the JWT authentication token of the connector.

func (Connector) Username added in v0.10.0

func (c Connector) Username() string

Username returns the username of the connector.

func (*Connector) WithDatabase added in v1.5.0

func (c *Connector) WithDatabase(databaseName string) *Connector

WithDatabase returns a new Connector supporting tenant database connections via database name.

Example

ExampleConnector_WithDatabase shows how to open a tenant database with the help of a connector using basic authentication.

package main

import (
	"database/sql"
	"log"
	"os"
	"strconv"

	"github.com/SAP/go-hdb/driver"
)

func lookupTLS() (string, bool, string, bool) {
	const (
		envServerName         = "GOHDBTLSSERVERNAME"
		envInsecureSkipVerify = "GOHDBINSECURESKIPVERIFY"
		envRootCAFile         = "GOHDBROOTCAFILE"
	)

	set := false

	serverName, ok := os.LookupEnv(envServerName)
	if ok {
		set = true
	}
	insecureSkipVerify := false
	if b, ok := os.LookupEnv(envInsecureSkipVerify); ok {
		var err error
		if insecureSkipVerify, err = strconv.ParseBool(b); err != nil {
			log.Fatal(err)
		}
		set = true
	}
	rootCAFile, ok := os.LookupEnv(envRootCAFile)
	if ok {
		set = true
	}
	return serverName, insecureSkipVerify, rootCAFile, set
}

func main() {
	const (
		envHost     = "GOHDBHOST"
		envUsername = "GOHDBUSERNAME"
		envPassword = "GOHDBPASSWORD"
		envDatabase = "GOHDBDATABASE"
	)

	host, ok := os.LookupEnv(envHost)
	if !ok {
		return
	}
	username, ok := os.LookupEnv(envUsername)
	if !ok {
		return
	}
	password, ok := os.LookupEnv(envPassword)
	if !ok {
		return
	}
	database, ok := os.LookupEnv(envDatabase)
	if !ok {
		return
	}

	connector := driver.NewBasicAuthConnector(host, username, password).WithDatabase(database)
	if serverName, insecureSkipVerify, rootCAFile, ok := lookupTLS(); ok {
		if err := connector.SetTLS(serverName, insecureSkipVerify, rootCAFile); err != nil {
			log.Fatal(err)
		}
	}
	db := sql.OpenDB(connector)
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
}
Output:

type DB added in v0.108.0

type DB struct {
	// The embedded sql.DB instance. Please use only the methods of the wrapper (driver.DB).
	// The field is exported to support use cases where a sql.DB object is requested, but please
	// use with care as some of the sql.DB methods (e.g. Close) might be redefined in driver.DB.
	*sql.DB
	// contains filtered or unexported fields
}

DB represents a driver database and can be used as a replacement for sql.DB. It provides all of the sql.DB methods plus additional methods only available for driver.DB.

Example

ExampleDB shows hot to print extended database statistics with the help of function driver.OpenDB and a driver.DB object.

package main

import (
	"database/sql"
	"log"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	// print default sql database statistics.
	db1 := sql.OpenDB(driver.MT.Connector())
	log.Printf("waitDuration: %d", db1.Stats().WaitDuration) // print field waitDuration of default database statistics.
	db1.Close()

	// print extended go-hdb driver db statistics.
	db2 := driver.OpenDB(driver.MT.Connector())
	log.Printf("waitDuration: %d", db2.Stats().WaitDuration)   // print field waitDuration of default database statistics.
	log.Printf("bytesWritten: %d", db2.ExStats().WrittenBytes) // print field bytesWritten of extended driver database statistics.
	db2.Close()
}
Output:

func OpenDB added in v0.108.0

func OpenDB(c *Connector) *DB

OpenDB opens and returns a database. It also calls the OpenDB method of the sql package and stores an embedded *sql.DB object.

func (*DB) Close added in v0.108.0

func (db *DB) Close() error

Close closes the database. It also calls the Close method of the sql package and returns its error.

func (*DB) ExStats added in v0.108.0

func (db *DB) ExStats() *Stats

ExStats returns the extended database statistics.

type DBConnectInfo added in v0.107.3

type DBConnectInfo struct {
	DatabaseName string
	Host         string
	Port         int
	IsConnected  bool
}

DBConnectInfo represents the connection information attributes returned by hdb.

func (*DBConnectInfo) String added in v0.107.3

func (ci *DBConnectInfo) String() string

type DBError added in v1.0.1

type DBError interface {
	Error() string   // Implements the golang error interface.
	StmtNo() int     // Returns the statement number of the error in multi statement contexts (e.g. bulk insert).
	Code() int       // Code return the database error code.
	Position() int   // Position returns the start position of erroneous sql statements sent to the database server.
	Level() int      // Level return one of the database server predefined error levels.
	Text() string    // Text return the error description sent from database server.
	IsWarning() bool // IsWarning returns true if the HDB error level equals 0.
	IsError() bool   // IsError returns true if the HDB error level equals 1.
	IsFatal() bool   // IsFatal returns true if the HDB error level equals 2.
}

DBError represents a single error returned by the database server.

type DSN

type DSN struct {
	// contains filtered or unexported fields
}

A DSN represents a parsed DSN string. A DSN string is an URL string with the following format

"hdb://<username>:<password>@<host address>:<port number>"

and optional query parameters (see DSN query parameters and DSN query default values).

Examples:

"hdb://myUser:myPassword@localhost:30015?databaseName=myTenantDatabaseName"
"hdb://myUser:myPassword@localhost:30015?timeout=60"

Examples TLS connection:

"hdb://myUser:myPassword@localhost:39013?TLSRootCAFile=trust.pem"
"hdb://myUser:myPassword@localhost:39013?TLSRootCAFile=trust.pem&TLSServerName=hostname"
"hdb://myUser:myPassword@localhost:39013?TLSInsecureSkipVerify"
Example

ExampleDSN shows how to construct a DSN (data source name) as url.

package main

import (
	"database/sql"
	"log"
	"net/url"

	"github.com/SAP/go-hdb/driver"
)

// dsn creates data source name with the help of the net/url package.
func dsn() string {
	dsn := &url.URL{
		Scheme: driver.DriverName,
		User:   url.UserPassword("user", "password"),
		Host:   "host:port",
	}
	return dsn.String()
}

// ExampleDSN shows how to construct a DSN (data source name) as url.
func main() {
	db, err := sql.Open(driver.DriverName, dsn())
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
}
Output:

func ParseDSN added in v1.6.11

func ParseDSN(s string) (*DSN, error)

ParseDSN parses a DSN string into a DSN structure.

func (*DSN) String added in v0.107.0

func (dsn *DSN) String() string

String reassembles the DSN into a valid DSN string.

type Decimal

type Decimal big.Rat

A Decimal is the driver representation of a database decimal field value as big.Rat.

Example

ExampleDecimal creates a table with a single decimal attribute, insert a record into it and select the entry afterwards. This demonstrates the usage of the type Decimal to write and scan decimal database attributes.

package main

import (
	"database/sql"
	"fmt"
	"log"
	"math/big"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	tableName := driver.RandomIdentifier("table_")

	if _, err := db.Exec(fmt.Sprintf("create table %s (x decimal)", tableName)); err != nil { // Create table with decimal attribute.
		log.Fatal(err)
	}

	// Decimal values are represented in Go as big.Rat.
	in := (*driver.Decimal)(big.NewRat(1, 1)) // Create *big.Rat and cast to Decimal.

	if _, err := db.Exec(fmt.Sprintf("insert into %s values(?)", tableName), in); err != nil { // Insert record.
		log.Fatal(err)
	}

	var out driver.Decimal // Declare scan variable.

	if err := db.QueryRow(fmt.Sprintf("select * from %s", tableName)).Scan(&out); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Decimal value: %s", (*big.Rat)(&out).String()) // Cast scan variable to *big.Rat to use *big.Rat methods.

}
Output:

Decimal value: 1/1

func (*Decimal) Scan

func (d *Decimal) Scan(src any) error

Scan implements the database/sql/Scanner interface.

func (Decimal) Value

func (d Decimal) Value() (driver.Value, error)

Value implements the database/sql/Valuer interface.

type Driver added in v0.103.0

type Driver interface {
	Name() string    // Name returns the driver name.
	Version() string // Version returns the driver version.
	Stats() *Stats   // Stats returns aggregated driver statistics.
}

Driver enhances a connection with go-hdb specific connection functions.

type Error

type Error interface {
	Error() string   // Implements the golang error interface.
	NumError() int   // NumError returns the number of errors.
	Unwrap() []error // Unwrap implements the standard error Unwrap function for errors wrapping multiple errors.
	SetIdx(idx int)  // SetIdx sets the error index in case number of errors are greater 1 in the range of 0 <= index < NumError().
	DBError          // DBError functions for error in case of single error, for error set by SetIdx in case of error collection.
}

Error represents errors (an error collection) send by the database server.

Example
//go:build !unit

package main

import (
	"database/sql"
	"errors"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

const (
	errCodeInvalidTableName = 259
)

func main() {
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	invalidTableName := driver.RandomIdentifier("table_")
	stmt, err := db.Query(fmt.Sprintf("select * from %s", invalidTableName))
	if err == nil {
		defer stmt.Close()
	}

	var dbError driver.Error
	if err != nil {
		// Check if error is driver.Error.
		if errors.As(err, &dbError) {
			switch dbError.Code() {
			case errCodeInvalidTableName:
				fmt.Print("invalid table name")
			default:
				log.Fatalf("code %d text %s", dbError.Code(), dbError.Text())
			}
		}
	}
}
Output:

invalid table name

type Identifier

type Identifier string

Identifier in hdb SQL statements like schema or table name.

func RandomIdentifier

func RandomIdentifier(prefix string) Identifier

RandomIdentifier returns a random Identifier prefixed by the prefix parameter. This function is used to generate database objects with random names for test and example code.

func (Identifier) String

func (i Identifier) String() string

type Lob

type Lob struct {
	// contains filtered or unexported fields
}

A Lob is the driver representation of a database large object field. A Lob object uses an io.Reader object as source for writing content to a database lob field. A Lob object uses an io.Writer object as destination for reading content from a database lob field. A Lob can be created by contructor method NewLob with io.Reader and io.Writer as parameters or created by new, setting io.Reader and io.Writer by SetReader and SetWriter methods.

Example (Pipe)

ExampleLobPipe: - inserts data read from a file into a database large object field - and retrieves the data afterwards An io.Pipe is used to insert and retrieve Lob data in chunks.

package main

import (
	"bufio"
	"database/sql"
	"fmt"
	"io"
	"log"
	"os"
	"sync"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	// Open test file.
	file, err := os.Open("example_lob_test.go")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// Open Test database.
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	tx, err := db.Begin() // Start Transaction to avoid database error: SQL Error 596 - LOB streaming is not permitted in auto-commit mode.
	if err != nil {
		log.Fatal(err)
	}

	// Create table.
	table := driver.RandomIdentifier("fileLob")
	if _, err := tx.Exec(fmt.Sprintf("create table %s (file nclob)", table)); err != nil {
		log.Fatalf("create table failed: %s", err)
	}

	stmt, err := tx.Prepare(fmt.Sprintf("insert into %s values (?)", table))
	if err != nil {
		log.Fatal(err)
	}

	lob := &driver.Lob{} // Lob field.

	pipeReader, pipeWriter := io.Pipe() // Create pipe for writing Lob.
	lob.SetReader(pipeReader)           // Use PipeReader as reader for Lob.

	// Use sync.WaitGroup to wait for go-routines to be ended.
	wg := new(sync.WaitGroup)
	wg.Add(1) // Select statement.

	// Start sql insert in own go-routine.
	// The go-routine is going to be ended when the data write via the PipeWriter is finalized.
	go func() {
		if _, err := stmt.Exec(lob); err != nil {
			log.Fatal(err)
		}
		fmt.Println("exec finalized")
		wg.Done()
	}()

	// Read file line by line and write data to pipe.
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if _, err := pipeWriter.Write(scanner.Bytes()); err != nil {
			log.Fatal(err)
		}
		if _, err := pipeWriter.Write([]byte{'\n'}); err != nil { // Write nl which was stripped off by scanner.
			log.Fatal(err)
		}
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	// Close pipeWriter (end insert into db).
	pipeWriter.Close()

	// Wait until exec go-routine is ended.
	wg.Wait()
	stmt.Close()

	if err := tx.Commit(); err != nil {
		log.Fatal(err)
	}

	pipeReader, pipeWriter = io.Pipe() // Create pipe for reading Lob.
	lob.SetWriter(pipeWriter)          // Use PipeWriter as writer for Lob.

	wg.Add(1) // Exec statement.

	// Start sql select in own go-routine.
	// The go-routine is going to be ended when the data read via the PipeReader is finalized.
	go func() {
		if err := db.QueryRow(fmt.Sprintf("select * from %s", table)).Scan(lob); err != nil {
			log.Fatal(err)
		}
		fmt.Println("scan finalized")
		wg.Done()
	}()

	// Read Lob line by line via bufio.Scanner.
	scanner = bufio.NewScanner(pipeReader)
	for scanner.Scan() {
		// Do something with scan result.
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	pipeReader.Close()

	// Wait until select go-routine is ended.
	wg.Wait()

}
Output:

exec finalized
scan finalized
Example (Read)

ExampleLobRead reads data from a large data object database field into a bytes.Buffer. Precondition: the test database table with one field of type BLOB, CLOB or NCLOB must exist. For illustrative purposes we assume, that the database table has exactly one record, so that we can use db.QueryRow.

package main

import (
	"bytes"
	"database/sql"
	"log"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	b := new(bytes.Buffer)

	db, err := sql.Open("hdb", "hdb://user:password@host:port")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	lob := new(driver.Lob)
	lob.SetWriter(b) // SetWriter sets the io.Writer object, to which the database content of the lob field is written.

	if err := db.QueryRow("select * from test").Scan(lob); err != nil {
		log.Fatal(err)
	}
}
Output:

Example (Write)

ExampleLobWrite inserts data read from a file into a database large object field. Precondition: the test database table with one field of type BLOB, CLOB or NCLOB and the test.txt file in the working directory must exist. Lob fields cannot be written in hdb auto commit mode - therefore the insert has to be executed within a transaction.

package main

import (
	"database/sql"
	"log"
	"os"

	"github.com/SAP/go-hdb/driver"
)

func main() {
	file, err := os.Open("test.txt") // Open file.
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	db, err := sql.Open("hdb", "hdb://user:password@host:port")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	tx, err := db.Begin() // Start Transaction to avoid database error: SQL Error 596 - LOB streaming is not permitted in auto-commit mode.
	if err != nil {
		log.Fatal(err)
	}

	stmt, err := tx.Prepare("insert into test values(?)")
	if err != nil {
		log.Fatal(err)
	}

	lob := new(driver.Lob)
	lob.SetReader(file) // SetReader sets the io.Reader object, which content is written to the database lob field.

	if _, err := stmt.Exec(lob); err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	if err := tx.Commit(); err != nil {
		log.Fatal(err)
	}
}
Output:

func NewLob

func NewLob(rd io.Reader, wr io.Writer) *Lob

NewLob creates a new Lob instance with the io.Reader and io.Writer given as parameters.

func (Lob) Reader added in v0.99.0

func (l Lob) Reader() io.Reader

Reader returns the io.Reader of the Lob.

func (*Lob) Scan

func (l *Lob) Scan(src any) error

Scan implements the database/sql/Scanner interface.

func (*Lob) SetReader

func (l *Lob) SetReader(rd io.Reader) *Lob

SetReader sets the io.Reader source for a lob field to be written to database and return *Lob, to enable simple call chaining.

func (*Lob) SetWriter

func (l *Lob) SetWriter(wr io.Writer) *Lob

SetWriter sets the io.Writer destination for a lob field to be read from database and return *Lob, to enable simple call chaining.

func (Lob) Writer added in v0.99.0

func (l Lob) Writer() io.Writer

Writer returns the io.Writer of the Lob.

type NullBytes added in v0.9.1

type NullBytes struct {
	Bytes []byte
	Valid bool // Valid is true if Bytes is not NULL
}

NullBytes represents an []byte that may be null. NullBytes implements the Scanner interface so it can be used as a scan destination, similar to NullString.

func (*NullBytes) Scan added in v0.9.1

func (n *NullBytes) Scan(value any) error

Scan implements the Scanner interface.

func (NullBytes) Value added in v0.9.1

func (n NullBytes) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullDecimal

type NullDecimal struct {
	Decimal *Decimal
	Valid   bool // Valid is true if Decimal is not NULL
}

NullDecimal represents an Decimal that may be null. NullDecimal implements the Scanner interface so it can be used as a scan destination, similar to NullString.

func (*NullDecimal) Scan

func (n *NullDecimal) Scan(value any) error

Scan implements the Scanner interface.

func (NullDecimal) Value

func (n NullDecimal) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullLob added in v0.11.1

type NullLob struct {
	Lob   *Lob
	Valid bool // Valid is true if Lob is not NULL
}

NullLob represents an Lob that may be null. NullLob implements the Scanner interface so it can be used as a scan destination, similar to NullString.

func (*NullLob) Scan added in v0.11.1

func (n *NullLob) Scan(value any) error

Scan implements the database/sql/Scanner interface.

func (NullLob) Value added in v0.99.0

func (n NullLob) Value() (driver.Value, error)

Value implements the database/sql/Valuer interface.

type ParameterType added in v1.8.21

type ParameterType interface {
	ColumnType
	In() bool
	Out() bool
	InOut() bool
}

ParameterType extends ColumnType with stored procedure metadata.

type ParseError added in v0.107.0

type ParseError struct {
	// contains filtered or unexported fields
}

ParseError is the error returned in case DSN is invalid.

func (ParseError) Cause added in v0.107.0

func (e ParseError) Cause() error

Cause returns the cause of the error.

func (ParseError) Error added in v0.107.0

func (e ParseError) Error() string

func (ParseError) Unwrap added in v0.107.0

func (e ParseError) Unwrap() error

Unwrap returns the nested error.

type SessionUser added in v1.10.0

type SessionUser struct {
	Username, Password string
	Schema             string
}

SessionUser provides the fields for a hdb 'connect' (switch user) statement.

type SessionVariables added in v0.14.0

type SessionVariables map[string]string

SessionVariables maps session variables to their values. All defined session variables will be set once after a database connection is opened.

type Sniffer added in v0.107.3

type Sniffer struct {
	// contains filtered or unexported fields
}

A Sniffer is a simple proxy for logging hdb protocol requests and responses.

func NewSniffer added in v0.107.3

func NewSniffer(conn net.Conn, dbConn net.Conn) *Sniffer

NewSniffer creates a new sniffer instance. The conn parameter is the net.Conn connection, where the Sniffer is listening for hdb protocol calls. The dbAddr is the hdb host port address in "host:port" format.

func (*Sniffer) Run added in v0.107.3

func (s *Sniffer) Run() error

Run starts the protocol request and response logging.

type Stats added in v0.103.0

type Stats struct {
	// Gauges
	OpenConnections  int // The number of current established driver connections.
	OpenTransactions int // The number of current open driver transactions.
	OpenStatements   int // The number of current open driver database statements.
	// Counters
	ReadBytes       uint64 // Total bytes read by client connection.
	WrittenBytes    uint64 // Total bytes written by client connection.
	SessionConnects uint64 // Total number of session connects (switch users).
	// Time histograms (Sum and upper bounds in Unit)
	TimeUnit  string                     // Time unit
	ReadTime  *StatsHistogram            // Time spent on reading from connection.
	WriteTime *StatsHistogram            // Time spent on writing to connection.
	AuthTime  *StatsHistogram            // Time spent on authentication.
	SQLTimes  map[string]*StatsHistogram // Time spent on different SQL statements.
}

Stats contains driver statistics.

type StatsHistogram added in v0.107.4

type StatsHistogram struct {
	// Count holds the number of measurements
	Count uint64
	// Sum holds the sum of the measurements.
	Sum float64
	// Buckets contains the count of measurements belonging to a bucket where the
	// value of the measurement is less or equal the bucket map key.
	Buckets map[float64]uint64
}

StatsHistogram represents statistic data in a histogram structure.

type StmtMetadata added in v1.8.21

type StmtMetadata interface {
	ParameterTypes() []ParameterType
	ColumnTypes() []ColumnType
}

StmtMetadata provides access to the parameter and result metadata of a prepared statement.

type StructScanner added in v1.7.0

type StructScanner[S any] struct {
	// contains filtered or unexported fields
}

StructScanner is a database scanner to scan rows into a struct of type S. This enables using structs as scan targets for the exported fields of the struct. For usage please refer to the example.

Example

ExampleStructScanner demonstrates how to read database rows into a go struct.

//go:build !unit

package main

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/SAP/go-hdb/driver"
)

// ExampleScanRow is used to showcase struct scanning.
type ExampleScanRow struct {
	Afield string `sql:"A"` // database field name is "A"
	Bfield int    `sql:"B"` // database field name is "B"
	C      bool   // database field name is "C"
	AsD    string // database field name is "D"
}

// Tag implements the Tagger interface to define tags for ExampleScanRow dynamically.
func (s *ExampleScanRow) Tag(fieldName string) (string, bool) {
	switch fieldName {
	case "AsD":
		return `sql:"D"`, true
	default:
		return "", false
	}
}

// ExampleStructScanner demonstrates how to read database rows into a go struct.
func main() {
	// Open Test database.
	db := sql.OpenDB(driver.MT.Connector())
	defer db.Close()

	table := driver.RandomIdentifier("structscanner_")

	// Create table.
	if _, err := db.Exec(fmt.Sprintf("create table %s (a varchar(30), b integer, c boolean, d varchar(20))", table)); err != nil {
		log.Fatal(err)
	}

	// Insert test row data.
	if _, err := db.Exec(fmt.Sprintf("insert into %s values (?,?,?,?)", table), "test", 42, true, "I am D"); err != nil {
		log.Fatal(err)
	}

	// Create scanner.
	scanner, err := driver.NewStructScanner[ExampleScanRow]()
	if err != nil {
		log.Fatal(err)
	}

	// Scan target.
	row := new(ExampleScanRow)

	// Scan rows with the help of the struct scanner.
	if err = func() error {
		rows, err := db.Query(fmt.Sprintf("select * from %s", table))
		if err != nil {
			return err
		}
		defer rows.Close()

		for rows.Next() {
			if err := scanner.Scan(rows, row); err != nil {
				return err
			}
		}
		if rows.Err() != nil {
			return err
		}
		return rows.Close()
	}(); err != nil {
		log.Fatal(err)
	}

	// Scan a single row with the help of the struct scanner.
	if err = func() error {
		rows, err := db.Query(fmt.Sprintf("select * from %s", table))
		if err != nil {
			return err
		}
		// Rows will be closed by scanner.ScanRow.
		return scanner.ScanRow(rows, row)
	}(); err != nil {
		log.Fatal(err)
	}

}
Output:

func NewStructScanner added in v1.7.0

func NewStructScanner[S any]() (*StructScanner[S], error)

NewStructScanner returns a new struct scanner.

func (StructScanner[S]) Scan added in v1.7.0

func (sc StructScanner[S]) Scan(rows *sql.Rows, s *S) error

Scan scans row field values into struct s of type *S.

func (StructScanner[S]) ScanRow added in v1.7.0

func (sc StructScanner[S]) ScanRow(rows *sql.Rows, s *S) error

ScanRow scans the field values of the first row in rows into struct s of type *S and closes rows.

type TLSPrms added in v0.107.0

type TLSPrms struct {
	ServerName         string
	InsecureSkipVerify bool
	RootCAFiles        []string
}

TLSPrms is holding the TLS parameters of a DSN structure.

type Tagger added in v1.7.0

type Tagger interface {
	Tag(fieldName string) (value string, ok bool)
}

Tagger is an interface used to tag structure fields dynamically.

type Version added in v0.107.3

type Version struct {
	// contains filtered or unexported fields
}

Version is representing a hdb version.

func (*Version) BuildID added in v0.107.3

func (v *Version) BuildID() uint64

BuildID returns the build id field of a HDBVersionNumber.

func (*Version) Major added in v0.107.3

func (v *Version) Major() uint64

Major returns the major field of a hdbVersionNumber.

func (*Version) Minor added in v0.107.3

func (v *Version) Minor() uint64

Minor returns the minor field of a HDBVersionNumber.

func (*Version) Patch added in v0.107.3

func (v *Version) Patch() uint64

Patch returns the patch field of a HDBVersionNumber.

func (*Version) Revision added in v0.107.3

func (v *Version) Revision() uint64

Revision returns the revision field of a HDBVersionNumber.

func (*Version) SPS added in v0.107.3

func (v *Version) SPS() uint64

SPS returns the sps field of a HDBVersionNumber.

func (*Version) String added in v0.107.3

func (v *Version) String() string

Directories

Path Synopsis
Package dial provides types to implement go-hdb custom dialers.
Package dial provides types to implement go-hdb custom dialers.
internal
protocol
Package protocol implements the hdb command network protocol.
Package protocol implements the hdb command network protocol.
protocol/auth
Package auth provides authentication methods.
Package auth provides authentication methods.
protocol/cache
Package cache provides generic cache types.
Package cache provides generic cache types.
protocol/encoding
Package encoding implements hdb field type en,- and decodings.
Package encoding implements hdb field type en,- and decodings.
protocol/julian
Package julian provided julian time conversion functions.
Package julian provided julian time conversion functions.
protocol/levenshtein
Package levenshtein includes the levenshtein distance algorithm plus additional helper functions.
Package levenshtein includes the levenshtein distance algorithm plus additional helper functions.
rand/alphanum
Package alphanum implements functions for randomized alphanum content.
Package alphanum implements functions for randomized alphanum content.
types
Package types provides database types.
Package types provides database types.
unsafe
Package unsafe provides wrapper functions for 'unsafe' type conversions.
Package unsafe provides wrapper functions for 'unsafe' type conversions.
Package spatial implements geo spatial types and functions.
Package spatial implements geo spatial types and functions.
unicode
cesu8
Package cesu8 implements functions and constants to support text encoded in CESU-8.
Package cesu8 implements functions and constants to support text encoded in CESU-8.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL