ase

package module
v0.0.0-...-6cde979 Latest Latest
Warning

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

Go to latest
Published: Dec 19, 2023 License: Apache-2.0 Imports: 19 Imported by: 0

README

cgo-ase

PkgGoDev Go Report Card REUSE status

Description

cgo-ase is a driver for the database/sql package of Go (golang) to provide access to SAP ASE instances. It is delivered as Go module.

SAP ASE is the shorthand for SAP Adaptive Server Enterprise, a relational model database server originally known as Sybase SQL Server.

cgo enables Go to call C code and to link against shared objects. A pure go implementation can be found here.

Requirements

The cgo driver requires the shared objects from either the ASE itself or Client-Library to compile.

The required shared objects from ASE can be found in the installation path of the ASE under OCS-16_0/lib, where 16_0 is the version of your ASE installation.

After installing the Client-Library SDK the shared objects can be found in the folder lib at the chosen installation path.

The headers are provided in the includes folder.

Aside from the shared object the cgo driver has no special requirements other than Go standard library and the third party modules listed in go.mod, e.g. github.com/SAP/go-dblib.

Download and Installation

The packages in this repo can be go get and imported as usual, e.g.:

go get github.com/SAP/cgo-ase

For specifics on how to use database/sql please see the documentation.

The command-line application cgoase can be go installed:

$ go install github.com/SAP/cgo-ase/cmd/cgoase@latest
go: downloading github.com/SAP/cgo-ase v0.0.0-20210506101112-3f277f8e0603
$ cgoase -h
Usage of cgoase:
      --appname string        Application Name to transmit to ASE
      --database string       Database
  -f, --f string              Read SQL commands from file
      --host string           Hostname to connect to
      --key string            Key of userstore data to use for login
      --log-client-msgs       Log client messages
      --log-server-msgs       Log server messages
      --maxColLength int      Maximum number of characters to print for column (default 50)
      --password string       Password
      --port string           Port (Example: '443' or 'tls') to connect to
      --tls-hostname string   Expected server TLS hostname to pass to C driver
      --username string       Username
2021/05/06 11:00:22 cgoase failed: pflag: help requested

Usage

Example code:

package main

import (
    "database/sql"
    _ "github.com/SAP/cgo-ase"
)

func main() {
    db, err := sql.Open("ase", "ase://user:pass@host:port/")
    if err != nil {
        log.Printf("Failed to open database: %v", err)
        return
    }
    defer db.Close()

    if err := db.Ping(); err != nil {
        log.Printf("Failed to ping database: %v", err)
        return
    }
}

/path/to/OCS is the path to your Client-Library SDK installation. /lib is the folder inside of the SDK installation containing the shared objects required for the cgo driver.

Compilation
CGO_LDFLAGS="-L/path/to/OCS/lib" go build -o cgoase ./cmd/cgoase/
Execution
LD_LIBRARY_PATH="/path/to/OCS/lib:/path/to/OCS/lib3p:/path/to/OCS/lib3p64:" ./cgoase

While /path/to/OCS/lib contains the libraries of the Open Client, /path/to/OCS/lib3p and /path/to/OCS/lib3p64 contain the libraries needed to use ASE user store keys.

Examples

More examples can be found in the folder examples.

Integration tests

Integration tests are available and can be run using go test --tags=integration and go test ./examples/... --tags=integration.

These require the following environment variables to be set:

  • ASE_USERSTOREKEY
  • ASE_HOST
  • ASE_PORT
  • ASE_USER
  • ASE_PASS

The integration tests will create new databases for each connection type to run tests against. After the tests are finished the created databases will be removed.

Configuration

The configuration is handled through either a data source name (DSN) in one of two forms or through a configuration struct passed to a connector.

All of these support additional properties which can tweak the connection, configuration options in Client-Library or the drivers themselves.

Data Source Names
URI DSN

The URI DSN is a common URI like ase://user:pass@host:port/?prop1=val1&prop2=val2.

DSNs in this form are parsed using url.Parse.

Simple DSN

The simple DSN is a key/value string: username=user password=pass host=hostname port=4901

Values with spaces must be quoted using single or double quotes.

Each member of dblib.dsn.DsnInfo can be set using any of their possible json tags. E.g. .Host will receive the values from the keys host and hostname.

Additional properties are set as key/value pairs as well: ... prop1=val1 prop2=val2. If the parser doesn't recognize a string as a json tag it assumes that the key/value pair is a property and its value.

Similar to the URI DSN those property/value pairs are purely additive. Any property that only recognizes a single argument (e.g. a boolean) will only honour the last given value for a property.

Connector

As an alternative to the string DSN ase.NewConnector accept a dsn.DsnInfo directly and return a driver.Connector, which can be passed to sql.OpenDB:

package main

