代码拉取完成,页面将自动刷新
package gbase8s
// #include "gbase8s.go.h"
import "C"
import (
"bytes"
"context"
"database/sql/driver"
"errors"
"fmt"
"time"
"unsafe"
)
// Ping database connection
func (conn *Conn) Ping(ctx context.Context) error {
if ctx.Err() != nil {
return ctx.Err()
}
done := make(chan struct{})
go conn.gciBreakDone(ctx, done)
result := C.GCIPing(conn.svc, conn.errHandle, C.GCI_DEFAULT)
close(done)
if result == C.GCI_SUCCESS || result == C.GCI_SUCCESS_WITH_INFO {
return nil
}
errorCode, err := conn.gciGetError()
if errorCode == 1010 {
// Older versions of gbase8s do not support ping,
// but a response of "GCI-01010: invalid GCI operation" confirms connectivity.
return nil
}
conn.logger.Print("Ping error: ", err)
return driver.ErrBadConn
}
// Close a connection
func (conn *Conn) Close() error {
if conn.closed {
return nil
}
conn.closed = true
var err error
if useGCISessionBegin {
if rv := C.GCISessionEnd(
conn.svc,
conn.errHandle,
conn.usrSession,
C.GCI_DEFAULT,
); rv != C.GCI_SUCCESS {
err = conn.getError(rv)
}
if rv := C.GCIServerDetach(
conn.srv,
conn.errHandle,
C.GCI_DEFAULT,
); rv != C.GCI_SUCCESS {
err = conn.getError(rv)
}
C.GCIHandleFree(unsafe.Pointer(conn.usrSession), C.GCI_HTYPE_SESSION)
C.GCIHandleFree(unsafe.Pointer(conn.srv), C.GCI_HTYPE_SERVER)
conn.usrSession = nil
conn.srv = nil
} else {
if rv := C.GCILogoff(
conn.svc,
conn.errHandle,
); rv != C.GCI_SUCCESS {
err = conn.getError(rv)
}
}
C.GCIHandleFree(unsafe.Pointer(conn.svc), C.GCI_HTYPE_SVCCTX)
C.GCIHandleFree(unsafe.Pointer(conn.errHandle), C.GCI_HTYPE_ERROR)
C.GCIHandleFree(unsafe.Pointer(conn.txHandle), C.GCI_HTYPE_TRANS)
C.GCIHandleFree(unsafe.Pointer(conn.env), C.GCI_HTYPE_ENV)
conn.svc = nil
conn.errHandle = nil
conn.txHandle = nil
conn.env = nil
return err
}
// Prepare prepares a query
func (conn *Conn) Prepare(query string) (driver.Stmt, error) {
return conn.PrepareContext(context.Background(), query)
}
// PrepareContext prepares a query with context
func (conn *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
if conn.enableQMPlaceholders {
query = placeholders(query)
}
queryP := cString(query)
defer C.free(unsafe.Pointer(queryP))
var stmtTemp *C.GCIStmt
stmt := &stmtTemp
if ctx.Err() != nil {
return nil, ctx.Err()
}
done := make(chan struct{})
go conn.gciBreakDone(ctx, done)
defer func() { close(done) }()
if conn.stmtCacheSize == 0 {
if rv := C.GCIStmtPrepare2(
conn.svc, // service context handle
stmt, // pointer to the statement handle returned
conn.errHandle, // error handle
queryP, // statement text
C.ub4(len(query)), // statement text length
nil, // key to be used for searching the statement in the statement cache
C.ub4(0), // length of the key
C.ub4(C.GCI_NTV_SYNTAX), // syntax - GCI_NTV_SYNTAX: syntax depends upon the version of the server
C.ub4(C.GCI_DEFAULT), // mode
); rv != C.GCI_SUCCESS {
return nil, conn.getError(rv)
}
return &Stmt{conn: conn, stmt: *stmt, ctx: ctx, releaseMode: C.GCI_DEFAULT}, nil
}
if rv := C.GCIStmtPrepare2(
conn.svc, // service context handle
stmt, // pointer to the statement handle returned
conn.errHandle, // error handle
queryP, // statement text
C.ub4(len(query)), // statement text length
queryP, // key to be used for searching the statement in the statement cache
C.ub4(len(query)), // length of the key
C.ub4(C.GCI_NTV_SYNTAX), // syntax - GCI_NTV_SYNTAX: syntax depends upon the version of the server
C.ub4(C.GCI_DEFAULT), // mode
); rv != C.GCI_SUCCESS && rv != C.GCI_SUCCESS_WITH_INFO {
// Note that C.GCI_SUCCESS_WITH_INFO is returned the first time a statement it put into the cache
return nil, conn.getError(rv)
}
return &Stmt{conn: conn, stmt: *stmt, ctx: ctx, releaseMode: C.GCI_DEFAULT, cacheKey: query}, nil
}
// Begin starts a transaction
func (conn *Conn) Begin() (driver.Tx, error) {
return conn.BeginTx(context.Background(), driver.TxOptions{})
}
// BeginTx starts a transaction
func (conn *Conn) BeginTx(ctx context.Context, txOptions driver.TxOptions) (driver.Tx, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}
if conn.transactionMode != C.GCI_TRANS_READWRITE {
if rv := C.GCITransStart(
conn.svc,
conn.errHandle,
0,
conn.transactionMode|C.GCI_TRANS_NEW, // mode is: C.GCI_TRANS_SERIALIZABLE, C.GCI_TRANS_READWRITE, or C.GCI_TRANS_READONLY
); rv != C.GCI_SUCCESS {
return nil, conn.getError(rv)
}
}
conn.inTransaction = true
return &Tx{conn: conn}, nil
}
// getError gets error from return result (sword) or GCIError
func (conn *Conn) getError(result C.sword) error {
switch result {
case C.GCI_SUCCESS:
return nil
case C.GCI_INVALID_HANDLE:
return ErrGCIInvalidHandle
case C.GCI_SUCCESS_WITH_INFO:
return ErrGCISuccessWithInfo
case C.GCI_RESERVED_FOR_INT_USE:
return ErrGCIReservedForIntUse
case C.GCI_NO_DATA:
return ErrGCINoData
case C.GCI_NEED_DATA:
return ErrGCINeedData
case C.GCI_STILL_EXECUTING:
return ErrGCIStillExecuting
case C.GCI_ERROR:
errorCode, err := conn.gciGetError()
switch errorCode {
/*
bad connection errors:
GCI-00028: your session has been killed
GCI-01012: Not logged on
GCI-01033: initialization or shutdown in progress
GCI-01034: not available
GCI-01089: immediate shutdown in progress - no operations are permitted
GCI-03113: end-of-file on communication channel
GCI-03114: Not Connected to gbase8s
GCI-03135: connection lost contact
GCI-12528: TNS:listener: all appropriate instances are blocking new connections
GCI-12537: TNS:connection closed
*/
case 28, 1012, 1033, 1034, 1089, 3113, 3114, 3135, 12528, 12537:
return driver.ErrBadConn
}
return err
}
return fmt.Errorf("received result code %d", result)
}
// gciGetError calls GCIErrorGet then returs error code and text
func (conn *Conn) gciGetError() (int, error) {
var errorCode C.sb4
errorText := make([]byte, 1024)
result := C.GCIErrorGet(
unsafe.Pointer(conn.errHandle), // error handle
1, // status record number, starts from 1
nil, // sqlstate, not supported in release 8.x or later
&errorCode, // error code
(*C.GCItext)(&errorText[0]), // error message text
1024, // size of the buffer provided in number of bytes
C.GCI_HTYPE_ERROR, // type of the handle (GCI_HTYPE_ERR or GCI_HTYPE_ENV)
)
if result != C.GCI_SUCCESS {
return 3114, errors.New("GCIErrorGet failed")
}
index := bytes.IndexByte(errorText, 0)
return int(errorCode), errors.New(string(errorText[:index]))
}
// gciAttrGet calls GCIAttrGet with GCIParam then returns attribute size and error.
// The attribute value is stored into passed value.
func (conn *Conn) gciAttrGet(paramHandle *C.GCIParam, value unsafe.Pointer, attributeType C.ub4) (C.ub4, error) {
var size C.ub4
result := C.GCIAttrGet(
unsafe.Pointer(paramHandle), // Pointer to a handle type
C.GCI_DTYPE_PARAM, // The handle type: GCI_DTYPE_PARAM, for a parameter descriptor
value, // Pointer to the storage for an attribute value
&size, // The size of the attribute value
attributeType, // The attribute type:
conn.errHandle, // An error handle
)
return size, conn.getError(result)
}
// gciAttrSet calls GCIAttrSet.
// Only uses errHandle from conn, so can be called in conn setup after errHandle has been set.
func (conn *Conn) gciAttrSet(
handle unsafe.Pointer,
handleType C.ub4,
value unsafe.Pointer,
valueSize C.ub4,
attributeType C.ub4,
) error {
result := C.GCIAttrSet(
handle, // Pointer to a handle whose attribute gets modified
handleType, // The handle type
value, // Pointer to an attribute value
valueSize, // The size of an attribute value
attributeType, // The type of attribute being set
conn.errHandle, // An error handle
)
return conn.getError(result)
}
// gciHandleAlloc calls GCIHandleAlloc then returns
// handle pointer to pointer, buffer pointer to pointer, and error
func (conn *Conn) gciHandleAlloc(handleType C.ub4, size C.size_t) (*unsafe.Pointer, *unsafe.Pointer, error) {
var handleTemp unsafe.Pointer
handle := &handleTemp
var bufferTemp unsafe.Pointer
var buffer *unsafe.Pointer
if size > 0 {
buffer = &bufferTemp
}
result := C.GCIHandleAlloc(
unsafe.Pointer(conn.env), // An environment handle
handle, // Returns a handle
handleType, // type of handle:
size, // amount of user memory to be allocated
buffer, // Returns a pointer to the user memory
)
err := conn.getError(result)
if err != nil {
return nil, nil, err
}
if size > 0 {
return handle, buffer, nil
}
return handle, nil, nil
}
// gciDescriptorAlloc calls GCIDescriptorAlloc then returns
// descriptor pointer to pointer, buffer pointer to pointer, and error
func (conn *Conn) gciDescriptorAlloc(descriptorType C.ub4, size C.size_t) (*unsafe.Pointer, *unsafe.Pointer, error) {
var descriptorTemp unsafe.Pointer
descriptor := &descriptorTemp
var bufferTemp unsafe.Pointer
var buffer *unsafe.Pointer
if size > 0 {
buffer = &bufferTemp
}
result := C.GCIDescriptorAlloc(
unsafe.Pointer(conn.env), // An environment handle
descriptor, // Returns a descriptor or LOB locator of desired type
descriptorType, // Specifies the type of descriptor or LOB locator to be allocated
size, // Specifies an amount of user memory to be allocated for use by the application for the lifetime of the descriptor
buffer, // Returns a pointer to the user memory of size xtramem_sz allocated by the call for the user for the lifetime of the descriptor
)
err := conn.getError(result)
if err != nil {
return nil, nil, err
}
if size > 0 {
return descriptor, buffer, nil
}
return descriptor, nil, nil
}
// gciLobCreateTemporary calls GCILobCreateTemporary then returns error
func (conn *Conn) gciLobCreateTemporary(lobLocator *C.GCILobLocator, form C.ub1, lobType C.ub1) error {
result := C.GCILobCreateTemporary(
conn.svc, // service context handle
conn.errHandle, // error handle
lobLocator, // locator that points to the temporary LOB
C.GCI_DEFAULT, // LOB character set ID. For gbase8s or later, pass as GCI_DEFAULT.
form, // character set form
lobType, // type of LOB to create: GCI_TEMP_BLOB or GCI_TEMP_CLOB
C.TRUE, // Pass TRUE if the temporary LOB should be read into the cache; pass FALSE if it should not. FALSE for NOCACHE functionality
C.GCI_DURATION_SESSION, // duration of the temporary LOB: GCI_DURATION_SESSION or GCI_DURATION_CALL
)
return conn.getError(result)
}
// gciLobRead calls GCILobRead then returns lob bytes and error.
func (conn *Conn) gciLobRead(lobLocator *C.GCILobLocator, form C.ub1) ([]byte, error) {
buffer := make([]byte, 0)
// set character set form
result := C.GCILobCharSetForm(
conn.env, // environment handle
conn.errHandle, // error handle
lobLocator, // LOB locator
&form, // character set form
)
if result != C.GCI_SUCCESS {
return buffer, conn.getError(result)
}
readBuffer := byteBufferPool.Get().([]byte)
piece := (C.ub1)(C.GCI_FIRST_PIECE)
result = C.GCI_NEED_DATA
for result == C.GCI_NEED_DATA {
readBytes := (C.gbsub8)(0)
// If both byte_amtp and char_amtp are set to point to zero and GCI_FIRST_PIECE is passed then polling mode is assumed and data is read till the end of the LOB
result = C.GCILobRead2(
conn.svc, // service context handle
conn.errHandle, // error handle
lobLocator, // LOB or BFILE locator
&readBytes, // number of bytes to read. Used for BLOB and BFILE always. For CLOB and NCLOB, it is used only when char_amtp is zero.
nil, // number of characters to read
1, // the offset in the first call and in subsequent polling calls the offset parameter is ignored
unsafe.Pointer(&readBuffer[0]), // pointer to a buffer into which the piece will be read
lobBufferSize, // length of the buffer
piece, // For polling, pass GCI_FIRST_PIECE the first time and GCI_NEXT_PIECE in subsequent calls.
nil, // context pointer for the callback function
nil, // If this is null, then GCI_NEED_DATA will be returned for each piece.
0, // character set ID of the buffer data. If this value is 0 then csid is set to the client's NLS_LANG or NLS_CHAR value, depending on the value of csfrm.
form, // character set form of the buffer data
)
if piece == C.GCI_FIRST_PIECE {
piece = C.GCI_NEXT_PIECE
}
if result == C.GCI_SUCCESS || result == C.GCI_NEED_DATA {
buffer = append(buffer, readBuffer[:int(readBytes)]...)
}
}
return buffer, conn.getError(result)
}
// gciLobWrite calls GCILobWrite then returns error.
func (conn *Conn) gciLobWrite(lobLocator *C.GCILobLocator, form C.ub1, data []byte) error {
start := 0
writeBuffer := byteBufferPool.Get().([]byte)
piece := (C.ub1)(C.GCI_FIRST_PIECE)
writeBytes := (C.gbsub8)(len(data))
if len(data) <= lobBufferSize {
piece = (C.ub1)(C.GCI_ONE_PIECE)
copy(writeBuffer, data)
} else {
copy(writeBuffer, data[0:lobBufferSize])
}
for {
result := C.GCILobWrite2(
conn.svc, // service context handle
conn.errHandle, // error handle
lobLocator, // LOB or BFILE locator
&writeBytes, // IN - The number of bytes to write to the database. OUT - The number of bytes written to the database.
nil, // maximum number of characters to write
(C.gbsub8)(1), // the offset in the first call and in subsequent polling calls the offset parameter is ignored
unsafe.Pointer(&writeBuffer[0]), // pointer to a buffer from which the piece is written
(C.gbsub8)(lobBufferSize), // length, in bytes, of the data in the buffer
piece, // which piece of the buffer is being written. GCI_ONE_PIECE, indicating that the buffer is written in a single piece. Piecewise or callback mode: GCI_FIRST_PIECE, GCI_NEXT_PIECE, and GCI_LAST_PIECE.
nil, // callback function
nil, // callback that can be registered
0, // character set ID
form, // character set form
)
if result != C.GCI_SUCCESS && result != C.GCI_NEED_DATA {
err := conn.getError(result)
fmt.Println(err)
return err
}
start += lobBufferSize
if start >= len(data) {
break
}
if start+lobBufferSize < len(data) {
piece = C.GCI_NEXT_PIECE
copy(writeBuffer, data[start:start+lobBufferSize])
} else {
piece = C.GCI_LAST_PIECE
copy(writeBuffer, data[start:])
}
}
return nil
}
// gciDateTimeToTime coverts GCIDateTime to Go Time
func (conn *Conn) gciDateTimeToTime(dateTime *C.GCIDateTime, gciDateTimeHasTimeZone bool) (*time.Time, error) {
// get date
var year C.sb2
var month C.ub1
var day C.ub1
result := C.GCIDateTimeGetDate(
unsafe.Pointer(conn.env), // environment handle
conn.errHandle, // error handle
dateTime, // pointer to an GCIDateTime
&year, // year
&month, // month
&day, // day
)
err := conn.getError(result)
if err != nil {
return nil, err
}
// get time
var hour C.ub1
var min C.ub1
var sec C.ub1
var fsec C.ub4
result = C.GCIDateTimeGetTime(
unsafe.Pointer(conn.env), // environment handle
conn.errHandle, // error handle
dateTime, // pointer to an GCIDateTime
&hour, // hour
&min, // min
&sec, // sec
&fsec, // fsec
)
err = conn.getError(result)
if err != nil {
return nil, err
}
if !gciDateTimeHasTimeZone {
aTime := time.Date(int(year), time.Month(month), int(day), int(hour), int(min), int(sec), int(fsec)*10000, conn.timeLocation)
return &aTime, nil
}
// get GCI time zone offset
var timeZoneHour C.sb1
var timeZoneMin C.sb1
result = C.GCIDateTimeGetTimeZoneOffset(
unsafe.Pointer(conn.env), // environment handle
conn.errHandle, // error handle
dateTime, // pointer to an GCIDateTime
&timeZoneHour, // time zone hour
&timeZoneMin, // time zone minute
)
err = conn.getError(result)
if err != nil {
return nil, err
}
// return Go Time using GCI time zone offset
aTime := time.Date(int(year), time.Month(month), int(day), int(hour), int(min), int(sec), int(fsec)*10000,
timezoneToLocation(int64(timeZoneHour), int64(timeZoneMin)))
return &aTime, nil
}
// timeToGCIDateTime coverts Go Time to GCIDateTime
func (conn *Conn) timeToGCIDateTime(aTime *time.Time) (*unsafe.Pointer, error) {
var err error
var dateTimePP *unsafe.Pointer
dateTimePP, _, err = conn.gciDescriptorAlloc(C.GCI_DTYPE_TIMESTAMP_TZ, 0)
if err != nil {
return nil, err
}
dateTimeP := (*C.GCIDateTime)(*dateTimePP)
// make time zone string formated: [+|-][HH:MM]
_, offset := aTime.Zone()
timeZone := make([]byte, 0, 6)
if offset < 0 {
timeZone = append(timeZone, '-')
offset = -offset
} else {
timeZone = append(timeZone, '+')
}
// hours
timeZone = appendSmallInt(timeZone, offset/3600)
offset %= 3600
timeZone = append(timeZone, ':')
// minutes
timeZone = appendSmallInt(timeZone, offset/60)
result := C.GCIDateTimeConstruct(
unsafe.Pointer(conn.env), // environment handle
conn.errHandle, // error handle
dateTimeP, // an GCIDateTime pointer
C.sb2(aTime.Year()), // year
C.ub1(aTime.Month()), // month
C.ub1(aTime.Day()), // day
C.ub1(aTime.Hour()), // hour
C.ub1(aTime.Minute()), // minute
C.ub1(aTime.Second()), // second
C.ub4(aTime.Nanosecond()), // fractional second
(*C.GCItext)(&timeZone[0]), // time zone string formated: [+|-][HH:MM]
C.size_t(6), // time zone string length
)
err = conn.getError(result)
if err != nil {
return nil, err
}
return dateTimePP, nil
}
// appendSmallInt takes small int and returns an appended byte slice
// if int is > 99 or < 0 the result may not be as expected
func appendSmallInt(slice []byte, num int) []byte {
if num == 0 {
return append(slice, '0', '0')
}
if num < 10 {
return append(slice, '0', byte('0'+num))
}
return append(slice, byte('0'+num/10), byte('0'+(num%10)))
}
// gciBreakDone calls GCIBreak if ctx.Done is finished before done chan is closed
func (conn *Conn) gciBreakDone(ctx context.Context, done chan struct{}) {
select {
case <-done:
case <-ctx.Done():
// select again to avoid race condition if both are done
select {
case <-done:
default:
conn.gciBreak()
}
}
}
// gciBreak calls GCIBreak
func (conn *Conn) gciBreak() {
result := C.GCIBreak(
unsafe.Pointer(conn.svc), // service or server context handle
conn.errHandle, // error handle
)
err := conn.getError(result)
if err != nil {
conn.logger.Print("GCIBreak error: ", err)
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。