1 Star 0 Fork 0

GBase8s/go-gci

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
gbase8s.go 17.54 KB
一键复制 编辑 原始数据 按行查看 历史
sunsuzhe 提交于 2024-09-18 17:41 . fix parase gbasedbtdir from url
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
package gbase8s
// #include "gbase8s.go.h"
import "C"
import (
"database/sql/driver"
"errors"
"fmt"
"io/ioutil"
"log"
"net/url"
"os"
"strconv"
"strings"
"time"
"unsafe"
)
// ParseDSN parses a DSN used to connect to gbase8s
//
// It expects to receive a string in the form:
//
// [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
//
// # Connection timeout can be set in the gbase8s files
//
// Supported parameters are:
//
// loc - the time location for reading timestamp (without time zone). Defaults to UTC
// Note that writing a timestamp (without time zone) just truncates the time zone.
//
// isolation - the isolation level that can be set to: READONLY, SERIALIZABLE, or DEFAULT
//
// prefetch_rows - the number of top level rows to be prefetched. Defaults to 0. A 0 means unlimited rows.
//
// prefetch_memory - the max memory for top level rows to be prefetched. Defaults to 4096. A 0 means unlimited memory.
//
// questionph - when true, enables question mark placeholders. Defaults to false. (uses strconv.ParseBool to check for true)
// ExtractLogTrace from DSN String extract GOGCILogTrace Parameters value,return DSN value after modified
func ExtractLogTrace(dsnString string) (string, string) {
logtracKey := "GOGCILogTrace="
var logtracValue string
startIdx := strings.Index(dsnString, logtracKey)
if startIdx != -1 {
startIdx += len(logtracKey)
endIdx := strings.Index(dsnString[startIdx:], ";")
if endIdx == -1 {
logtracValue = dsnString[startIdx:]
dsnString = dsnString[:startIdx-len(logtracKey)]
} else {
logtracValue = dsnString[startIdx : startIdx+endIdx]
dsnString = dsnString[:startIdx-len(logtracKey)] + dsnString[startIdx+endIdx+1:]
}
}
return dsnString, logtracValue
}
func ConvertToGBaseFormat(dsn string) (string, error) {
// Parse the provided DSN string using the url.Parse function.
u, err := url.Parse(dsn)
if err != nil {
// If an error occurs while parsing, return the error.
return "", err
}
// Extract the username and password from the URL.
username := u.User.Username()
password, _ := u.User.Password()
// Extract the hostname and port from the URL.
host := u.Hostname()
port := u.Port()
// Extract the database name by trimming the leading "/" from the URL path.
database := strings.TrimPrefix(u.Path, "/")
// Extract the query parameters from the URL.
queryParams := u.Query()
// Initialize an empty slice to hold the individual connection parameters.
params := make([]string, 0)
// If the query parameter "GBASEDBTSERVER" exists, add it to the params list.
if val := queryParams.Get("GBASEDBTSERVER"); val != "" {
params = append(params, fmt.Sprintf("GBASEDBTSERVER=%s", val))
}
// Add the host to the params list.
params = append(params, fmt.Sprintf("host=%s", host))
// Add the service (port) to the params list.
params = append(params, fmt.Sprintf("SERVICE=%s", port))
// If the query parameter "PROTOCOL" exists, add it to the params list.
if val := queryParams.Get("PROTOCOL"); val != "" {
params = append(params, fmt.Sprintf("PROTOCOL=%s", val))
}
// Add the database name to the params list.
params = append(params, fmt.Sprintf("DATABASE=%s", database))
// Define a list of other potential query parameters that need to be checked.
otherParams := []string{"sqlmode", "delimident", "gbasedbtdir", "GCI_FACTORY", "GOGCILogTrace"}
// Iterate over the list of other query parameters.
for _, key := range otherParams {
// If the query parameter exists, add it to the params list after decoding it.
if val := queryParams.Get(key); val != "" {
decodedVal, _ := url.QueryUnescape(val)
params = append(params, fmt.Sprintf("%s=%s", key, decodedVal))
}
}
// Join all the connection parameters into a single string, separated by semicolons.
paramString := strings.Join(params, ";")
// Format the final GBase DSN using the username, password, and parameter string.
gbaseDSN := fmt.Sprintf("gbase8s://%s/%s@gbase8s-sqli:%s;", username, password, paramString)
// Return the formatted GBase DSN.
return gbaseDSN, nil
}
func ParseDSN(dsnString string) (dsn *DSN, err error) {
if dsnString == "" {
return nil, errors.New("empty dsn")
}
dsnString, _ = ConvertToGBaseFormat(dsnString)
dsnString, logTraceValue := ExtractLogTrace(dsnString)
const prefix = "gbase8s://"
if strings.HasPrefix(dsnString, prefix) {
dsnString = dsnString[len(prefix):]
}
dsn = &DSN{
prefetchRows: 0,
prefetchMemory: 4096,
stmtCacheSize: 0,
operationMode: C.GCI_DEFAULT,
timeLocation: time.UTC,
}
if logTraceValue == "1" {
dsn.GOGCILogTrac = true
} else {
dsn.GOGCILogTrac = false
}
authority, dsnString := splitRight(dsnString, "@")
if authority != "" {
dsn.Username, dsn.Password, err = parseAuthority(authority)
if err != nil {
return nil, err
}
}
host, params := splitRight(dsnString, "?")
if host, err = unescape(host, encodeHost); err != nil {
return nil, err
}
dsn.Connect = host
qp, err := ParseQuery(params)
for k, v := range qp {
switch k {
case "loc":
if len(v) > 0 {
if dsn.timeLocation, err = time.LoadLocation(v[0]); err != nil {
return nil, fmt.Errorf("Invalid loc: %v: %v", v[0], err)
}
}
case "isolation":
switch v[0] {
case "READONLY":
dsn.transactionMode = C.GCI_TRANS_READONLY
case "SERIALIZABLE":
dsn.transactionMode = C.GCI_TRANS_SERIALIZABLE
case "DEFAULT":
dsn.transactionMode = C.GCI_TRANS_READWRITE
default:
return nil, fmt.Errorf("Invalid isolation: %v", v[0])
}
case "questionph":
dsn.enableQMPlaceholders, err = strconv.ParseBool(v[0])
if err != nil {
return nil, fmt.Errorf("Invalid questionph: %v", v[0])
}
case "prefetch_rows":
z, err := strconv.ParseUint(v[0], 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid prefetch_rows: %v", v[0])
}
dsn.prefetchRows = C.ub4(z)
case "prefetch_memory":
z, err := strconv.ParseUint(v[0], 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid prefetch_memory: %v", v[0])
}
dsn.prefetchMemory = C.ub4(z)
case "as":
switch v[0] {
case "SYSDBA", "sysdba":
dsn.operationMode = C.GCI_SYSDBA
case "SYSASM", "sysasm":
dsn.operationMode = C.GCI_SYSASM
case "SYSOPER", "sysoper":
dsn.operationMode = C.GCI_SYSOPER
default:
return nil, fmt.Errorf("Invalid as: %v", v[0])
}
case "stmt_cache_size":
z, err := strconv.ParseUint(v[0], 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid stmt_cache_size: %v", v[0])
}
dsn.stmtCacheSize = C.ub4(z)
}
}
return dsn, nil
}
// Commit transaction commit
func (tx *Tx) Commit() error {
tx.conn.inTransaction = false
if rv := C.GCITransCommit(
tx.conn.svc,
tx.conn.errHandle,
0,
); rv != C.GCI_SUCCESS {
return tx.conn.getError(rv)
}
return nil
}
// Rollback transaction rollback
func (tx *Tx) Rollback() error {
tx.conn.inTransaction = false
if rv := C.GCITransRollback(
tx.conn.svc,
tx.conn.errHandle,
0,
); rv != C.GCI_SUCCESS {
return tx.conn.getError(rv)
}
return nil
}
// Open opens a new database connection
func (drv *DriverStruct) Open(dsnString string) (driver.Conn, error) {
var err error
var dsn *DSN
if dsn, err = ParseDSN(dsnString); err != nil {
return nil, err
}
conn := Conn{
operationMode: dsn.operationMode,
stmtCacheSize: dsn.stmtCacheSize,
logger: drv.Logger,
}
if conn.logger == nil {
conn.logger = log.New(ioutil.Discard, "", 0)
}
// environment handle
var envP *C.GCIEnv
envPP := &envP
var result C.sword
charset := C.ub2(0)
if os.Getenv("NLS_LANG") == "" && os.Getenv("NLS_NCHAR") == "" {
charset = defaultCharset
}
result = C.GCIEnvNlsCreate(
envPP, // pointer to a handle to the environment
C.GCI_THREADED, // environment mode
nil, // Specifies the user-defined context for the memory callback routines.
nil, // Specifies the user-defined memory allocation function. If mode is GCI_THREADED, this memory allocation routine must be thread-safe.
nil, // Specifies the user-defined memory re-allocation function. If the mode is GCI_THREADED, this memory allocation routine must be thread safe.
nil, // Specifies the user-defined memory free function. If mode is GCI_THREADED, this memory free routine must be thread-safe.
0, // Specifies the amount of user memory to be allocated for the duration of the environment.
nil, // Returns a pointer to the user memory of size xtramemsz allocated by the call for the user.
charset, // The client-side character set for the current environment handle. If it is 0, the NLS_LANG setting is used.
charset, // The client-side national character set for the current environment handle. If it is 0, NLS_NCHAR setting is used.
)
if result != C.GCI_SUCCESS {
return nil, errors.New("GCIEnvNlsCreate error")
}
conn.env = *envPP
// defer on error handle free
var doneSessionBegin bool
var doneServerAttach bool
var doneLogon bool
defer func(errP *error) {
if *errP != nil {
if doneSessionBegin {
C.GCISessionEnd(
conn.svc,
conn.errHandle,
conn.usrSession,
C.GCI_DEFAULT,
)
}
if doneLogon {
C.GCILogoff(
conn.svc,
conn.errHandle,
)
}
if doneServerAttach {
C.GCIServerDetach(
conn.srv,
conn.errHandle,
C.GCI_DEFAULT,
)
}
if conn.txHandle != nil {
C.GCIHandleFree(unsafe.Pointer(conn.txHandle), C.GCI_HTYPE_TRANS)
conn.txHandle = nil
}
if conn.usrSession != nil {
C.GCIHandleFree(unsafe.Pointer(conn.usrSession), C.GCI_HTYPE_SESSION)
conn.usrSession = nil
}
if conn.svc != nil {
C.GCIHandleFree(unsafe.Pointer(conn.svc), C.GCI_HTYPE_SVCCTX)
conn.svc = nil
}
if conn.srv != nil {
C.GCIHandleFree(unsafe.Pointer(conn.srv), C.GCI_HTYPE_SERVER)
conn.srv = nil
}
if conn.errHandle != nil {
C.GCIHandleFree(unsafe.Pointer(conn.errHandle), C.GCI_HTYPE_ERROR)
conn.errHandle = nil
}
C.GCIHandleFree(unsafe.Pointer(conn.env), C.GCI_HTYPE_ENV)
}
}(&err)
// error handle
var handleTemp unsafe.Pointer
handle := &handleTemp
result = C.GCIHandleAlloc(
unsafe.Pointer(conn.env), // An environment handle
handle, // Returns a handle
C.GCI_HTYPE_ERROR, // type of handle
0, // amount of user memory to be allocated
nil, // Returns a pointer to the user memory
)
if result != C.GCI_SUCCESS {
// TODO: error handle not yet allocated, how to get string error from gbase8s?
err = errors.New("allocate error handle error")
return nil, err
}
conn.errHandle = (*C.GCIError)(*handle)
connectString := cString(dsn.Connect)
defer C.free(unsafe.Pointer(connectString))
username := cString(dsn.Username)
defer C.free(unsafe.Pointer(username))
password := cString(dsn.Password)
defer C.free(unsafe.Pointer(password))
if useGCISessionBegin {
// server handle
handle, _, err = conn.gciHandleAlloc(C.GCI_HTYPE_SERVER, 0)
if err != nil {
return nil, fmt.Errorf("allocate server handle error: %v", err)
}
conn.srv = (*C.GCIServer)(*handle)
if len(dsn.Connect) < 1 {
result = C.GCIServerAttach(
conn.srv, // uninitialized server handle, which gets initialized by this call. Passing in an initialized server handle causes an error.
conn.errHandle, // error handle
nil, // connect string or a service point
0, // length of the database server
C.GCI_DEFAULT, // mode of operation: GCI_DEFAULT or GCI_CPOOL
)
} else {
result = C.GCIServerAttach(
conn.srv, // uninitialized server handle, which gets initialized by this call. Passing in an initialized server handle causes an error.
conn.errHandle, // error handle
connectString, // connect string or a service point
C.sb4(len(dsn.Connect)), // length of the database server
C.GCI_DEFAULT, // mode of operation: GCI_DEFAULT or GCI_CPOOL
)
}
if result != C.GCI_SUCCESS {
err = conn.getError(result)
return nil, conn.getError(result)
}
doneServerAttach = true
// service handle
handle, _, err = conn.gciHandleAlloc(C.GCI_HTYPE_SVCCTX, 0)
if err != nil {
return nil, fmt.Errorf("allocate service handle error: %v", err)
}
conn.svc = (*C.GCISvcCtx)(*handle)
// sets the server context attribute of the service context
err = conn.gciAttrSet(unsafe.Pointer(conn.svc), C.GCI_HTYPE_SVCCTX, unsafe.Pointer(conn.srv), 0, C.GCI_ATTR_SERVER)
if err != nil {
return nil, fmt.Errorf("server context attribute set error: %v", err)
}
// user session handle
handle, _, err = conn.gciHandleAlloc(C.GCI_HTYPE_SESSION, 0)
if err != nil {
return nil, fmt.Errorf("allocate user session handle error: %v", err)
}
conn.usrSession = (*C.GCISession)(*handle)
credentialType := C.ub4(C.GCI_CRED_EXT)
if len(dsn.Username) > 0 {
// specifies a username to use for authentication
err = conn.gciAttrSet(unsafe.Pointer(conn.usrSession), C.GCI_HTYPE_SESSION, unsafe.Pointer(username), C.ub4(len(dsn.Username)), C.GCI_ATTR_USERNAME)
if err != nil {
return nil, fmt.Errorf("username attribute set error: %v", err)
}
// specifies a password to use for authentication
err = conn.gciAttrSet(unsafe.Pointer(conn.usrSession), C.GCI_HTYPE_SESSION, unsafe.Pointer(password), C.ub4(len(dsn.Password)), C.GCI_ATTR_PASSWORD)
if err != nil {
return nil, fmt.Errorf("password attribute set error: %v", err)
}
credentialType = C.GCI_CRED_RDBMS
}
result = C.GCISessionBegin(
conn.svc, // service context
conn.errHandle, // error handle
conn.usrSession, // user session context
credentialType, // type of credentials to use for establishing the user session: GCI_CRED_RDBMS or GCI_CRED_EXT
conn.operationMode, // mode of operation.
)
if result != C.GCI_SUCCESS && result != C.GCI_SUCCESS_WITH_INFO {
err = conn.getError(result)
return nil, err
}
doneSessionBegin = true
// sets the authentication context attribute of the service context
err = conn.gciAttrSet(unsafe.Pointer(conn.svc), C.GCI_HTYPE_SVCCTX, unsafe.Pointer(conn.usrSession), 0, C.GCI_ATTR_SESSION)
if err != nil {
return nil, fmt.Errorf("authentication context attribute set error: %v", err)
}
if dsn.stmtCacheSize > 0 {
stmtCacheSize := dsn.stmtCacheSize
err = conn.gciAttrSet(unsafe.Pointer(conn.svc), C.GCI_HTYPE_SVCCTX, unsafe.Pointer(&stmtCacheSize), 0, C.GCI_ATTR_STMTCACHESIZE)
if err != nil {
return nil, fmt.Errorf("stmt cache size attribute set error: %v", err)
}
}
} else {
var svcCtxP *C.GCISvcCtx
svcCtxPP := &svcCtxP
result = C.GCILogon(
conn.env, // environment handle
conn.errHandle, // error handle
svcCtxPP, // service context pointer
username, // user name. Must be in the encoding specified by the charset parameter of a previous call to GCIEnvNlsCreate().
C.ub4(len(dsn.Username)), // length of user name, in number of bytes, regardless of the encoding
password, // user's password. Must be in the encoding specified by the charset parameter of a previous call to GCIEnvNlsCreate().
C.ub4(len(dsn.Password)), // length of password, in number of bytes, regardless of the encoding.
connectString, // name of the database to connect to. Must be in the encoding specified by the charset parameter of a previous call to GCIEnvNlsCreate().
C.ub4(len(dsn.Connect)), // length of dbname, in number of bytes, regardless of the encoding.
)
if result != C.GCI_SUCCESS && result != C.GCI_SUCCESS_WITH_INFO {
err = conn.getError(result)
return nil, err
}
conn.svc = *svcCtxPP
doneLogon = true
}
// Create transaction context.
handle, _, err = conn.gciHandleAlloc(C.GCI_HTYPE_TRANS, 0)
if err != nil {
return nil, fmt.Errorf("allocate transaction handle error: %v", err)
}
conn.txHandle = (*C.GCITrans)(*handle)
// Set transaction context attribute of the service context.
err = conn.gciAttrSet(unsafe.Pointer(conn.svc), C.GCI_HTYPE_SVCCTX, *handle, 0, C.GCI_ATTR_TRANS)
if err != nil {
return nil, fmt.Errorf("service context attribute set error: %v", err)
}
conn.transactionMode = dsn.transactionMode
conn.prefetchRows = dsn.prefetchRows
conn.prefetchMemory = dsn.prefetchMemory
conn.timeLocation = dsn.timeLocation
conn.enableQMPlaceholders = dsn.enableQMPlaceholders
return &conn, nil
}
// GetLastInsertId returns rowid from LastInsertId
func GetLastInsertId(id int64) string {
return *(*string)(unsafe.Pointer(uintptr(id)))
}
// LastInsertId returns last inserted ID
func (result *Result) LastInsertId() (int64, error) {
num, err := strconv.ParseInt(result.rowid, 10, 64)
if err != nil {
num = -1
}
return num, result.rowidErr
return int64(uintptr(unsafe.Pointer(&result.rowid))), result.rowidErr
}
// RowsAffected returns rows affected
func (result *Result) RowsAffected() (int64, error) {
return result.rowsAffected, result.rowsAffectedErr
}
// converts "?" characters to :1, :2, ... :n
func placeholders(sql string) string {
n := 0
return phre.ReplaceAllStringFunc(sql, func(string) string {
n++
return ":" + strconv.Itoa(n)
})
}
func timezoneToLocation(hour int64, minute int64) *time.Location {
if minute != 0 || hour > 14 || hour < -12 {
// create location with FixedZone
var name string
if hour < 0 {
name = strconv.FormatInt(hour, 10) + ":"
} else {
name = "+" + strconv.FormatInt(hour, 10) + ":"
}
if minute == 0 {
name += "00"
} else {
if minute < 10 {
name += "0"
}
name += strconv.FormatInt(minute, 10)
}
return time.FixedZone(name, (3600*int(hour))+(60*int(minute)))
}
// use location from timeLocations cache
return timeLocations[12+hour]
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/GBase8s/go-gci.git
git@gitee.com:GBase8s/go-gci.git
GBase8s
go-gci
go-gci
master

搜索帮助