import (
    "database/sql"

    "github.com/SAP/cgo-ase"
)

func main() {
    info := ase.NewInfo()
    info.Host = "hostname"
    info.Port = "4901"
    info.Username = "user"
    info.Password = "pass"

    connector, err := ase.NewConnector(info)
    if err != nil {
        log.Printf("Failed to create connector: %v", err)
        return
    }

    db, err := sql.OpenDB(connector)
    if err != nil {
        log.Printf("Failed to open database: %v", err)
        return
    }
    defer db.Close()

    if err := db.Ping(); err != nil {
        log.Printf("Failed to ping ASE: %v", err)
    }
}
Properties
AppName / app-name

Recognized values: string

When set overrides Client-Libraries default application name sent to the ASE server.

Userstorekey / userstorekey

Recognized values: string

When set uses the ASE userstore instead of username/password to authenticate with ASE.

TLSHostname / tls-hostname

Recognized values: string

Expected server TLS hostname to pass to Client-Library for validation.

LogClientMsgs / log-client-msgs

Recognized values: true or false

When set to true all client messages will be printed to stderr.

Please note that this is a debug property - for logging you should register your own message handler with the GlobalClientMessageBroker.

When unset the callback will not bet set.

LogServerMsgs / log-server-msgs

Recognized values: true or false

When set to true all server messages will be printed to stderr.

Please note that this is a debug property - for logging you should register your own message handler with the GlobalServerMessageBroker.

When unset the callback will not bet set.

Limitations

Prepared statements

Regarding the limitations of prepared statements/dynamic SQL please see the Client-Library documentation.

Unsupported ASE data types

Currently the following data types are not supported:

  • Timestamp
  • Univarchar
Null types

Due to the limitations of the Client-Library it is not possible to support null types.

Additionally columns of the following data types must be nullable:

  • Image
  • Binary

Known Issues

The list of known issues is available here.

How to obtain support

Feel free to open issues for feature requests, bugs or general feedback here.

Contributing

Any help to improve this package is highly appreciated.

For details on how to contribute please see the contributing file.

License

Copyright (c) 2019-2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache License 2.0 except as noted otherwise in the LICENSE file.

Documentation

Overview

Package ase contains code of the cgo-ase driver for the database/sql package of Go (golang) to provide access to SAP ASE instances.

By using cgo this code is also enabled to call C code and to link against shared object. Thus, it is a shim to satisfy the databse/sql/driver-interface.

Index

Constants

View Source
const DriverName = "ase"

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

Variables

View Source
var (
	// GlobalServerMessageBroker sends server messages to all registered
	// handlers.
	GlobalServerMessageBroker = newMessageBroker()
	// GlobalClientMessageBroker sends client messages to all registered
	// handlers.
	GlobalClientMessageBroker = newMessageBroker()
)

Functions

func NewConnector

func NewConnector(info *Info) (driver.Connector, error)

NewConnector returns a new connector with the passed configuration.

func SetCallbackTarget

func SetCallbackTarget(target *os.File)

SetCallbackTarget sets the os.File target the callback functions for client and server messages will write to.

Types

type ASEType

type ASEType byte

ASEType is the byte-representation of an ASE-datatype.

const (
	BIGDATETIME    ASEType = 35
	BIGINT         ASEType = 30
	BIGTIME        ASEType = 36
	BINARY         ASEType = 1
	BIT            ASEType = 11
	BLOB           ASEType = 26
	BOUNDARY       ASEType = 22
	CHAR           ASEType = 0
	DATE           ASEType = 27
	DATETIME       ASEType = 12
	DATETIME4      ASEType = 13
	DECIMAL        ASEType = 17
	FLOAT          ASEType = 10
	IMAGE          ASEType = 5
	IMAGELOCATOR   ASEType = 38
	INT            ASEType = 8
	LONG           ASEType = 20
	LONGBINARY     ASEType = 3
	LONGCHAR       ASEType = 2
	MONEY          ASEType = 14
	MONEY4         ASEType = 15
	NUMERIC        ASEType = 16
	REAL           ASEType = 9
	SENSITIVITY    ASEType = 21
	SMALLINT       ASEType = 7
	TEXT           ASEType = 4
	TEXTLOCATOR    ASEType = 37
	TIME           ASEType = 28
	TINYINT        ASEType = 6
	UBIGINT        ASEType = 33
	UINT           ASEType = 32
	UNICHAR        ASEType = 25
	UNITEXT        ASEType = 29
	UNITEXTLOCATOR ASEType = 39
	USER           ASEType = 100
	USHORT         ASEType = 24
	USMALLINT      ASEType = 31
	VARBINARY      ASEType = 19
	VARCHAR        ASEType = 18
	VOID           ASEType = 23
	XML            ASEType = 34
)

func (ASEType) String

func (t ASEType) String() string

