driver

package
v0.105.1 Latest Latest
Warning

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

Go to latest
Published: Jul 1, 2021 License: Apache-2.0 Imports: 29 Imported by: 96

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
// SPDX-FileCopyrightText: 2014-2021 SAP SE
//
// SPDX-License-Identifier: Apache-2.0

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 1000 rows into a database table.

The insert SQL command is "bulk insert" instead of "insert". After the insertion of the values a final stmt.Exec() without parameters must be executed.

Caution: Bulk statements need to be executed in the context of a transaction or connection to guarantee that that all statement operations are done within the same connection.

package main

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

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

func main() {
	connector, err := driver.NewConnector(drivertest.DefaultAttrs())
	if err != nil {
		log.Fatal(err)
	}
	db := sql.OpenDB(connector)
	defer db.Close()

	tableName := driver.RandomIdentifier("table_")

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

	// Get connection for bulk insert.
	conn, err := db.Conn(context.Background())
	if err != nil {
		log.Fatal(err)
	}

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

	// Bulk insert.
	for i := 0; i < 1000; i++ {
		if _, err := stmt.Exec(i, float64(i)); err != nil {
			log.Fatal(err)
		}
	}
	// Call final stmt.Exec().
	if _, err := stmt.Exec(); err != nil {
		log.Fatal(err)
	}

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

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

}
Output:

1000
Example (CallSimpleOut)

ExampleCallSimpleOut creates a stored procedure with one output parameter and executes it. Stored procedures with output parameters must be executed by sql.Query or sql.QueryRow. For TestConnector see main_test.go.

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

connector, err := NewConnector(drivertest.DefaultAttrs())
if err != nil {
	log.Fatal(err)
}
db := sql.OpenDB(connector)
defer db.Close()

procedure := RandomIdentifier("procOut_")

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

var out string

if err := db.QueryRow(fmt.Sprintf("call %s(?)", procedure)).Scan(&out); err != nil {
	log.Fatal(err)
}

fmt.Print(out)
Output:

Hello World!
Example (CallTableOut)

ExampleCallTableOut creates a stored procedure with one table output parameter and executes it making use of sql.Rows scan parameters (non-legacy mode - *please see connector.SetLegacy(false)). Stored procedures with table output parameters must be executed by sql.Query as sql.QueryRow will close the query after execution and prevent querying output table values. For TestConnector see main_test.go.

const procTable = `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
`
connector, err := NewConnector(drivertest.DefaultAttrs())
if err != nil {
	log.Fatal(err)
}
// *Switch to non-legacy mode.
connector.SetLegacy(false)
db := sql.OpenDB(connector)
defer db.Close()

tableType := RandomIdentifier("TableType_")
procedure := 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(procTable, procedure, tableType)); err != nil { // Create stored procedure.
	log.Fatal(err)
}

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

// Query stored procedure.
rows, err := db.Query(fmt.Sprintf("call %s(?)", procedure))
if err != nil {
	log.Fatal(err)
}
defer rows.Close()

if !rows.Next() {
	log.Fatal(rows.Err())
}
if err := rows.Scan(&tableRows); err != nil {
	log.Fatal(err)
}

for tableRows.Next() {
	var x string

	if err := tableRows.Scan(&x); err != nil {
		log.Fatal(err)
	}

	fmt.Println(x)
}
if err := tableRows.Err(); err != nil {
	log.Fatal(err)
}
Output:

Hello, 世界
SAP HANA
Go driver
Example (CallTableOutLegacy)

ExampleCallTableOutLegacy creates a stored procedure with one table output parameter and executes it in legacy mode. Legacy mode: Stored procedures with table output parameters must be executed by sql.Query as sql.QueryRow will close the query after execution and prevent querying output table values. The scan type of a table output parameter is a string containing an opaque value to query table output values by standard sql.Query or sql.QueryRow methods. For TestConnector see main_test.go.

const procTable = `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
`

connector, err := NewConnector(drivertest.DefaultAttrs())
if err != nil {
	log.Fatal(err)
}
db := sql.OpenDB(connector)
defer db.Close()

tableType := RandomIdentifier("TableType_")
procedure := 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(procTable, procedure, tableType)); err != nil { // Create stored procedure.
	log.Fatal(err)
}

var tableQuery string // Scan variable of table output parameter.

// Query stored procedure.
rows, err := db.Query(fmt.Sprintf("call %s(?)", procedure))
if err != nil {
	log.Fatal(err)
}
defer rows.Close()

