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/doc/63cef00e229b4010be06cfa01e1d338b/1.0.12/en-US/SAP_HANA_SQL_Command_Network_Protocol_Reference_en.pdf
Example ¶
package main
import (
"context"
"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.PingContext(context.Background()); 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 an 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.
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf("create table %s (i integer, f double)", tableName)); err != nil {
log.Fatal(err)
}
// Prepare statement.
stmt, err := db.PrepareContext(ctx, 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.ExecContext(ctx, args...); err != nil {
log.Fatal(err)
}
// Bulk insert via function.
i := 0
if _, err := stmt.ExecContext(ctx, 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.QueryRowContext(ctx, fmt.Sprintf("select count(*) from %s", tableName)).Scan(&count); err != nil {
log.Fatal(err)
}
fmt.Print(count)
// Drop table.
if _, err := db.ExecContext(ctx, fmt.Sprintf("drop table %s", tableName)); err != nil {
log.Fatal(err)
}
}
Output: 2000
Example (BulkInsertViaIterator) ¶
ExampleBulkInsert inserts 2000 rows into a database table:
1000 rows are inserted via a slices chunk 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.
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf("create table %s (i integer, f double)", tableName)); err != nil {
log.Fatal(err)
}
// Prepare statement.
stmt, err := db.PrepareContext(ctx, fmt.Sprintf("insert into %s values (?, ?)", tableName))
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
// Bulk insert via slices chunk iterator.
args := make([]any, numRow*2)
for i := range numRow {
args[i*2], args[i*2+1] = i, float64(i)
}
if _, err := stmt.ExecContext(ctx, 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.ExecContext(ctx, myIter); err != nil {
log.Fatal(err)
}
// Select number of inserted rows.
var count int
if err := db.QueryRowContext(ctx, fmt.Sprintf("select count(*) from %s", tableName)).Scan(&count); err != nil {
log.Fatal(err)
}
fmt.Print(count)
// Drop table.
if _, err := db.ExecContext(ctx, fmt.Sprintf("drop table %s", tableName)); err != nil {
log.Fatal(err)
}
}
Output: 2000
Example (CallProcedure) ¶
//go:build !unit
package main
import (
"context"
"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_")
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf(procedureOut, procedureName)); err != nil { // Create stored procedure.
log.Fatal(err)
}
var out string
if _, err := db.ExecContext(ctx, 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_")
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf("create type %s as table (x nvarchar(256))", tableType)); err != nil { // Create table type.
log.Fatal(err)
}
if _, err := db.ExecContext(ctx, 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.PrepareContext(ctx, fmt.Sprintf("call %s(?)", procedureName))
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
if _, err := stmt.ExecContext(ctx, 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_")
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf("create type %s as table (x nvarchar(256))", tableType)); err != nil { // Create table type.
log.Fatal(err)
}
if _, err := db.ExecContext(ctx, fmt.Sprintf(procedureTable, procedureName, tableType)); err != nil { // Create stored procedure.
log.Fatal(err)
}
if _, err := db.ExecContext(ctx, fmt.Sprintf("create local temporary table %s like %s", tableName, tableType)); err != nil {
log.Fatal(err)
}
if _, err := db.ExecContext(ctx, 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.PrepareContext(ctx, fmt.Sprintf("call %s(%s, ?)", procedureName, tableName))
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
if _, err := stmt.ExecContext(ctx, 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
Example (CustomDecimal) ¶
Example_customDecimal creates a table with a single decimal attribute, inserts a record using a custom decimal type implementing the database/sql decimalDecompose and decimalCompose interfaces, and selects the entry afterwards scanning into the same custom type.
package main
import (
"context"
"database/sql"
"fmt"
"log"
"math/big"
"github.com/SAP/go-hdb/driver"
)
// customDecimal is a minimal custom decimal type implementing the database/sql
// decimalDecompose and decimalCompose interfaces (unexported, defined in the Go
// standard library in database/sql/convert.go and database/sql/driver/types.go):
//
// Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32)
// Compose(form byte, negative bool, coefficient []byte, exponent int32) error
//
// with value = (-1)^negative × coefficient × 10^exponent (form 0 = finite).
//
// It demonstrates that go-hdb can write and scan decimal / fixed database
// attributes via any type implementing these interfaces - without using
// driver.Decimal.
type customDecimal struct {
form byte
negative bool
coeff *big.Int
exp int32
}
// String renders the decompose fields as coefficient and exponent.
func (d customDecimal) String() string {
sign := ""
if d.negative {
sign = "-"
}
return fmt.Sprintf("%s%sE%d", sign, d.coeff, d.exp)
}
// Decompose implements the database/sql decimalDecompose interface (write side).
func (d customDecimal) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
if d.form != 0 {
return d.form, d.negative, nil, 0
}
n := (d.coeff.BitLen() + 7) / 8
if cap(buf) >= n {
buf = buf[:n]
coefficient = d.coeff.FillBytes(buf)
} else {
coefficient = d.coeff.Bytes()
}
return 0, d.negative, coefficient, d.exp
}
// Compose implements the database/sql decimalCompose interface (scan side).
func (d *customDecimal) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
d.form = form
d.negative = negative
if d.coeff == nil {
d.coeff = new(big.Int)
}
d.coeff.SetBytes(coefficient)
d.exp = exponent
return nil
}
func main() {
db := sql.OpenDB(driver.MT.Connector())
defer db.Close()
tableName := driver.RandomIdentifier("table_")
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf("create table %s (x decimal)", tableName)); err != nil {
log.Fatal(err)
}
// value 1 = 1 × 10^0
in := customDecimal{form: 0, negative: false, coeff: big.NewInt(1), exp: 0}
if _, err := db.ExecContext(ctx, fmt.Sprintf("insert into %s values(?)", tableName), in); err != nil {
log.Fatal(err)
}
var out customDecimal
if err := db.QueryRowContext(ctx, fmt.Sprintf("select * from %s", tableName)).Scan(&out); err != nil {
log.Fatal(err)
}
fmt.Printf("Decimal value: %s", out)
}
Output: Decimal value: 1E0
Example (CustomFixed) ¶
Example_customFixed is like Example_customDecimal but uses a fixed decimal column (decimal(precision, scale)), exercising the fixed encode/decode path with a custom decimal type implementing the database/sql decimalDecompose and decimalCompose interfaces.
package main
import (
"context"
"database/sql"
"fmt"
"log"
"math/big"
"github.com/SAP/go-hdb/driver"
)
// customDecimal is a minimal custom decimal type implementing the database/sql
// decimalDecompose and decimalCompose interfaces (unexported, defined in the Go
// standard library in database/sql/convert.go and database/sql/driver/types.go):
//
// Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32)
// Compose(form byte, negative bool, coefficient []byte, exponent int32) error
//
// with value = (-1)^negative × coefficient × 10^exponent (form 0 = finite).
//
// It demonstrates that go-hdb can write and scan decimal / fixed database
// attributes via any type implementing these interfaces - without using
// driver.Decimal.
type customDecimal struct {
form byte
negative bool
coeff *big.Int
exp int32
}
// String renders the decompose fields as coefficient and exponent.
func (d customDecimal) String() string {
sign := ""
if d.negative {
sign = "-"
}
return fmt.Sprintf("%s%sE%d", sign, d.coeff, d.exp)
}
// Decompose implements the database/sql decimalDecompose interface (write side).
func (d customDecimal) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
if d.form != 0 {
return d.form, d.negative, nil, 0
}
n := (d.coeff.BitLen() + 7) / 8
if cap(buf) >= n {
buf = buf[:n]
coefficient = d.coeff.FillBytes(buf)
} else {
coefficient = d.coeff.Bytes()
}
return 0, d.negative, coefficient, d.exp
}
// Compose implements the database/sql decimalCompose interface (scan side).
func (d *customDecimal) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
d.form = form
d.negative = negative
if d.coeff == nil {
d.coeff = new(big.Int)
}
d.coeff.SetBytes(coefficient)
d.exp = exponent
return nil
}
func main() {
db := sql.OpenDB(driver.MT.Connector())
defer db.Close()
tableName := driver.RandomIdentifier("table_")
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf("create table %s (x decimal(18,2))", tableName)); err != nil {
log.Fatal(err)
}
// value -12.34 = -1234 × 10^-2
in := customDecimal{form: 0, negative: true, coeff: big.NewInt(1234), exp: -2}
if _, err := db.ExecContext(ctx, fmt.Sprintf("insert into %s values(?)", tableName), in); err != nil {
log.Fatal(err)
}
var out customDecimal
if err := db.QueryRowContext(ctx, fmt.Sprintf("select * from %s", tableName)).Scan(&out); err != nil {
log.Fatal(err)
}
fmt.Printf("Fixed value: %s", out)
}
Output: Fixed value: -1234E-2
Index ¶
- Constants
- Variables
- func NewTLSConfig(serverName string, insecureSkipVerify bool, rootCAFiles ...string) (*tls.Config, error)
- func ProtTrace() bool
- func SQLTrace() bool
- func ScanLobBytes(_ any, _ *[]byte) error
- func ScanLobString(_ any, _ *string) error
- func ScanLobWriter(_ any, _ io.Writer) error
- func SetProtTrace(on bool)
- func SetSQLTrace(on bool)
- func Unregister() errordeprecated
- func WithStmtMetadata(ctx context.Context, stmtMetadata *StmtMetadata) context.Context
- func WithUserSwitch(ctx context.Context, u *SessionUser) context.Context
- type ColumnType
- type Conn
- type Connector
- func NewBasicAuthConnector(host, username, password string) *Connectordeprecated
- func NewConfigConnector(cfg *ConnectorConfig) (*Connector, error)
- func NewConnector() *Connectordeprecated
- func NewDSNConnector(dsnStr string) (*Connector, error)deprecated
- func NewJWTAuthConnector(host, token string) *Connectordeprecated
- func NewX509AuthConnector(host string, clientCert, clientKey []byte) (*Connector, error)deprecated
- func NewX509AuthConnectorByFiles(host, clientCertFile, clientKeyFile string) (*Connector, error)deprecated
- func (c *Connector) ApplicationName() stringdeprecated
- func (c *Connector) BufferSize() intdeprecated
- func (c *Connector) BulkSize() intdeprecated
- func (c *Connector) CESU8Decoder() func() transform.Transformerdeprecated
- func (c *Connector) CESU8Encoder() func() transform.Transformerdeprecated
- func (c *Connector) ClientCert() (clientCert, clientKey []byte)deprecated
- func (c *Connector) Compressor() compress.Compressordeprecated
- func (c *Connector) Config() ConnectorConfig
- func (c *Connector) Connect(ctx context.Context) (driver.Conn, error)
- func (c *Connector) ConnectionRouting() booldeprecated
- func (c *Connector) DatabaseName() stringdeprecated
- func (c *Connector) DefaultSchema() stringdeprecated
- func (c *Connector) Dfv() intdeprecated
- func (c *Connector) Dialer() dial.Dialerdeprecated
- func (c *Connector) Driver() driver.Driver
- func (c *Connector) EmptyDateAsNull() booldeprecated
- func (c *Connector) FetchSize() intdeprecated
- func (c *Connector) Host() stringdeprecated
- func (c *Connector) LobChunkSize() intdeprecated
- func (c *Connector) Locale() stringdeprecated
- func (c *Connector) Logger() *slog.Loggerdeprecated
- func (c *Connector) NativeDriver() Driver
- func (c *Connector) Password() stringdeprecated
- func (c *Connector) PingInterval() time.Durationdeprecated
- func (c *Connector) RefreshClientCert() func() (clientCert, clientKey []byte, ok bool)deprecated
- func (c *Connector) RefreshPassword() func() (password string, ok bool)deprecated
- func (c *Connector) RefreshToken() func() (token string, ok bool)deprecated
- func (c *Connector) SessionVariables() SessionVariablesdeprecated
- func (c *Connector) SetApplicationName(name string)deprecated
- func (c *Connector) SetBufferSize(bufferSize int)deprecated
- func (c *Connector) SetBulkSize(bulkSize int)deprecated
- func (c *Connector) SetCESU8Decoder(cesu8DecoderFn func() transform.Transformer)deprecated
- func (c *Connector) SetCESU8Encoder(cesu8EncoderFn func() transform.Transformer)deprecated
- func (c *Connector) SetCompressor(compressor compress.Compressor)deprecated
- func (c *Connector) SetConnectionRouting(connectionRouting bool)deprecated
- func (c *Connector) SetDefaultSchema(schema string)deprecated
- func (c *Connector) SetDfv(dfv int)deprecated
- func (c *Connector) SetDialer(dialer dial.Dialer)deprecated
- func (c *Connector) SetEmptyDateAsNull(emptyDateAsNull bool)deprecated
- func (c *Connector) SetFetchSize(fetchSize int)deprecated
- func (c *Connector) SetLobChunkSize(lobChunkSize int)deprecated
- func (c *Connector) SetLocale(locale string)deprecated
- func (c *Connector) SetLogger(logger *slog.Logger)deprecated
- func (c *Connector) SetPassword(password string)deprecated
- func (c *Connector) SetPingInterval(d time.Duration)deprecated
- func (c *Connector) SetRefreshClientCert(refreshClientCertFn func() (clientCert, clientKey []byte, ok bool))deprecated
- func (c *Connector) SetRefreshPassword(refreshPasswordFn func() (password string, ok bool))deprecated
- func (c *Connector) SetRefreshToken(refreshTokenFn func() (token string, ok bool))deprecated
- func (c *Connector) SetSessionVariables(sessionVariables SessionVariables)deprecated
- func (c *Connector) SetTCPKeepAlive(tcpKeepAlive time.Duration)deprecated
- func (c *Connector) SetTCPKeepAliveConfig(tcpKeepAliveConfig net.KeepAliveConfig)deprecated
- func (c *Connector) SetTLS(serverName string, insecureSkipVerify bool, rootCAFiles ...string) errordeprecated
- func (c *Connector) SetTLSConfig(tlsConfig *tls.Config)deprecated
- func (c *Connector) SetTimeout(timeout time.Duration)deprecated
- func (c *Connector) TCPKeepAlive() time.Durationdeprecated
- func (c *Connector) TCPKeepAliveConfig() net.KeepAliveConfigdeprecated
- func (c *Connector) TLSConfig() *tls.Configdeprecated
- func (c *Connector) Timeout() time.Durationdeprecated
- func (c *Connector) Token() stringdeprecated
- func (c *Connector) Username() stringdeprecated
- func (c *Connector) WithDatabase(databaseName string) *Connectordeprecated
- type ConnectorConfig
- type DB
- type DBConnectInfo
- type DBError
- type DSN
- type Decimal
- type Driver
- type Error
- type Identifier
- type Lob
- type NullBytes
- type NullDecimal
- type NullLob
- type ParameterType
- type ParseError
- type ProtTraceConfig
- type SQLTraceConfig
- type SessionUser
- type SessionVariablesdeprecated
- type Sniffer
- type Stats
- type StatsHistogram
- type StmtMetadata
- type StructScanner
- type Tagger
- type Version
Examples ¶
- Package
- Package (BulkInsert)
- Package (BulkInsertViaIterator)
- Package (CallProcedure)
- Package (CustomDecimal)
- Package (CustomFixed)
- DB
- DSN
- Decimal
- Error
- Lob (Pipe)
- Lob (Read)
- Lob (Write)
- NewConfigConnector
- NewConfigConnector (Jwt)
- NewConfigConnector (X509)
- ParseDSNConfig
- StructScanner
- WithStmtMetadata
- WithUserSwitch
Constants ¶
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.
const ( DSNTLSRootCAFile = "TLSRootCAFile" // Path/filename to root certificate(s). DSNTLSServerName = "TLSServerName" // ServerName to verify the hostname. DSNTLSInsecureSkipVerify = "TLSInsecureSkipVerify" // Disables TLS certificate verification. Exposes the connection to man-in-the-middle attacks. Use only in development or testing environments, never in production. )
DSN TLS parameters. For more information please see https://golang.org/pkg/crypto/tls/#Config. For more flexibility in TLS configuration please see driver.ConnectorConfig.
const ( HdbWarning = 0 HdbError = 1 HdbFatalError = 2 )
HDB error levels.
const DriverName = "hdb"
DriverName is the driver name to use with sql.Open for hdb databases.
const DriverVersion = "1.19.0"
DriverVersion is the version number of the hdb driver.
Variables ¶
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.
var ErrNestedQuery = errors.New("nested sql queries are not supported") // deprecated
ErrNestedQuery is deprecated, so currently not used (raised as an error) by the driver.
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.
var ErrScanOnClosedResultset = errors.New("scan on closed resultset")
ErrScanOnClosedResultset is the error raised in case a scan is executed on a closed resultset.
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 disallowed context.
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 NewTLSConfig ¶ added in v1.19.0
func NewTLSConfig(serverName string, insecureSkipVerify bool, rootCAFiles ...string) (*tls.Config, error)
NewTLSConfig builds a TLS configuration with the given server name, skip-verify flag and root CA files: with no files the system root store is used, with files only the file CAs are trusted.
func ProtTrace ¶ added in v1.16.6
func ProtTrace() bool
ProtTrace returns true if protocol tracing output is active, false otherwise.
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
ScanLobBytes deprecated: starting with go1.27 bytes based scan targets are supported natively.
func ScanLobString ¶ added in v1.0.0
ScanLobString deprecated: starting with go1.27 string based scan targets are supported natively.
func ScanLobWriter ¶ added in v1.0.0
ScanLobWriter deprecated: starting with go1.27 io.Writer based scan targets are supported natively.
func SetProtTrace ¶ added in v1.16.6
func SetProtTrace(on bool)
SetProtTrace sets protocol tracing output active or inactive. Credential fields in protocol trace output are masked by default and only shown in full if redaction is explicitly disabled. If the application cannot fully control command-line flags (e.g. in shared or managed environments), call SetProtTrace(false) at startup to override the -hdb.protTrace flag.
func SetSQLTrace ¶ added in v1.2.0
func SetSQLTrace(on bool)
SetSQLTrace sets sql tracing output active or inactive.
func Unregister
deprecated
added in
v1.8.14
func Unregister() error
Unregister is deprecated.
Deprecated: Unregister no longer performs any action; it exists only to keep existing callers compiling.
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_")
ctx := context.Background()
if _, err := db.ExecContext(ctx, fmt.Sprintf(procOut, procedure)); err != nil { // Create stored procedure.
log.Fatal(err)
}
// Call PrepareContext with statement metadata context value.
var stmtMetadata driver.StmtMetadata
ctxMeta := driver.WithStmtMetadata(context.Background(), &stmtMetadata)
stmt, err := db.PrepareContext(ctxMeta, 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.ExecContext(ctx, 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() {
ctrCfg := driver.MT.Connector().Config()
ctr, err := driver.NewConfigConnector(&ctrCfg)
if err != nil {
log.Fatal(err)
}
db := sql.OpenDB(ctr)
defer db.Close()
tableName := driver.RandomIdentifier("table_")
// Own connector, freshly built: no refresh can have run, so the
// config copy is current.
sessionUser := &driver.SessionUser{Username: ctrCfg.Username, Password: ctrCfg.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
deprecated
added in
v0.10.0
func NewConfigConnector ¶ added in v1.19.0
func NewConfigConnector(cfg *ConnectorConfig) (*Connector, error)
NewConfigConnector validates cfg and returns a Connector holding a deep copy of it. Invalid values are returned as error, never silently clamped. The X509 identity is established like the legacy constructors: files are read first when set, else static bytes; file read and key errors fail fast here, never on first connect.
Example ¶
ExampleNewConfigConnector shows how to open a database with the help of a connector using basic authentication.
package main
import (
"context"
"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
}
cfg := driver.NewConnectorConfig()
cfg.Host = host
cfg.Username = username
cfg.Password = password
cfg.DatabaseName = database
if serverName, insecureSkipVerify, rootCAFile, ok := lookupTLS(); ok {
tlsConfig, err := driver.NewTLSConfig(serverName, insecureSkipVerify, rootCAFile)
if err != nil {
log.Fatal(err)
}
cfg.TLSConfig = tlsConfig
}
connector, err := driver.NewConfigConnector(cfg)
if err != nil {
log.Fatal(err)
}
db := sql.OpenDB(connector)
defer db.Close()
if err := db.PingContext(context.Background()); err != nil {
log.Fatal(err)
}
}
Output:
Example (Jwt) ¶
ExampleNewConfigConnector_jwt shows how to open a database with the help of a connector using JWT authentication.
package main
import (
"context"
"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"
cfg := driver.NewConnectorConfig()
cfg.Host = host
cfg.Token = invalidToken
// in case JWT authentication fails provide a (new) valid token.
cfg.RefreshToken = func() (string, bool) { return token, true }
if serverName, insecureSkipVerify, rootCAFile, ok := lookupTLS(); ok {
tlsConfig, err := driver.NewTLSConfig(serverName, insecureSkipVerify, rootCAFile)
if err != nil {
log.Fatal(err)
}
cfg.TLSConfig = tlsConfig
}
connector, err := driver.NewConfigConnector(cfg)
if err != nil {
log.Fatal(err)
}
db := sql.OpenDB(connector)
defer db.Close()
if err := db.PingContext(context.Background()); err != nil {
log.Fatal(err)
}
}
Output:
Example (X509) ¶
ExampleNewConfigConnector_x509 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 (
"context"
"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
}
cfg := driver.NewConnectorConfig()
cfg.Host = host
cfg.ClientCertFile = clientCertFile
cfg.ClientKeyFile = clientKeyFile
if serverName, insecureSkipVerify, rootCAFile, ok := lookupTLS(); ok {
tlsConfig, err := driver.NewTLSConfig(serverName, insecureSkipVerify, rootCAFile)
if err != nil {
log.Fatal(err)
}
cfg.TLSConfig = tlsConfig
}
connector, err := driver.NewConfigConnector(cfg)
if err != nil {
log.Fatal(err)
}
db := sql.OpenDB(connector)
defer db.Close()
if err := db.PingContext(context.Background()); err != nil {
log.Fatal(err)
}
}
Output:
func NewConnector
deprecated
added in
v0.103.0
func NewConnector() *Connector
NewConnector returns a new Connector instance with default values.
Deprecated: build a ConnectorConfig with NewConnectorConfig and construct with NewConfigConnector.
func NewDSNConnector
deprecated
added in
v0.10.0
func NewJWTAuthConnector
deprecated
added in
v0.107.0
func NewX509AuthConnector
deprecated
added in
v0.107.0
NewX509AuthConnector creates a connector for X509 (client certificate) authentication. Parameters clientCert and clientKey in PEM format, clientKey not password encrypted.
Deprecated: build a ConnectorConfig (NewConnectorConfig, set ClientCert/ClientKey) and construct with NewConfigConnector.
func NewX509AuthConnectorByFiles
deprecated
added in
v0.107.0
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 encrypted.
Deprecated: build a ConnectorConfig (NewConnectorConfig, set ClientCertFile/ClientKeyFile) and construct with NewConfigConnector.
func (*Connector) ApplicationName
deprecated
added in
v0.103.0
func (*Connector) BufferSize
deprecated
added in
v0.99.0
func (*Connector) CESU8Decoder
deprecated
added in
v0.105.1
func (c *Connector) CESU8Decoder() func() transform.Transformer
CESU8Decoder returns the CESU-8 decoder constructor of the connector.
Deprecated: read from own ConnectorConfig or Connector.Config() instead (see its field documentation).
func (*Connector) CESU8Encoder
deprecated
added in
v0.105.1
func (c *Connector) CESU8Encoder() func() transform.Transformer
CESU8Encoder returns the CESU-8 encoder constructor of the connector.
Deprecated: read from own ConnectorConfig or Connector.Config() instead (see its field documentation).
func (*Connector) ClientCert
deprecated
added in
v0.107.0
func (*Connector) Compressor
deprecated
added in
v1.17.2
func (c *Connector) Compressor() compress.Compressor
Compressor returns the lz4 compressor of the connector.
Deprecated: read from own ConnectorConfig or Connector.Config() instead.
func (*Connector) Config ¶ added in v1.19.0
func (c *Connector) Config() ConnectorConfig
Config returns a deep copy of the connector's user configuration for derive-tweak-construct cycles: construction input plus deprecated setter mutations. Runtime-refreshed credentials stay connector-local; derives inherit the refresh callbacks and re-acquire on first use.
func (*Connector) Connect ¶ added in v0.10.0
Connect implements the database/sql/driver/Connector interface.
func (*Connector) ConnectionRouting
deprecated
added in
v1.17.3
func (*Connector) DatabaseName
deprecated
added in
v1.5.0
func (*Connector) DefaultSchema
deprecated
added in
v0.99.0
func (*Connector) Driver ¶ added in v0.10.0
Driver implements the database/sql/driver/Connector interface.
func (*Connector) EmptyDateAsNull
deprecated
added in
v1.1.7
func (*Connector) LobChunkSize
deprecated
added in
v0.99.0
func (*Connector) NativeDriver ¶ added in v0.107.0
NativeDriver returns the go-hdb Driver interface of the Connector, exposing the go-hdb specific driver functions (Name, Version, Stats). Use Driver for the standard database/sql/driver.Driver.
func (*Connector) PingInterval
deprecated
added in
v0.100.9
func (*Connector) RefreshClientCert
deprecated
added in
v0.107.0
func (*Connector) RefreshPassword
deprecated
added in
v0.107.0
func (*Connector) RefreshToken
deprecated
added in
v0.107.0
func (*Connector) SessionVariables
deprecated
added in
v0.14.0
func (c *Connector) SessionVariables() SessionVariables
SessionVariables returns the session variables stored in connector.
Deprecated: read from own ConnectorConfig or Connector.Config() instead.
func (*Connector) SetApplicationName
deprecated
added in
v0.103.0
func (*Connector) SetBufferSize
deprecated
added in
v0.103.0
func (*Connector) SetBulkSize
deprecated
added in
v0.99.0
func (*Connector) SetCESU8Decoder
deprecated
added in
v0.105.1
func (c *Connector) SetCESU8Decoder(cesu8DecoderFn func() transform.Transformer)
SetCESU8Decoder sets the CESU-8 decoder constructor of the connector. A nil constructor resets to the default decoder.
Deprecated: configure via ConnectorConfig.CESU8Decoder.
func (*Connector) SetCESU8Encoder
deprecated
added in
v0.105.1
func (c *Connector) SetCESU8Encoder(cesu8EncoderFn func() transform.Transformer)
SetCESU8Encoder sets the CESU-8 encoder constructor of the connector. A nil constructor resets to the default encoder.
Deprecated: configure via ConnectorConfig.CESU8Encoder.
func (*Connector) SetCompressor
deprecated
added in
v1.17.2
func (c *Connector) SetCompressor(compressor compress.Compressor)
SetCompressor sets the lz4 compressor of the connector.
Deprecated: configure via ConnectorConfig.Compressor.
func (*Connector) SetConnectionRouting
deprecated
added in
v1.17.3
func (*Connector) SetDefaultSchema
deprecated
added in
v0.99.0
func (*Connector) SetEmptyDateAsNull
deprecated
added in
v1.1.7
func (*Connector) SetFetchSize
deprecated
added in
v0.10.0
func (*Connector) SetLobChunkSize
deprecated
added in
v0.105.1
func (*Connector) SetPassword
deprecated
added in
v0.108.0
SetPassword sets the basic authentication password of the connector. It writes through to the stored configuration so Config() gives it back; refresh-driven updates stay runtime-local (see ConnectorConfig).
Deprecated: configure via ConnectorConfig.Password.
func (*Connector) SetPingInterval
deprecated
added in
v0.100.9
func (*Connector) SetRefreshClientCert
deprecated
added in
v0.107.0
func (c *Connector) SetRefreshClientCert(refreshClientCertFn func() (clientCert, clientKey []byte, ok bool))
SetRefreshClientCert sets the callback function for X509 authentication client certificate and key refresh.
Deprecated: configure via ConnectorConfig.RefreshClientCert (see the ConnectorConfig field documentation for concurrency).
func (*Connector) SetRefreshPassword
deprecated
added in
v0.107.0
SetRefreshPassword sets the callback function for basic authentication password refresh.
Deprecated: configure via ConnectorConfig.RefreshPassword (see the ConnectorConfig field documentation for concurrency).
func (*Connector) SetRefreshToken
deprecated
added in
v0.107.0
func (*Connector) SetSessionVariables
deprecated
added in
v0.14.0
func (c *Connector) SetSessionVariables(sessionVariables SessionVariables)
SetSessionVariables sets the session variables of the connector.
Deprecated: configure via ConnectorConfig.SessionVariables.
func (*Connector) SetTCPKeepAlive
deprecated
added in
v0.100.8
SetTCPKeepAlive sets the tcp keep-alive value of the connector.
Mirrors net.Dialer: zero uses the net default (15s), negative disables.
For more information please see net.Dialer structure.
Deprecated: configure via ConnectorConfig.TCPKeepAlive.
func (*Connector) SetTCPKeepAliveConfig
deprecated
added in
v1.12.0
func (c *Connector) SetTCPKeepAliveConfig(tcpKeepAliveConfig net.KeepAliveConfig)
SetTCPKeepAliveConfig sets the tcp keep-alive config value of the connector.
Deprecated: configure via ConnectorConfig.TCPKeepAliveConfig (see its field documentation).
func (*Connector) SetTLS
deprecated
added in
v0.107.0
SetTLS sets the TLS configuration of the connector with given parameters. An existing connector TLS configuration is replaced.
Deprecated: configure via ConnectorConfig.TLSConfig, built with NewTLSConfig.
func (*Connector) SetTLSConfig
deprecated
added in
v0.11.0
func (*Connector) SetTimeout
deprecated
added in
v0.10.0
func (*Connector) TCPKeepAlive
deprecated
added in
v0.100.8
func (*Connector) TCPKeepAliveConfig
deprecated
added in
v1.12.0
func (c *Connector) TCPKeepAliveConfig() net.KeepAliveConfig
TCPKeepAliveConfig returns the tcp keep-alive config value of the connector.
Deprecated: read from own ConnectorConfig or Connector.Config() instead.
func (*Connector) WithDatabase
deprecated
added in
v1.5.0
type ConnectorConfig ¶ added in v1.19.0
type ConnectorConfig struct {
// Endpoint.
Host string
DatabaseName string
// Authentication. Password doubles as external-ticket carrier
// when Username is empty (hdbcli parity: ticket prefix selects
// the method); Token is the explicit JWT door. Refresh callbacks
// may run concurrently when shared across Connectors.
Username string
Password string
RefreshPassword func() (password string, ok bool)
Token string
RefreshToken func() (token string, ok bool)
// X509 client identity (PEM-encoded, key not password encrypted).
// Static bytes and files are mutually exclusive: set one pair,
// not both (validate rejects both). Files are read at construction
// (fail fast) and re-read on refresh while RefreshClientCert is nil.
ClientCert []byte // cert bytes, with ClientKey
ClientKey []byte // key bytes, with ClientCert
ClientCertFile string // cert file path, with ClientKeyFile
ClientKeyFile string // key file path, with ClientCertFile
RefreshClientCert func() (clientCert, clientKey []byte, ok bool) // explicit refresh override, nil unless set
// Network.
DialTimeout time.Duration // budgets establishment (dial + TLS handshake); zero disables
ReadTimeout time.Duration // budgets socket reads; zero disables deadlines
WriteTimeout time.Duration // budgets socket writes; zero disables deadlines
// PingInterval is the time between connection validity checks.
// Pinging detects broken connections: if the ping fails, another
// connection out of the pool is used automatically instead of
// returning an error. Zero disables pinging; otherwise a ping
// runs when an idle pooled connection is reused and the time
// since its last use reaches the interval.
PingInterval time.Duration
TCPKeepAlive time.Duration // see net.Dialer: zero uses the net default (15s), negative disables
TCPKeepAliveConfig net.KeepAliveConfig // see net.Dialer
TLSConfig *tls.Config
Dialer dial.Dialer // required; NewConnectorConfig provides the default dialer
// Session.
DefaultSchema string
// SessionVariables maps session variables to their values. All
// defined session variables will be set once after a database
// connection is opened.
SessionVariables map[string]string
ApplicationName string
// Locale follows "SAP HANA SQL Command Network Protocol".
Locale string
// Transfer tuning: FetchSize is rows; the rest are bytes/count.
BufferSize int
FetchSize int
LobChunkSize int
BulkSize int
Dfv int // client data format version, see protocol.SupportedDfvs
// Protocol behavior. All function/interface fields are required:
// NewConnectorConfig provides the defaults.
// CESU8Decoder is the CESU-8 decoder constructor, called once per
// connection: it is a factory (not a decoder) because one
// transformer is created per connection.
CESU8Decoder func() transform.Transformer
// CESU8Encoder is the CESU-8 encoder constructor, called once per
// connection: it is a factory (not an encoder) because one
// transformer is created per connection.
CESU8Encoder func() transform.Transformer
// EmptyDateAsNull returns NULL for empty dates ('0000-00-00').
// For data format version 1 the backend returns the NULL
// indicator for empty date fields; for other versions (field
// type daydate) it does not and the value reads 0. Since 1 means
// '0001-01-01' (the minimal valid date), leaving this unset
// yields '0000-12-31' for empty dates, keeping NULL, empty, and
// valid dates distinct.
//
// https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/3f81ccc7e35d44cbbc595c7d552c202a.html?locale=en-US
EmptyDateAsNull bool
Compressor compress.Compressor // nil disables compression
// ConnectionRouting requests connection routing by the client.
// The server may not support it; the effective routing state
// depends on the value negotiated during authentication.
ConnectionRouting bool
// Observability.
Logger *slog.Logger // required; NewConnectorConfig provides the default logger
// SQLTrace controls per-statement logging.
SQLTrace SQLTraceConfig
// ProtTrace controls protocol tracing.
ProtTrace ProtTraceConfig
}
ConnectorConfig holds the static connector configuration. A ConnectorConfig may be freely mutated until it is handed to NewConfigConnector, which validates and deep-copies it: later mutations affect nothing. Start from NewConnectorConfig, which fills in defaults; a hand-built ConnectorConfig must set every required field (validate rejects nil function/interface fields and out-of-range numbers).
func NewConnectorConfig ¶ added in v1.19.0
func NewConnectorConfig() *ConnectorConfig
NewConnectorConfig returns a ConnectorConfig with driver defaults.
func ParseDSNConfig ¶ added in v1.19.0
func ParseDSNConfig(s string) (*ConnectorConfig, error)
ParseDSNConfig parses a DSN string into a ConnectorConfig: parse, fill, resolve TLS, apply the legacy timeout normalization (unset stays zero - no deadlines - negative clamps to zero) to all three timeout budgets alike. Tweak the result and hand it to NewConfigConnector.
Example ¶
ExampleParseDSNConfig shows how to open a database with the help of a connector configured by DSN.
package main
import (
"context"
"database/sql"
"log"
"os"
"github.com/SAP/go-hdb/driver"
)
func main() {
const (
envDSN = "GOHDBDSN"
)
dsn, ok := os.LookupEnv(envDSN)
if !ok {
return
}
cfg, err := driver.ParseDSNConfig(dsn)
if err != nil {
log.Fatal(err)
}
connector, err := driver.NewConfigConnector(cfg)
if err != nil {
log.Fatal(err)
}
db := sql.OpenDB(connector)
defer db.Close()
if err := db.PingContext(context.Background()); 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 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 how 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:
type DBConnectInfo ¶ added in v0.107.3
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 returns the database error code.
Position() int // Position returns the start position of erroneous sql statements sent to the database server.
Level() int // Level returns one of the database server predefined error levels.
Text() string // Text returns the error description sent from the 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 (
"context"
"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.PingContext(context.Background()); err != nil {
log.Fatal(err)
}
}
Output:
type Decimal ¶
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 (
"context"
"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_")
ctx := context.Background()
if _, err := db.ExecContext(ctx, 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.ExecContext(ctx, 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.QueryRowContext(ctx, 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
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) sent by the database server.
Example ¶
//go:build !unit
package main
import (
"context"
"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.QueryContext(context.Background(), fmt.Sprintf("select * from %s", invalidTableName))
if err == nil {
defer stmt.Close()
}
// Check if error is driver.Error.
if dbError, ok := errors.AsType[driver.Error](err); ok {
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
String returns the HANA-quoted form: a simple identifier passes through unquoted; otherwise it is wrapped in double quotes with embedded " escaped by doubling (HANA SQL Reference — Quotation marks).
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 with the NewLob constructor with io.Reader and io.Writer as parameters or with new, setting io.Reader and io.Writer with the 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"
"context"
"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()
ctx := context.Background()
tx, err := db.BeginTx(ctx, nil) // 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.ExecContext(ctx, fmt.Sprintf("create table %s (file nclob)", table)); err != nil {
log.Fatalf("create table failed: %s", err)
}
stmt, err := tx.PrepareContext(ctx, fmt.Sprintf("insert into %s values (?)", table)) //nolint: sqlclosecheck
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)
// Start sql insert in own go-routine.
// The go-routine is going to be ended when the data write via the PipeWriter is finalized.
wg.Go(func() {
if _, err := stmt.ExecContext(ctx, lob); err != nil {
log.Fatal(err)
}
fmt.Println("exec finalized")
})
// 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.
// Start sql select in own go-routine.
// The go-routine is going to be ended when the data read via the PipeReader is finalized.
wg.Go(func() {
if err := db.QueryRowContext(ctx, fmt.Sprintf("select * from %s", table)).Scan(lob); err != nil {
log.Fatal(err)
}
fmt.Println("scan finalized")
})
// 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"
"context"
"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.QueryRowContext(context.Background(), "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 (
"context"
"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()
ctx := context.Background()
tx, err := db.BeginTx(ctx, nil) // 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.PrepareContext(ctx, "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.ExecContext(ctx, lob); err != nil {
log.Fatal(err)
}
defer stmt.Close()
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
}
Output:
func NewLob ¶
NewLob creates a new Lob instance with the io.Reader and io.Writer given as parameters.
func (*Lob) SetReader ¶
SetReader sets the io.Reader source for a lob field to be written to the database and returns *Lob, to enable simple call chaining.
type NullBytes ¶ added in v0.9.1
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.
type NullDecimal ¶
NullDecimal represents a 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.
type NullLob ¶ added in v0.11.1
NullLob represents a Lob that may be null. NullLob implements the Scanner interface so it can be used as a scan destination, similar to NullString.
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 ProtTraceConfig ¶ added in v1.19.0
type ProtTraceConfig struct {
// Enabled dumps protocol parts (credentials redacted); snapshotted
// from the global flag in NewConnectorConfig.
Enabled bool
}
ProtTraceConfig controls protocol tracing. Zero value traces nothing.
type SQLTraceConfig ¶ added in v1.19.0
type SQLTraceConfig struct {
// Enabled logs every statement at Info (prefilled from the
// global sqlTrace flag in NewConnectorConfig). Statements reaching a
// threshold below are logged at Warn instead, traced or not.
Enabled bool
// ServerThreshold trips on server processing time
// from the reply StatementContext; 0 leaves this leg quiet.
ServerThreshold time.Duration
// TotalThreshold trips on client elapsed (network + fetch included);
// 0 leaves this leg quiet.
TotalThreshold time.Duration
}
SQLTraceConfig controls per-statement logging. Zero value logs nothing.
type SessionUser ¶ added in v1.10.0
SessionUser provides the fields for a hdb 'connect' (switch user) statement.
type SessionVariables
deprecated
added in
v0.14.0
SessionVariables maps session variables to their values. All defined session variables will be set once after a database connection is opened.
Deprecated: use map[string]string with ConnectorConfig.SessionVariables instead. Only the deprecated accessors below still use this type.
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
NewSniffer creates a new sniffer instance. The conn parameter is the net.Conn connection, where the Sniffer is listening for hdb protocol calls. The dbConn is the hdb connection to the database.
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 (
"context"
"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.
ctx := context.Background()
if _, err := db.ExecContext(ctx, 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.ExecContext(ctx, 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.QueryContext(ctx, 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 err := rows.Err(); 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.QueryContext(ctx, 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.
type Version ¶ added in v0.107.3
type Version struct {
// contains filtered or unexported fields
}
Version represents an HDB version.
Source Files
¶
- bytes.go
- calldriver.go
- conn.go
- connector.go
- connector_deprecated.go
- connlifecycle.go
- convert.go
- dbconn.go
- dbconnectinfo.go
- decimal.go
- deprecated.go
- doc.go
- driver.go
- dsn.go
- error.go
- identifier.go
- lob.go
- lob1.27.go
- metadata.go
- metrics.go
- null.go
- result.go
- result1.27.go
- routing.go
- scan1.27.go
- scanlob_deprecated1.27.go
- scanstruct.go
- session.go
- session1.27.go
- sniffer.go
- sniffer1.27.go
- stats.go
- statscfg.go
- stmt.go
- trace.go
- version.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package compress defines the Compressor interface for go-hdb LZ4 wire compression.
|
Package compress defines the Compressor interface for go-hdb LZ4 wire compression. |
|
Package dial provides types to implement go-hdb custom dialers.
|
Package dial provides types to implement go-hdb custom dialers. |
|
internal
|
|
|
coltest
Package coltest provides database column definitions used by the driver tests.
|
Package coltest provides database column definitions used by the driver tests. |
|
profile
Package profile enables profile support.
|
Package profile enables profile support. |
|
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/encoding
Package encoding implements hdb field type encodings and decodings.
|
Package encoding implements hdb field type encodings and decodings. |
|
protocol/julian
Package julian provides julian time conversion functions.
|
Package julian provides 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. |
|
trace
Package trace provides helpers to format values for protocol trace output.
|
Package trace provides helpers to format values for protocol trace output. |
|
unsafe
Package unsafe provides wrapper functions for 'unsafe' type conversions.
|
Package unsafe provides wrapper functions for 'unsafe' type conversions. |
|
Package spatial implements geospatial types and functions.
|
Package spatial implements geospatial 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. |