String returns the ASEType as string and satisfies the stringer interface.

func (ASEType) ToDataType

func (t ASEType) ToDataType() asetypes.DataType

ToDataType returns the equivalent asetypes.DataType for an ASEType.

type ClientMessage

type ClientMessage struct {
	Severity  int64
	MsgNumber uint64
	Text      string
	OSNumber  int64
	OSString  string
	Status    int64
	SQLState  string
}

ClientMessage is a message generated by Client-Library.

func (ClientMessage) Content

func (msg ClientMessage) Content() string

Content returns the message-content of a client-message.

func (ClientMessage) MessageNumber

func (msg ClientMessage) MessageNumber() uint64

MessageNumber returns the message-number of a client-message.

func (ClientMessage) MessageSeverity

func (msg ClientMessage) MessageSeverity() int64

MessageSeverity returns the message-severity of a client-message.

type Command

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

Command contains the C.command and indicates if that command is dynamic.

func (*Command) Cancel

func (cmd *Command) Cancel() error

Cancel cancels the current result set.

func (*Command) ConsumeResponse

func (cmd *Command) ConsumeResponse(ctx context.Context) (*Rows, *Result, error)

ConsumeResponse is a wrapper around .Response that guarantees that all results have been read.

func (*Command) Drop

func (cmd *Command) Drop() error

Drop deallocates the command.

func (*Command) Response

func (cmd *Command) Response() (*Rows, *Result, C.CS_INT, error)

Response reads a single response from the command structure and handles it.

When no more results are available this method returns io.EOF.

The third return value is a CS_INT, which may be the CS_RETCODE of ct_results when the command failed or finished or the result type if the result set requires further processing.

type Connection

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

Connection implements the driver.Conn interface.

func NewConnection

func NewConnection(driverCtx *csContext, info *Info) (*Connection, error)

NewConnection allocates a new connection based on the options in the dsn.

If driverCtx is nil a new csContext will be initialized.

func (*Connection) Begin

func (conn *Connection) Begin() (driver.Tx, error)

Begin implements the driver.Conn interface.

func (*Connection) BeginTx

func (conn *Connection) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error)

BeginTx implements the driver.ConnBeginTx interface.

func (*Connection) CheckNamedValue

func (conn *Connection) CheckNamedValue(nv *driver.NamedValue) error

CheckNamedValue implements the driver.NamedValueChecker interface.

func (*Connection) Close

func (conn *Connection) Close() error

Close implements the driver.Conn interface. It closes and deallocates a connection.

func (*Connection) Exec

func (conn *Connection) Exec(query string, args []driver.Value) (driver.Result, error)

Exec implements the driver.Execer interface.

func (*Connection) ExecContext

func (conn *Connection) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error)

ExecContext implements the driver.ExecerContext interface.

func (*Connection) GenericExec

func (conn *Connection) GenericExec(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, driver.Result, error)

GenericExec is the central method through which SQL statements are sent to ASE.

func (*Connection) NewCommand

func (conn *Connection) NewCommand(ctx context.Context, query string) (*Command, error)

NewCommand creates a new command.

func (*Connection) Ping

func (conn *Connection) Ping(ctx context.Context) error

Ping implements the driver.Pinger interface.

func (*Connection) Prepare

func (conn *Connection) Prepare(query string) (driver.Stmt, error)

Prepare implements the driver.Conn interface.

func (*Connection) PrepareContext

func (conn *Connection) PrepareContext(ctx context.Context, query string) (driver.Stmt, error)

PrepareContext implements the driver.ConnPrepareContext interface.

func (*Connection) Query

func (conn *Connection) Query(query string, args []driver.Value) (driver.Rows, error)

Query implements the driver.Queryer interface.

func (*Connection) QueryContext

func (conn *Connection) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error)

QueryContext implements the driver.QueryerContext interface.

type Info

type Info struct {
	dsn.Info

	AppName string `json:"appname" doc:"Application Name to transmit to ASE"`

	Userstorekey string `json:"key" multiref:"userstorekey" doc:"Key of userstore data to use for login"`

	TLSHostname string `json:"tls-hostname" doc:"Expected server TLS hostname to pass to C driver"`

	LogClientMsgs bool `json:"log-client-msgs" doc:"Log client messages"`
	LogServerMsgs bool `json:"log-server-msgs" doc:"Log server messages"`
}

func NewInfo

func NewInfo() (*Info, error)

NewInfo returns a bare Info for github.com/SAP/go-dblib/dsn with defaults.

func NewInfoWithEnv

func NewInfoWithEnv() (*Info, error)

NewInfoWithEnv is a convenience function returning an Info with values filled from the environment with the prefix 'ASE'.

func NewInfoWithFlags

func NewInfoWithFlags() (*Info, *flag.FlagSet, error)