if !rows.Next() {
	log.Fatal(rows.Err())
}
if err := rows.Scan(&tableQuery); err != nil {
	log.Fatal(err)
}

// Query stored procedure output table.
tableRows, err := db.Query(tableQuery)
if err != nil {
	log.Fatal(err)
}
defer tableRows.Close()

for tableRows.Next() {
	var x string

	if err := tableRows.Scan(&x); err != nil {
		log.Fatal(err)
	}

	fmt.Println(x)
}
if err := tableRows.Err(); err != nil {
	log.Fatal(err)
}
Output:

Hello, 世界
SAP HANA
Go driver
Example (ManyInsert)

ExampleManyInsert inserts 1000 rows into a database table via a 'many' operation.

package main

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

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

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

	connector, err := driver.NewConnector(drivertest.DefaultAttrs())
	if err != nil {
		log.Fatal(err)
	}
	db := sql.OpenDB(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()

	// Prepare data.
	data := make([][]interface{}, numRow)
	for i := 0; i < numRow; i++ {
		data[i] = []interface{}{i, float64(i)}
	}

	// Insert many.
	if _, err := stmt.Exec(data); err != nil {
		log.Fatal(err)
	}

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

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

}
Output:

1000
Example (Query)

TODO ExampleQuery: tbd

connector, err := NewConnector(drivertest.DefaultAttrs())
if err != nil {
	log.Fatal(err)
}
db := sql.OpenDB(connector)
defer db.Close()

table := RandomIdentifier("testNamedArg_")
if _, err := db.Exec(fmt.Sprintf("create table %s (i integer, j integer)", table)); err != nil {
	log.Fatal(err)
}

var i = 0
if err := db.QueryRow(fmt.Sprintf("select count(*) from %s where i = :1 and j = :1", table), 1).Scan(&i); err != nil {
	log.Fatal(err)
}

if err := db.QueryRow(fmt.Sprintf("select count(*) from %s where i = ? and j = :3", table), 1, "soso", 2).Scan(&i); err != nil {
	log.Fatal(err)
}

fmt.Print(i)
Output:

0

Index

Examples

Constants

View Source
const (
	LevelReadCommitted  = "READ COMMITTED"
	LevelRepeatableRead = "REPEATABLE READ"
	LevelSerializable   = "SERIALIZABLE"
)

Transaction isolation levels supported by hdb.

View Source
const (
	DfvLevel0 int = 0 // base data format
	DfvLevel1 int = 1 // eval types support all data types
	DfvLevel2 int = 2 // reserved, broken, do not use
	DfvLevel3 int = 3 // additional types Longdate, Secondate, Daydate, Secondtime supported for NGAP
	DfvLevel4 int = 4 // generic support for new date/time types
	DfvLevel5 int = 5 // spatial types in ODBC on request
	DfvLevel6 int = 6 // BINTEXT
	DfvLevel7 int = 7 // with boolean support
	DfvLevel8 int = 8 // with FIXED8/12/16 support
)

Data format version values.

View Source
const (
	DefaultDfv          = DfvLevel8         // Default data version format level.
	DefaultTimeout      = 300 * time.Second // Default value connection timeout (300 seconds = 5 minutes).
	DefaultTCPKeepAlive = 15 * time.Second  // Default TCP keep-alive value (copied from net.dial.go)
	DefaultBufferSize   = 16276             // Default value bufferSize.
	DefaultFetchSize    = 128               // Default value fetchSize.
	DefaultBulkSize     = 10000             // Default value bulkSize.
	DefaultLobChunkSize = 8192              // Default value lobChunkSize.
	DefaultLegacy       = true              // Default value legacy.
)

Connector default values.

View Source
const (
	DSNLocale    = "locale"    // Client locale as described in the protocol reference.
	DSNTimeout   = "timeout"   // Driver side connection timeout in seconds.
	DSNFetchSize = "fetchSize" // Maximum number of fetched records from database by database/sql/driver/Rows.Next().
)

DSN parameters. For parameter client locale see http://help.sap.com/hana/SAP_HANA_SQL_Command_Network_Protocol_Reference_en.pdf.

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.ir/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 = "0.105.1"

DriverVersion is the version number of the hdb driver.

View Source
const (
	MaxBulkSize = p.MaxNumArg // Maximum bulk size.

)

Connector minimal / maximal values.

Variables

