Merge pull request #1600 from taosdata/change-go-driver-to-gitsubmodule-for-2.0
change go driver to git submodule.
This commit is contained in:
commit
d30ab1c58e
|
@ -0,0 +1,3 @@
|
|||
[submodule "src/connector/go"]
|
||||
path = src/connector/go
|
||||
url = https://github.com/taosdata/driver-go
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8c58c512b6acda8bcdfa48fdc7140227b5221766
|
|
@ -1,368 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package taosSql
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"database/sql/driver"
|
||||
"unsafe"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type taosConn struct {
|
||||
taos unsafe.Pointer
|
||||
affectedRows int
|
||||
insertId int
|
||||
cfg *config
|
||||
status statusFlag
|
||||
parseTime bool
|
||||
reset bool // set when the Go SQL package calls ResetSession
|
||||
}
|
||||
|
||||
type taosSqlResult struct {
|
||||
affectedRows int64
|
||||
insertId int64
|
||||
}
|
||||
|
||||
func (res *taosSqlResult) LastInsertId() (int64, error) {
|
||||
return res.insertId, nil
|
||||
}
|
||||
|
||||
func (res *taosSqlResult) RowsAffected() (int64, error) {
|
||||
return res.affectedRows, nil
|
||||
}
|
||||
|
||||
func (mc *taosConn) Begin() (driver.Tx, error) {
|
||||
taosLog.Println("taosSql not support transaction")
|
||||
return nil, errors.New("taosSql not support transaction")
|
||||
}
|
||||
|
||||
func (mc *taosConn) Close() (err error) {
|
||||
if mc.taos == nil {
|
||||
return errConnNoExist
|
||||
}
|
||||
mc.taos_close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *taosConn) Prepare(query string) (driver.Stmt, error) {
|
||||
if mc.taos == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
|
||||
stmt := &taosSqlStmt{
|
||||
mc: mc,
|
||||
pSql: query,
|
||||
}
|
||||
|
||||
// find ? count and save to stmt.paramCount
|
||||
stmt.paramCount = strings.Count(query, "?")
|
||||
|
||||
//fmt.Printf("prepare alloc stmt:%p, sql:%s\n", stmt, query)
|
||||
taosLog.Printf("prepare alloc stmt:%p, sql:%s\n", stmt, query)
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (mc *taosConn) interpolateParams(query string, args []driver.Value) (string, error) {
|
||||
// Number of ? should be same to len(args)
|
||||
if strings.Count(query, "?") != len(args) {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
|
||||
buf := make([]byte, defaultBufSize)
|
||||
buf = buf[:0] // clear buf
|
||||
argPos := 0
|
||||
|
||||
for i := 0; i < len(query); i++ {
|
||||
q := strings.IndexByte(query[i:], '?')
|
||||
if q == -1 {
|
||||
buf = append(buf, query[i:]...)
|
||||
break
|
||||
}
|
||||
buf = append(buf, query[i:i+q]...)
|
||||
i += q
|
||||
|
||||
arg := args[argPos]
|
||||
argPos++
|
||||
|
||||
if arg == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := arg.(type) {
|
||||
case int64:
|
||||
buf = strconv.AppendInt(buf, v, 10)
|
||||
case uint64:
|
||||
// Handle uint64 explicitly because our custom ConvertValue emits unsigned values
|
||||
buf = strconv.AppendUint(buf, v, 10)
|
||||
case float64:
|
||||
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
|
||||
case bool:
|
||||
if v {
|
||||
buf = append(buf, '1')
|
||||
} else {
|
||||
buf = append(buf, '0')
|
||||
}
|
||||
case time.Time:
|
||||
if v.IsZero() {
|
||||
buf = append(buf, "'0000-00-00'"...)
|
||||
} else {
|
||||
v := v.In(mc.cfg.loc)
|
||||
v = v.Add(time.Nanosecond * 500) // To round under microsecond
|
||||
year := v.Year()
|
||||
year100 := year / 100
|
||||
year1 := year % 100
|
||||
month := v.Month()
|
||||
day := v.Day()
|
||||
hour := v.Hour()
|
||||
minute := v.Minute()
|
||||
second := v.Second()
|
||||
micro := v.Nanosecond() / 1000
|
||||
|
||||
buf = append(buf, []byte{
|
||||
'\'',
|
||||
digits10[year100], digits01[year100],
|
||||
digits10[year1], digits01[year1],
|
||||
'-',
|
||||
digits10[month], digits01[month],
|
||||
'-',
|
||||
digits10[day], digits01[day],
|
||||
' ',
|
||||
digits10[hour], digits01[hour],
|
||||
':',
|
||||
digits10[minute], digits01[minute],
|
||||
':',
|
||||
digits10[second], digits01[second],
|
||||
}...)
|
||||
|
||||
if micro != 0 {
|
||||
micro10000 := micro / 10000
|
||||
micro100 := micro / 100 % 100
|
||||
micro1 := micro % 100
|
||||
buf = append(buf, []byte{
|
||||
'.',
|
||||
digits10[micro10000], digits01[micro10000],
|
||||
digits10[micro100], digits01[micro100],
|
||||
digits10[micro1], digits01[micro1],
|
||||
}...)
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
}
|
||||
case []byte:
|
||||
if v == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
} else {
|
||||
buf = append(buf, "_binary'"...)
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeBytesBackslash(buf, v)
|
||||
} else {
|
||||
buf = escapeBytesQuotes(buf, v)
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
}
|
||||
case string:
|
||||
//buf = append(buf, '\'')
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeStringBackslash(buf, v)
|
||||
} else {
|
||||
buf = escapeStringQuotes(buf, v)
|
||||
}
|
||||
//buf = append(buf, '\'')
|
||||
default:
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
|
||||
//if len(buf)+4 > mc.maxAllowedPacket {
|
||||
if len(buf)+4 > maxTaosSqlLen {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
}
|
||||
if argPos != len(args) {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func (mc *taosConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
if mc.taos == nil {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if len(args) != 0 {
|
||||
if !mc.cfg.interpolateParams {
|
||||
return nil, driver.ErrSkip
|
||||
}
|
||||
// try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
|
||||
prepared, err := mc.interpolateParams(query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
}
|
||||
|
||||
mc.affectedRows = 0
|
||||
mc.insertId = 0
|
||||
_, err := mc.taosQuery(query)
|
||||
if err == nil {
|
||||
return &taosSqlResult{
|
||||
affectedRows: int64(mc.affectedRows),
|
||||
insertId: int64(mc.insertId),
|
||||
}, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (mc *taosConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
return mc.query(query, args)
|
||||
}
|
||||
|
||||
func (mc *taosConn) query(query string, args []driver.Value) (*textRows, error) {
|
||||
if mc.taos == nil {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if len(args) != 0 {
|
||||
if !mc.cfg.interpolateParams {
|
||||
return nil, driver.ErrSkip
|
||||
}
|
||||
// try client-side prepare to reduce roundtrip
|
||||
prepared, err := mc.interpolateParams(query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
}
|
||||
|
||||
num_fields, err := mc.taosQuery(query)
|
||||
if err == nil {
|
||||
// Read Result
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
|
||||
// Columns field
|
||||
rows.rs.columns, err = mc.readColumns(num_fields)
|
||||
return rows, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ping implements driver.Pinger interface
|
||||
func (mc *taosConn) Ping(ctx context.Context) (err error) {
|
||||
if mc.taos != nil {
|
||||
return nil
|
||||
}
|
||||
return errInvalidConn
|
||||
}
|
||||
|
||||
// BeginTx implements driver.ConnBeginTx interface
|
||||
func (mc *taosConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
||||
taosLog.Println("taosSql not support transaction")
|
||||
return nil, errors.New("taosSql not support transaction")
|
||||
}
|
||||
|
||||
func (mc *taosConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||
if mc.taos == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := mc.query(query, dargs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (mc *taosConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||
if mc.taos == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mc.Exec(query, dargs)
|
||||
}
|
||||
|
||||
func (mc *taosConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
||||
if mc.taos == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
|
||||
stmt, err := mc.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (stmt *taosSqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||
if stmt.mc == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
dargs, err := namedValueToValue(args)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := stmt.query(dargs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (stmt *taosSqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||
if stmt.mc == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stmt.Exec(dargs)
|
||||
}
|
||||
|
||||
func (mc *taosConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
||||
nv.Value, err = converter{}.ConvertValue(nv.Value)
|
||||
return
|
||||
}
|
||||
|
||||
// ResetSession implements driver.SessionResetter.
|
||||
// (From Go 1.10)
|
||||
func (mc *taosConn) ResetSession(ctx context.Context) error {
|
||||
if mc.taos == nil {
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
mc.reset = true
|
||||
return nil
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package taosSql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
type connector struct {
|
||||
cfg *config
|
||||
}
|
||||
|
||||
// Connect implements driver.Connector interface.
|
||||
// Connect returns a connection to the database.
|
||||
func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
var err error
|
||||
// New taosConn
|
||||
mc := &taosConn{
|
||||
cfg: c.cfg,
|
||||
parseTime: c.cfg.parseTime,
|
||||
}
|
||||
|
||||
// Connect to Server
|
||||
mc.taos, err = mc.taosConnect(mc.cfg.addr, mc.cfg.user, mc.cfg.passwd, mc.cfg.dbName, mc.cfg.port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mc, nil
|
||||
}
|
||||
|
||||
// Driver implements driver.Connector interface.
|
||||
// Driver returns &taosSQLDriver{}.
|
||||
func (c *connector) Driver() driver.Driver {
|
||||
return &taosSQLDriver{}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package taosSql
|
||||
|
||||
const (
|
||||
timeFormat = "2006-01-02 15:04:05"
|
||||
maxTaosSqlLen = 65380
|
||||
defaultBufSize = maxTaosSqlLen + 32
|
||||
)
|
||||
|
||||
type fieldType byte
|
||||
|
||||
type fieldFlag uint16
|
||||
|
||||
const (
|
||||
flagNotNULL fieldFlag = 1 << iota
|
||||
)
|
||||
|
||||
type statusFlag uint16
|
||||
|
||||
const (
|
||||
statusInTrans statusFlag = 1 << iota
|
||||
statusInAutocommit
|
||||
statusReserved // Not in documentation
|
||||
statusMoreResultsExists
|
||||
statusNoGoodIndexUsed
|
||||
statusNoIndexUsed
|
||||
statusCursorExists
|
||||
statusLastRowSent
|
||||
statusDbDropped
|
||||
statusNoBackslashEscapes
|
||||
statusMetadataChanged
|
||||
statusQueryWasSlow
|
||||
statusPsOutParams
|
||||
statusInTransReadonly
|
||||
statusSessionStateChanged
|
||||
)
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package taosSql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// taosSqlDriver is exported to make the driver directly accessible.
|
||||
// In general the driver is used via the database/sql package.
|
||||
type taosSQLDriver struct{}
|
||||
|
||||
// Open new Connection.
|
||||
// the DSN string is formatted
|
||||
func (d taosSQLDriver) Open(dsn string) (driver.Conn, error) {
|
||||
cfg, err := parseDSN(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &connector{
|
||||
cfg: cfg,
|
||||
}
|
||||
return c.Connect(context.Background())
|
||||
}
|
||||
|
||||
func init() {
|
||||
sql.Register("taosSql", &taosSQLDriver{})
|
||||
taosLogInit()
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package taosSql
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
|
||||
errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
|
||||
errInvalidDSNPort = errors.New("invalid DSN: network port is not a valid number")
|
||||
errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
|
||||
)
|
||||
|
||||
// Config is a configuration parsed from a DSN string.
|
||||
// If a new Config is created instead of being parsed from a DSN string,
|
||||
// the NewConfig function should be used, which sets default values.
|
||||
type config struct {
|
||||
user string // Username
|
||||
passwd string // Password (requires User)
|
||||
net string // Network type
|
||||
addr string // Network address (requires Net)
|
||||
port int
|
||||
dbName string // Database name
|
||||
params map[string]string // Connection parameters
|
||||
loc *time.Location // Location for time.Time values
|
||||
columnsWithAlias bool // Prepend table alias to column names
|
||||
interpolateParams bool // Interpolate placeholders into query string
|
||||
parseTime bool // Parse time values to time.Time
|
||||
}
|
||||
|
||||
// NewConfig creates a new Config and sets default values.
|
||||
func newConfig() *config {
|
||||
return &config{
|
||||
loc: time.UTC,
|
||||
interpolateParams: true,
|
||||
parseTime: true,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseDSN parses the DSN string to a Config
|
||||
func parseDSN(dsn string) (cfg *config, err error) {
|
||||
taosLog.Println("input dsn:", dsn)
|
||||
|
||||
// New config with some default values
|
||||
cfg = newConfig()
|
||||
|
||||
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
||||
// Find the last '/' (since the password or the net addr might contain a '/')
|
||||
foundSlash := false
|
||||
for i := len(dsn) - 1; i >= 0; i-- {
|
||||
if dsn[i] == '/' {
|
||||
foundSlash = true
|
||||
var j, k int
|
||||
|
||||
// left part is empty if i <= 0
|
||||
if i > 0 {
|
||||
// [username[:password]@][protocol[(address)]]
|
||||
// Find the last '@' in dsn[:i]
|
||||
for j = i; j >= 0; j-- {
|
||||
if dsn[j] == '@' {
|
||||
// username[:password]
|
||||
// Find the first ':' in dsn[:j]
|
||||
for k = 0; k < j; k++ {
|
||||
if dsn[k] == ':' {
|
||||
cfg.passwd = dsn[k+1 : j]
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.user = dsn[:k]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// [protocol[(address)]]
|
||||
// Find the first '(' in dsn[j+1:i]
|
||||
for k = j + 1; k < i; k++ {
|
||||
if dsn[k] == '(' {
|
||||
// dsn[i-1] must be == ')' if an address is specified
|
||||
if dsn[i-1] != ')' {
|
||||
if strings.ContainsRune(dsn[k+1:i], ')') {
|
||||
return nil, errInvalidDSNUnescaped
|
||||
}
|
||||
return nil, errInvalidDSNAddr
|
||||
}
|
||||
strs := strings.Split(dsn[k+1:i-1], ":")
|
||||
if len(strs) == 1 {
|
||||
return nil, errInvalidDSNAddr
|
||||
}
|
||||
cfg.addr = strs[0]
|
||||
cfg.port, err = strconv.Atoi(strs[1])
|
||||
if err != nil {
|
||||
return nil, errInvalidDSNPort
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.net = dsn[j+1 : k]
|
||||
}
|
||||
|
||||
// dbname[?param1=value1&...¶mN=valueN]
|
||||
// Find the first '?' in dsn[i+1:]
|
||||
for j = i + 1; j < len(dsn); j++ {
|
||||
if dsn[j] == '?' {
|
||||
if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.dbName = dsn[i+1 : j]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundSlash && len(dsn) > 0 {
|
||||
return nil, errInvalidDSNNoSlash
|
||||
}
|
||||
|
||||
taosLog.Printf("cfg info: %+v", cfg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseDSNParams parses the DSN "query string"
|
||||
// Values must be url.QueryEscape'ed
|
||||
func parseDSNParams(cfg *config, params string) (err error) {
|
||||
for _, v := range strings.Split(params, "&") {
|
||||
param := strings.SplitN(v, "=", 2)
|
||||
if len(param) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// cfg params
|
||||
switch value := param[1]; param[0] {
|
||||
case "columnsWithAlias":
|
||||
var isBool bool
|
||||
cfg.columnsWithAlias, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return errors.New("invalid bool value: " + value)
|
||||
}
|
||||
|
||||
// Enable client side placeholder substitution
|
||||
case "interpolateParams":
|
||||
var isBool bool
|
||||
cfg.interpolateParams, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return errors.New("invalid bool value: " + value)
|
||||
}
|
||||
|
||||
// Time Location
|
||||
case "loc":
|
||||
if value, err = url.QueryUnescape(value); err != nil {
|
||||
return
|
||||
}
|
||||
cfg.loc, err = time.LoadLocation(value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// time.Time parsing
|
||||
case "parseTime":
|
||||
var isBool bool
|
||||
cfg.parseTime, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return errors.New("invalid bool value: " + value)
|
||||
}
|
||||
|
||||
default:
|
||||
// lazy init
|
||||
if cfg.params == nil {
|
||||
cfg.params = make(map[string]string)
|
||||
}
|
||||
|
||||
if cfg.params[param[0]], err = url.QueryUnescape(value); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package taosSql
|
||||
|
||||
/*
|
||||
#cgo CFLAGS : -I/usr/include
|
||||
#cgo LDFLAGS: -L/usr/lib -ltaos
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <taos.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/******************************************************************************
|
||||
* Result *
|
||||
******************************************************************************/
|
||||
// Read Packets as Field Packets until EOF-Packet or an Error appears
|
||||
func (mc *taosConn) readColumns(count int) ([]taosSqlField, error) {
|
||||
|
||||
columns := make([]taosSqlField, count)
|
||||
var result unsafe.Pointer
|
||||
result = C.taos_use_result(mc.taos)
|
||||
if result == nil {
|
||||
return nil, errors.New("invalid result")
|
||||
}
|
||||
|
||||
pFields := (*C.struct_taosField)(C.taos_fetch_fields(result))
|
||||
|
||||
// TODO: Optimized rewriting !!!!
|
||||
fields := (*[1 << 30]C.struct_taosField)(unsafe.Pointer(pFields))
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
//columns[i].tableName = ms.taos.
|
||||
//fmt.Println(reflect.TypeOf(fields[i].name))
|
||||
var charray []byte
|
||||
for j := range fields[i].name {
|
||||
//fmt.Println("fields[i].name[j]: ", fields[i].name[j])
|
||||
if fields[i].name[j] != 0 {
|
||||
charray = append(charray, byte(fields[i].name[j]))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
columns[i].name = string(charray)
|
||||
columns[i].length = (uint32)(fields[i].bytes)
|
||||
columns[i].fieldType = fieldType(fields[i]._type)
|
||||
columns[i].flags = 0
|
||||
// columns[i].decimals = 0
|
||||
//columns[i].charSet = 0
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) readRow(dest []driver.Value) error {
|
||||
mc := rows.mc
|
||||
|
||||
if rows.rs.done || mc == nil {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
var result unsafe.Pointer
|
||||
result = C.taos_use_result(mc.taos)
|
||||
if result == nil {
|
||||
return errors.New(C.GoString(C.taos_errstr(mc.taos)))
|
||||
}
|
||||
|
||||
//var row *unsafe.Pointer
|
||||
row := C.taos_fetch_row(result)
|
||||
if row == nil {
|
||||
rows.rs.done = true
|
||||
C.taos_free_result(result)
|
||||
rows.mc = nil
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
// because sizeof(void*) == sizeof(int*) == 8
|
||||
// notes: sizeof(int) == 8 in go, but sizeof(int) == 4 in C.
|
||||
for i := range dest {
|
||||
currentRow := (unsafe.Pointer)(uintptr(*((*int)(unsafe.Pointer(uintptr(unsafe.Pointer(row)) + uintptr(i)*unsafe.Sizeof(int(0)))))))
|
||||
|
||||
if currentRow == nil {
|
||||
dest[i] = nil
|
||||
continue
|
||||
}
|
||||
|
||||
switch rows.rs.columns[i].fieldType {
|
||||
case C.TSDB_DATA_TYPE_BOOL:
|
||||
if (*((*byte)(currentRow))) != 0 {
|
||||
dest[i] = true
|
||||
} else {
|
||||
dest[i] = false
|
||||
}
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_TINYINT:
|
||||
dest[i] = (int)(*((*byte)(currentRow)))
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_SMALLINT:
|
||||
dest[i] = (int16)(*((*int16)(currentRow)))
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_INT:
|
||||
dest[i] = (int)(*((*int32)(currentRow))) // notes int32 of go <----> int of C
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_BIGINT:
|
||||
dest[i] = (int64)(*((*int64)(currentRow)))
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_FLOAT:
|
||||
dest[i] = (*((*float32)(currentRow)))
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_DOUBLE:
|
||||
dest[i] = (*((*float64)(currentRow)))
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_BINARY, C.TSDB_DATA_TYPE_NCHAR:
|
||||
charLen := rows.rs.columns[i].length
|
||||
var index uint32
|
||||
binaryVal := make([]byte, charLen)
|
||||
for index = 0; index < charLen; index++ {
|
||||
binaryVal[index] = *((*byte)(unsafe.Pointer(uintptr(currentRow) + uintptr(index))))
|
||||
}
|
||||
dest[i] = string(binaryVal[:])
|
||||
break
|
||||
|
||||
case C.TSDB_DATA_TYPE_TIMESTAMP:
|
||||
if mc.cfg.parseTime == true {
|
||||
timestamp := (int64)(*((*int64)(currentRow)))
|
||||
dest[i] = timestampConvertToString(timestamp, int(C.taos_result_precision(result)))
|
||||
} else {
|
||||
dest[i] = (int64)(*((*int64)(currentRow)))
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
fmt.Println("default fieldType: set dest[] to nil")
|
||||
dest[i] = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read result as Field format until all rows or an Error appears
|
||||
// call this func in conn mode
|
||||
func (rows *textRows) readRow(dest []driver.Value) error {
|
||||
return rows.taosSqlRows.readRow(dest)
|
||||
}
|
||||
|
||||
// call thsi func in stmt mode
|
||||
func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||
return rows.taosSqlRows.readRow(dest)
|
||||
}
|
||||
|
||||
func timestampConvertToString(timestamp int64, precision int) string {
|
||||
var decimal, sVal, nsVal int64
|
||||
if precision == 0 {
|
||||
decimal = timestamp % 1000
|
||||
sVal = timestamp / 1000
|
||||
nsVal = decimal * 1000
|
||||
} else {
|
||||
decimal = timestamp % 1000000
|
||||
sVal = timestamp / 1000000
|
||||
nsVal = decimal * 1000000
|
||||
}
|
||||
|
||||
date_time := time.Unix(sVal, nsVal)
|
||||
|
||||
//const base_format = "2006-01-02 15:04:05"
|
||||
str_time := date_time.Format(timeFormat)
|
||||
|
||||
return (str_time + "." + strconv.Itoa(int(decimal)))
|
||||
}
|
|
@ -1,296 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package taosSql
|
||||
/*
|
||||
#cgo CFLAGS : -I/usr/include
|
||||
#cgo LDFLAGS: -L/usr/lib -ltaos
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <taos.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type taosSqlField struct {
|
||||
tableName string
|
||||
name string
|
||||
length uint32
|
||||
flags fieldFlag // indicate whether this field can is null
|
||||
fieldType fieldType
|
||||
decimals byte
|
||||
charSet uint8
|
||||
}
|
||||
|
||||
type resultSet struct {
|
||||
columns []taosSqlField
|
||||
columnNames []string
|
||||
done bool
|
||||
}
|
||||
|
||||
type taosSqlRows struct {
|
||||
mc *taosConn
|
||||
rs resultSet
|
||||
}
|
||||
|
||||
type binaryRows struct {
|
||||
taosSqlRows
|
||||
}
|
||||
|
||||
type textRows struct {
|
||||
taosSqlRows
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) Columns() []string {
|
||||
if rows.rs.columnNames != nil {
|
||||
return rows.rs.columnNames
|
||||
}
|
||||
|
||||
columns := make([]string, len(rows.rs.columns))
|
||||
if rows.mc != nil && rows.mc.cfg.columnsWithAlias {
|
||||
for i := range columns {
|
||||
if tableName := rows.rs.columns[i].tableName; len(tableName) > 0 {
|
||||
columns[i] = tableName + "." + rows.rs.columns[i].name
|
||||
} else {
|
||||
columns[i] = rows.rs.columns[i].name
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range columns {
|
||||
columns[i] = rows.rs.columns[i].name
|
||||
}
|
||||
}
|
||||
|
||||
rows.rs.columnNames = columns
|
||||
|
||||
return columns
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) ColumnTypeDatabaseTypeName(i int) string {
|
||||
return rows.rs.columns[i].typeDatabaseName()
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) ColumnTypeLength(i int) (length int64, ok bool) {
|
||||
return int64(rows.rs.columns[i].length), true
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) ColumnTypeNullable(i int) (nullable, ok bool) {
|
||||
return rows.rs.columns[i].flags&flagNotNULL == 0, true
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) ColumnTypePrecisionScale(i int) (int64, int64, bool) {
|
||||
column := rows.rs.columns[i]
|
||||
decimals := int64(column.decimals)
|
||||
|
||||
switch column.fieldType {
|
||||
case C.TSDB_DATA_TYPE_FLOAT:
|
||||
fallthrough
|
||||
case C.TSDB_DATA_TYPE_DOUBLE:
|
||||
if decimals == 0x1f {
|
||||
return math.MaxInt64, math.MaxInt64, true
|
||||
}
|
||||
return math.MaxInt64, decimals, true
|
||||
}
|
||||
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) ColumnTypeScanType(i int) reflect.Type {
|
||||
return rows.rs.columns[i].scanType()
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) Close() error {
|
||||
if rows.mc != nil {
|
||||
result := C.taos_use_result(rows.mc.taos)
|
||||
if result != nil {
|
||||
C.taos_free_result(result)
|
||||
}
|
||||
rows.mc = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) HasNextResultSet() (b bool) {
|
||||
if rows.mc == nil {
|
||||
return false
|
||||
}
|
||||
return rows.mc.status&statusMoreResultsExists != 0
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) nextResultSet() (int, error) {
|
||||
if rows.mc == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// Remove unread packets from stream
|
||||
if !rows.rs.done {
|
||||
rows.rs.done = true
|
||||
}
|
||||
|
||||
if !rows.HasNextResultSet() {
|
||||
rows.mc = nil
|
||||
return 0, io.EOF
|
||||
}
|
||||
rows.rs = resultSet{}
|
||||
return 0,nil
|
||||
}
|
||||
|
||||
func (rows *taosSqlRows) nextNotEmptyResultSet() (int, error) {
|
||||
for {
|
||||
resLen, err := rows.nextResultSet()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if resLen > 0 {
|
||||
return resLen, nil
|
||||
}
|
||||
|
||||
rows.rs.done = true
|
||||
}
|
||||
}
|
||||
|
||||
func (rows *binaryRows) NextResultSet() error {
|
||||
resLen, err := rows.nextNotEmptyResultSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows.rs.columns, err = rows.mc.readColumns(resLen)
|
||||
return err
|
||||
}
|
||||
|
||||
// stmt.Query return binary rows, and get row from this func
|
||||
func (rows *binaryRows) Next(dest []driver.Value) error {
|
||||
if mc := rows.mc; mc != nil {
|
||||
// Fetch next row from stream
|
||||
return rows.readRow(dest)
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func (rows *textRows) NextResultSet() (err error) {
|
||||
resLen, err := rows.nextNotEmptyResultSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows.rs.columns, err = rows.mc.readColumns(resLen)
|
||||
return err
|
||||
}
|
||||
|
||||
// db.Query return text rows, and get row from this func
|
||||
func (rows *textRows) Next(dest []driver.Value) error {
|
||||
if mc := rows.mc; mc != nil {
|
||||
// Fetch next row from stream
|
||||
return rows.readRow(dest)
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func (mf *taosSqlField) typeDatabaseName() string {
|
||||
//fmt.Println("######## (mf *taosSqlField) typeDatabaseName() mf.fieldType:", mf.fieldType)
|
||||
switch mf.fieldType {
|
||||
case C.TSDB_DATA_TYPE_BOOL:
|
||||
return "BOOL"
|
||||
|
||||
case C.TSDB_DATA_TYPE_TINYINT:
|
||||
return "TINYINT"
|
||||
|
||||
case C.TSDB_DATA_TYPE_SMALLINT:
|
||||
return "SMALLINT"
|
||||
|
||||
case C.TSDB_DATA_TYPE_INT:
|
||||
return "INT"
|
||||
|
||||
case C.TSDB_DATA_TYPE_BIGINT:
|
||||
return "BIGINT"
|
||||
|
||||
case C.TSDB_DATA_TYPE_FLOAT:
|
||||
return "FLOAT"
|
||||
|
||||
case C.TSDB_DATA_TYPE_DOUBLE:
|
||||
return "DOUBLE"
|
||||
|
||||
case C.TSDB_DATA_TYPE_BINARY:
|
||||
return "BINARY"
|
||||
|
||||
case C.TSDB_DATA_TYPE_NCHAR:
|
||||
return "NCHAR"
|
||||
|
||||
case C.TSDB_DATA_TYPE_TIMESTAMP:
|
||||
return "TIMESTAMP"
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
scanTypeFloat32 = reflect.TypeOf(float32(0))
|
||||
scanTypeFloat64 = reflect.TypeOf(float64(0))
|
||||
scanTypeInt8 = reflect.TypeOf(int8(0))
|
||||
scanTypeInt16 = reflect.TypeOf(int16(0))
|
||||
scanTypeInt32 = reflect.TypeOf(int32(0))
|
||||
scanTypeInt64 = reflect.TypeOf(int64(0))
|
||||
scanTypeNullTime = reflect.TypeOf(NullTime{})
|
||||
scanTypeRawBytes = reflect.TypeOf(sql.RawBytes{})
|
||||
scanTypeUnknown = reflect.TypeOf(new(interface{}))
|
||||
)
|
||||
|
||||
func (mf *taosSqlField) scanType() reflect.Type {
|
||||
//fmt.Println("######## (mf *taosSqlField) scanType() mf.fieldType:", mf.fieldType)
|
||||
switch mf.fieldType {
|
||||
case C.TSDB_DATA_TYPE_BOOL:
|
||||
return scanTypeInt8
|
||||
|
||||
case C.TSDB_DATA_TYPE_TINYINT:
|
||||
return scanTypeInt8
|
||||
|
||||
case C.TSDB_DATA_TYPE_SMALLINT:
|
||||
return scanTypeInt16
|
||||
|
||||
case C.TSDB_DATA_TYPE_INT:
|
||||
return scanTypeInt32
|
||||
|
||||
case C.TSDB_DATA_TYPE_BIGINT:
|
||||
return scanTypeInt64
|
||||
|
||||
case C.TSDB_DATA_TYPE_FLOAT:
|
||||
return scanTypeFloat32
|
||||
|
||||
case C.TSDB_DATA_TYPE_DOUBLE:
|
||||
return scanTypeFloat64
|
||||
|
||||
case C.TSDB_DATA_TYPE_BINARY:
|
||||
return scanTypeRawBytes
|
||||
|
||||
case C.TSDB_DATA_TYPE_NCHAR:
|
||||
return scanTypeRawBytes
|
||||
|
||||
case C.TSDB_DATA_TYPE_TIMESTAMP:
|
||||
return scanTypeNullTime
|
||||
|
||||
default:
|
||||
return scanTypeUnknown
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package taosSql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type taosSqlStmt struct {
|
||||
mc *taosConn
|
||||
id uint32
|
||||
pSql string
|
||||
paramCount int
|
||||
}
|
||||
|
||||
func (stmt *taosSqlStmt) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stmt *taosSqlStmt) NumInput() int {
|
||||
return stmt.paramCount
|
||||
}
|
||||
|
||||
func (stmt *taosSqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
if stmt.mc == nil || stmt.mc.taos == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
return stmt.mc.Exec(stmt.pSql, args)
|
||||
}
|
||||
|
||||
func (stmt *taosSqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
if stmt.mc == nil || stmt.mc.taos == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
return stmt.query(args)
|
||||
}
|
||||
|
||||
func (stmt *taosSqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
||||
mc := stmt.mc
|
||||
if mc == nil || mc.taos == nil {
|
||||
return nil, errInvalidConn
|
||||
}
|
||||
|
||||
querySql := stmt.pSql
|
||||
|
||||
if len(args) != 0 {
|
||||
if !mc.cfg.interpolateParams {
|
||||
return nil, driver.ErrSkip
|
||||
}
|
||||
// try client-side prepare to reduce roundtrip
|
||||
prepared, err := mc.interpolateParams(stmt.pSql, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
querySql = prepared
|
||||
}
|
||||
|
||||
num_fields, err := mc.taosQuery(querySql)
|
||||
if err == nil {
|
||||
// Read Result
|
||||
rows := new(binaryRows)
|
||||
rows.mc = mc
|
||||
// Columns field
|
||||
rows.rs.columns, err = mc.readColumns(num_fields)
|
||||
return rows, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type converter struct{}
|
||||
|
||||
// ConvertValue mirrors the reference/default converter in database/sql/driver
|
||||
// with _one_ exception. We support uint64 with their high bit and the default
|
||||
// implementation does not. This function should be kept in sync with
|
||||
// database/sql/driver defaultConverter.ConvertValue() except for that
|
||||
// deliberate difference.
|
||||
func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
|
||||
|
||||
if driver.IsValue(v) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
if vr, ok := v.(driver.Valuer); ok {
|
||||
sv, err := callValuerValue(vr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !driver.IsValue(sv) {
|
||||
return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
|
||||
}
|
||||
|
||||
return sv, nil
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Ptr:
|
||||
// indirect pointers
|
||||
if rv.IsNil() {
|
||||
return nil, nil
|
||||
} else {
|
||||
return c.ConvertValue(rv.Elem().Interface())
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return rv.Uint(), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return rv.Float(), nil
|
||||
case reflect.Bool:
|
||||
return rv.Bool(), nil
|
||||
case reflect.Slice:
|
||||
ek := rv.Type().Elem().Kind()
|
||||
if ek == reflect.Uint8 {
|
||||
return rv.Bytes(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek)
|
||||
case reflect.String:
|
||||
return rv.String(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
|
||||
}
|
||||
|
||||
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
|
||||
|
||||
// callValuerValue returns vr.Value(), with one exception:
|
||||
// If vr.Value is an auto-generated method on a pointer type and the
|
||||
// pointer is nil, it would panic at runtime in the panicwrap
|
||||
// method. Treat it like nil instead.
|
||||
//
|
||||
// This is so people can implement driver.Value on value types and
|
||||
// still use nil pointers to those types to mean nil/NULL, just like
|
||||
// string/*string.
|
||||
//
|
||||
// This is an exact copy of the same-named unexported function from the
|
||||
// database/sql package.
|
||||
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
|
||||
if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
|
||||
rv.IsNil() &&
|
||||
rv.Type().Elem().Implements(valuerReflectType) {
|
||||
return nil, nil
|
||||
}
|
||||
return vr.Value()
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package taosSql
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Various errors the driver might return.
|
||||
var (
|
||||
errInvalidConn = errors.New("invalid connection")
|
||||
errConnNoExist = errors.New("no existent connection ")
|
||||
)
|
||||
|
||||
var taosLog *log.Logger
|
||||
|
||||
// SetLogger is used to set the logger for critical errors.
|
||||
// The initial logger
|
||||
func taosLogInit() {
|
||||
cfgName := "/etc/taos/taos.cfg"
|
||||
logNameDefault := "/var/log/taos/taosgo.log"
|
||||
var logName string
|
||||
|
||||
// get log path from cfg file
|
||||
cfgFile, err := os.OpenFile(cfgName, os.O_RDONLY, 0644)
|
||||
defer cfgFile.Close()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
logName = logNameDefault
|
||||
} else {
|
||||
logName, err = getLogNameFromCfg(cfgFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
logName = logNameDefault
|
||||
}
|
||||
}
|
||||
|
||||
logFile, err := os.OpenFile(logName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
taosLog = log.New(logFile, "", log.LstdFlags)
|
||||
taosLog.SetPrefix("TAOS DRIVER ")
|
||||
taosLog.SetFlags(log.LstdFlags|log.Lshortfile)
|
||||
}
|
||||
|
||||
func getLogNameFromCfg(f *os.File) (string, error) {
|
||||
// Create file buf, *Reader
|
||||
r := bufio.NewReader(f)
|
||||
for {
|
||||
//read one line, return to slice b
|
||||
b, _, err := r.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Remove space of left and right
|
||||
s := strings.TrimSpace(string(b))
|
||||
if strings.Index(s, "#") == 0 {
|
||||
// comment line
|
||||
continue
|
||||
}
|
||||
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var ns string
|
||||
// If there is a comment on the right of the line, must be remove
|
||||
index := strings.Index(s, "#")
|
||||
if index > 0 {
|
||||
// Gets the string to the left of the comment to determine whether it is empty
|
||||
ns = s[:index]
|
||||
if len(ns) == 0 {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
ns = s;
|
||||
}
|
||||
|
||||
ss := strings.Fields(ns)
|
||||
if strings.Compare("logDir", ss[0]) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(ss) < 2 {
|
||||
break
|
||||
}
|
||||
|
||||
// Add a filename after the path
|
||||
logName := ss[1] + "/taosgo.log"
|
||||
return logName,nil
|
||||
}
|
||||
|
||||
return "", errors.New("no config log path, use default")
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package taosSql
|
||||
|
||||
/*
|
||||
#cgo CFLAGS : -I/usr/include
|
||||
#cgo LDFLAGS: -L/usr/lib -ltaos
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <taos.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (mc *taosConn) taosConnect(ip, user, pass, db string, port int) (taos unsafe.Pointer, err error) {
|
||||
cuser := C.CString(user)
|
||||
cpass := C.CString(pass)
|
||||
cip := C.CString(ip)
|
||||
cdb := C.CString(db)
|
||||
defer C.free(unsafe.Pointer(cip))
|
||||
defer C.free(unsafe.Pointer(cuser))
|
||||
defer C.free(unsafe.Pointer(cpass))
|
||||
defer C.free(unsafe.Pointer(cdb))
|
||||
|
||||
taosObj := C.taos_connect(cip, cuser, cpass, cdb, (C.ushort)(port))
|
||||
if taosObj == nil {
|
||||
return nil, errors.New("taos_connect() fail!")
|
||||
}
|
||||
|
||||
return (unsafe.Pointer)(taosObj), nil
|
||||
}
|
||||
|
||||
func (mc *taosConn) taosQuery(sqlstr string) (int, error) {
|
||||
//taosLog.Printf("taosQuery() input sql:%s\n", sqlstr)
|
||||
|
||||
csqlstr := C.CString(sqlstr)
|
||||
defer C.free(unsafe.Pointer(csqlstr))
|
||||
code := int(C.taos_query(mc.taos, csqlstr))
|
||||
|
||||
if 0 != code {
|
||||
mc.taos_error()
|
||||
errStr := C.GoString(C.taos_errstr(mc.taos))
|
||||
taosLog.Println("taos_query() failed:", errStr)
|
||||
taosLog.Printf("taosQuery() input sql:%s\n", sqlstr)
|
||||
return 0, errors.New(errStr)
|
||||
}
|
||||
|
||||
// read result and save into mc struct
|
||||
num_fields := int(C.taos_field_count(mc.taos))
|
||||
if 0 == num_fields { // there are no select and show kinds of commands
|
||||
mc.affectedRows = int(C.taos_affected_rows(mc.taos))
|
||||
mc.insertId = 0
|
||||
}
|
||||
|
||||
return num_fields, nil
|
||||
}
|
||||
|
||||
func (mc *taosConn) taos_close() {
|
||||
C.taos_close(mc.taos)
|
||||
}
|
||||
|
||||
func (mc *taosConn) taos_error() {
|
||||
// free local resouce: allocated memory/metric-meta refcnt
|
||||
//var pRes unsafe.Pointer
|
||||
pRes := C.taos_use_result(mc.taos)
|
||||
C.taos_free_result(pRes)
|
||||
}
|
|
@ -1,422 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package taosSql
|
||||
|
||||
/*
|
||||
#cgo CFLAGS : -I/usr/include
|
||||
#include <stdlib.h>
|
||||
#cgo LDFLAGS: -L/usr/lib -ltaos
|
||||
void taosSetAllocMode(int mode, const char* path, _Bool autoDump);
|
||||
void taosDumpMemoryLeak();
|
||||
*/
|
||||
import "C"
|
||||
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Returns the bool value of the input.
|
||||
// The 2nd return value indicates if the input was a valid bool value
|
||||
func readBool(input string) (value bool, valid bool) {
|
||||
switch input {
|
||||
case "1", "true", "TRUE", "True":
|
||||
return true, true
|
||||
case "0", "false", "FALSE", "False":
|
||||
return false, true
|
||||
}
|
||||
|
||||
// Not a valid bool value
|
||||
return
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Time related utils *
|
||||
******************************************************************************/
|
||||
|
||||
// NullTime represents a time.Time that may be NULL.
|
||||
// NullTime implements the Scanner interface so
|
||||
// it can be used as a scan destination:
|
||||
//
|
||||
// var nt NullTime
|
||||
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
|
||||
// ...
|
||||
// if nt.Valid {
|
||||
// // use nt.Time
|
||||
// } else {
|
||||
// // NULL value
|
||||
// }
|
||||
//
|
||||
// This NullTime implementation is not driver-specific
|
||||
type NullTime struct {
|
||||
Time time.Time
|
||||
Valid bool // Valid is true if Time is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
// The value type must be time.Time or string / []byte (formatted time-string),
|
||||
// otherwise Scan fails.
|
||||
func (nt *NullTime) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
nt.Time, nt.Valid = time.Time{}, false
|
||||
return
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
nt.Time, nt.Valid = v, true
|
||||
return
|
||||
case []byte:
|
||||
nt.Time, err = parseDateTime(string(v), time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
case string:
|
||||
nt.Time, err = parseDateTime(v, time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
nt.Valid = false
|
||||
return fmt.Errorf("Can't convert %T to time.Time", value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (nt NullTime) Value() (driver.Value, error) {
|
||||
if !nt.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return nt.Time, nil
|
||||
}
|
||||
|
||||
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
|
||||
base := "0000-00-00 00:00:00.0000000"
|
||||
switch len(str) {
|
||||
case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
|
||||
if str == base[:len(str)] {
|
||||
return
|
||||
}
|
||||
t, err = time.Parse(timeFormat[:len(str)], str)
|
||||
default:
|
||||
err = fmt.Errorf("invalid time string: %s", str)
|
||||
return
|
||||
}
|
||||
|
||||
// Adjust location
|
||||
if err == nil && loc != time.UTC {
|
||||
y, mo, d := t.Date()
|
||||
h, mi, s := t.Clock()
|
||||
t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation
|
||||
// if the DATE or DATETIME has the zero value.
|
||||
// It must never be changed.
|
||||
// The current behavior depends on database/sql copying the result.
|
||||
var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
|
||||
|
||||
const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
|
||||
|
||||
/******************************************************************************
|
||||
* Convert from and to bytes *
|
||||
******************************************************************************/
|
||||
|
||||
func uint64ToBytes(n uint64) []byte {
|
||||
return []byte{
|
||||
byte(n),
|
||||
byte(n >> 8),
|
||||
byte(n >> 16),
|
||||
byte(n >> 24),
|
||||
byte(n >> 32),
|
||||
byte(n >> 40),
|
||||
byte(n >> 48),
|
||||
byte(n >> 56),
|
||||
}
|
||||
}
|
||||
|
||||
func uint64ToString(n uint64) []byte {
|
||||
var a [20]byte
|
||||
i := 20
|
||||
|
||||
// U+0030 = 0
|
||||
// ...
|
||||
// U+0039 = 9
|
||||
|
||||
var q uint64
|
||||
for n >= 10 {
|
||||
i--
|
||||
q = n / 10
|
||||
a[i] = uint8(n-q*10) + 0x30
|
||||
n = q
|
||||
}
|
||||
|
||||
i--
|
||||
a[i] = uint8(n) + 0x30
|
||||
|
||||
return a[i:]
|
||||
}
|
||||
|
||||
// treats string value as unsigned integer representation
|
||||
func stringToInt(b []byte) int {
|
||||
val := 0
|
||||
for i := range b {
|
||||
val *= 10
|
||||
val += int(b[i] - 0x30)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
|
||||
// If cap(buf) is not enough, reallocate new buffer.
|
||||
func reserveBuffer(buf []byte, appendSize int) []byte {
|
||||
newSize := len(buf) + appendSize
|
||||
if cap(buf) < newSize {
|
||||
// Grow buffer exponentially
|
||||
newBuf := make([]byte, len(buf)*2+appendSize)
|
||||
copy(newBuf, buf)
|
||||
buf = newBuf
|
||||
}
|
||||
return buf[:newSize]
|
||||
}
|
||||
|
||||
// escapeBytesBackslash escapes []byte with backslashes (\)
|
||||
// This escapes the contents of a string (provided as []byte) by adding backslashes before special
|
||||
// characters, and turning others into specific escape sequences, such as
|
||||
// turning newlines into \n and null bytes into \0.
|
||||
func escapeBytesBackslash(buf, v []byte) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
for _, c := range v {
|
||||
switch c {
|
||||
case '\x00':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '0'
|
||||
pos += 2
|
||||
case '\n':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'n'
|
||||
pos += 2
|
||||
case '\r':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'r'
|
||||
pos += 2
|
||||
case '\x1a':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'Z'
|
||||
pos += 2
|
||||
case '\'':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '\''
|
||||
pos += 2
|
||||
case '"':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '"'
|
||||
pos += 2
|
||||
case '\\':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '\\'
|
||||
pos += 2
|
||||
default:
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeStringBackslash is similar to escapeBytesBackslash but for string.
|
||||
func escapeStringBackslash(buf []byte, v string) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
c := v[i]
|
||||
switch c {
|
||||
case '\x00':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '0'
|
||||
pos += 2
|
||||
case '\n':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'n'
|
||||
pos += 2
|
||||
case '\r':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'r'
|
||||
pos += 2
|
||||
case '\x1a':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'Z'
|
||||
pos += 2
|
||||
//case '\'':
|
||||
// buf[pos] = '\\'
|
||||
// buf[pos+1] = '\''
|
||||
// pos += 2
|
||||
case '"':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '"'
|
||||
pos += 2
|
||||
case '\\':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '\\'
|
||||
pos += 2
|
||||
default:
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
|
||||
// This escapes the contents of a string by doubling up any apostrophes that
|
||||
// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
|
||||
// effect on the server.
|
||||
func escapeBytesQuotes(buf, v []byte) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for _, c := range v {
|
||||
if c == '\'' {
|
||||
buf[pos] = '\''
|
||||
buf[pos+1] = '\''
|
||||
pos += 2
|
||||
} else {
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeStringQuotes is similar to escapeBytesQuotes but for string.
|
||||
func escapeStringQuotes(buf []byte, v string) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
c := v[i]
|
||||
if c == '\'' {
|
||||
buf[pos] = '\''
|
||||
buf[pos+1] = '\''
|
||||
pos += 2
|
||||
} else {
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Sync utils *
|
||||
******************************************************************************/
|
||||
|
||||
// noCopy may be embedded into structs which must not be copied
|
||||
// after the first use.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/8005#issuecomment-190753527
|
||||
// for details.
|
||||
type noCopy struct{}
|
||||
|
||||
// Lock is a no-op used by -copylocks checker from `go vet`.
|
||||
func (*noCopy) Lock() {}
|
||||
|
||||
// atomicBool is a wrapper around uint32 for usage as a boolean value with
|
||||
// atomic access.
|
||||
type atomicBool struct {
|
||||
_noCopy noCopy
|
||||
value uint32
|
||||
}
|
||||
|
||||
// IsSet returns whether the current boolean value is true
|
||||
func (ab *atomicBool) IsSet() bool {
|
||||
return atomic.LoadUint32(&ab.value) > 0
|
||||
}
|
||||
|
||||
// Set sets the value of the bool regardless of the previous value
|
||||
func (ab *atomicBool) Set(value bool) {
|
||||
if value {
|
||||
atomic.StoreUint32(&ab.value, 1)
|
||||
} else {
|
||||
atomic.StoreUint32(&ab.value, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// TrySet sets the value of the bool and returns whether the value changed
|
||||
func (ab *atomicBool) TrySet(value bool) bool {
|
||||
if value {
|
||||
return atomic.SwapUint32(&ab.value, 1) == 0
|
||||
}
|
||||
return atomic.SwapUint32(&ab.value, 0) > 0
|
||||
}
|
||||
|
||||
// atomicError is a wrapper for atomically accessed error values
|
||||
type atomicError struct {
|
||||
_noCopy noCopy
|
||||
value atomic.Value
|
||||
}
|
||||
|
||||
// Set sets the error value regardless of the previous value.
|
||||
// The value must not be nil
|
||||
func (ae *atomicError) Set(value error) {
|
||||
ae.value.Store(value)
|
||||
}
|
||||
|
||||
// Value returns the current error value
|
||||
func (ae *atomicError) Value() error {
|
||||
if v := ae.value.Load(); v != nil {
|
||||
// this will panic if the value doesn't implement the error interface
|
||||
return v.(error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
|
||||
dargs := make([]driver.Value, len(named))
|
||||
for n, param := range named {
|
||||
if len(param.Name) > 0 {
|
||||
// TODO: support the use of Named Parameters #561
|
||||
return nil, errors.New("taosSql: driver does not support the use of Named Parameters")
|
||||
}
|
||||
dargs[n] = param.Value
|
||||
}
|
||||
return dargs, nil
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utils for C memory issues debugging *
|
||||
******************************************************************************/
|
||||
func SetAllocMode(mode int32, path string) {
|
||||
cpath := C.CString(path)
|
||||
defer C.free(unsafe.Pointer(cpath))
|
||||
C.taosSetAllocMode(C.int(mode), cpath, false)
|
||||
}
|
||||
|
||||
func DumpMemoryLeak() {
|
||||
C.taosDumpMemoryLeak()
|
||||
}
|
Loading…
Reference in New Issue