NewInfoFlags is a convenience function returning an Info filled with defaults and a flagset with flags bound to the members of the returned info.

type Message

type Message interface {
	MessageNumber() uint64
	MessageSeverity() int64
	Content() string
}

Message defines the generic interface Server- und ClientMessage adhere to.

type MessageHandler

type MessageHandler func(Message)

MessageHandler describes the signature of a handler for server- and client messages.

type MessageRecorder

type MessageRecorder struct {
	Messages []string
	// contains filtered or unexported fields
}

MessageRecorder can be utilized to record non-SQL responses from the server.

See the example examples/recorder on how to utilize the MessageRecorder.

func NewMessageRecorder

func NewMessageRecorder() *MessageRecorder

NewMessageRecorder returns an initialized MessageRecorder.

func (*MessageRecorder) HandleMessage

func (rec *MessageRecorder) HandleMessage(msg Message)

HandleMessage implements the MessageHandler interface.

func (*MessageRecorder) Reset

func (rec *MessageRecorder) Reset()

Reset prepares the MessageRecorder to record a new message.

func (*MessageRecorder) Text

func (rec *MessageRecorder) Text() []string

Text returns the received messages.

type Result

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

Result implements the driver.Result interface.

func (Result) LastInsertId

func (result Result) LastInsertId() (int64, error)

LastInsertId implements the driver.Result interface.

func (Result) RowsAffected

func (result Result) RowsAffected() (int64, error)

RowsAffected implements the driver.Result interface.

type Rows

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

Rows implements the driver.Rows interface.

func (*Rows) Close

func (rows *Rows) Close() error

Close implements the driver.Rows interface.

func (*Rows) ColumnTypeDatabaseTypeName

func (rows *Rows) ColumnTypeDatabaseTypeName(index int) string

ColumnTypeDatabaseTypeName implements the driver.RowsColumnTypeDatabaseTypeName interface.

func (*Rows) ColumnTypeLength

func (rows *Rows) ColumnTypeLength(index int) (int64, bool)

ColumnTypeLength implements the driver.RowsColumnTypeLength interface.

func (*Rows) ColumnTypeMaxLength

func (rows *Rows) ColumnTypeMaxLength(index int) int64

ColumnTypeMaxLength returns the maximum length of the column-datatype.

func (*Rows) ColumnTypeNullable

func (rows *Rows) ColumnTypeNullable(index int) (bool, bool)

ColumnTypeNullable implements the driver.RowsColumnTypeNullable interface.

func (*Rows) ColumnTypePrecisionScale

func (rows *Rows) ColumnTypePrecisionScale(index int) (int64, int64, bool)

ColumnTypePrecisionScale implements the driver.RowsColumnTypePrecisionScale interface.

func (*Rows) ColumnTypeScanType

func (rows *Rows) ColumnTypeScanType(index int) reflect.Type

ColumnTypeScanType returns the datatype of the column.

func (*Rows) Columns

func (rows *Rows) Columns() []string

Columns implements the driver.Rows interface.

func (*Rows) Next

func (rows *Rows) Next(dest []driver.Value) error

Next implements the driver.Rows interface.

type ServerMessage

type ServerMessage struct {
	MsgNumber uint64
	State     int64
	Severity  int64
	Text      string
	Server    string
	Proc      string
	Line      int64
	SQLState  string
}

ServerMessage is a message sent from the ASE server to the client.

func (ServerMessage) Content

func (msg ServerMessage) Content() string

Content returns the message-content of a server-message.

func (ServerMessage) MessageNumber

func (msg ServerMessage) MessageNumber() uint64

MessageNumber returns the message-number of a server-message.

func (ServerMessage) MessageSeverity

func (msg ServerMessage) MessageSeverity() int64

MessageSeverity returns the message-severity of a server-message.

Directories

Path Synopsis
cmd
examples
genericexec
This example shows how to utilize the GenericExecer interface if both driver.Rows and driver.Result of a statement are required.
This example shows how to utilize the GenericExecer interface if both driver.Rows and driver.Result of a statement are required.
recorder
This example shows how the cgo.MessageRecorder can be used to process messages from the TDS server.
This example shows how the cgo.MessageRecorder can be used to process messages from the TDS server.
recorder2
This example shows how a custom recorder can be implemented to process messages from the TDS server.
This example shows how a custom recorder can be implemented to process messages from the TDS server.
simple
This example shows a simple interaction with a TDS server using the database/sql interface and the cgo-based driver.
This example shows a simple interaction with a TDS server using the database/sql interface and the cgo-based driver.
simple2
This example shows a simple interaction with a TDS server using the database/sql interface, prepared statements and the cgo-based driver.
This example shows a simple interaction with a TDS server using the database/sql interface, prepared statements and the cgo-based driver.

Jump to

Keyboard shortcuts

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