View Source
var (
	// NoFlush is to be used as parameter in bulk statements to delay execution.
	NoFlush = sql.Named(bulk, &noFlushTok)
	// Flush can be used as optional parameter in bulk statements but is not required to trigger execution.
	Flush = sql.Named(bulk, &flushTok)
)
View Source
var ErrNestedQuery = errors.New("nested sql queries are not supported")

ErrNestedQuery is the error raised if a sql statement is executed before an "active" statement is closed. Example: execute sql statement before rows of previous select statement are closed.

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 ErrUnsupportedIsolationLevel = errors.New("unsupported isolation level")

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

SupportedDfvs returns a slice of data format versions supported by the driver.

Functions

func IsSupportedDfv added in v0.105.0

func IsSupportedDfv(dfv int) bool

IsSupportedDfv returns true if the data format version dfv is supported by the driver, false otherwise.

func Sniff added in v0.105.0

func Sniff(conn net.Conn, dbConn net.Conn) error

Sniff instatiates and runs a protocol sniffer.

Types

type Conn added in v0.103.0

type Conn interface {
	HDBVersion() *hdb.Version
}

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 (starting from go 1.10) allowing users to bypass a string based data source name.

Example

ExampleConnector shows how to open a database with the help of a connector.

package main

import (
	"database/sql"
	"log"

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

func main() {
	connector := driver.NewBasicAuthConnector("host:port", "username", "password")
	connector.SetTimeout(60)
	db := sql.OpenDB(connector)
	defer db.Close()

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

func NewBasicAuthConnector added in v0.10.0

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

NewBasicAuthConnector creates a connector for basic authentication.

func NewConnector added in v0.103.0

func NewConnector(attrs map[string]interface{}) (*Connector, error)

NewConnector returns a new connector instance setting connector attributes to values defined in attrs. Example:

dsn := "hdb://SYSTEM:MyPassword@localhost:39013"
schema:= "MySchema"
connector := NewConnector(map[string]interface{}{"dsn": dsn, "defaultSchema": schema}

func NewDSNConnector added in v0.10.0

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

NewDSNConnector creates a connector from a data source name.

func (*Connector) ApplicationName added in v0.103.0

func (c *Connector) ApplicationName() string

ApplicationName returns the locale of the connector.

func (*Connector) BasicAuthDSN added in v0.10.0

func (c *Connector) BasicAuthDSN() string

BasicAuthDSN return the connector DSN for basic authentication.

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) 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) 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) DriverName added in v0.103.0

func (c *Connector) DriverName() string

DriverName returns the driver name of the connector.

func (*Connector) DriverVersion added in v0.103.0

func (c *Connector) DriverVersion() string

DriverVersion returns the driver version of the connector.

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) Legacy added in v0.99.0

func (c *Connector) Legacy() bool

Legacy returns the connector legacy flag.

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) Password added in v0.10.0

func (c *Connector) Password() string

Password returns the 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) 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) error

SetApplicationName sets the application name of the connector.

func (*Connector) SetBufferSize added in v0.103.0

func (c *Connector) SetBufferSize(bufferSize int) error

SetBufferSize sets the bufferSize of the connector.

func (*Connector) SetBulkSize added in v0.99.0

func (c *Connector) SetBulkSize(bulkSize int) error

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) error

SetDefaultSchema sets the database default schema of the connector.

func (*Connector) SetDfv added in v0.99.0

func (c *Connector) SetDfv(dfv int) error

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) error

SetDialer sets the dialer object of the connector.

func (*Connector) SetFetchSize added in v0.10.0

func (c *Connector) SetFetchSize(fetchSize int) error

SetFetchSize sets the fetchSize of the connector.

For more information please see DSNFetchSize.

func (*Connector) SetLegacy added in v0.99.0

func (c *Connector) SetLegacy(b bool) error

SetLegacy sets the connector legacy flag.

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 DSNLocale.

func (*Connector) SetPingInterval added in v0.100.9

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

SetPingInterval sets the connection ping interval value of the connector.

If the ping interval is greater than zero, the driver pings all open connections (active or idle in connection pool) periodically. Parameter d defines the time between the pings.

func (*Connector) SetSessionVariables added in v0.14.0

func (c *Connector) SetSessionVariables(sessionVariables SessionVariables) error

SetSessionVariables sets the session varibles of the connector.

func (*Connector) SetTCPKeepAlive added in v0.100.8

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

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

For more information please see net.Dialer structure.

func (*Connector) SetTLSConfig added in v0.11.0

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

SetTLSConfig sets the TLS configuration of the connector.

func (*Connector) SetTimeout added in v0.10.0

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

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) 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) Username added in v0.10.0

func (c *Connector) Username() string

Username returns the username of the connector.

type DSN

type DSN string

DSN is here for the purposes of documentation only. 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).

Example:

"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.

// SPDX-FileCopyrightText: 2014-2021 SAP SE
//
// SPDX-License-Identifier: Apache-2.0

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:

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. For variables TestDSN and TestSchema see main_test.go.

package main

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

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

func main() {
	connector, err := driver.NewConnector(drivertest.DefaultAttrs())
	if err != nil {
		log.Fatal(err)
	}
	db := sql.OpenDB(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 interface{}) 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 struct {
	// contains filtered or unexported fields
}

Driver represents the go sql driver implementation for hdb.

func (*Driver) Name added in v0.103.0

func (d *Driver) Name() string

Name returns the driver name.

func (*Driver) Open added in v0.103.0

func (d *Driver) Open(dsn string) (driver.Conn, error)

Open implements the driver.Driver interface.

func (*Driver) OpenConnector added in v0.103.0

func (d *Driver) OpenConnector(dsn string) (driver.Connector, error)

OpenConnector implements the driver.DriverContext interface.

func (*Driver) Stats added in v0.103.0

func (d *Driver) Stats() Stats

Stats returns driver statistics.

func (*Driver) Version added in v0.103.0

func (d *Driver) Version() string

Version returns the driver version.

type Error

type Error interface {
	Error() string   // Implements the golang error interface.
	NumError() int   // NumError returns the number of errors.
	SetIdx(idx int)  // Sets the error index in case number of errors are greater 1 in the range of 0 <= index < NumError().
	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.
}

Error represents errors send by the database server.

Example
//go:build !unit
// +build !unit

// SPDX-FileCopyrightText: 2014-2021 SAP SE
//
// SPDX-License-Identifier: Apache-2.0

package main

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

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

const (
	errCodeInvalidTableName = 259
)

func main() {
	connector, err := driver.NewConnector(drivertest.DefaultAttrs())
	if err != nil {
		log.Fatal(err)
	}
	db := sql.OpenDB(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"
	"github.com/SAP/go-hdb/driver/drivertest"
)

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.
	connector, err := driver.NewConnector(drivertest.DefaultAttrs())
	if err != nil {
		log.Fatal(err)
	}
	db := sql.OpenDB(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 largs 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)
	}

	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 interface{}) 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) Value

func (l Lob) Value() (driver.Value, error)

Value implements the database/sql/Valuer interface.

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 interface{}) 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 interface{}) 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 (l *NullLob) Scan(src interface{}) error

Scan implements the database/sql/Scanner interface.

func (NullLob) Value added in v0.99.0

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

Value implements the database/sql/Valuer interface.

type NullTime

type NullTime = sql.NullTime

NullTime represents an time.Time that may be null. Deprecated: Please use database/sql NullTime instead.

type ParseDSNError added in v0.99.0

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

ParseDSNError is the error returned in case DSN is invalid.

func (ParseDSNError) Error added in v0.99.0

func (e ParseDSNError) Error() string

func (ParseDSNError) Unwrap added in v0.99.0

func (e ParseDSNError) Unwrap() error

Unwrap returns the nested error.

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 Stats added in v0.103.0

type Stats struct {
	OpenConnections  int // Number of open driver connections.
	OpenTransactions int // Number of open driver transactions.
	OpenStatements   int // Number of open driver database statements.
}

Stats contains driver statistics.

Directories

Path Synopsis
Package dial provides types to implement go-hdb custom dialers.
Package dial provides types to implement go-hdb custom dialers.
Package drivertest provides utilities for driver testing.
Package drivertest provides utilities for driver testing.
Package hdb defines hdb specific data structures used by the driver.
Package hdb defines hdb specific data structures used by the driver.
internal
container/vermap
Package vermap implements a key value map like used in session variables.
Package vermap implements a key value map like used in session variables.
protocol
Package protocol implements the hdb command network protocol.
Package protocol implements the hdb command network protocol.
protocol/encoding
Package encoding implements hdb field type en,- and decodings.
Package encoding implements hdb field type en,- and decodings.
protocol/scanner
Package scanner implements a HANA SQL query scanner.
Package scanner implements a HANA SQL query scanner.
rand
Package rand implements random value functions.
Package rand implements random value functions.
Package spatial implements geo spatial types and functions.
Package spatial implements geo spatial types and functions.
Package sqltrace implements driver sql trace functions.
Package sqltrace implements driver sql trace 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