├── common ├── parser │ ├── mem.s │ ├── mem.go │ └── mem_test.go ├── pointer │ ├── unsafe.go │ └── unsafe_test.go ├── process.go ├── ws.go ├── tmq │ ├── config.go │ ├── config_test.go │ └── tmq.go ├── reqid_test.go ├── datatype_test.go ├── decimal.go ├── column.go ├── change.go ├── const.go ├── reqid.go ├── sql.go ├── stmt │ └── field.go ├── tdversion │ └── version.go ├── decimal_test.go └── sql_test.go ├── .gitignore ├── .codecov.yml ├── wrapper ├── cgo │ ├── README.md │ ├── handle_test.go │ └── handle.go ├── thread │ ├── locker.go │ └── locker_test.go ├── whitelist_test.go ├── notify.go ├── stmt2async.go ├── notifycb.go ├── setconfig.go ├── whitelistcb.go ├── asynccb.go ├── whitelist.go ├── setconfig_test.go ├── tmqcb.go ├── handler │ ├── handlerpool_test.go │ └── handlerpool.go ├── whitelistcb_test.go ├── field.go ├── row.go ├── block.go ├── notify_test.go └── row_3360_test.go ├── .github └── workflows │ ├── taos.cfg │ ├── taosadapter.toml │ └── compatibility-3360.yml ├── bench ├── valgrind │ ├── README.md │ └── main.go ├── README.md ├── standard │ ├── native │ │ ├── query │ │ │ └── read.go │ │ └── exec │ │ │ └── write.go │ └── restful │ │ ├── query │ │ └── read.go │ │ └── exec │ │ └── write.go ├── sql_test.go ├── query │ └── main.go └── stmt │ ├── insert │ └── main.go │ └── query │ └── main.go ├── go.mod ├── .drone.yml ├── af ├── tmq │ ├── config_test.go │ └── config.go ├── locker │ ├── locker.go │ └── locker_test.go ├── async │ ├── handler_test.go │ └── handler.go ├── insertstmt │ └── stmt.go └── rows.go ├── taosWS ├── error_test.go ├── error.go ├── driver.go ├── connector.go ├── connection_test.go └── cloud_test.go ├── examples ├── schemaless │ ├── telnet │ │ └── main.go │ └── json │ │ └── main.go ├── varbinary │ ├── stmt │ │ ├── native │ │ │ └── main.go │ │ └── ws │ │ │ └── main.go │ └── query │ │ ├── ws │ │ └── main.go │ │ └── native │ │ └── main.go ├── geometry │ ├── stmt │ │ ├── native │ │ │ └── main.go │ │ └── ws │ │ │ └── main.go │ └── query │ │ ├── ws │ │ └── main.go │ │ └── native │ │ └── main.go ├── tmq │ └── main.go ├── stmtinsert │ └── main.go ├── taosWS │ └── main.go ├── taosSql │ └── main.go ├── restful │ └── main.go ├── sqlstmt │ └── main.go ├── tmqoverws │ └── main.go └── platform │ └── main.go ├── ws ├── stmt │ ├── config_test.go │ └── config.go ├── schemaless │ ├── proto.go │ ├── config.go │ └── cloud_test.go ├── client │ └── conn_test.go └── tmq │ └── config.go ├── errors ├── errors.go └── errors_test.go ├── taosRestful ├── connector.go ├── driver.go ├── rows.go └── cloud_test.go ├── LICENSE ├── benchmark ├── README.md ├── run_bench.sh └── data │ └── only_create_table_with_json_tag.json ├── taosSql ├── ns_test.go ├── driver.go ├── connection_test.go ├── connector.go └── rows.go ├── .golangci.yaml ├── types └── taostype.go └── go.sum /common/parser/mem.s: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | vendor -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "bench" 3 | - "benchmark" 4 | - "examples" -------------------------------------------------------------------------------- /wrapper/cgo/README.md: -------------------------------------------------------------------------------- 1 | Copy from go 1.17.2 runtime/cgo. In order to be compatible with lower versions -------------------------------------------------------------------------------- /.github/workflows/taos.cfg: -------------------------------------------------------------------------------- 1 | fqdn localhost 2 | firstEp localhost:6030 3 | asyncLog 0 4 | debugFlag 143 5 | supportVnodes 256 -------------------------------------------------------------------------------- /bench/valgrind/README.md: -------------------------------------------------------------------------------- 1 | ##Usage 2 | ```shell 3 | go build -o t main.go 4 | valgrind --log-file="result.txt" --leak-check=full ./t 5 | ``` -------------------------------------------------------------------------------- /common/pointer/unsafe.go: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | import "unsafe" 4 | 5 | func AddUintptr(ptr unsafe.Pointer, len uintptr) unsafe.Pointer { 6 | return unsafe.Pointer(uintptr(ptr) + len) 7 | } 8 | -------------------------------------------------------------------------------- /bench/README.md: -------------------------------------------------------------------------------- 1 | ##Usage 2 | ```shell 3 | go test -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out 4 | ``` 5 | ```shell 6 | go tool pprof memprofile.out 7 | ``` 8 | ```shell 9 | go tool pprof profile.out 10 | ``` 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/taosdata/driver-go/v3 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/google/uuid v1.6.0 7 | github.com/gorilla/websocket v1.5.0 8 | github.com/hashicorp/go-version v1.7.0 9 | github.com/json-iterator/go v1.1.12 10 | github.com/stretchr/testify v1.8.2 11 | ) 12 | -------------------------------------------------------------------------------- /common/parser/mem.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import "unsafe" 4 | 5 | //go:noescape 6 | func memmove(to, from unsafe.Pointer, n uintptr) 7 | 8 | //go:linkname memmove runtime.memmove 9 | 10 | func Copy(source unsafe.Pointer, data []byte, index int, length int) { 11 | memmove(unsafe.Pointer(&data[index]), source, uintptr(length)) 12 | } 13 | -------------------------------------------------------------------------------- /wrapper/thread/locker.go: -------------------------------------------------------------------------------- 1 | package thread 2 | 3 | type Locker struct { 4 | c chan struct{} 5 | } 6 | 7 | var nothing = struct{}{} 8 | 9 | func NewLocker(count int) *Locker { 10 | return &Locker{c: make(chan struct{}, count)} 11 | } 12 | 13 | func (l *Locker) Lock() { 14 | l.c <- nothing 15 | } 16 | 17 | func (l *Locker) Unlock() { 18 | <-l.c 19 | } 20 | -------------------------------------------------------------------------------- /common/pointer/unsafe_test.go: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | import ( 4 | "testing" 5 | "unsafe" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestAddUintptr(t *testing.T) { 11 | data := []byte{1, 2, 3, 4, 5} 12 | p1 := unsafe.Pointer(&data[0]) 13 | p2 := AddUintptr(p1, 1) 14 | assert.Equal(t, unsafe.Pointer(&data[1]), p2) 15 | v2 := *(*byte)(p2) 16 | assert.Equal(t, byte(2), v2) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /common/process.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "sync" 7 | ) 8 | 9 | var processName string 10 | var once sync.Once 11 | 12 | func GetProcessName() string { 13 | once.Do(func() { 14 | processPath := os.Args[0] 15 | processName = filepath.Base(processPath) 16 | if processName == "" { 17 | processName = "go_unknown" 18 | } 19 | }) 20 | return processName 21 | } 22 | -------------------------------------------------------------------------------- /bench/standard/native/query/read.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/taosdata/driver-go/v3/bench/standard/executor" 5 | _ "github.com/taosdata/driver-go/v3/taosSql" 6 | ) 7 | 8 | func main() { 9 | test := executor.NewTDTest(executor.DefaultNativeDriverName, executor.DefaultNativeDSN) 10 | 11 | test.Clean() 12 | tableName := test.PrepareRead(1000, 100) 13 | test.BenchmarkRead("select * from " + tableName) 14 | } 15 | -------------------------------------------------------------------------------- /bench/standard/restful/query/read.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/taosdata/driver-go/v3/bench/standard/executor" 5 | _ "github.com/taosdata/driver-go/v3/taosRestful" 6 | ) 7 | 8 | func main() { 9 | test := executor.NewTDTest(executor.DefaultRestfulDriverName, executor.DefaultRestfulDSN) 10 | 11 | test.Clean() 12 | tableName := test.PrepareRead(1000, 100) 13 | test.BenchmarkRead("select * from " + tableName) 14 | } 15 | -------------------------------------------------------------------------------- /common/parser/mem_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | "unsafe" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCopy(t *testing.T) { 11 | data := []byte("World") 12 | data1 := make([]byte, 10) 13 | data1[0] = 'H' 14 | data1[1] = 'e' 15 | data1[2] = 'l' 16 | data1[3] = 'l' 17 | data1[4] = 'o' 18 | Copy(unsafe.Pointer(&data[0]), data1, 5, 5) 19 | assert.Equal(t, "HelloWorld", string(data1)) 20 | } 21 | -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | type: docker 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - wget https://www.taosdata.com/assets-download/TDengine-server-2.3.0.0-beta-Linux-x64.tar.gz 10 | - tar xvf TDengine-server-2.3.0.0-beta-Linux-x64.tar.gz 11 | - cd TDengine-server-2.3.0.0/ 12 | - printf "\n\n"|./install.sh 13 | - nohup taosd & 14 | - cd ../ 15 | - go mod tidy 16 | - go test -v ./... 17 | -------------------------------------------------------------------------------- /af/tmq/config_test.go: -------------------------------------------------------------------------------- 1 | package tmq 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/taosdata/driver-go/v3/wrapper" 7 | ) 8 | 9 | // @author: xftan 10 | // @date: 2023/10/13 11:10 11 | // @description: test config 12 | func TestConfig(t *testing.T) { 13 | conf := newConfig() 14 | conf.destroy() 15 | } 16 | 17 | // @author: xftan 18 | // @date: 2023/10/13 11:11 19 | // @description: test topic list 20 | func TestList(t *testing.T) { 21 | topicList := wrapper.TMQListNew() 22 | wrapper.TMQListAppend(topicList, "123") 23 | wrapper.TMQListDestroy(topicList) 24 | } 25 | -------------------------------------------------------------------------------- /af/locker/locker.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | 7 | "github.com/taosdata/driver-go/v3/wrapper/thread" 8 | ) 9 | 10 | var locker *thread.Locker 11 | var once = sync.Once{} 12 | 13 | func Lock() { 14 | if locker == nil { 15 | SetMaxThreadSize(runtime.NumCPU()) 16 | } 17 | locker.Lock() 18 | } 19 | func Unlock() { 20 | if locker == nil { 21 | SetMaxThreadSize(runtime.NumCPU()) 22 | } 23 | locker.Unlock() 24 | } 25 | 26 | func SetMaxThreadSize(size int) { 27 | once.Do(func() { 28 | locker = thread.NewLocker(size) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /af/async/handler_test.go: -------------------------------------------------------------------------------- 1 | package async 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | // @author: xftan 9 | // @date: 2022/1/25 16:33 10 | // @description: test async handler pool available 11 | func TestHandler(t *testing.T) { 12 | SetHandlerSize(12) 13 | timer := time.NewTimer(time.Second) 14 | done := make(chan struct{}) 15 | go func() { 16 | handler := GetHandler() 17 | PutHandler(handler) 18 | done <- struct{}{} 19 | }() 20 | select { 21 | case <-done: 22 | return 23 | case <-timer.C: 24 | timer.Stop() 25 | timer = nil 26 | t.Error("dead lock") 27 | return 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /af/locker/locker_test.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // @author: xftan 10 | // @date: 2022/1/25 16:38 11 | // @description: test thread locker available 12 | func TestLock(t *testing.T) { 13 | SetMaxThreadSize(runtime.NumCPU()) 14 | timer := time.NewTimer(time.Second) 15 | done := make(chan struct{}) 16 | go func() { 17 | Lock() 18 | Unlock() 19 | done <- struct{}{} 20 | }() 21 | select { 22 | case <-done: 23 | return 24 | case <-timer.C: 25 | timer.Stop() 26 | timer = nil 27 | t.Error("dead lock") 28 | return 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /wrapper/thread/locker_test.go: -------------------------------------------------------------------------------- 1 | package thread 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // @author: xftan 8 | // @date: 2021/12/14 15:16 9 | // @description: test NewLocker 10 | func TestNewLocker(t *testing.T) { 11 | type args struct { 12 | count int 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | }{ 18 | { 19 | name: "test", 20 | args: args{ 21 | count: 1, 22 | }, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | locker := NewLocker(tt.args.count) 28 | locker.Lock() 29 | defer locker.Unlock() 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /wrapper/whitelist_test.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 8 | ) 9 | 10 | func TestGetWhiteList(t *testing.T) { 11 | conn, err := TaosConnect("", "root", "taosdata", "", 0) 12 | assert.NoError(t, err) 13 | defer TaosClose(conn) 14 | c := make(chan *WhitelistResult, 1) 15 | handler := cgo.NewHandle(c) 16 | TaosFetchWhitelistA(conn, handler) 17 | data := <-c 18 | assert.Equal(t, int32(0), data.ErrCode) 19 | assert.Equal(t, 1, len(data.IPNets)) 20 | assert.Equal(t, "0.0.0.0/0", data.IPNets[0].String()) 21 | } 22 | -------------------------------------------------------------------------------- /taosWS/error_test.go: -------------------------------------------------------------------------------- 1 | package taosWS 2 | 3 | import ( 4 | "database/sql/driver" 5 | "errors" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | // @author: xftan 12 | // @date: 2023/10/13 11:26 13 | // @description: test bad conn error 14 | func TestBadConnError(t *testing.T) { 15 | nothingErr := errors.New("error") 16 | err := NewBadConnError(nothingErr) 17 | assert.ErrorIs(t, err, driver.ErrBadConn) 18 | assert.Equal(t, "error", err.Error()) 19 | err = NewBadConnErrorWithCtx(nothingErr, "nothing") 20 | assert.ErrorIs(t, err, driver.ErrBadConn) 21 | assert.Equal(t, "error error: context: nothing", err.Error()) 22 | } 23 | -------------------------------------------------------------------------------- /common/ws.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net/http" 5 | "sync" 6 | "time" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | const ( 12 | BufferSize4M = 4 * 1024 * 1024 13 | DefaultMessageTimeout = time.Minute * 5 14 | DefaultPongWait = 60 * time.Second 15 | DefaultPingPeriod = (60 * time.Second * 9) / 10 16 | DefaultWriteWait = 10 * time.Second 17 | ) 18 | 19 | var DefaultDialer = websocket.Dialer{ 20 | Proxy: http.ProxyFromEnvironment, 21 | HandshakeTimeout: 45 * time.Second, 22 | ReadBufferSize: BufferSize4M, 23 | WriteBufferSize: BufferSize4M, 24 | WriteBufferPool: &sync.Pool{}, 25 | } 26 | -------------------------------------------------------------------------------- /examples/schemaless/telnet/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/taosdata/driver-go/v3/af" 5 | ) 6 | 7 | func main() { 8 | conn, err := af.Open("", "root", "taosdata", "", 0) 9 | if err != nil { 10 | panic(err) 11 | } 12 | defer conn.Close() 13 | _, err = conn.Exec("create database if not exists example_telnet") 14 | if err != nil { 15 | panic(err) 16 | } 17 | _, err = conn.Exec("use example_telnet") 18 | err = conn.OpenTSDBInsertTelnetLines([]string{ 19 | "sys_if_bytes_out 1479496100 1.3E3 host=web01 interface=eth0", 20 | "sys_procs_running 1479496100 42 host=web01", 21 | }) 22 | if err != nil { 23 | panic(err) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wrapper/notify.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | #include 8 | extern void NotifyCallback(void *param, void *ext, int type); 9 | int taos_set_notify_cb_wrapper(TAOS *taos, void *param, int type){ 10 | return taos_set_notify_cb(taos,NotifyCallback,param,type); 11 | }; 12 | */ 13 | import "C" 14 | import ( 15 | "unsafe" 16 | 17 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 18 | ) 19 | 20 | func TaosSetNotifyCB(taosConnect unsafe.Pointer, caller cgo.Handle, notifyType int) int32 { 21 | return int32(C.taos_set_notify_cb_wrapper(taosConnect, caller.Pointer(), (C.int)(notifyType))) 22 | } 23 | -------------------------------------------------------------------------------- /taosWS/error.go: -------------------------------------------------------------------------------- 1 | package taosWS 2 | 3 | import ( 4 | "database/sql/driver" 5 | "fmt" 6 | ) 7 | 8 | type BadConnError struct { 9 | err error 10 | ctx string 11 | } 12 | 13 | func NewBadConnError(err error) *BadConnError { 14 | return &BadConnError{err: err} 15 | } 16 | 17 | func NewBadConnErrorWithCtx(err error, ctx string) *BadConnError { 18 | return &BadConnError{err: err, ctx: ctx} 19 | } 20 | 21 | func (*BadConnError) Unwrap() error { 22 | return driver.ErrBadConn 23 | } 24 | 25 | func (e *BadConnError) Error() string { 26 | if len(e.ctx) == 0 { 27 | return e.err.Error() 28 | } 29 | return fmt.Sprintf("error %s: context: %s", e.err.Error(), e.ctx) 30 | } 31 | -------------------------------------------------------------------------------- /ws/stmt/config_test.go: -------------------------------------------------------------------------------- 1 | package stmt 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSetConnectionTimezone(t *testing.T) { 10 | cfg := NewConfig("", 1) 11 | err := cfg.SetConnectionTimezone("Europe/Paris") 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | if cfg.Timezone.String() != "Europe/Paris" { 16 | t.Fatalf("expected Europe/Paris, got %s", cfg.Timezone.String()) 17 | } 18 | 19 | err = cfg.SetConnectionTimezone("Invalid/Timezone") 20 | assert.Error(t, err) 21 | err = cfg.SetConnectionTimezone("Local") 22 | assert.Error(t, err) 23 | err = cfg.SetConnectionTimezone("") 24 | assert.Error(t, err) 25 | } 26 | -------------------------------------------------------------------------------- /examples/schemaless/json/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/taosdata/driver-go/v3/af" 4 | 5 | func main() { 6 | conn, err := af.Open("", "root", "taosdata", "", 0) 7 | if err != nil { 8 | panic(err) 9 | } 10 | defer conn.Close() 11 | _, err = conn.Exec("create database if not exists example_json") 12 | if err != nil { 13 | panic(err) 14 | } 15 | _, err = conn.Exec("use example_json") 16 | err = conn.OpenTSDBInsertJsonPayload(`{ 17 | "metric": "sys", 18 | "timestamp": 1346846400, 19 | "value": 18, 20 | "tags": { 21 | "host": "web01", 22 | "dc": "lga" 23 | } 24 | }`) 25 | if err != nil { 26 | panic(err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wrapper/stmt2async.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | */ 10 | import "C" 11 | import ( 12 | "unsafe" 13 | 14 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 15 | ) 16 | 17 | type TaosStmt2CallbackCaller interface { 18 | ExecCall(res unsafe.Pointer, affected int, code int) 19 | } 20 | 21 | //export Stmt2ExecCallback 22 | func Stmt2ExecCallback(p unsafe.Pointer, res *C.TAOS_RES, code C.int) { 23 | caller := (*(*cgo.Handle)(p)).Value().(TaosStmt2CallbackCaller) 24 | affectedRows := int(C.taos_affected_rows(unsafe.Pointer(res))) 25 | caller.ExecCall(unsafe.Pointer(res), affectedRows, int(code)) 26 | } 27 | -------------------------------------------------------------------------------- /af/async/handler.go: -------------------------------------------------------------------------------- 1 | package async 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/taosdata/driver-go/v3/wrapper/handler" 7 | ) 8 | 9 | const defaultPoolSize = 10000 10 | 11 | var HandlerPool *handler.HandlerPool 12 | var once = sync.Once{} 13 | 14 | func SetHandlerSize(size int) { 15 | once.Do(func() { 16 | HandlerPool = handler.NewHandlerPool(size) 17 | }) 18 | } 19 | 20 | func GetHandler() *handler.Handler { 21 | //if HandlerPool == nil { 22 | // SetHandlerSize(defaultPoolSize) 23 | //} 24 | return HandlerPool.Get() 25 | } 26 | 27 | func PutHandler(h *handler.Handler) { 28 | if HandlerPool == nil { 29 | return 30 | } 31 | HandlerPool.Put(h) 32 | } 33 | 34 | func init() { 35 | HandlerPool = handler.NewHandlerPool(defaultPoolSize) 36 | } 37 | -------------------------------------------------------------------------------- /af/tmq/config.go: -------------------------------------------------------------------------------- 1 | package tmq 2 | 3 | import ( 4 | "time" 5 | "unsafe" 6 | 7 | "github.com/taosdata/driver-go/v3/errors" 8 | "github.com/taosdata/driver-go/v3/wrapper" 9 | ) 10 | 11 | type config struct { 12 | cConfig unsafe.Pointer 13 | timezone *time.Location 14 | } 15 | 16 | func newConfig() *config { 17 | return &config{cConfig: wrapper.TMQConfNew()} 18 | } 19 | 20 | func (c *config) setConfig(key string, value string) error { 21 | errCode := wrapper.TMQConfSet(c.cConfig, key, value) 22 | if errCode != errors.SUCCESS { 23 | errStr := wrapper.TMQErr2Str(errCode) 24 | return errors.NewError(int(errCode), errStr) 25 | } 26 | return nil 27 | } 28 | 29 | // Destroy Release TMQ config 30 | func (c *config) destroy() { 31 | wrapper.TMQConfDestroy(c.cConfig) 32 | } 33 | -------------------------------------------------------------------------------- /common/tmq/config.go: -------------------------------------------------------------------------------- 1 | package tmq 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type ConfigValue interface{} 9 | type ConfigMap map[string]ConfigValue 10 | 11 | func (m ConfigMap) Get(key string, defval ConfigValue) (ConfigValue, error) { 12 | return m.get(key, defval) 13 | } 14 | 15 | func (m ConfigMap) get(key string, defval ConfigValue) (ConfigValue, error) { 16 | v, ok := m[key] 17 | if !ok { 18 | return defval, nil 19 | } 20 | 21 | if defval != nil && reflect.TypeOf(defval) != reflect.TypeOf(v) { 22 | return nil, fmt.Errorf("%s expects type %T, not %T", key, defval, v) 23 | } 24 | 25 | return v, nil 26 | } 27 | 28 | func (m ConfigMap) Clone() ConfigMap { 29 | m2 := make(ConfigMap) 30 | for k, v := range m { 31 | m2[k] = v 32 | } 33 | return m2 34 | } 35 | -------------------------------------------------------------------------------- /common/reqid_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func BenchmarkGetReqID(b *testing.B) { 8 | b.ResetTimer() 9 | 10 | b.RunParallel(func(pb *testing.PB) { 11 | for pb.Next() { 12 | GetReqID() 13 | } 14 | }) 15 | } 16 | 17 | func BenchmarkGetReqIDParallel(b *testing.B) { 18 | b.ResetTimer() 19 | 20 | b.RunParallel(func(pb *testing.PB) { 21 | for pb.Next() { 22 | GetReqID() 23 | } 24 | }) 25 | } 26 | 27 | // @author: xftan 28 | // @date: 2023/10/13 11:20 29 | // @description: test get req id 30 | func TestGetReqID(t *testing.T) { 31 | t.Log(GetReqID()) 32 | } 33 | 34 | // @author: xftan 35 | // @date: 2023/10/13 11:20 36 | // @description: test MurmurHash 37 | func TestMurmurHash(t *testing.T) { 38 | if murmurHash32([]byte("driver-go"), 0) != 3037880692 { 39 | t.Fatal("fail") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /common/datatype_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "testing" 4 | 5 | func TestGetTypeName(t *testing.T) { 6 | type args struct { 7 | dataType int 8 | } 9 | tests := []struct { 10 | name string 11 | args args 12 | want string 13 | }{ 14 | { 15 | name: "invalid data type", 16 | args: args{ 17 | dataType: -1, 18 | }, 19 | want: "", 20 | }, 21 | { 22 | name: "over max data type", 23 | args: args{ 24 | dataType: TSDB_DATA_TYPE_MAX, 25 | }, 26 | want: "", 27 | }, 28 | { 29 | name: "valid data type", 30 | args: args{ 31 | dataType: TSDB_DATA_TYPE_BINARY, 32 | }, 33 | want: "VARCHAR", 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | if got := GetTypeName(tt.args.dataType); got != tt.want { 39 | t.Errorf("GetTypeName() = %v, want %v", got, tt.want) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /errors/errors.go: -------------------------------------------------------------------------------- 1 | //! TDengine error codes. 2 | //! THIS IS AUTO GENERATED FROM TDENGINE , MAKE SURE YOU KNOW WHAT YOU ARE CHANING. 3 | 4 | package errors 5 | 6 | import "fmt" 7 | 8 | type TaosError struct { 9 | Code int32 10 | ErrStr string 11 | } 12 | 13 | const ( 14 | SUCCESS int32 = 0 15 | //revive:disable-next-line 16 | TSC_INVALID_CONNECTION int32 = 0x020B 17 | UNKNOWN int32 = 0xffff 18 | ) 19 | 20 | func (e *TaosError) Error() string { 21 | if e.Code != UNKNOWN { 22 | return fmt.Sprintf("[0x%x] %s", e.Code, e.ErrStr) 23 | } 24 | return e.ErrStr 25 | } 26 | 27 | var ( 28 | ErrTscInvalidConnection = &TaosError{ 29 | Code: TSC_INVALID_CONNECTION, 30 | ErrStr: "Invalid connection", 31 | } 32 | ) 33 | 34 | func NewError(code int, errStr string) error { 35 | return &TaosError{ 36 | Code: int32(code) & 0xffff, 37 | ErrStr: errStr, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ws/schemaless/proto.go: -------------------------------------------------------------------------------- 1 | package schemaless 2 | 3 | type wsConnectReq struct { 4 | ReqID uint64 `json:"req_id"` 5 | User string `json:"user"` 6 | Password string `json:"password"` 7 | DB string `json:"db"` 8 | } 9 | 10 | type wsConnectResp struct { 11 | Code int `json:"code"` 12 | Message string `json:"message"` 13 | Action string `json:"action"` 14 | ReqID uint64 `json:"req_id"` 15 | Timing int64 `json:"timing"` 16 | } 17 | 18 | type schemalessReq struct { 19 | ReqID uint64 `json:"req_id"` 20 | DB string `json:"db"` 21 | Protocol int `json:"protocol"` 22 | Precision string `json:"precision"` 23 | TTL int `json:"ttl"` 24 | Data string `json:"data"` 25 | } 26 | 27 | type schemalessResp struct { 28 | Code int `json:"code"` 29 | Message string `json:"message"` 30 | ReqID uint64 `json:"req_id"` 31 | Action string `json:"action"` 32 | Timing int64 `json:"timing"` 33 | } 34 | -------------------------------------------------------------------------------- /wrapper/notifycb.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | #include 8 | */ 9 | import "C" 10 | import ( 11 | "unsafe" 12 | 13 | "github.com/taosdata/driver-go/v3/common" 14 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 15 | ) 16 | 17 | //export NotifyCallback 18 | func NotifyCallback(p unsafe.Pointer, ext unsafe.Pointer, notifyType C.int) { 19 | defer func() { 20 | // channel may be closed 21 | _ = recover() 22 | }() 23 | switch int(notifyType) { 24 | case common.TAOS_NOTIFY_PASSVER: 25 | version := int32(*(*C.int32_t)(ext)) 26 | c := (*(*cgo.Handle)(p)).Value().(chan int32) 27 | c <- version 28 | case common.TAOS_NOTIFY_WHITELIST_VER: 29 | version := int64(*(*C.int64_t)(ext)) 30 | c := (*(*cgo.Handle)(p)).Value().(chan int64) 31 | c <- version 32 | case common.TAOS_NOTIFY_USER_DROPPED: 33 | c := (*(*cgo.Handle)(p)).Value().(chan struct{}) 34 | c <- struct{}{} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/decimal.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "math/big" 5 | "strings" 6 | ) 7 | 8 | func FormatI128(hi int64, lo uint64) string { 9 | num := new(big.Int).SetInt64(hi) 10 | num.Lsh(num, 64) 11 | num.Or(num, new(big.Int).SetUint64(lo)) 12 | return num.String() 13 | } 14 | 15 | func FormatDecimal(str string, scale int) string { 16 | if scale == 0 { 17 | return str 18 | } 19 | builder := strings.Builder{} 20 | if strings.HasPrefix(str, "-") { 21 | str = str[1:] 22 | builder.WriteByte('-') 23 | } 24 | 25 | delta := len(str) - scale 26 | if delta > 0 { 27 | builder.Grow(len(str) + 1) 28 | builder.WriteString(str[:delta]) 29 | builder.WriteString(".") 30 | builder.WriteString(str[delta:]) 31 | return builder.String() 32 | } 33 | delta = -delta 34 | builder.Grow(len(str) + 2 + delta) 35 | builder.WriteString("0.") 36 | for i := 0; i < delta; i++ { 37 | builder.WriteString("0") 38 | } 39 | builder.WriteString(str) 40 | return builder.String() 41 | } 42 | -------------------------------------------------------------------------------- /taosRestful/connector.go: -------------------------------------------------------------------------------- 1 | package taosRestful 2 | 3 | import ( 4 | "context" 5 | "database/sql/driver" 6 | 7 | "github.com/taosdata/driver-go/v3/common" 8 | ) 9 | 10 | type connector struct { 11 | cfg *Config 12 | } 13 | 14 | // Connect implements driver.Connector interface. 15 | // Connect returns a connection to the database. 16 | func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { 17 | // Connect to Server 18 | if len(c.cfg.User) == 0 { 19 | c.cfg.User = common.DefaultUser 20 | } 21 | if len(c.cfg.Passwd) == 0 { 22 | c.cfg.Passwd = common.DefaultPassword 23 | } 24 | if c.cfg.Port == 0 { 25 | c.cfg.Port = common.DefaultHttpPort 26 | } 27 | if len(c.cfg.Net) == 0 { 28 | c.cfg.Net = "http" 29 | } 30 | if len(c.cfg.Addr) == 0 { 31 | c.cfg.Addr = "127.0.0.1" 32 | } 33 | tc, err := newTaosConn(c.cfg) 34 | return tc, err 35 | } 36 | 37 | // Driver implements driver.Connector interface. 38 | // Driver returns &TDengineDriver{}. 39 | func (c *connector) Driver() driver.Driver { 40 | return &TDengineDriver{} 41 | } 42 | -------------------------------------------------------------------------------- /wrapper/setconfig.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | #include 8 | */ 9 | import "C" 10 | import ( 11 | "strings" 12 | "unsafe" 13 | 14 | "github.com/taosdata/driver-go/v3/errors" 15 | ) 16 | 17 | // TaosSetConfig int taos_set_config(const char *config); 18 | func TaosSetConfig(params map[string]string) error { 19 | if len(params) == 0 { 20 | return nil 21 | } 22 | buf := &strings.Builder{} 23 | for k, v := range params { 24 | buf.WriteString(k) 25 | buf.WriteString(" ") 26 | buf.WriteString(v) 27 | } 28 | cConfig := C.CString(buf.String()) 29 | defer C.free(unsafe.Pointer(cConfig)) 30 | result := (C.struct_setConfRet)(C.taos_set_config(cConfig)) 31 | if int(result.retCode) == -5 || int(result.retCode) == 0 { 32 | return nil 33 | } 34 | buf.Reset() 35 | for _, c := range result.retMsg { 36 | if c == 0 { 37 | break 38 | } 39 | buf.WriteByte(byte(c)) 40 | } 41 | return &errors.TaosError{Code: int32(result.retCode) & 0xffff, ErrStr: buf.String()} 42 | } 43 | -------------------------------------------------------------------------------- /wrapper/whitelistcb.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import "C" 4 | import ( 5 | "net" 6 | "unsafe" 7 | 8 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 9 | ) 10 | 11 | type WhitelistResult struct { 12 | ErrCode int32 13 | IPNets []*net.IPNet 14 | } 15 | 16 | //export WhitelistCallback 17 | func WhitelistCallback(param unsafe.Pointer, code int, taosConnect unsafe.Pointer, numOfWhiteLists int, pWhiteLists unsafe.Pointer) { 18 | c := (*(*cgo.Handle)(param)).Value().(chan *WhitelistResult) 19 | if code != 0 { 20 | c <- &WhitelistResult{ErrCode: int32(code)} 21 | return 22 | } 23 | ips := make([]*net.IPNet, 0, numOfWhiteLists) 24 | for i := 0; i < numOfWhiteLists; i++ { 25 | ipNet := make([]byte, 8) 26 | for j := 0; j < 8; j++ { 27 | ipNet[j] = *(*byte)(unsafe.Pointer(uintptr(pWhiteLists) + uintptr(i*8) + uintptr(j))) 28 | } 29 | ip := net.IP{ipNet[0], ipNet[1], ipNet[2], ipNet[3]} 30 | ones := int(ipNet[4]) 31 | ipMask := net.CIDRMask(ones, 32) 32 | ips = append(ips, &net.IPNet{IP: ip, Mask: ipMask}) 33 | } 34 | c <- &WhitelistResult{IPNets: ips} 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 taosdata.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | -------------------------------------------------------------------------------- /wrapper/asynccb.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | */ 10 | import "C" 11 | import ( 12 | "unsafe" 13 | 14 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 15 | ) 16 | 17 | type Caller interface { 18 | QueryCall(res unsafe.Pointer, code int) 19 | FetchCall(res unsafe.Pointer, numOfRows int) 20 | } 21 | 22 | //export QueryCallback 23 | func QueryCallback(p unsafe.Pointer, res *C.TAOS_RES, code C.int) { 24 | caller := (*(*cgo.Handle)(p)).Value().(Caller) 25 | caller.QueryCall(unsafe.Pointer(res), int(code)) 26 | } 27 | 28 | //export FetchRowsCallback 29 | func FetchRowsCallback(p unsafe.Pointer, res *C.TAOS_RES, numOfRows C.int) { 30 | caller := (*(*cgo.Handle)(p)).Value().(Caller) 31 | caller.FetchCall(unsafe.Pointer(res), int(numOfRows)) 32 | } 33 | 34 | //export FetchRawBlockCallback 35 | func FetchRawBlockCallback(p unsafe.Pointer, res *C.TAOS_RES, numOfRows C.int) { 36 | caller := (*(*cgo.Handle)(p)).Value().(Caller) 37 | caller.FetchCall(unsafe.Pointer(res), int(numOfRows)) 38 | } 39 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | # Benchmark for TDengine-go-driver 2 | 3 | ## Test tool 4 | 5 | We use [hyperfine](https://github.com/sharkdp/hyperfine) to test TDengin-go-driver 6 | 7 | ## Test case 8 | 9 | - insert 10 | - batch insert 11 | - query 12 | - average 13 | 14 | ## Usage 15 | 16 | ```shell 17 | sh run_bench.sh ${BENCHMARK_TIMES} ${BATCH_TABLES} ${BATCH_ROWS} 18 | ``` 19 | 20 | - BENCHMARK_TIMES: ${BENCHMARK_TIMES} identifies how many tests [Hyperfine](https://github.com/sharkdp/hyperfine) will 21 | perform. 22 | - BATCH_TABLES: ${BENCHMARK_TIMES} identifies how many sub-tables will be used in batch insert testing case. In this 23 | benchmark, there are 10000 sub-tables in each super table. So this value should not greater than 10000. 24 | - BATCH_ROWS: ${BATCH_ROWS} identifies how many rows will be inserted into each sub-table in batch insert case. 25 | The maximum SQL length in TDengine is 1M. Therefore, if this parameter is too large, the benchmark will fail. In this 26 | benchmark, this value should not greater than 5000. 27 | 28 | example: 29 | 30 | ```shell 31 | sh run_bench.sh 10 100 1000 32 | ``` 33 | -------------------------------------------------------------------------------- /wrapper/whitelist.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #cgo CFLAGS: -IC:/TDengine/include -I/usr/include 5 | #cgo linux LDFLAGS: -L/usr/lib -ltaos 6 | #cgo windows LDFLAGS: -LC:/TDengine/driver -ltaos 7 | #cgo darwin LDFLAGS: -L/usr/local/lib -ltaos 8 | #include 9 | #include 10 | #include 11 | #include 12 | extern void WhitelistCallback(void *param, int code, TAOS *taos, int numOfWhiteLists, uint64_t* pWhiteLists); 13 | void taos_fetch_whitelist_a_wrapper(TAOS *taos, void *param){ 14 | return taos_fetch_whitelist_a(taos, WhitelistCallback, param); 15 | }; 16 | */ 17 | import "C" 18 | import ( 19 | "unsafe" 20 | 21 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 22 | ) 23 | 24 | // typedef void (*__taos_async_whitelist_fn_t)(void *param, int code, TAOS *taos, int numOfWhiteLists, uint64_t* pWhiteLists); 25 | 26 | // TaosFetchWhitelistA DLL_EXPORT void taos_fetch_whitelist_a(TAOS *taos, __taos_async_whitelist_fn_t fp, void *param); 27 | func TaosFetchWhitelistA(taosConnect unsafe.Pointer, caller cgo.Handle) { 28 | C.taos_fetch_whitelist_a_wrapper(taosConnect, caller.Pointer()) 29 | } 30 | -------------------------------------------------------------------------------- /taosWS/driver.go: -------------------------------------------------------------------------------- 1 | package taosWS 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "database/sql/driver" 7 | ) 8 | 9 | // TDengineDriver is exported to make the driver directly accessible. 10 | // In general the driver is used via the database/sql package. 11 | type TDengineDriver struct{} 12 | 13 | // Open new Connection. 14 | // the DSN string is formatted 15 | func (d TDengineDriver) Open(dsn string) (driver.Conn, error) { 16 | cfg, err := ParseDSN(dsn) 17 | if err != nil { 18 | return nil, err 19 | } 20 | c := &connector{ 21 | cfg: cfg, 22 | } 23 | return c.Connect(context.Background()) 24 | } 25 | 26 | func init() { 27 | sql.Register("taosWS", &TDengineDriver{}) 28 | } 29 | 30 | // NewConnector returns new driver.Connector. 31 | func NewConnector(cfg *Config) (driver.Connector, error) { 32 | return &connector{cfg: cfg}, nil 33 | } 34 | 35 | // OpenConnector implements driver.DriverContext. 36 | func (d TDengineDriver) OpenConnector(dsn string) (driver.Connector, error) { 37 | cfg, err := ParseDSN(dsn) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return &connector{ 42 | cfg: cfg, 43 | }, nil 44 | } 45 | -------------------------------------------------------------------------------- /errors/errors_test.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | // @author: xftan 10 | // @date: 2023/10/13 11:20 11 | // @description: test new error 12 | func TestNewError(t *testing.T) { 13 | type args struct { 14 | code int 15 | errStr string 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | wantErr bool 21 | }{ 22 | { 23 | name: "common", 24 | args: args{ 25 | code: 0, 26 | errStr: "success", 27 | }, 28 | wantErr: true, 29 | }, 30 | } 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | if err := NewError(tt.args.code, tt.args.errStr); (err != nil) != tt.wantErr { 34 | t.Errorf("NewError() error = %v, wantErr %v", err, tt.wantErr) 35 | } 36 | }) 37 | } 38 | } 39 | 40 | func TestError(t *testing.T) { 41 | invalidError := ErrTscInvalidConnection.Error() 42 | assert.Equal(t, "[0x20b] Invalid connection", invalidError) 43 | unknownError := &TaosError{ 44 | Code: 0xffff, 45 | ErrStr: "unknown error", 46 | } 47 | assert.Equal(t, "unknown error", unknownError.Error()) 48 | } 49 | -------------------------------------------------------------------------------- /taosRestful/driver.go: -------------------------------------------------------------------------------- 1 | package taosRestful 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "database/sql/driver" 7 | ) 8 | 9 | // TDengineDriver is exported to make the driver directly accessible. 10 | // In general the driver is used via the database/sql package. 11 | type TDengineDriver struct{} 12 | 13 | // Open new Connection. 14 | // the DSN string is formatted 15 | func (d TDengineDriver) Open(dsn string) (driver.Conn, error) { 16 | cfg, err := ParseDSN(dsn) 17 | if err != nil { 18 | return nil, err 19 | } 20 | c := &connector{ 21 | cfg: cfg, 22 | } 23 | return c.Connect(context.Background()) 24 | } 25 | 26 | func init() { 27 | sql.Register("taosRestful", &TDengineDriver{}) 28 | } 29 | 30 | // NewConnector returns new driver.Connector. 31 | func NewConnector(cfg *Config) (driver.Connector, error) { 32 | return &connector{cfg: cfg}, nil 33 | } 34 | 35 | // OpenConnector implements driver.DriverContext. 36 | func (d TDengineDriver) OpenConnector(dsn string) (driver.Connector, error) { 37 | cfg, err := ParseDSN(dsn) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return &connector{ 42 | cfg: cfg, 43 | }, nil 44 | } 45 | -------------------------------------------------------------------------------- /taosWS/connector.go: -------------------------------------------------------------------------------- 1 | package taosWS 2 | 3 | import ( 4 | "context" 5 | "database/sql/driver" 6 | 7 | "github.com/taosdata/driver-go/v3/common" 8 | ) 9 | 10 | type connector struct { 11 | cfg *Config 12 | } 13 | 14 | // Connect implements driver.Connector interface. 15 | // Connect returns a connection to the database. 16 | func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { 17 | // Connect to Server 18 | if len(c.cfg.User) == 0 { 19 | c.cfg.User = common.DefaultUser 20 | } 21 | if len(c.cfg.Passwd) == 0 { 22 | c.cfg.Passwd = common.DefaultPassword 23 | } 24 | if c.cfg.Port == 0 { 25 | c.cfg.Port = common.DefaultHttpPort 26 | } 27 | if len(c.cfg.Net) == 0 { 28 | c.cfg.Net = "ws" 29 | } 30 | if len(c.cfg.Addr) == 0 { 31 | c.cfg.Addr = "127.0.0.1" 32 | } 33 | if c.cfg.ReadTimeout == 0 { 34 | c.cfg.ReadTimeout = common.DefaultMessageTimeout 35 | } 36 | if c.cfg.WriteTimeout == 0 { 37 | c.cfg.WriteTimeout = common.DefaultWriteWait 38 | } 39 | tc, err := newTaosConn(c.cfg) 40 | return tc, err 41 | } 42 | 43 | // Driver implements driver.Connector interface. 44 | // Driver returns &TDengineDriver{}. 45 | func (c *connector) Driver() driver.Driver { 46 | return &TDengineDriver{} 47 | } 48 | -------------------------------------------------------------------------------- /wrapper/setconfig_test.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // @author: xftan 8 | // @date: 2022/1/27 17:27 9 | // @description: test taos_set_config 10 | func TestSetConfig(t *testing.T) { 11 | source := map[string]string{ 12 | "numOfThreadsPerCore": "1.000000", 13 | "rpcTimer": "300", 14 | "rpcForceTcp": "0", 15 | "rpcMaxTime": "600", 16 | "compressMsgSize": "-1", 17 | "maxSQLLength": "1048576", 18 | "maxWildCardsLength": "100", 19 | "maxNumOfOrderedRes": "100000", 20 | "keepColumnName": "0", 21 | "timezone": "Asia/Shanghai (CST, +0800)", 22 | "locale": "C.UTF-8", 23 | "charset": "UTF-8", 24 | "numOfLogLines": "10000000", 25 | "asyncLog": "1", 26 | "debugFlag": "135", 27 | "rpcDebugFlag": "131", 28 | "tmrDebugFlag": "131", 29 | "cDebugFlag": "131", 30 | "jniDebugFlag": "131", 31 | "odbcDebugFlag": "131", 32 | "uDebugFlag": "131", 33 | "qDebugFlag": "131", 34 | "maxBinaryDisplayWidth": "30", 35 | "tempDir": "/tmp/", 36 | } 37 | err := TaosSetConfig(source) 38 | if err != nil { 39 | t.Error(err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /taosSql/ns_test.go: -------------------------------------------------------------------------------- 1 | package taosSql 2 | 3 | import ( 4 | "database/sql" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // @author: xftan 10 | // @date: 2022/1/27 16:19 11 | // @description: test nano second timestamp 12 | func TestNanosecond(t *testing.T) { 13 | db, err := sql.Open("taosSql", dataSourceName) 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | err = db.Ping() 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | defer func() { 22 | _, err = exec(db, "drop database if exists test_go_ns_") 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | }() 27 | _, err = exec(db, "create database if not exists test_go_ns_ precision 'ns'") 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | _, err = exec(db, "create table if not exists test_go_ns_.tb1 (ts timestamp, n int)") 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | _, err = exec(db, "insert into test_go_ns_.tb1 values(1629363529469478001, 1)(1629363529469478002,2)") 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | rows, err := db.Query("select ts from test_go_ns_.tb1") 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | for rows.Next() { 44 | var ts time.Time 45 | err := rows.Scan(&ts) 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | if ts.Nanosecond()%1000 == 0 { 50 | t.Log(ts.UnixNano()) 51 | t.Fatal("nanosecond is not correct") 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/tmq/config_test.go: -------------------------------------------------------------------------------- 1 | package tmq 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestConfigMap_Get(t *testing.T) { 10 | t.Parallel() 11 | 12 | config := ConfigMap{ 13 | "key1": "value1", 14 | "key2": 123, 15 | } 16 | 17 | t.Run("Existing Key", func(t *testing.T) { 18 | want := "value1" 19 | if got, err := config.Get("key1", nil); err != nil || got != want { 20 | t.Errorf("Get() = %v, want %v (error: %v)", got, want, err) 21 | } 22 | }) 23 | 24 | t.Run("Type Mismatch", func(t *testing.T) { 25 | wantErr := fmt.Errorf("key2 expects type string, not int") 26 | if got, err := config.Get("key2", "default"); err == nil || got != nil || err.Error() != wantErr.Error() { 27 | t.Errorf("Get() = %v, want error: %v", got, wantErr) 28 | } 29 | }) 30 | 31 | t.Run("Non-Existing Key with Default Value", func(t *testing.T) { 32 | want := "default" 33 | if got, err := config.Get("key3", "default"); err != nil || got != want { 34 | t.Errorf("Get() = %v, want %v (error: %v)", got, want, err) 35 | } 36 | }) 37 | } 38 | 39 | func TestConfigMap_Clone(t *testing.T) { 40 | t.Parallel() 41 | 42 | config := ConfigMap{ 43 | "key1": "value1", 44 | "key2": 123, 45 | } 46 | 47 | clone := config.Clone() 48 | 49 | if !reflect.DeepEqual(config, clone) { 50 | t.Errorf("Clone() = %v, want %v", clone, config) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /taosRestful/rows.go: -------------------------------------------------------------------------------- 1 | package taosRestful 2 | 3 | import ( 4 | "database/sql/driver" 5 | "io" 6 | "reflect" 7 | 8 | "github.com/taosdata/driver-go/v3/common" 9 | ) 10 | 11 | type rows struct { 12 | result *common.TDEngineRestfulResp 13 | rowIndex int 14 | } 15 | 16 | func (rs *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { 17 | if rs.result.ColTypes[index] == common.TSDB_DATA_TYPE_DECIMAL || rs.result.ColTypes[index] == common.TSDB_DATA_TYPE_DECIMAL64 { 18 | return rs.result.Precisions[index], rs.result.Scales[index], true 19 | } 20 | return 0, 0, false 21 | } 22 | 23 | func (rs *rows) Columns() []string { 24 | return rs.result.ColNames 25 | } 26 | 27 | func (rs *rows) ColumnTypeDatabaseTypeName(i int) string { 28 | return common.GetTypeName(rs.result.ColTypes[i]) 29 | } 30 | 31 | func (rs *rows) ColumnTypeLength(i int) (length int64, ok bool) { 32 | return rs.result.ColLength[i], ok 33 | } 34 | 35 | func (rs *rows) ColumnTypeScanType(i int) reflect.Type { 36 | t, exist := common.ColumnTypeMap[rs.result.ColTypes[i]] 37 | if !exist { 38 | return common.UnknownType 39 | } 40 | return t 41 | } 42 | 43 | func (rs *rows) Close() error { 44 | return nil 45 | } 46 | 47 | func (rs *rows) Next(dest []driver.Value) error { 48 | if rs.rowIndex >= len(rs.result.Data) { 49 | return io.EOF 50 | } 51 | copy(dest, rs.result.Data[rs.rowIndex]) 52 | rs.rowIndex += 1 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /taosSql/driver.go: -------------------------------------------------------------------------------- 1 | package taosSql 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "database/sql/driver" 7 | "sync" 8 | 9 | "github.com/taosdata/driver-go/v3/wrapper/handler" 10 | "github.com/taosdata/driver-go/v3/wrapper/thread" 11 | ) 12 | 13 | var locker *thread.Locker 14 | var onceInitLock = sync.Once{} 15 | var asyncHandlerPool *handler.HandlerPool 16 | var onceInitHandlerPool = sync.Once{} 17 | 18 | // TDengineDriver is exported to make the driver directly accessible. 19 | // In general the driver is used via the database/sql package. 20 | type TDengineDriver struct{} 21 | 22 | // Open new Connection. 23 | // the DSN string is formatted 24 | func (d TDengineDriver) Open(dsn string) (driver.Conn, error) { 25 | cfg, err := ParseDSN(dsn) 26 | if err != nil { 27 | return nil, err 28 | } 29 | c := &connector{ 30 | cfg: cfg, 31 | } 32 | 33 | return c.Connect(context.Background()) 34 | } 35 | 36 | func init() { 37 | sql.Register("taosSql", &TDengineDriver{}) 38 | } 39 | 40 | // NewConnector returns new driver.Connector. 41 | func NewConnector(cfg *Config) (driver.Connector, error) { 42 | return &connector{cfg: cfg}, nil 43 | } 44 | 45 | // OpenConnector implements driver.DriverContext. 46 | func (d TDengineDriver) OpenConnector(dsn string) (driver.Connector, error) { 47 | cfg, err := ParseDSN(dsn) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return &connector{ 52 | cfg: cfg, 53 | }, nil 54 | } 55 | -------------------------------------------------------------------------------- /wrapper/tmqcb.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | #include 8 | */ 9 | import "C" 10 | import ( 11 | "unsafe" 12 | 13 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 14 | ) 15 | 16 | //typedef void(tmq_commit_cb(tmq_t *, int32_t code, void *param)); 17 | 18 | //export TMQCommitCB 19 | func TMQCommitCB(consumer unsafe.Pointer, resp C.int32_t, param unsafe.Pointer) { 20 | c := (*(*cgo.Handle)(param)).Value().(chan *TMQCommitCallbackResult) 21 | r := GetTMQCommitCallbackResult(int32(resp), consumer) 22 | defer func() { 23 | // Avoid panic due to channel closed 24 | _ = recover() 25 | }() 26 | c <- r 27 | } 28 | 29 | //export TMQAutoCommitCB 30 | func TMQAutoCommitCB(consumer unsafe.Pointer, resp C.int32_t, param unsafe.Pointer) { 31 | c := (*(*cgo.Handle)(param)).Value().(chan *TMQCommitCallbackResult) 32 | r := GetTMQCommitCallbackResult(int32(resp), consumer) 33 | defer func() { 34 | // Avoid panic due to channel closed 35 | _ = recover() 36 | }() 37 | c <- r 38 | } 39 | 40 | //export TMQCommitOffsetCB 41 | func TMQCommitOffsetCB(consumer unsafe.Pointer, resp C.int32_t, param unsafe.Pointer) { 42 | c := (*(*cgo.Handle)(param)).Value().(chan *TMQCommitCallbackResult) 43 | r := GetTMQCommitCallbackResult(int32(resp), consumer) 44 | defer func() { 45 | // Avoid panic due to channel closed 46 | _ = recover() 47 | }() 48 | c <- r 49 | } 50 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | modules-download-mode: readonly 4 | allow-parallel-runners: true 5 | 6 | linters: 7 | enable: 8 | - revive 9 | - govet 10 | - staticcheck 11 | settings: 12 | revive: 13 | rules: 14 | - name: context-keys-type 15 | - name: time-naming 16 | - name: var-declaration 17 | - name: unexported-return 18 | - name: errorf 19 | - name: blank-imports 20 | - name: context-as-argument 21 | - name: dot-imports 22 | - name: error-return 23 | - name: error-strings 24 | - name: error-naming 25 | - name: var-naming 26 | arguments: 27 | - - ID 28 | - IP 29 | - JSON 30 | - URL 31 | - HTTP 32 | - SQL 33 | - CPU 34 | - URI 35 | - [ ] 36 | - - skipPackageNameChecks: true 37 | - name: range 38 | - name: receiver-naming 39 | - name: indent-error-flow 40 | - name: unreachable-code 41 | staticcheck: 42 | checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022","-ST1012" ] 43 | exclusions: 44 | generated: lax 45 | paths: 46 | - examples 47 | issues: 48 | max-issues-per-linter: 0 49 | max-same-issues: 0 50 | formatters: 51 | enable: 52 | - goimports 53 | exclusions: 54 | generated: lax 55 | paths: 56 | - examples 57 | -------------------------------------------------------------------------------- /bench/sql_test.go: -------------------------------------------------------------------------------- 1 | package bench 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | _ "github.com/taosdata/driver-go/v3/taosSql" 10 | ) 11 | 12 | var ( 13 | driverName = "taosSql" 14 | user = "root" 15 | password = "taosdata" 16 | host = "" 17 | port = 6030 18 | dataSourceName = fmt.Sprintf("%s:%s@/tcp(%s:%d)/%s?interpolateParams=true", user, password, host, port, "") 19 | ) 20 | 21 | func BenchmarkInsert(b *testing.B) { 22 | db, err := sql.Open(driverName, dataSourceName) 23 | if err != nil { 24 | b.Fatalf("error on: sql.open %s", err.Error()) 25 | return 26 | } 27 | for i := 0; i < b.N; i++ { 28 | _, err = db.Exec("insert into bench_test.test_insert values (now,123.456)") 29 | if err != nil { 30 | b.Fatalf("insert data error %s", err) 31 | } 32 | } 33 | } 34 | 35 | func BenchmarkSelect(b *testing.B) { 36 | db, err := sql.Open(driverName, dataSourceName) 37 | if err != nil { 38 | b.Fatalf("error on: sql.open %s", err.Error()) 39 | return 40 | } 41 | for i := 0; i < b.N; i++ { 42 | rows, err := db.Query("select * from bench_test.test_select") 43 | if err != nil { 44 | b.Fatalf("select data error %s", err.Error()) 45 | } 46 | var t time.Time 47 | var s float64 48 | for rows.Next() { 49 | err := rows.Scan(&t, &s) 50 | if err != nil { 51 | b.Fatalf("scan error %s", err.Error()) 52 | } 53 | if s != 123.456 { 54 | b.Fatalf("result error expect 123.456 got %f", s) 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /wrapper/handler/handlerpool_test.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func BenchmarkName(b *testing.B) { 9 | pool := NewHandlerPool(1) 10 | for i := 0; i < b.N; i++ { 11 | h := pool.Get() 12 | pool.Put(h) 13 | } 14 | } 15 | 16 | // @author: xftan 17 | // @date: 2021/12/14 15:00 18 | // @description: test func NewHandlerPool 19 | func TestNewHandlerPool(t *testing.T) { 20 | type args struct { 21 | count int 22 | } 23 | tests := []struct { 24 | name string 25 | args args 26 | }{ 27 | { 28 | name: "test", 29 | args: args{ 30 | count: 100, 31 | }, 32 | }, 33 | } 34 | for _, tt := range tests { 35 | t.Run(tt.name, func(t *testing.T) { 36 | got := NewHandlerPool(tt.args.count) 37 | l := make([]*Handler, tt.args.count) 38 | for i := 0; i < tt.args.count; i++ { 39 | l[i] = got.Get() 40 | } 41 | for _, handler := range l { 42 | got.Put(handler) 43 | } 44 | }) 45 | } 46 | } 47 | 48 | // @author: xftan 49 | // @date: 2021/12/14 15:01 50 | // @description: test func HandlerPool.Get 51 | func TestHandlerPool_Get(t *testing.T) { 52 | pool := NewHandlerPool(1) 53 | h := pool.Get() 54 | go func() { 55 | time.Sleep(time.Millisecond) 56 | pool.Put(h) 57 | }() 58 | h2 := pool.Get() 59 | pool.Put(h2) 60 | } 61 | 62 | // @author: xftan 63 | // @date: 2023/10/13 11:27 64 | // @description: test caller query 65 | func TestCaller_QueryCall(t *testing.T) { 66 | caller := NewCaller() 67 | caller.QueryCall(nil, 0) 68 | } 69 | 70 | // @author: xftan 71 | // @date: 2023/10/13 11:27 72 | // @description: test caller fetch 73 | func TestCaller_FetchCall(t *testing.T) { 74 | caller := NewCaller() 75 | caller.FetchCall(nil, 0) 76 | } 77 | -------------------------------------------------------------------------------- /types/taostype.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "reflect" 5 | "time" 6 | ) 7 | 8 | type ( 9 | TaosBool bool 10 | TaosTinyint int8 11 | TaosSmallint int16 12 | TaosInt int32 13 | TaosBigint int64 14 | TaosUTinyint uint8 15 | TaosUSmallint uint16 16 | TaosUInt uint32 17 | TaosUBigint uint64 18 | TaosFloat float32 19 | TaosDouble float64 20 | TaosBinary []byte 21 | TaosVarBinary []byte 22 | TaosNchar string 23 | TaosTimestamp struct { 24 | T time.Time 25 | Precision int 26 | } 27 | TaosJson []byte 28 | TaosGeometry []byte 29 | TaosBlob []byte 30 | ) 31 | 32 | var ( 33 | TaosBoolType = reflect.TypeOf(TaosBool(false)) 34 | TaosTinyintType = reflect.TypeOf(TaosTinyint(0)) 35 | TaosSmallintType = reflect.TypeOf(TaosSmallint(0)) 36 | TaosIntType = reflect.TypeOf(TaosInt(0)) 37 | TaosBigintType = reflect.TypeOf(TaosBigint(0)) 38 | TaosUTinyintType = reflect.TypeOf(TaosUTinyint(0)) 39 | TaosUSmallintType = reflect.TypeOf(TaosUSmallint(0)) 40 | TaosUIntType = reflect.TypeOf(TaosUInt(0)) 41 | TaosUBigintType = reflect.TypeOf(TaosUBigint(0)) 42 | TaosFloatType = reflect.TypeOf(TaosFloat(0)) 43 | TaosDoubleType = reflect.TypeOf(TaosDouble(0)) 44 | TaosBinaryType = reflect.TypeOf(TaosBinary(nil)) 45 | TaosVarBinaryType = reflect.TypeOf(TaosVarBinary(nil)) 46 | TaosNcharType = reflect.TypeOf(TaosNchar("")) 47 | TaosTimestampType = reflect.TypeOf(TaosTimestamp{}) 48 | TaosJsonType = reflect.TypeOf(TaosJson("")) 49 | TaosGeometryType = reflect.TypeOf(TaosGeometry(nil)) 50 | TaosBlobType = reflect.TypeOf(TaosBlob(nil)) 51 | ) 52 | 53 | type ColumnType struct { 54 | Type reflect.Type 55 | MaxLen int 56 | } 57 | -------------------------------------------------------------------------------- /common/column.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/taosdata/driver-go/v3/types" 7 | ) 8 | 9 | var ( 10 | NullInt8 = reflect.TypeOf(types.NullInt8{}) 11 | NullInt16 = reflect.TypeOf(types.NullInt16{}) 12 | NullInt32 = reflect.TypeOf(types.NullInt32{}) 13 | NullInt64 = reflect.TypeOf(types.NullInt64{}) 14 | NullUInt8 = reflect.TypeOf(types.NullUInt8{}) 15 | NullUInt16 = reflect.TypeOf(types.NullUInt16{}) 16 | NullUInt32 = reflect.TypeOf(types.NullUInt32{}) 17 | NullUInt64 = reflect.TypeOf(types.NullUInt64{}) 18 | NullFloat32 = reflect.TypeOf(types.NullFloat32{}) 19 | NullFloat64 = reflect.TypeOf(types.NullFloat64{}) 20 | NullTime = reflect.TypeOf(types.NullTime{}) 21 | NullBool = reflect.TypeOf(types.NullBool{}) 22 | NullString = reflect.TypeOf(types.NullString{}) 23 | Bytes = reflect.TypeOf([]byte{}) 24 | NullJson = reflect.TypeOf(types.NullJson{}) 25 | UnknownType = reflect.TypeOf(new(interface{})).Elem() 26 | ) 27 | 28 | var ColumnTypeMap = map[int]reflect.Type{ 29 | TSDB_DATA_TYPE_BOOL: NullBool, 30 | TSDB_DATA_TYPE_TINYINT: NullInt8, 31 | TSDB_DATA_TYPE_SMALLINT: NullInt16, 32 | TSDB_DATA_TYPE_INT: NullInt32, 33 | TSDB_DATA_TYPE_BIGINT: NullInt64, 34 | TSDB_DATA_TYPE_UTINYINT: NullUInt8, 35 | TSDB_DATA_TYPE_USMALLINT: NullUInt16, 36 | TSDB_DATA_TYPE_UINT: NullUInt32, 37 | TSDB_DATA_TYPE_UBIGINT: NullUInt64, 38 | TSDB_DATA_TYPE_FLOAT: NullFloat32, 39 | TSDB_DATA_TYPE_DOUBLE: NullFloat64, 40 | TSDB_DATA_TYPE_BINARY: NullString, 41 | TSDB_DATA_TYPE_NCHAR: NullString, 42 | TSDB_DATA_TYPE_TIMESTAMP: NullTime, 43 | TSDB_DATA_TYPE_JSON: NullJson, 44 | TSDB_DATA_TYPE_VARBINARY: Bytes, 45 | TSDB_DATA_TYPE_GEOMETRY: Bytes, 46 | TSDB_DATA_TYPE_DECIMAL: NullString, 47 | TSDB_DATA_TYPE_DECIMAL64: NullString, 48 | TSDB_DATA_TYPE_BLOB: Bytes, 49 | } 50 | -------------------------------------------------------------------------------- /common/change.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | func TimestampConvertToTime(timestamp int64, precision int) time.Time { 10 | switch precision { 11 | case PrecisionMilliSecond: // milli-second 12 | return time.Unix(0, timestamp*1e6) 13 | case PrecisionMicroSecond: // micro-second 14 | return time.Unix(0, timestamp*1e3) 15 | case PrecisionNanoSecond: // nano-second 16 | return time.Unix(0, timestamp) 17 | default: 18 | s := fmt.Sprintln("unknown precision", precision, "timestamp", timestamp) 19 | panic(s) 20 | } 21 | } 22 | 23 | func TimestampConvertToTimeWithLocation(timestamp int64, precision int, loc *time.Location) time.Time { 24 | switch precision { 25 | case PrecisionMilliSecond: // milli-second 26 | return time.Unix(0, timestamp*1e6).In(loc) 27 | case PrecisionMicroSecond: // micro-second 28 | return time.Unix(0, timestamp*1e3).In(loc) 29 | case PrecisionNanoSecond: // nano-second 30 | return time.Unix(0, timestamp).In(loc) 31 | default: 32 | s := fmt.Sprintln("unknown precision", precision, "timestamp", timestamp) 33 | panic(s) 34 | } 35 | } 36 | 37 | func TimeToTimestamp(t time.Time, precision int) (timestamp int64) { 38 | switch precision { 39 | case PrecisionMilliSecond: 40 | return t.UnixNano() / 1e6 41 | case PrecisionMicroSecond: 42 | return t.UnixNano() / 1e3 43 | case PrecisionNanoSecond: 44 | return t.UnixNano() 45 | default: 46 | s := fmt.Sprintln("unknown precision", precision, "time", t) 47 | panic(s) 48 | } 49 | } 50 | 51 | func ParseTimezone(tz string) (*time.Location, error) { 52 | if tz == "" { 53 | return nil, fmt.Errorf("empty string") 54 | } 55 | if strings.ToLower(tz) == "local" { 56 | return nil, fmt.Errorf("timezone cannot be 'Local'") 57 | } 58 | loc, err := time.LoadLocation(tz) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return loc, nil 63 | } 64 | -------------------------------------------------------------------------------- /bench/standard/native/exec/write.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/taosdata/driver-go/v3/bench/standard/executor" 8 | _ "github.com/taosdata/driver-go/v3/taosSql" 9 | ) 10 | 11 | func main() { 12 | test := executor.NewTDTest(executor.DefaultNativeDriverName, executor.DefaultNativeDSN) 13 | { 14 | test.Clean() 15 | test.PrepareWrite() 16 | s := time.Now() 17 | singleCount := 1000 18 | test.BenchmarkWriteSingleCommon(singleCount) 19 | writeSingleCommonCost := time.Since(s) 20 | fmt.Printf("write single common, count: %d,cost: %d ns,average: %f ns\n", singleCount, writeSingleCommonCost.Nanoseconds(), float64(writeSingleCommonCost.Nanoseconds())/float64(singleCount)) 21 | 22 | test.Clean() 23 | test.PrepareWrite() 24 | batchCount := 1000 25 | batch := 100 26 | s = time.Now() 27 | test.BenchmarkWriteBatchJson(batchCount, batch) 28 | writeBatchCost := time.Since(s) 29 | fmt.Printf("write batch common, count: %d,cost: %d ns,average: %f ns\n", batchCount, writeBatchCost.Nanoseconds(), float64(writeBatchCost.Nanoseconds())/float64(batch*batchCount)) 30 | } 31 | 32 | { 33 | test.PrepareWrite() 34 | s := time.Now() 35 | singleCount := 1000 36 | test.BenchmarkWriteSingleJson(singleCount) 37 | writeSingleCommonCost := time.Since(s) 38 | fmt.Printf("write single json, count: %d,cost: %d ns,average: %f ns\n", singleCount, writeSingleCommonCost.Nanoseconds(), float64(writeSingleCommonCost.Nanoseconds())/float64(singleCount)) 39 | 40 | test.Clean() 41 | test.PrepareWrite() 42 | batchCount := 1000 43 | batch := 100 44 | s = time.Now() 45 | test.BenchmarkWriteBatchJson(batchCount, batch) 46 | writeBatchCost := time.Since(s) 47 | fmt.Printf("write batch json, count: %d,cost: %d ns,average: %f ns\n", batchCount, writeBatchCost.Nanoseconds(), float64(writeBatchCost.Nanoseconds())/float64(batch*batchCount)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /bench/standard/restful/exec/write.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/taosdata/driver-go/v3/bench/standard/executor" 8 | _ "github.com/taosdata/driver-go/v3/taosRestful" 9 | ) 10 | 11 | func main() { 12 | test := executor.NewTDTest(executor.DefaultRestfulDriverName, executor.DefaultRestfulDSN) 13 | { 14 | test.Clean() 15 | test.PrepareWrite() 16 | s := time.Now() 17 | singleCount := 1000 18 | test.BenchmarkWriteSingleCommon(singleCount) 19 | writeSingleCommonCost := time.Since(s) 20 | fmt.Printf("write single common, count: %d,cost: %d ns,average: %f ns\n", singleCount, writeSingleCommonCost.Nanoseconds(), float64(writeSingleCommonCost.Nanoseconds())/float64(singleCount)) 21 | 22 | test.Clean() 23 | test.PrepareWrite() 24 | batchCount := 1000 25 | batch := 100 26 | s = time.Now() 27 | test.BenchmarkWriteBatchJson(batchCount, batch) 28 | writeBatchCost := time.Since(s) 29 | fmt.Printf("write batch common, count: %d,cost: %d ns,average: %f ns\n", batchCount, writeBatchCost.Nanoseconds(), float64(writeBatchCost.Nanoseconds())/float64(batch*batchCount)) 30 | } 31 | 32 | { 33 | test.PrepareWrite() 34 | s := time.Now() 35 | singleCount := 1000 36 | test.BenchmarkWriteSingleJson(singleCount) 37 | writeSingleCommonCost := time.Since(s) 38 | fmt.Printf("write single json, count: %d,cost: %d ns,average: %f ns\n", singleCount, writeSingleCommonCost.Nanoseconds(), float64(writeSingleCommonCost.Nanoseconds())/float64(singleCount)) 39 | 40 | test.Clean() 41 | test.PrepareWrite() 42 | batchCount := 1000 43 | batch := 100 44 | s = time.Now() 45 | test.BenchmarkWriteBatchJson(batchCount, batch) 46 | writeBatchCost := time.Since(s) 47 | fmt.Printf("write batch json, count: %d,cost: %d ns,average: %f ns\n", batchCount, writeBatchCost.Nanoseconds(), float64(writeBatchCost.Nanoseconds())/float64(batch*batchCount)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /wrapper/whitelistcb_test.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "unsafe" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 10 | ) 11 | 12 | func TestWhitelistCallback_ErrorCode(t *testing.T) { 13 | // Create a channel to receive the result 14 | resultChan := make(chan *WhitelistResult, 1) 15 | handle := cgo.NewHandle(resultChan) 16 | // Simulate an error (code != 0) 17 | go WhitelistCallback(handle.Pointer(), 1, nil, 0, nil) 18 | 19 | // Expect the result to have an error code 20 | result := <-resultChan 21 | assert.Equal(t, int32(1), result.ErrCode) 22 | assert.Nil(t, result.IPNets) // No IPs should be returned 23 | } 24 | 25 | func TestWhitelistCallback_Success(t *testing.T) { 26 | // Prepare the test data: a list of byte slices representing IPs and masks 27 | ipList := []byte{ 28 | 192, 168, 1, 1, 24, // 192.168.1.1/24 29 | 0, 0, 0, 30 | 10, 0, 0, 1, 16, // 10.0.0.1/16 31 | 0, 0, 0, 32 | } 33 | 34 | // Create a channel to receive the result 35 | resultChan := make(chan *WhitelistResult, 1) 36 | 37 | // Cast the byte slice to an unsafe pointer 38 | pWhiteLists := unsafe.Pointer(&ipList[0]) 39 | handle := cgo.NewHandle(resultChan) 40 | // Simulate a successful callback (code == 0) 41 | go WhitelistCallback(handle.Pointer(), 0, nil, 2, pWhiteLists) 42 | 43 | // Expect the result to have two IPNets 44 | result := <-resultChan 45 | assert.Equal(t, int32(0), result.ErrCode) 46 | assert.Len(t, result.IPNets, 2) 47 | 48 | // Validate the first IPNet (192.168.1.1/24) 49 | assert.Equal(t, net.IPv4(192, 168, 1, 1).To4(), result.IPNets[0].IP) 50 | 51 | ones, _ := result.IPNets[0].Mask.Size() 52 | assert.Equal(t, 24, ones) 53 | 54 | // Validate the second IPNet (10.0.0.1/16) 55 | assert.Equal(t, net.IPv4(10, 0, 0, 1).To4(), result.IPNets[1].IP) 56 | ones, _ = result.IPNets[1].Mask.Size() 57 | assert.Equal(t, 16, ones) 58 | } 59 | -------------------------------------------------------------------------------- /common/const.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "unsafe" 4 | 5 | //revive:disable 6 | const ( 7 | MaxTaosSqlLen = 1048576 8 | DefaultUser = "root" 9 | DefaultPassword = "taosdata" 10 | ) 11 | 12 | const ( 13 | PrecisionMilliSecond = 0 14 | PrecisionMicroSecond = 1 15 | PrecisionNanoSecond = 2 16 | ) 17 | 18 | const ( 19 | TSDB_OPTION_LOCALE = iota 20 | TSDB_OPTION_CHARSET 21 | TSDB_OPTION_TIMEZONE 22 | TSDB_OPTION_CONFIGDIR 23 | TSDB_OPTION_SHELL_ACTIVITY_TIMER 24 | TSDB_OPTION_USE_ADAPTER 25 | ) 26 | 27 | const ( 28 | TSDB_OPTION_CONNECTION_CLEAR = iota - 1 29 | TSDB_OPTION_CONNECTION_CHARSET 30 | TSDB_OPTION_CONNECTION_TIMEZONE 31 | TSDB_OPTION_CONNECTION_USER_IP 32 | TSDB_OPTION_CONNECTION_USER_APP 33 | ) 34 | 35 | const ( 36 | TMQ_RES_INVALID = -1 37 | TMQ_RES_DATA = 1 38 | TMQ_RES_TABLE_META = 2 39 | TMQ_RES_METADATA = 3 40 | ) 41 | 42 | var TypeLengthMap = map[int]int{ 43 | TSDB_DATA_TYPE_NULL: 1, 44 | TSDB_DATA_TYPE_BOOL: 1, 45 | TSDB_DATA_TYPE_TINYINT: 1, 46 | TSDB_DATA_TYPE_SMALLINT: 2, 47 | TSDB_DATA_TYPE_INT: 4, 48 | TSDB_DATA_TYPE_BIGINT: 8, 49 | TSDB_DATA_TYPE_FLOAT: 4, 50 | TSDB_DATA_TYPE_DOUBLE: 8, 51 | TSDB_DATA_TYPE_TIMESTAMP: 8, 52 | TSDB_DATA_TYPE_UTINYINT: 1, 53 | TSDB_DATA_TYPE_USMALLINT: 2, 54 | TSDB_DATA_TYPE_UINT: 4, 55 | TSDB_DATA_TYPE_UBIGINT: 8, 56 | } 57 | 58 | const ( 59 | Int8Size = unsafe.Sizeof(int8(0)) 60 | Int16Size = unsafe.Sizeof(int16(0)) 61 | Int32Size = unsafe.Sizeof(int32(0)) 62 | Int64Size = unsafe.Sizeof(int64(0)) 63 | UInt8Size = unsafe.Sizeof(uint8(0)) 64 | UInt16Size = unsafe.Sizeof(uint16(0)) 65 | UInt32Size = unsafe.Sizeof(uint32(0)) 66 | UInt64Size = unsafe.Sizeof(uint64(0)) 67 | Float32Size = unsafe.Sizeof(float32(0)) 68 | Float64Size = unsafe.Sizeof(float64(0)) 69 | ) 70 | 71 | const ReqIDKey = "taos_req_id" 72 | 73 | const ( 74 | TAOS_NOTIFY_PASSVER = 0 75 | TAOS_NOTIFY_WHITELIST_VER = 1 76 | TAOS_NOTIFY_USER_DROPPED = 2 77 | ) 78 | 79 | const ( 80 | TAOS_CONN_MODE_BI = 0 81 | ) 82 | -------------------------------------------------------------------------------- /bench/query/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "encoding/json" 6 | "fmt" 7 | 8 | _ "github.com/taosdata/driver-go/v3/taosSql" 9 | ) 10 | 11 | var ( 12 | driverName = "taosSql" 13 | user = "root" 14 | password = "taosdata" 15 | host = "" 16 | port = 6030 17 | dataSourceName = fmt.Sprintf("%s:%s@/tcp(%s:%d)/%s?interpolateParams=true", user, password, host, port, "") 18 | ) 19 | 20 | func main() { 21 | db, err := sql.Open(driverName, dataSourceName) 22 | if err != nil { 23 | panic(err) 24 | } 25 | defer func() { 26 | err = db.Close() 27 | if err != nil { 28 | panic(err) 29 | } 30 | }() 31 | _, err = db.Exec("create database if not exists test_json") 32 | if err != nil { 33 | panic(err) 34 | } 35 | _, err = db.Exec("drop table if exists test_json.tjson") 36 | if err != nil { 37 | panic(err) 38 | } 39 | _, err = db.Exec("create stable if not exists test_json.tjson(ts timestamp,v int )tags(t json)") 40 | if err != nil { 41 | panic(err) 42 | } 43 | _, err = db.Exec(`insert into test_json.tj_1 using test_json.tjson tags('{"a":1,"b":"b"}')values (now,1)`) 44 | if err != nil { 45 | panic(err) 46 | } 47 | _, err = db.Exec(`insert into test_json.tj_2 using test_json.tjson tags('{"a":1,"c":"c"}')values (now,1)`) 48 | if err != nil { 49 | panic(err) 50 | } 51 | _, err = db.Exec(`insert into test_json.tj_3 using test_json.tjson tags('null')values (now,1)`) 52 | if err != nil { 53 | panic(err) 54 | } 55 | rows, err := db.Query("select t from test_json.tjson") 56 | if err != nil { 57 | panic(err) 58 | } 59 | counter := 0 60 | for rows.Next() { 61 | var info []byte 62 | err := rows.Scan(&info) 63 | if err != nil { 64 | panic(err) 65 | } 66 | if info != nil && !json.Valid(info) { 67 | fmt.Println("invalid json ", string(info)) 68 | return 69 | } 70 | if info == nil { 71 | fmt.Println("null") 72 | } else { 73 | fmt.Printf("%s", string(info)) 74 | } 75 | counter += 1 76 | } 77 | fmt.Println(counter) 78 | //assert.Equal(t, 3, counter) 79 | } 80 | -------------------------------------------------------------------------------- /bench/valgrind/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | _ "github.com/taosdata/driver-go/v3/taosSql" 10 | ) 11 | 12 | var ( 13 | driverName = "taosSql" 14 | user = "root" 15 | password = "taosdata" 16 | host = "" 17 | port = 6030 18 | dataSourceName = fmt.Sprintf("%s:%s@/tcp(%s:%d)/%s?interpolateParams=true", user, password, host, port, "log") 19 | db *sql.DB 20 | ) 21 | 22 | func main() { 23 | var err error 24 | db, err = sql.Open(driverName, dataSourceName) 25 | if err != nil { 26 | log.Fatalf("error on: sql.open %s", err.Error()) 27 | } 28 | defer func(db *sql.DB) { 29 | err := db.Close() 30 | if err != nil { 31 | log.Fatalf("close db error:%s", err.Error()) 32 | } 33 | }(db) 34 | _, err = db.Exec("create database if not exists bench_test") 35 | if err != nil { 36 | log.Fatalf("create database banch_test error %s", err.Error()) 37 | } 38 | _, err = db.Exec("drop table IF EXISTS bench_test.valgrind") 39 | if err != nil { 40 | log.Fatalf("drop table bench_test.valgrind error %s", err.Error()) 41 | } 42 | _, err = db.Exec("create table bench_test.valgrind (ts timestamp ,value double)") 43 | if err != nil { 44 | log.Fatalf("create table bench_test.valgrind error %s", err.Error()) 45 | } 46 | insert() 47 | query() 48 | } 49 | 50 | func insert() { 51 | var err error 52 | for i := 0; i < 100; i++ { 53 | _, err = db.Exec(fmt.Sprintf("insert into bench_test.valgrind values ( now + %ds ,123.456)", i)) 54 | if err != nil { 55 | log.Fatalf("insert data error %s", err) 56 | } 57 | } 58 | } 59 | 60 | func query() { 61 | for i := 0; i < 100; i++ { 62 | rows, err := db.Query("select * from bench_test.test_select") 63 | if err != nil { 64 | log.Fatalf("select data error %s", err.Error()) 65 | } 66 | var t time.Time 67 | var s float64 68 | for rows.Next() { 69 | err := rows.Scan(&t, &s) 70 | if err != nil { 71 | log.Fatalf("scan error %s", err.Error()) 72 | } 73 | if s != 123.456 { 74 | log.Fatalf("result error expect 123.456 got %f", s) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /wrapper/field.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | import ( 8 | "bytes" 9 | "reflect" 10 | "unsafe" 11 | 12 | "github.com/taosdata/driver-go/v3/common" 13 | "github.com/taosdata/driver-go/v3/errors" 14 | ) 15 | 16 | type RowsHeader struct { 17 | ColNames []string 18 | ColTypes []uint8 19 | ColLength []int64 20 | Precisions []int64 21 | Scales []int64 22 | } 23 | 24 | func ReadColumn(result unsafe.Pointer, count int) (*RowsHeader, error) { 25 | if result == nil { 26 | return nil, &errors.TaosError{Code: 0xffff, ErrStr: "invalid result"} 27 | } 28 | rowsHeader := &RowsHeader{ 29 | ColNames: make([]string, count), 30 | ColTypes: make([]uint8, count), 31 | ColLength: make([]int64, count), 32 | Precisions: make([]int64, count), 33 | Scales: make([]int64, count), 34 | } 35 | pFields := TaosFetchFieldsE(result) 36 | for i := 0; i < count; i++ { 37 | field := *(*C.struct_TAOS_FIELD_E)(unsafe.Pointer(uintptr(pFields) + uintptr(C.sizeof_struct_TAOS_FIELD_E*C.int(i)))) 38 | buf := bytes.NewBufferString("") 39 | for _, c := range field.name { 40 | if c == 0 { 41 | break 42 | } 43 | buf.WriteByte(byte(c)) 44 | } 45 | rowsHeader.ColNames[i] = buf.String() 46 | rowsHeader.ColTypes[i] = (uint8)(field._type) 47 | rowsHeader.ColLength[i] = int64((uint32)(field.bytes)) 48 | rowsHeader.Precisions[i] = int64((uint8)(field.precision)) 49 | rowsHeader.Scales[i] = int64((uint8)(field.scale)) 50 | } 51 | return rowsHeader, nil 52 | } 53 | 54 | func (rh *RowsHeader) TypeDatabaseName(i int) string { 55 | return common.GetTypeName(int(rh.ColTypes[i])) 56 | } 57 | 58 | func (rh *RowsHeader) ScanType(i int) reflect.Type { 59 | t, exist := common.ColumnTypeMap[int(rh.ColTypes[i])] 60 | if !exist { 61 | return common.UnknownType 62 | } 63 | return t 64 | } 65 | 66 | func FetchLengths(res unsafe.Pointer, count int) []int { 67 | lengths := TaosFetchLengths(res) 68 | result := make([]int, count) 69 | for i := 0; i < count; i++ { 70 | result[i] = int(*(*C.int)(unsafe.Pointer(uintptr(lengths) + uintptr(C.sizeof_int*C.int(i))))) 71 | } 72 | return result 73 | } 74 | -------------------------------------------------------------------------------- /common/reqid.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "math/bits" 7 | "os" 8 | "sync/atomic" 9 | "time" 10 | "unsafe" 11 | 12 | "github.com/google/uuid" 13 | "github.com/taosdata/driver-go/v3/common/pointer" 14 | ) 15 | 16 | var tUUIDHashId int64 17 | var serialNo int64 18 | var pid int64 19 | 20 | func init() { 21 | var tUUID = uuid.New().String() 22 | tUUIDHashId = (int64(murmurHash32([]byte(tUUID), uint32(len(tUUID)))) & 0x07ff) << 52 23 | pid = (int64(os.Getpid()) & 0x0f) << 48 24 | } 25 | 26 | func GetReqID() int64 { 27 | ts := (time.Now().UnixNano() / 1e6) >> 8 28 | val := atomic.AddInt64(&serialNo, 1) 29 | return tUUIDHashId | pid | ((ts & 0x3ffffff) << 20) | (val & 0xfffff) 30 | } 31 | 32 | const ( 33 | c1 uint32 = 0xcc9e2d51 34 | c2 uint32 = 0x1b873593 35 | ) 36 | 37 | // MurmurHash32 returns the MurmurHash3 sum of data. 38 | func murmurHash32(data []byte, seed uint32) uint32 { 39 | h1 := seed 40 | 41 | nBlocks := len(data) / 4 42 | p := unsafe.Pointer(&data[0]) 43 | for i := 0; i < nBlocks; i++ { 44 | k1 := *(*uint32)(pointer.AddUintptr(p, uintptr(i*4))) 45 | 46 | k1 *= c1 47 | k1 = bits.RotateLeft32(k1, 15) 48 | k1 *= c2 49 | 50 | h1 ^= k1 51 | h1 = bits.RotateLeft32(h1, 13) 52 | h1 = h1*4 + h1 + 0xe6546b64 53 | } 54 | 55 | tail := data[nBlocks*4:] 56 | 57 | var k1 uint32 58 | switch len(tail) & 3 { 59 | case 3: 60 | k1 ^= uint32(tail[2]) << 16 61 | fallthrough 62 | case 2: 63 | k1 ^= uint32(tail[1]) << 8 64 | fallthrough 65 | case 1: 66 | k1 ^= uint32(tail[0]) 67 | k1 *= c1 68 | k1 = bits.RotateLeft32(k1, 15) 69 | k1 *= c2 70 | h1 ^= k1 71 | } 72 | 73 | h1 ^= uint32(len(data)) 74 | 75 | h1 ^= h1 >> 16 76 | h1 *= 0x85ebca6b 77 | h1 ^= h1 >> 13 78 | h1 *= 0xc2b2ae35 79 | h1 ^= h1 >> 16 80 | 81 | return h1 82 | } 83 | 84 | func GetReqIDFromCtx(ctx context.Context) (int64, error) { 85 | var reqIDValue int64 86 | var ok bool 87 | reqID := ctx.Value(ReqIDKey) 88 | if reqID != nil { 89 | reqIDValue, ok = reqID.(int64) 90 | if !ok { 91 | return 0, fmt.Errorf("invalid taos_req_id: %v, should be int64, got %T", reqID, reqID) 92 | } 93 | return reqIDValue, nil 94 | } 95 | return 0, nil 96 | } 97 | -------------------------------------------------------------------------------- /wrapper/handler/handlerpool.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "container/list" 5 | "sync" 6 | "unsafe" 7 | 8 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 9 | ) 10 | 11 | type AsyncResult struct { 12 | Res unsafe.Pointer 13 | N int 14 | } 15 | type Caller struct { 16 | QueryResult chan *AsyncResult 17 | FetchResult chan *AsyncResult 18 | } 19 | 20 | func NewCaller() *Caller { 21 | return &Caller{ 22 | QueryResult: make(chan *AsyncResult, 1), 23 | FetchResult: make(chan *AsyncResult, 1), 24 | } 25 | } 26 | 27 | func (c *Caller) QueryCall(res unsafe.Pointer, code int) { 28 | c.QueryResult <- &AsyncResult{ 29 | Res: res, 30 | N: code, 31 | } 32 | } 33 | 34 | func (c *Caller) FetchCall(res unsafe.Pointer, numOfRows int) { 35 | c.FetchResult <- &AsyncResult{ 36 | Res: res, 37 | N: numOfRows, 38 | } 39 | } 40 | 41 | type poolReq struct { 42 | idleHandler *Handler 43 | } 44 | 45 | type HandlerPool struct { 46 | mu sync.RWMutex 47 | count int 48 | handlers chan *Handler 49 | reqList *list.List 50 | } 51 | 52 | type Handler struct { 53 | Handler cgo.Handle 54 | Caller *Caller 55 | } 56 | 57 | func NewHandlerPool(count int) *HandlerPool { 58 | c := &HandlerPool{ 59 | count: count, 60 | handlers: make(chan *Handler, count), 61 | reqList: list.New(), 62 | } 63 | for i := 0; i < count; i++ { 64 | caller := NewCaller() 65 | c.handlers <- &Handler{ 66 | Handler: cgo.NewHandle(caller), 67 | Caller: caller, 68 | } 69 | } 70 | return c 71 | } 72 | 73 | func (c *HandlerPool) Get() *Handler { 74 | for { 75 | select { 76 | case wrapConn := <-c.handlers: 77 | return wrapConn 78 | default: 79 | c.mu.Lock() 80 | req := make(chan poolReq, 1) 81 | c.reqList.PushBack(req) 82 | c.mu.Unlock() 83 | ret := <-req 84 | return ret.idleHandler 85 | } 86 | } 87 | } 88 | 89 | func (c *HandlerPool) Put(handler *Handler) { 90 | c.mu.Lock() 91 | e := c.reqList.Front() 92 | if e != nil { 93 | req := e.Value.(chan poolReq) 94 | c.reqList.Remove(e) 95 | req <- poolReq{ 96 | idleHandler: handler, 97 | } 98 | c.mu.Unlock() 99 | return 100 | } 101 | c.handlers <- handler 102 | c.mu.Unlock() 103 | } 104 | -------------------------------------------------------------------------------- /taosRestful/cloud_test.go: -------------------------------------------------------------------------------- 1 | package taosRestful 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCloudRest(t *testing.T) { 14 | db := "go_test" 15 | endPoint := os.Getenv("TDENGINE_CLOUD_ENDPOINT") 16 | token := os.Getenv("TDENGINE_CLOUD_TOKEN") 17 | if endPoint == "" || token == "" { 18 | t.Skip("TDENGINE_CLOUD_TOKEN or TDENGINE_CLOUD_ENDPOINT is not set, skip cloud test") 19 | return 20 | } 21 | now := time.Now() 22 | tbname := fmt.Sprintf("rest_query_test_%d", now.UnixNano()) 23 | t.Log("table name:", tbname) 24 | dsn := fmt.Sprintf("https(%s:443)/%s?token=%s", endPoint, db, token) 25 | taos, err := sql.Open("taosRestful", dsn) 26 | if !assert.NoError(t, err) { 27 | return 28 | } 29 | defer func() { 30 | dropTableSql := fmt.Sprintf("drop table if exists %s", tbname) 31 | res, err := exec(taos, dropTableSql) 32 | assert.NoError(t, err) 33 | affected, err := res.RowsAffected() 34 | assert.NoError(t, err) 35 | assert.Equal(t, int64(0), affected) 36 | err = taos.Close() 37 | assert.NoError(t, err) 38 | }() 39 | createTableSql := fmt.Sprintf("create table if not exists %s (ts timestamp, c1 int, c2 int, c3 int)", tbname) 40 | res, err := exec(taos, createTableSql) 41 | assert.NoError(t, err) 42 | affected, err := res.RowsAffected() 43 | assert.NoError(t, err) 44 | assert.Equal(t, int64(0), affected) 45 | insertSql := fmt.Sprintf("insert into %s values (now, 1, 2, 3)", tbname) 46 | res, err = exec(taos, insertSql) 47 | assert.NoError(t, err) 48 | affected, err = res.RowsAffected() 49 | assert.NoError(t, err) 50 | assert.Equal(t, int64(1), affected) 51 | querySql := fmt.Sprintf("select * from %s", tbname) 52 | rows, err := taos.Query(querySql) 53 | assert.NoError(t, err) 54 | defer func() { 55 | err = rows.Close() 56 | assert.NoError(t, err) 57 | }() 58 | var ts time.Time 59 | var c1, c2, c3 int 60 | var rowCount int 61 | for rows.Next() { 62 | rowCount++ 63 | err = rows.Scan(&ts, &c1, &c2, &c3) 64 | assert.NoError(t, err) 65 | assert.Equal(t, 1, c1) 66 | assert.Equal(t, 2, c2) 67 | assert.Equal(t, 3, c3) 68 | t.Logf("ts: %s, c1: %d, c2: %d, c3: %d", ts.Format(time.RFC3339), c1, c2, c3) 69 | } 70 | assert.Equal(t, 1, rowCount) 71 | } 72 | -------------------------------------------------------------------------------- /ws/schemaless/config.go: -------------------------------------------------------------------------------- 1 | package schemaless 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | connAction = "conn" 9 | insertAction = "insert" 10 | ) 11 | 12 | type Config struct { 13 | url string 14 | chanLength uint 15 | user string 16 | password string 17 | db string 18 | readTimeout time.Duration 19 | writeTimeout time.Duration 20 | errorHandler func(error) 21 | enableCompression bool 22 | autoReconnect bool 23 | reconnectIntervalMs int 24 | reconnectRetryCount int 25 | } 26 | 27 | func NewConfig(url string, chanLength uint, opts ...func(*Config)) *Config { 28 | c := Config{url: url, chanLength: chanLength, reconnectRetryCount: 3, reconnectIntervalMs: 2000} 29 | for _, opt := range opts { 30 | opt(&c) 31 | } 32 | 33 | return &c 34 | } 35 | 36 | func SetUser(user string) func(*Config) { 37 | return func(c *Config) { 38 | c.user = user 39 | } 40 | } 41 | 42 | func SetPassword(password string) func(*Config) { 43 | return func(c *Config) { 44 | c.password = password 45 | } 46 | } 47 | 48 | func SetDb(db string) func(*Config) { 49 | return func(c *Config) { 50 | c.db = db 51 | } 52 | } 53 | 54 | func SetReadTimeout(readTimeout time.Duration) func(*Config) { 55 | return func(c *Config) { 56 | c.readTimeout = readTimeout 57 | } 58 | } 59 | 60 | func SetWriteTimeout(writeTimeout time.Duration) func(*Config) { 61 | return func(c *Config) { 62 | c.writeTimeout = writeTimeout 63 | } 64 | } 65 | 66 | func SetErrorHandler(errorHandler func(error)) func(*Config) { 67 | return func(c *Config) { 68 | c.errorHandler = errorHandler 69 | } 70 | } 71 | 72 | func SetEnableCompression(enableCompression bool) func(*Config) { 73 | return func(c *Config) { 74 | c.enableCompression = enableCompression 75 | } 76 | } 77 | 78 | func SetAutoReconnect(reconnect bool) func(*Config) { 79 | return func(c *Config) { 80 | c.autoReconnect = reconnect 81 | } 82 | } 83 | 84 | func SetReconnectIntervalMs(reconnectIntervalMs int) func(*Config) { 85 | return func(c *Config) { 86 | c.reconnectIntervalMs = reconnectIntervalMs 87 | } 88 | } 89 | 90 | func SetReconnectRetryCount(reconnectRetryCount int) func(*Config) { 91 | return func(c *Config) { 92 | c.reconnectRetryCount = reconnectRetryCount 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /taosSql/connection_test.go: -------------------------------------------------------------------------------- 1 | package taosSql 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/taosdata/driver-go/v3/common" 10 | ) 11 | 12 | // @author: xftan 13 | // @date: 2023/10/13 11:21 14 | // @description: test taos connection exec context 15 | func TestTaosConn_ExecContext(t *testing.T) { 16 | //nolint:staticcheck 17 | ctx := context.WithValue(context.Background(), common.ReqIDKey, common.GetReqID()) 18 | db, err := sql.Open("taosSql", dataSourceName) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | defer func() { 23 | err = db.Close() 24 | assert.NoError(t, err) 25 | }() 26 | defer func() { 27 | _, err = execContext(ctx, db, "drop database if exists test_connection") 28 | }() 29 | _, err = execContext(ctx, db, "create database if not exists test_connection") 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | _, err = execContext(ctx, db, "use test_connection") 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | _, err = execContext(ctx, db, "create stable if not exists meters (ts timestamp, current float, voltage int, phase float) tags (location binary(64), groupId int)") 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | _, err = execContext(ctx, db, "INSERT INTO d21001 USING meters TAGS ('California.SanFrancisco', 2) VALUES ('?', ?, ?, ?)", "2021-07-13 14:06:32.272", 10.2, 219, 0.32) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | rs, err := db.QueryContext(ctx, "select count(*) from meters") 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | defer func() { 50 | err = rs.Close() 51 | assert.NoError(t, err) 52 | }() 53 | rs.Next() 54 | var count int64 55 | if err = rs.Scan(&count); err != nil { 56 | t.Fatal(err) 57 | } 58 | if count != 1 { 59 | t.Fatal("result miss") 60 | } 61 | } 62 | 63 | func TestWrongReqID(t *testing.T) { 64 | //nolint:staticcheck 65 | ctx := context.WithValue(context.Background(), common.ReqIDKey, uint64(1234)) 66 | db, err := sql.Open("taosSql", dataSourceName) 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | defer func() { 71 | err = db.Close() 72 | assert.NoError(t, err) 73 | }() 74 | rs, err := db.QueryContext(ctx, "select 1") 75 | assert.Error(t, err) 76 | assert.Nil(t, rs) 77 | _, err = execContext(ctx, db, "create database if not exists test_wrong_req_id") 78 | assert.Error(t, err) 79 | } 80 | -------------------------------------------------------------------------------- /examples/varbinary/stmt/native/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/taosdata/driver-go/v3/af" 9 | "github.com/taosdata/driver-go/v3/common" 10 | "github.com/taosdata/driver-go/v3/common/param" 11 | ) 12 | 13 | func main() { 14 | host := "127.0.0.1" 15 | db, err := af.Open(host, "root", "taosdata", "", 0) 16 | if err != nil { 17 | log.Fatalln("Failed to connect to " + host + "; ErrMessage: " + err.Error()) 18 | } 19 | defer db.Close() 20 | // prepare database and table 21 | _, err = db.Exec("CREATE DATABASE IF NOT EXISTS example_stmt_varbinary_native") 22 | if err != nil { 23 | log.Fatalln("Failed to create database example_stmt_varbinary_native, ErrMessage: " + err.Error()) 24 | } 25 | _, err = db.Exec("USE example_stmt_varbinary_native") 26 | if err != nil { 27 | log.Fatalln("Failed to use database example_stmt_varbinary_native, ErrMessage: " + err.Error()) 28 | } 29 | _, err = db.Exec("CREATE TABLE IF NOT EXISTS ntb (ts TIMESTAMP, val VARBINARY(100))") 30 | if err != nil { 31 | log.Fatalln("Failed to create table ntb, ErrMessage: " + err.Error()) 32 | } 33 | // prepare statement 34 | sql := "INSERT INTO ntb VALUES (?,?)" 35 | stmt := db.Stmt() 36 | err = stmt.Prepare(sql) 37 | if err != nil { 38 | log.Fatalln("Failed to prepare sql, sql: " + sql + ", ErrMessage: " + err.Error()) 39 | } 40 | 41 | // bind column data 42 | current := time.Now() 43 | // "\x98f46e" 44 | varbinaryData := []byte{0x98, 0xf4, 0x6e} 45 | 46 | row := param.NewParam(2). 47 | AddTimestamp(current, common.PrecisionMilliSecond). 48 | AddVarBinary(varbinaryData) 49 | err = stmt.BindRow(row) 50 | if err != nil { 51 | log.Fatalln("Failed to bind params, ErrMessage: " + err.Error()) 52 | } 53 | 54 | // add batch 55 | err = stmt.AddBatch() 56 | if err != nil { 57 | log.Fatalln("Failed to add batch, ErrMessage: " + err.Error()) 58 | } 59 | // execute batch 60 | err = stmt.Execute() 61 | if err != nil { 62 | log.Fatalln("Failed to exec, ErrMessage: " + err.Error()) 63 | } 64 | // get affected rows 65 | affected := stmt.GetAffectedRows() 66 | // you can check exeResult here 67 | fmt.Printf("Successfully inserted %d rows.\n", affected) 68 | // close statement 69 | err = stmt.Close() 70 | if err != nil { 71 | log.Fatal("failed to close statement, err:", err) 72 | } 73 | // select * from example_stmt_varbinary_native.ntb 74 | } 75 | -------------------------------------------------------------------------------- /common/tmq/tmq.go: -------------------------------------------------------------------------------- 1 | package tmq 2 | 3 | import "fmt" 4 | 5 | type Meta struct { 6 | Type string `json:"type"` 7 | TableName string `json:"tableName"` 8 | TableType string `json:"tableType"` 9 | CreateList []*CreateItem `json:"createList"` 10 | Columns []*Column `json:"columns"` 11 | Using string `json:"using"` 12 | TagNum int `json:"tagNum"` 13 | Tags []*Tag `json:"tags"` 14 | TableNameList []string `json:"tableNameList"` 15 | AlterType int `json:"alterType"` 16 | ColName string `json:"colName"` 17 | ColNewName string `json:"colNewName"` 18 | ColType int `json:"colType"` 19 | ColLength int `json:"colLength"` 20 | ColValue string `json:"colValue"` 21 | ColValueNull bool `json:"colValueNull"` 22 | } 23 | 24 | type Tag struct { 25 | Name string `json:"name"` 26 | Type int `json:"type"` 27 | Value interface{} `json:"value"` 28 | } 29 | 30 | type Column struct { 31 | Name string `json:"name"` 32 | Type int `json:"type"` 33 | Length int `json:"length"` 34 | } 35 | 36 | type CreateItem struct { 37 | TableName string `json:"tableName"` 38 | Using string `json:"using"` 39 | TagNum int `json:"tagNum"` 40 | Tags []*Tag `json:"tags"` 41 | } 42 | 43 | type Offset int64 44 | 45 | const OffsetInvalid = Offset(-2147467247) 46 | 47 | func (o Offset) String() string { 48 | if o == OffsetInvalid { 49 | return "unset" 50 | } 51 | return fmt.Sprintf("%d", int64(o)) 52 | } 53 | 54 | func (o Offset) Valid() bool { 55 | if o < 0 && o != OffsetInvalid { 56 | return false 57 | } 58 | return true 59 | } 60 | 61 | type TopicPartition struct { 62 | Topic *string 63 | Partition int32 64 | Offset Offset 65 | Metadata *string 66 | Error error 67 | } 68 | 69 | func (p TopicPartition) String() string { 70 | topic := "" 71 | if p.Topic != nil { 72 | topic = *p.Topic 73 | } 74 | if p.Error != nil { 75 | return fmt.Sprintf("%s[%d]@%s(%s)", 76 | topic, p.Partition, p.Offset, p.Error) 77 | } 78 | return fmt.Sprintf("%s[%d]@%s", 79 | topic, p.Partition, p.Offset) 80 | } 81 | 82 | type Assignment struct { 83 | VGroupID int32 `json:"vgroup_id"` 84 | Offset int64 `json:"offset"` 85 | Begin int64 `json:"begin"` 86 | End int64 `json:"end"` 87 | } 88 | -------------------------------------------------------------------------------- /examples/geometry/stmt/native/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/taosdata/driver-go/v3/af" 9 | "github.com/taosdata/driver-go/v3/common" 10 | "github.com/taosdata/driver-go/v3/common/param" 11 | ) 12 | 13 | func main() { 14 | host := "127.0.0.1" 15 | db, err := af.Open(host, "root", "taosdata", "", 0) 16 | if err != nil { 17 | log.Fatalln("Failed to connect to " + host + "; ErrMessage: " + err.Error()) 18 | } 19 | defer db.Close() 20 | // prepare database and table 21 | _, err = db.Exec("CREATE DATABASE IF NOT EXISTS example_stmt_geometry_native") 22 | if err != nil { 23 | log.Fatalln("Failed to create database example_stmt_geometry_native, ErrMessage: " + err.Error()) 24 | } 25 | _, err = db.Exec("USE example_stmt_geometry_native") 26 | if err != nil { 27 | log.Fatalln("Failed to use database example_stmt_geometry_native, ErrMessage: " + err.Error()) 28 | } 29 | _, err = db.Exec("CREATE TABLE IF NOT EXISTS ntb (ts TIMESTAMP, val GEOMETRY(100))") 30 | if err != nil { 31 | log.Fatalln("Failed to create table ntb, ErrMessage: " + err.Error()) 32 | } 33 | // prepare statement 34 | sql := "INSERT INTO ntb VALUES (?,?)" 35 | stmt := db.Stmt() 36 | err = stmt.Prepare(sql) 37 | if err != nil { 38 | log.Fatalln("Failed to prepare sql, sql: " + sql + ", ErrMessage: " + err.Error()) 39 | } 40 | 41 | // bind column data 42 | current := time.Now() 43 | // point(100 100) 44 | geometryData := []byte{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40} 45 | 46 | row := param.NewParam(2). 47 | AddTimestamp(current, common.PrecisionMilliSecond). 48 | AddGeometry(geometryData) 49 | err = stmt.BindRow(row) 50 | if err != nil { 51 | log.Fatalln("Failed to bind params, ErrMessage: " + err.Error()) 52 | } 53 | 54 | // add batch 55 | err = stmt.AddBatch() 56 | if err != nil { 57 | log.Fatalln("Failed to add batch, ErrMessage: " + err.Error()) 58 | } 59 | // execute batch 60 | err = stmt.Execute() 61 | if err != nil { 62 | log.Fatalln("Failed to exec, ErrMessage: " + err.Error()) 63 | } 64 | // get affected rows 65 | affected := stmt.GetAffectedRows() 66 | // you can check exeResult here 67 | fmt.Printf("Successfully inserted %d rows.\n", affected) 68 | // close statement 69 | err = stmt.Close() 70 | if err != nil { 71 | log.Fatal("failed to close statement, err:", err) 72 | } 73 | // select * from example_stmt_geometry_native.ntb 74 | } 75 | -------------------------------------------------------------------------------- /taosSql/connector.go: -------------------------------------------------------------------------------- 1 | package taosSql 2 | 3 | import ( 4 | "context" 5 | "database/sql/driver" 6 | "runtime" 7 | "sync" 8 | 9 | "github.com/taosdata/driver-go/v3/common" 10 | "github.com/taosdata/driver-go/v3/errors" 11 | "github.com/taosdata/driver-go/v3/wrapper" 12 | "github.com/taosdata/driver-go/v3/wrapper/handler" 13 | "github.com/taosdata/driver-go/v3/wrapper/thread" 14 | ) 15 | 16 | type connector struct { 17 | cfg *Config 18 | } 19 | 20 | var once = sync.Once{} 21 | 22 | // Connect implements driver.Connector interface. 23 | // Connect returns a connection to the database. 24 | func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { 25 | onceInitLock.Do(func() { 26 | threads := c.cfg.CgoThread 27 | if threads <= 0 { 28 | threads = runtime.NumCPU() 29 | } 30 | locker = thread.NewLocker(threads) 31 | }) 32 | onceInitHandlerPool.Do(func() { 33 | poolSize := c.cfg.CgoAsyncHandlerPoolSize 34 | if poolSize <= 0 { 35 | poolSize = 10000 36 | } 37 | asyncHandlerPool = handler.NewHandlerPool(poolSize) 38 | }) 39 | var err error 40 | tc := newTaosConn(c.cfg) 41 | if c.cfg.Net == "cfg" && len(c.cfg.ConfigPath) > 0 { 42 | once.Do(func() { 43 | locker.Lock() 44 | code := wrapper.TaosOptions(common.TSDB_OPTION_CONFIGDIR, c.cfg.ConfigPath) 45 | locker.Unlock() 46 | if code != 0 { 47 | err = errors.NewError(code, wrapper.TaosErrorStr(nil)) 48 | } 49 | }) 50 | } 51 | if err != nil { 52 | return nil, err 53 | } 54 | // Connect to Server 55 | if len(tc.cfg.User) == 0 { 56 | tc.cfg.User = common.DefaultUser 57 | } 58 | if len(tc.cfg.Passwd) == 0 { 59 | tc.cfg.Passwd = common.DefaultPassword 60 | } 61 | locker.Lock() 62 | err = wrapper.TaosSetConfig(tc.cfg.Params) 63 | locker.Unlock() 64 | if err != nil { 65 | return nil, err 66 | } 67 | locker.Lock() 68 | taos, err := wrapper.TaosConnect(tc.cfg.Addr, tc.cfg.User, tc.cfg.Passwd, tc.cfg.DbName, tc.cfg.Port) 69 | locker.Unlock() 70 | if err != nil { 71 | return nil, err 72 | } 73 | if tc.timezoneStr != "" { 74 | code := wrapper.TaosOptionsConnection(taos, common.TSDB_OPTION_CONNECTION_TIMEZONE, &tc.timezoneStr) 75 | if code != 0 { 76 | err = errors.NewError(code, wrapper.TaosErrorStr(nil)) 77 | wrapper.TaosClose(taos) 78 | return nil, err 79 | } 80 | } 81 | tc.taos = taos 82 | return tc, nil 83 | } 84 | 85 | // Driver implements driver.Connector interface. 86 | // Driver returns &TDengineDriver{}. 87 | func (c *connector) Driver() driver.Driver { 88 | return &TDengineDriver{} 89 | } 90 | -------------------------------------------------------------------------------- /examples/tmq/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/taosdata/driver-go/v3/af" 8 | "github.com/taosdata/driver-go/v3/af/tmq" 9 | tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" 10 | ) 11 | 12 | func main() { 13 | db, err := af.Open("", "root", "taosdata", "", 0) 14 | if err != nil { 15 | panic(err) 16 | } 17 | defer db.Close() 18 | _, err = db.Exec("create database if not exists example_tmq WAL_RETENTION_PERIOD 86400") 19 | if err != nil { 20 | panic(err) 21 | } 22 | _, err = db.Exec("create topic if not exists example_tmq_topic as DATABASE example_tmq") 23 | if err != nil { 24 | panic(err) 25 | } 26 | if err != nil { 27 | panic(err) 28 | } 29 | consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ 30 | "group.id": "test", 31 | "auto.offset.reset": "earliest", 32 | "td.connect.ip": "127.0.0.1", 33 | "td.connect.user": "root", 34 | "td.connect.pass": "taosdata", 35 | "td.connect.port": "6030", 36 | "client.id": "test_tmq_client", 37 | "enable.auto.commit": "false", 38 | "msg.with.table.name": "true", 39 | }) 40 | if err != nil { 41 | panic(err) 42 | } 43 | err = consumer.Subscribe("example_tmq_topic", nil) 44 | if err != nil { 45 | panic(err) 46 | } 47 | _, err = db.Exec("create table example_tmq.t1 (ts timestamp,v int)") 48 | if err != nil { 49 | panic(err) 50 | } 51 | _, err = db.Exec("insert into example_tmq.t1 values(now,1)") 52 | if err != nil { 53 | panic(err) 54 | } 55 | for i := 0; i < 5; i++ { 56 | ev := consumer.Poll(500) 57 | if ev != nil { 58 | switch e := ev.(type) { 59 | case *tmqcommon.DataMessage: 60 | fmt.Printf("get message:%v\n", e) 61 | case tmqcommon.Error: 62 | fmt.Fprintf(os.Stderr, "%% Error: %v: %v\n", e.Code(), e) 63 | panic(e) 64 | } 65 | consumer.Commit() 66 | } 67 | } 68 | partitions, err := consumer.Assignment() 69 | if err != nil { 70 | panic(err) 71 | } 72 | for i := 0; i < len(partitions); i++ { 73 | fmt.Println(partitions[i]) 74 | err = consumer.Seek(tmqcommon.TopicPartition{ 75 | Topic: partitions[i].Topic, 76 | Partition: partitions[i].Partition, 77 | Offset: 0, 78 | }, 0) 79 | if err != nil { 80 | panic(err) 81 | } 82 | } 83 | 84 | partitions, err = consumer.Assignment() 85 | if err != nil { 86 | panic(err) 87 | } 88 | for i := 0; i < len(partitions); i++ { 89 | fmt.Println(partitions[i]) 90 | } 91 | 92 | err = consumer.Close() 93 | if err != nil { 94 | panic(err) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /.github/workflows/taosadapter.toml: -------------------------------------------------------------------------------- 1 | debug = true 2 | taosConfigDir = "" 3 | port = 6041 4 | logLevel = "info" 5 | httpCodeServerError = false 6 | SMLAutoCreateDB = false 7 | 8 | [cors] 9 | allowAllOrigins = true 10 | 11 | #[pool] 12 | #maxConnect = 0 13 | #maxIdle = 0 14 | #idleTimeout = 0 15 | 16 | [ssl] 17 | enable = false 18 | certFile = "" 19 | keyFile = "" 20 | 21 | [log] 22 | #path = "/var/log/taos" 23 | rotationCount = 30 24 | rotationTime = "24h" 25 | rotationSize = "1GB" 26 | enableRecordHttpSql = false 27 | sqlRotationCount = 2 28 | sqlRotationTime = "24h" 29 | sqlRotationSize = "1GB" 30 | 31 | [monitor] 32 | disable = true 33 | collectDuration = "3s" 34 | disableCollectClientIP = true 35 | incgroup = false 36 | pauseQueryMemoryThreshold = 70 37 | pauseAllMemoryThreshold = 80 38 | identity = "" 39 | writeToTD = false 40 | user = "root" 41 | password = "taosdata" 42 | writeInterval = "30s" 43 | 44 | [uploadKeeper] 45 | enable = false 46 | url = "http://127.0.0.1:6043/adapter_report" 47 | interval = "15s" 48 | timeout = "5s" 49 | retryTimes = 3 50 | retryInterval = "5s" 51 | 52 | [opentsdb] 53 | enable = true 54 | 55 | [influxdb] 56 | enable = true 57 | 58 | [statsd] 59 | enable = false 60 | port = 6044 61 | db = "statsd" 62 | user = "root" 63 | password = "taosdata" 64 | worker = 10 65 | gatherInterval = "5s" 66 | protocol = "udp" 67 | maxTCPConnections = 250 68 | tcpKeepAlive = false 69 | allowPendingMessages = 50000 70 | deleteCounters = true 71 | deleteGauges = true 72 | deleteSets = true 73 | deleteTimings = true 74 | 75 | [collectd] 76 | enable = false 77 | port = 6045 78 | db = "collectd" 79 | user = "root" 80 | password = "taosdata" 81 | worker = 10 82 | 83 | 84 | [opentsdb_telnet] 85 | enable = false 86 | maxTCPConnections = 250 87 | tcpKeepAlive = false 88 | dbs = ["opentsdb_telnet", "collectd", "icinga2", "tcollector"] 89 | ports = [6046, 6047, 6048, 6049] 90 | user = "root" 91 | password = "taosdata" 92 | batchSize = 1 93 | flushInterval = "0s" 94 | 95 | [node_exporter] 96 | enable = false 97 | db = "node_exporter" 98 | user = "root" 99 | password = "taosdata" 100 | urls = ["http://localhost:9100"] 101 | responseTimeout = "5s" 102 | httpUsername = "" 103 | httpPassword = "" 104 | httpBearerTokenString = "" 105 | caCertFile = "" 106 | certFile = "" 107 | keyFile = "" 108 | insecureSkipVerify = true 109 | gatherDuration = "5s" 110 | 111 | [prometheus] 112 | enable = true 113 | 114 | [tmq] 115 | releaseIntervalMultiplierForAutocommit = 2 -------------------------------------------------------------------------------- /common/sql.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "database/sql/driver" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func InterpolateParams(query string, args []driver.NamedValue) (string, error) { 13 | // Number of ? should be same to len(args) 14 | if strings.Count(query, "?") != len(args) { 15 | return "", driver.ErrSkip 16 | } 17 | buf := &strings.Builder{} 18 | argPos := 0 19 | 20 | for i := 0; i < len(query); i++ { 21 | q := strings.IndexByte(query[i:], '?') 22 | if q == -1 { 23 | buf.WriteString(query[i:]) 24 | break 25 | } 26 | buf.WriteString(query[i : i+q]) 27 | i += q 28 | 29 | arg := args[argPos].Value 30 | argPos++ 31 | 32 | if arg == nil { 33 | buf.WriteString("NULL") 34 | continue 35 | } 36 | switch v := arg.(type) { 37 | case int8: 38 | buf.WriteString(strconv.FormatInt(int64(v), 10)) 39 | case int16: 40 | buf.WriteString(strconv.FormatInt(int64(v), 10)) 41 | case int32: 42 | buf.WriteString(strconv.FormatInt(int64(v), 10)) 43 | case int64: 44 | buf.WriteString(strconv.FormatInt(v, 10)) 45 | case uint8: 46 | buf.WriteString(strconv.FormatUint(uint64(v), 10)) 47 | case uint16: 48 | buf.WriteString(strconv.FormatUint(uint64(v), 10)) 49 | case uint32: 50 | buf.WriteString(strconv.FormatUint(uint64(v), 10)) 51 | case uint64: 52 | buf.WriteString(strconv.FormatUint(v, 10)) 53 | case float32: 54 | fmt.Fprintf(buf, "%f", v) 55 | case float64: 56 | fmt.Fprintf(buf, "%f", v) 57 | case int: 58 | buf.WriteString(strconv.Itoa(v)) 59 | case uint: 60 | buf.WriteString(strconv.FormatUint(uint64(v), 10)) 61 | case bool: 62 | if v { 63 | buf.WriteByte('1') 64 | } else { 65 | buf.WriteByte('0') 66 | } 67 | case time.Time: 68 | t := v.Format(time.RFC3339Nano) 69 | buf.WriteByte('\'') 70 | buf.WriteString(t) 71 | buf.WriteByte('\'') 72 | case []byte: 73 | buf.Write(v) 74 | case string: 75 | buf.WriteString(v) 76 | default: 77 | return "", driver.ErrSkip 78 | } 79 | if buf.Len() > MaxTaosSqlLen { 80 | return "", errors.New("sql statement exceeds the maximum length") 81 | } 82 | } 83 | if argPos != len(args) { 84 | return "", driver.ErrSkip 85 | } 86 | return buf.String(), nil 87 | } 88 | 89 | func ValueArgsToNamedValueArgs(args []driver.Value) (values []driver.NamedValue) { 90 | values = make([]driver.NamedValue, len(args)) 91 | for i, arg := range args { 92 | values[i] = driver.NamedValue{ 93 | Ordinal: i + 1, 94 | Value: arg, 95 | } 96 | } 97 | return 98 | } 99 | -------------------------------------------------------------------------------- /wrapper/cgo/handle_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cgo 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | // @author: xftan 15 | // @date: 2022/1/27 17:21 16 | // @description: test cgo handler 17 | func TestHandle(t *testing.T) { 18 | v := 42 19 | 20 | tests := []struct { 21 | v1 interface{} 22 | v2 interface{} 23 | }{ 24 | {v1: v, v2: v}, 25 | {v1: &v, v2: &v}, 26 | {v1: nil, v2: nil}, 27 | } 28 | 29 | for _, tt := range tests { 30 | h1 := NewHandle(tt.v1) 31 | h2 := NewHandle(tt.v2) 32 | 33 | if uintptr(h1) == 0 || uintptr(h2) == 0 { 34 | t.Fatalf("NewHandle returns zero") 35 | } 36 | 37 | if uintptr(h1) == uintptr(h2) { 38 | t.Fatalf("Duplicated Go values should have different handles, but got equal") 39 | } 40 | 41 | h1v := h1.Value() 42 | h2v := h2.Value() 43 | if !reflect.DeepEqual(h1v, h2v) || !reflect.DeepEqual(h1v, tt.v1) { 44 | t.Fatalf("Value of a Handle got wrong, got %+v %+v, want %+v", h1v, h2v, tt.v1) 45 | } 46 | 47 | h1.Delete() 48 | h2.Delete() 49 | } 50 | 51 | siz := 0 52 | handles.Range(func(k, v interface{}) bool { 53 | siz++ 54 | return true 55 | }) 56 | if siz != 0 { 57 | t.Fatalf("handles are not cleared, got %d, want %d", siz, 0) 58 | } 59 | } 60 | 61 | func TestPointer(t *testing.T) { 62 | v := 42 63 | h := NewHandle(&v) 64 | p := h.Pointer() 65 | assert.Equal(t, *(*Handle)(p), h) 66 | h.Delete() 67 | defer func() { 68 | if r := recover(); r != nil { 69 | return 70 | } 71 | t.Fatalf("Pointer should panic") 72 | }() 73 | h.Pointer() 74 | } 75 | 76 | func TestInvalidValue(t *testing.T) { 77 | v := 42 78 | h := NewHandle(&v) 79 | h.Delete() 80 | defer func() { 81 | if r := recover(); r != nil { 82 | return 83 | } 84 | t.Fatalf("Value should panic") 85 | }() 86 | h.Value() 87 | } 88 | 89 | func BenchmarkHandle(b *testing.B) { 90 | b.Run("non-concurrent", func(b *testing.B) { 91 | for i := 0; i < b.N; i++ { 92 | h := NewHandle(i) 93 | _ = h.Value() 94 | h.Delete() 95 | } 96 | }) 97 | b.Run("concurrent", func(b *testing.B) { 98 | b.RunParallel(func(pb *testing.PB) { 99 | var v int 100 | for pb.Next() { 101 | h := NewHandle(v) 102 | _ = h.Value() 103 | h.Delete() 104 | } 105 | }) 106 | }) 107 | } 108 | -------------------------------------------------------------------------------- /wrapper/row.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | import ( 8 | "database/sql/driver" 9 | "unsafe" 10 | 11 | "github.com/taosdata/driver-go/v3/common" 12 | "github.com/taosdata/driver-go/v3/common/pointer" 13 | ) 14 | 15 | const ( 16 | PointerSize = unsafe.Sizeof(uintptr(1)) 17 | ) 18 | 19 | type FormatTimeFunc func(ts int64, precision int) driver.Value 20 | 21 | func FetchRow(row unsafe.Pointer, offset int, colType uint8, length int, arg ...interface{}) driver.Value { 22 | base := *(**C.void)(pointer.AddUintptr(row, uintptr(offset)*PointerSize)) 23 | p := unsafe.Pointer(base) 24 | if p == nil { 25 | return nil 26 | } 27 | switch colType { 28 | case C.TSDB_DATA_TYPE_BOOL: 29 | if v := *((*byte)(p)); v != 0 { 30 | return true 31 | } 32 | return false 33 | case C.TSDB_DATA_TYPE_TINYINT: 34 | return *((*int8)(p)) 35 | case C.TSDB_DATA_TYPE_SMALLINT: 36 | return *((*int16)(p)) 37 | case C.TSDB_DATA_TYPE_INT: 38 | return *((*int32)(p)) 39 | case C.TSDB_DATA_TYPE_BIGINT: 40 | return *((*int64)(p)) 41 | case C.TSDB_DATA_TYPE_UTINYINT: 42 | return *((*uint8)(p)) 43 | case C.TSDB_DATA_TYPE_USMALLINT: 44 | return *((*uint16)(p)) 45 | case C.TSDB_DATA_TYPE_UINT: 46 | return *((*uint32)(p)) 47 | case C.TSDB_DATA_TYPE_UBIGINT: 48 | return *((*uint64)(p)) 49 | case C.TSDB_DATA_TYPE_FLOAT: 50 | return *((*float32)(p)) 51 | case C.TSDB_DATA_TYPE_DOUBLE: 52 | return *((*float64)(p)) 53 | case C.TSDB_DATA_TYPE_BINARY, C.TSDB_DATA_TYPE_NCHAR: 54 | data := make([]byte, length) 55 | for i := 0; i < length; i++ { 56 | data[i] = *((*byte)(pointer.AddUintptr(p, uintptr(i)))) 57 | } 58 | return string(data) 59 | case C.TSDB_DATA_TYPE_DECIMAL64, C.TSDB_DATA_TYPE_DECIMAL: 60 | data := make([]byte, 0, length) 61 | var b byte 62 | for i := 0; i < length; i++ { 63 | b = *((*byte)(pointer.AddUintptr(p, uintptr(i)))) 64 | if b == 0 { 65 | break 66 | } 67 | data = append(data, b) 68 | } 69 | return string(data) 70 | case C.TSDB_DATA_TYPE_TIMESTAMP: 71 | if len(arg) == 1 { 72 | return common.TimestampConvertToTime(*((*int64)(p)), arg[0].(int)) 73 | } else if len(arg) == 2 { 74 | return arg[1].(FormatTimeFunc)(*((*int64)(p)), arg[0].(int)) 75 | } 76 | panic("convertTime error") 77 | case C.TSDB_DATA_TYPE_JSON, C.TSDB_DATA_TYPE_VARBINARY, C.TSDB_DATA_TYPE_GEOMETRY, C.TSDB_DATA_TYPE_BLOB: 78 | data := make([]byte, length) 79 | for i := 0; i < length; i++ { 80 | data[i] = *((*byte)(pointer.AddUintptr(p, uintptr(i)))) 81 | } 82 | return data 83 | default: 84 | return nil 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /ws/schemaless/cloud_test.go: -------------------------------------------------------------------------------- 1 | package schemaless 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestCloudSchemaless(t *testing.T) { 11 | db := "go_test" 12 | endPoint := os.Getenv("TDENGINE_CLOUD_ENDPOINT") 13 | token := os.Getenv("TDENGINE_CLOUD_TOKEN") 14 | if endPoint == "" || token == "" { 15 | t.Skip("TDENGINE_CLOUD_TOKEN or TDENGINE_CLOUD_ENDPOINT is not set, skip cloud test") 16 | return 17 | } 18 | cases := []struct { 19 | name string 20 | protocol int 21 | precision string 22 | data string 23 | ttl int 24 | code int 25 | }{ 26 | { 27 | name: "influxdb", 28 | protocol: InfluxDBLineProtocol, 29 | precision: "ms", 30 | data: "measurement,host=host1 field1=2i,field2=2.0 1577837300000\n" + 31 | "measurement,host=host1 field1=2i,field2=2.0 1577837400000\n" + 32 | "measurement,host=host1 field1=2i,field2=2.0 1577837500000\n" + 33 | "measurement,host=host1 field1=2i,field2=2.0 1577837600000", 34 | ttl: 1000, 35 | }, 36 | { 37 | name: "opentsdb_telnet", 38 | protocol: OpenTSDBTelnetLineProtocol, 39 | precision: "ms", 40 | data: "meters.current 1648432611249 10.3 location=California.SanFrancisco group=2\n" + 41 | "meters.current 1648432611250 12.6 location=California.SanFrancisco group=2\n" + 42 | "meters.current 1648432611251 10.8 location=California.LosAngeles group=3\n" + 43 | "meters.current 1648432611252 11.3 location=California.LosAngeles group=3\n", 44 | ttl: 1000, 45 | }, 46 | { 47 | name: "opentsdb_json", 48 | protocol: OpenTSDBJsonFormatProtocol, 49 | precision: "ms", 50 | data: "[{\"metric\": \"meters.voltage\", \"timestamp\": 1648432611249, \"value\": 219, \"tags\": " + 51 | "{\"location\": \"California.LosAngeles\", \"groupid\": 1 } }, {\"metric\": \"meters.voltage\", " + 52 | "\"timestamp\": 1648432611250, \"value\": 221, \"tags\": {\"location\": \"California.LosAngeles\", " + 53 | "\"groupid\": 1 } }]", 54 | ttl: 100, 55 | }, 56 | } 57 | url := fmt.Sprintf("wss://%s?token=%s", endPoint, token) 58 | s, err := NewSchemaless(NewConfig(url, 1, 59 | SetDb(db), 60 | SetReadTimeout(10*time.Second), 61 | SetWriteTimeout(10*time.Second), 62 | SetUser("root"), 63 | SetPassword("taosdata"), 64 | SetEnableCompression(true), 65 | )) 66 | if err != nil { 67 | t.Fatal(err) 68 | } 69 | defer s.Close() 70 | 71 | for _, c := range cases { 72 | t.Run(c.name, func(t *testing.T) { 73 | if err := s.Insert(c.data, c.protocol, c.precision, c.ttl, 0); err != nil { 74 | t.Fatal(err) 75 | } 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /wrapper/block.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | #include 8 | */ 9 | import "C" 10 | import ( 11 | "unsafe" 12 | ) 13 | 14 | // TaosFetchRawBlock int taos_fetch_raw_block(TAOS_RES *res, int* numOfRows, void** pData); 15 | func TaosFetchRawBlock(result unsafe.Pointer) (int, int, unsafe.Pointer) { 16 | var cSize int 17 | size := unsafe.Pointer(&cSize) 18 | var block unsafe.Pointer 19 | errCode := int(C.taos_fetch_raw_block(result, (*C.int)(size), &block)) 20 | return cSize, errCode, block 21 | } 22 | 23 | // TaosWriteRawBlock DLL_EXPORT int taos_write_raw_block(TAOS *taos, int numOfRows, char *pData, const char* tbname); 24 | func TaosWriteRawBlock(conn unsafe.Pointer, numOfRows int, pData unsafe.Pointer, tableName string) int { 25 | cStr := C.CString(tableName) 26 | defer C.free(unsafe.Pointer(cStr)) 27 | return int(C.taos_write_raw_block(conn, (C.int)(numOfRows), (*C.char)(pData), cStr)) 28 | } 29 | 30 | // TaosWriteRawBlockWithFields DLL_EXPORT int taos_write_raw_block_with_fields(TAOS* taos, int rows, char* pData, const char* tbname, TAOS_FIELD *fields, int numFields); 31 | func TaosWriteRawBlockWithFields(conn unsafe.Pointer, numOfRows int, pData unsafe.Pointer, tableName string, fields unsafe.Pointer, numFields int) int { 32 | cStr := C.CString(tableName) 33 | defer C.free(unsafe.Pointer(cStr)) 34 | return int(C.taos_write_raw_block_with_fields(conn, (C.int)(numOfRows), (*C.char)(pData), cStr, (*C.struct_taosField)(fields), (C.int)(numFields))) 35 | } 36 | 37 | // DLL_EXPORT int taos_write_raw_block_with_reqid(TAOS *taos, int numOfRows, char *pData, const char *tbname, int64_t reqid); 38 | func TaosWriteRawBlockWithReqID(conn unsafe.Pointer, numOfRows int, pData unsafe.Pointer, tableName string, reqID int64) int { 39 | cStr := C.CString(tableName) 40 | defer C.free(unsafe.Pointer(cStr)) 41 | return int(C.taos_write_raw_block_with_reqid(conn, (C.int)(numOfRows), (*C.char)(pData), cStr, (C.int64_t)(reqID))) 42 | } 43 | 44 | // DLL_EXPORT int taos_write_raw_block_with_fields_with_reqid(TAOS *taos, int rows, char *pData, const char *tbname,TAOS_FIELD *fields, int numFields, int64_t reqid); 45 | func TaosWriteRawBlockWithFieldsWithReqID(conn unsafe.Pointer, numOfRows int, pData unsafe.Pointer, tableName string, fields unsafe.Pointer, numFields int, reqID int64) int { 46 | cStr := C.CString(tableName) 47 | defer C.free(unsafe.Pointer(cStr)) 48 | return int(C.taos_write_raw_block_with_fields_with_reqid(conn, (C.int)(numOfRows), (*C.char)(pData), cStr, (*C.struct_taosField)(fields), (C.int)(numFields), (C.int64_t)(reqID))) 49 | } 50 | -------------------------------------------------------------------------------- /common/stmt/field.go: -------------------------------------------------------------------------------- 1 | package stmt 2 | 3 | import ( 4 | "database/sql/driver" 5 | "fmt" 6 | 7 | "github.com/taosdata/driver-go/v3/common" 8 | "github.com/taosdata/driver-go/v3/types" 9 | ) 10 | 11 | type StmtField struct { 12 | Name string `json:"name"` 13 | FieldType int8 `json:"field_type"` 14 | Precision uint8 `json:"precision"` 15 | Scale uint8 `json:"scale"` 16 | Bytes int32 `json:"bytes"` 17 | } 18 | 19 | func (s *StmtField) GetType() (*types.ColumnType, error) { 20 | switch s.FieldType { 21 | case common.TSDB_DATA_TYPE_BOOL: 22 | return &types.ColumnType{Type: types.TaosBoolType}, nil 23 | case common.TSDB_DATA_TYPE_TINYINT: 24 | return &types.ColumnType{Type: types.TaosTinyintType}, nil 25 | case common.TSDB_DATA_TYPE_SMALLINT: 26 | return &types.ColumnType{Type: types.TaosSmallintType}, nil 27 | case common.TSDB_DATA_TYPE_INT: 28 | return &types.ColumnType{Type: types.TaosIntType}, nil 29 | case common.TSDB_DATA_TYPE_BIGINT: 30 | return &types.ColumnType{Type: types.TaosBigintType}, nil 31 | case common.TSDB_DATA_TYPE_UTINYINT: 32 | return &types.ColumnType{Type: types.TaosUTinyintType}, nil 33 | case common.TSDB_DATA_TYPE_USMALLINT: 34 | return &types.ColumnType{Type: types.TaosUSmallintType}, nil 35 | case common.TSDB_DATA_TYPE_UINT: 36 | return &types.ColumnType{Type: types.TaosUIntType}, nil 37 | case common.TSDB_DATA_TYPE_UBIGINT: 38 | return &types.ColumnType{Type: types.TaosUBigintType}, nil 39 | case common.TSDB_DATA_TYPE_FLOAT: 40 | return &types.ColumnType{Type: types.TaosFloatType}, nil 41 | case common.TSDB_DATA_TYPE_DOUBLE: 42 | return &types.ColumnType{Type: types.TaosDoubleType}, nil 43 | case common.TSDB_DATA_TYPE_BINARY: 44 | return &types.ColumnType{Type: types.TaosBinaryType}, nil 45 | case common.TSDB_DATA_TYPE_VARBINARY: 46 | return &types.ColumnType{Type: types.TaosVarBinaryType}, nil 47 | case common.TSDB_DATA_TYPE_NCHAR: 48 | return &types.ColumnType{Type: types.TaosNcharType}, nil 49 | case common.TSDB_DATA_TYPE_TIMESTAMP: 50 | return &types.ColumnType{Type: types.TaosTimestampType}, nil 51 | case common.TSDB_DATA_TYPE_JSON: 52 | return &types.ColumnType{Type: types.TaosJsonType}, nil 53 | case common.TSDB_DATA_TYPE_GEOMETRY: 54 | return &types.ColumnType{Type: types.TaosGeometryType}, nil 55 | } 56 | return nil, fmt.Errorf("unsupported type: %d, name %s", s.FieldType, s.Name) 57 | } 58 | 59 | //revive:disable 60 | const ( 61 | TAOS_FIELD_COL = iota + 1 62 | TAOS_FIELD_TAG 63 | TAOS_FIELD_QUERY 64 | TAOS_FIELD_TBNAME 65 | ) 66 | 67 | //revive:enable 68 | 69 | type TaosStmt2BindData struct { 70 | TableName string 71 | Tags []driver.Value // row format 72 | Cols [][]driver.Value // column format 73 | } 74 | -------------------------------------------------------------------------------- /wrapper/cgo/handle.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cgo 6 | 7 | import ( 8 | "sync" 9 | "sync/atomic" 10 | "unsafe" 11 | ) 12 | 13 | // Handle provides a way to pass values that contain Go pointers 14 | // (pointers to memory allocated by Go) between Go and C without 15 | // breaking the cgo pointer passing rules. A Handle is an integer 16 | // value that can represent any Go value. A Handle can be passed 17 | // through C and back to Go, and Go code can use the Handle to 18 | // retrieve the original Go value. 19 | // 20 | // The underlying type of Handle is guaranteed to fit in an integer type 21 | // that is large enough to hold the bit pattern of any pointer. The zero 22 | // value of a Handle is not valid, and thus is safe to use as a sentinel 23 | // in C APIs. 24 | 25 | type Handle uintptr 26 | 27 | // NewHandle returns a handle for a given value. 28 | // 29 | // The handle is valid until the program calls Delete on it. The handle 30 | // uses resources, and this package assumes that C code may hold on to 31 | // the handle, so a program must explicitly call Delete when the handle 32 | // is no longer needed. 33 | // 34 | // The intended use is to pass the returned handle to C code, which 35 | // passes it back to Go, which calls Value. 36 | func NewHandle(v interface{}) Handle { 37 | h := atomic.AddUintptr(&handleIdx, 1) 38 | if h == 0 { 39 | panic("runtime/cgo: ran out of handle space") 40 | } 41 | 42 | handles.Store(h, v) 43 | handle := Handle(h) 44 | handlePointers.Store(h, &handle) 45 | return handle 46 | } 47 | 48 | // Value returns the associated Go value for a valid handle. 49 | // 50 | // The method panics if the handle is invalid. 51 | func (h Handle) Value() interface{} { 52 | v, ok := handles.Load(uintptr(h)) 53 | if !ok { 54 | panic("runtime/cgo: misuse of an invalid Handle") 55 | } 56 | return v 57 | } 58 | 59 | func (h Handle) Pointer() unsafe.Pointer { 60 | p, ok := handlePointers.Load(uintptr(h)) 61 | if !ok { 62 | panic("runtime/cgo: misuse of an invalid Handle") 63 | } 64 | return unsafe.Pointer(p.(*Handle)) 65 | } 66 | 67 | // Delete invalidates a handle. This method should only be called once 68 | // the program no longer needs to pass the handle to C and the C code 69 | // no longer has a copy of the handle value. 70 | // 71 | // The method panics if the handle is invalid. 72 | func (h Handle) Delete() { 73 | handles.Delete(uintptr(h)) 74 | handlePointers.Delete(uintptr(h)) 75 | } 76 | 77 | var ( 78 | handles = sync.Map{} // map[Handle]interface{} 79 | handlePointers = sync.Map{} // map[Handle]*Handle 80 | handleIdx uintptr // atomic 81 | ) 82 | -------------------------------------------------------------------------------- /examples/stmtinsert/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/taosdata/driver-go/v3/af" 7 | "github.com/taosdata/driver-go/v3/common" 8 | "github.com/taosdata/driver-go/v3/common/param" 9 | ) 10 | 11 | func main() { 12 | db, err := af.Open("", "root", "taosdata", "", 0) 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer db.Close() 17 | _, err = db.Exec("create database if not exists example_stmt") 18 | if err != nil { 19 | panic(err) 20 | } 21 | _, err = db.Exec("create table if not exists example_stmt.tb1(ts timestamp," + 22 | "c1 bool," + 23 | "c2 tinyint," + 24 | "c3 smallint," + 25 | "c4 int," + 26 | "c5 bigint," + 27 | "c6 tinyint unsigned," + 28 | "c7 smallint unsigned," + 29 | "c8 int unsigned," + 30 | "c9 bigint unsigned," + 31 | "c10 float," + 32 | "c11 double," + 33 | "c12 binary(20)," + 34 | "c13 nchar(20)" + 35 | ")") 36 | if err != nil { 37 | panic(err) 38 | } 39 | stmt := db.InsertStmt() 40 | err = stmt.Prepare("insert into example_stmt.tb1 values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)") 41 | if err != nil { 42 | panic(err) 43 | } 44 | now := time.Now() 45 | params := make([]*param.Param, 14) 46 | params[0] = param.NewParam(2). 47 | AddTimestamp(now, common.PrecisionMilliSecond). 48 | AddTimestamp(now.Add(time.Second), common.PrecisionMilliSecond) 49 | params[1] = param.NewParam(2).AddBool(true).AddNull() 50 | params[2] = param.NewParam(2).AddTinyint(2).AddNull() 51 | params[3] = param.NewParam(2).AddSmallint(3).AddNull() 52 | params[4] = param.NewParam(2).AddInt(4).AddNull() 53 | params[5] = param.NewParam(2).AddBigint(5).AddNull() 54 | params[6] = param.NewParam(2).AddUTinyint(6).AddNull() 55 | params[7] = param.NewParam(2).AddUSmallint(7).AddNull() 56 | params[8] = param.NewParam(2).AddUInt(8).AddNull() 57 | params[9] = param.NewParam(2).AddUBigint(9).AddNull() 58 | params[10] = param.NewParam(2).AddFloat(10).AddNull() 59 | params[11] = param.NewParam(2).AddDouble(11).AddNull() 60 | params[12] = param.NewParam(2).AddBinary([]byte("binary")).AddNull() 61 | params[13] = param.NewParam(2).AddNchar("nchar").AddNull() 62 | 63 | paramTypes := param.NewColumnType(14). 64 | AddTimestamp(). 65 | AddBool(). 66 | AddTinyint(). 67 | AddSmallint(). 68 | AddInt(). 69 | AddBigint(). 70 | AddUTinyint(). 71 | AddUSmallint(). 72 | AddUInt(). 73 | AddUBigint(). 74 | AddFloat(). 75 | AddDouble(). 76 | AddBinary(6). 77 | AddNchar(5) 78 | err = stmt.BindParam(params, paramTypes) 79 | if err != nil { 80 | panic(err) 81 | } 82 | err = stmt.AddBatch() 83 | if err != nil { 84 | panic(err) 85 | } 86 | err = stmt.Execute() 87 | if err != nil { 88 | panic(err) 89 | } 90 | err = stmt.Close() 91 | if err != nil { 92 | panic(err) 93 | } 94 | // select * from example_stmt.tb1 95 | } 96 | -------------------------------------------------------------------------------- /common/tdversion/version.go: -------------------------------------------------------------------------------- 1 | package tdversion 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/gorilla/websocket" 9 | "github.com/hashicorp/go-version" 10 | "github.com/taosdata/driver-go/v3/errors" 11 | ) 12 | 13 | var MinimumVersion = version.Must(version.NewVersion("3.3.6.0")) 14 | 15 | type VersionMismatchError struct { 16 | CurrentVersion string 17 | MinimumVersion string 18 | } 19 | 20 | func (e *VersionMismatchError) Error() string { 21 | return fmt.Sprintf("Version mismatch. The minimum required TDengine version is %s.", e.MinimumVersion) 22 | } 23 | 24 | type UnknownVersionError struct { 25 | Version string 26 | } 27 | 28 | func (e *UnknownVersionError) Error() string { 29 | return fmt.Sprintf("Unknown TDengine version: %s.", e.Version) 30 | } 31 | 32 | func ParseVersion(v string) (*version.Version, error) { 33 | parts := strings.Split(v, ".") 34 | if len(parts) < 4 { 35 | return nil, &UnknownVersionError{Version: v} 36 | } 37 | v = strings.Join(parts[:4], ".") 38 | ver, err := version.NewVersion(v) 39 | if err != nil { 40 | return nil, &UnknownVersionError{Version: v} 41 | } 42 | return ver, nil 43 | } 44 | 45 | func CheckVersionCompatibility(ver string) error { 46 | currentVersion, err := ParseVersion(ver) 47 | if err != nil { 48 | return err 49 | } 50 | if currentVersion.LessThan(MinimumVersion) { 51 | return &VersionMismatchError{ 52 | CurrentVersion: currentVersion.String(), 53 | MinimumVersion: MinimumVersion.String(), 54 | } 55 | } 56 | return nil 57 | } 58 | 59 | var versionReq = []byte(`{"action": "version"}`) 60 | 61 | type VersionResp struct { 62 | Code int `json:"code"` 63 | Message string `json:"message"` 64 | Action string `json:"action"` 65 | Timing int `json:"timing"` 66 | Version string `json:"version"` 67 | } 68 | 69 | type WebSocketConn interface { 70 | WriteMessage(messageType int, data []byte) error 71 | ReadMessage() (messageType int, p []byte, err error) 72 | } 73 | 74 | func WSCheckVersion(conn WebSocketConn) error { 75 | if err := conn.WriteMessage(websocket.TextMessage, versionReq); err != nil { 76 | return err 77 | } 78 | mt, msg, err := conn.ReadMessage() 79 | if err != nil { 80 | return err 81 | } 82 | if mt != websocket.TextMessage { 83 | return fmt.Errorf("get version: response got wrong message type %d, message:%s", mt, msg) 84 | } 85 | var resp VersionResp 86 | if err = json.Unmarshal(msg, &resp); err != nil { 87 | return fmt.Errorf("get version: unmarshal json error, err:%s, message:%s", err, msg) 88 | } 89 | if resp.Code != 0 { 90 | return errors.NewError(resp.Code, resp.Message) 91 | } 92 | if resp.Action != "version" { 93 | return errors.NewError(-1, fmt.Sprintf("unexpected action: "+resp.Action)) 94 | } 95 | return CheckVersionCompatibility(resp.Version) 96 | } 97 | -------------------------------------------------------------------------------- /examples/geometry/query/ws/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | _ "github.com/taosdata/driver-go/v3/taosWS" 10 | ) 11 | 12 | func main() { 13 | var taosDSN = "root:taosdata@ws(localhost:6041)/" 14 | db, err := sql.Open("taosWS", taosDSN) 15 | if err != nil { 16 | log.Fatalln("Failed to connect to " + taosDSN + ", ErrMessage: " + err.Error()) 17 | } 18 | defer db.Close() 19 | // create database 20 | res, err := db.Exec("CREATE DATABASE IF NOT EXISTS example_query_geometry_ws") 21 | if err != nil { 22 | log.Fatalln("Failed to create database example_query_geometry_ws, ErrMessage: " + err.Error()) 23 | } 24 | rowsAffected, err := res.RowsAffected() 25 | if err != nil { 26 | log.Fatalln("Failed to get create database rowsAffected, ErrMessage: " + err.Error()) 27 | } 28 | // you can check rowsAffected here 29 | fmt.Println("Create database example_query_geometry_ws successfully, rowsAffected: ", rowsAffected) 30 | // create table 31 | res, err = db.Exec("CREATE TABLE IF NOT EXISTS example_query_geometry_ws.ntb (ts TIMESTAMP, val GEOMETRY(100))") 32 | if err != nil { 33 | log.Fatalln("Failed to create table example_query_geometry_ws, ErrMessage: " + err.Error()) 34 | } 35 | rowsAffected, err = res.RowsAffected() 36 | if err != nil { 37 | log.Fatalln("Failed to get create table rowsAffected, ErrMessage: " + err.Error()) 38 | } 39 | // you can check rowsAffected here 40 | fmt.Println("Create table example_query_geometry_ws.ntb successfully, rowsAffected:", rowsAffected) 41 | // insert data, please make sure the database and table are created before 42 | insertQuery := "INSERT INTO example_query_geometry_ws.ntb VALUES (now, 'POINT(100 100)')" 43 | res, err = db.Exec(insertQuery) 44 | if err != nil { 45 | log.Fatalf("Failed to insert data to example_query_geometry_ws.ntb, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 46 | } 47 | rowsAffected, err = res.RowsAffected() 48 | if err != nil { 49 | log.Fatalf("Failed to get insert rowsAffected, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 50 | } 51 | // you can check affectedRows here 52 | fmt.Printf("Successfully inserted %d rows to example_query_geometry_ws.ntb.\n", rowsAffected) 53 | // query data 54 | sql := "SELECT ts, val FROM example_query_geometry_ws.ntb" 55 | rows, err := db.Query(sql) 56 | if err != nil { 57 | log.Fatalf("Failed to query data from example_query_geometry_ws.ntb, sql: %s, ErrMessage: %s\n", sql, err.Error()) 58 | } 59 | for rows.Next() { 60 | // Add your data processing logic here 61 | var ( 62 | ts time.Time 63 | val []byte 64 | ) 65 | err = rows.Scan(&ts, &val) 66 | if err != nil { 67 | log.Fatalf("Failed to scan data, sql: %s, ErrMessage: %s\n", sql, err) 68 | } 69 | fmt.Printf("ts: %s, val: %v\n", ts, val) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/varbinary/query/ws/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | _ "github.com/taosdata/driver-go/v3/taosWS" 10 | ) 11 | 12 | func main() { 13 | var taosDSN = "root:taosdata@ws(localhost:6041)/" 14 | db, err := sql.Open("taosWS", taosDSN) 15 | if err != nil { 16 | log.Fatalln("Failed to connect to " + taosDSN + ", ErrMessage: " + err.Error()) 17 | } 18 | defer db.Close() 19 | // create database 20 | res, err := db.Exec("CREATE DATABASE IF NOT EXISTS example_query_varbinary_ws") 21 | if err != nil { 22 | log.Fatalln("Failed to create database example_query_varbinary_ws, ErrMessage: " + err.Error()) 23 | } 24 | rowsAffected, err := res.RowsAffected() 25 | if err != nil { 26 | log.Fatalln("Failed to get create database rowsAffected, ErrMessage: " + err.Error()) 27 | } 28 | // you can check rowsAffected here 29 | fmt.Println("Create database example_query_varbinary_ws successfully, rowsAffected: ", rowsAffected) 30 | // create table 31 | res, err = db.Exec("CREATE TABLE IF NOT EXISTS example_query_varbinary_ws.ntb (ts TIMESTAMP, val VARBINARY(100))") 32 | if err != nil { 33 | log.Fatalln("Failed to create table example_query_varbinary_ws, ErrMessage: " + err.Error()) 34 | } 35 | rowsAffected, err = res.RowsAffected() 36 | if err != nil { 37 | log.Fatalln("Failed to get create table rowsAffected, ErrMessage: " + err.Error()) 38 | } 39 | // you can check rowsAffected here 40 | fmt.Println("Create table example_query_varbinary_ws.ntb successfully, rowsAffected:", rowsAffected) 41 | // insert data, please make sure the database and table are created before 42 | insertQuery := "INSERT INTO example_query_varbinary_ws.ntb VALUES (now, \"\\x98f46e\")" 43 | res, err = db.Exec(insertQuery) 44 | if err != nil { 45 | log.Fatalf("Failed to insert data to example_query_varbinary_ws.ntb, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 46 | } 47 | rowsAffected, err = res.RowsAffected() 48 | if err != nil { 49 | log.Fatalf("Failed to get insert rowsAffected, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 50 | } 51 | // you can check affectedRows here 52 | fmt.Printf("Successfully inserted %d rows to example_query_varbinary_ws.ntb.\n", rowsAffected) 53 | // query data 54 | sql := "SELECT ts, val FROM example_query_varbinary_ws.ntb" 55 | rows, err := db.Query(sql) 56 | if err != nil { 57 | log.Fatalf("Failed to query data from example_query_varbinary_ws.ntb, sql: %s, ErrMessage: %s\n", sql, err.Error()) 58 | } 59 | for rows.Next() { 60 | // Add your data processing logic here 61 | var ( 62 | ts time.Time 63 | val []byte 64 | ) 65 | err = rows.Scan(&ts, &val) 66 | if err != nil { 67 | log.Fatalf("Failed to scan data, sql: %s, ErrMessage: %s\n", sql, err) 68 | } 69 | fmt.Printf("ts: %s, val: %v\n", ts, val) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/geometry/query/native/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | _ "github.com/taosdata/driver-go/v3/taosSql" 10 | ) 11 | 12 | func main() { 13 | var taosDSN = "root:taosdata@tcp(localhost:6030)/" 14 | db, err := sql.Open("taosSql", taosDSN) 15 | if err != nil { 16 | log.Fatalln("Failed to connect to " + taosDSN + ", ErrMessage: " + err.Error()) 17 | } 18 | defer db.Close() 19 | // create database 20 | res, err := db.Exec("CREATE DATABASE IF NOT EXISTS example_query_geometry_native") 21 | if err != nil { 22 | log.Fatalln("Failed to create database example_query_geometry_native, ErrMessage: " + err.Error()) 23 | } 24 | rowsAffected, err := res.RowsAffected() 25 | if err != nil { 26 | log.Fatalln("Failed to get create database rowsAffected, ErrMessage: " + err.Error()) 27 | } 28 | // you can check rowsAffected here 29 | fmt.Println("Create database example_query_geometry_native successfully, rowsAffected: ", rowsAffected) 30 | // create table 31 | res, err = db.Exec("CREATE TABLE IF NOT EXISTS example_query_geometry_native.ntb (ts TIMESTAMP, val GEOMETRY(100))") 32 | if err != nil { 33 | log.Fatalln("Failed to create table example_query_geometry_native, ErrMessage: " + err.Error()) 34 | } 35 | rowsAffected, err = res.RowsAffected() 36 | if err != nil { 37 | log.Fatalln("Failed to get create table rowsAffected, ErrMessage: " + err.Error()) 38 | } 39 | // you can check rowsAffected here 40 | fmt.Println("Create table example_query_geometry_native.ntb successfully, rowsAffected:", rowsAffected) 41 | // insert data, please make sure the database and table are created before 42 | insertQuery := "INSERT INTO example_query_geometry_native.ntb VALUES (now, 'POINT(100 100)')" 43 | res, err = db.Exec(insertQuery) 44 | if err != nil { 45 | log.Fatalf("Failed to insert data to example_query_geometry_native.ntb, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 46 | } 47 | rowsAffected, err = res.RowsAffected() 48 | if err != nil { 49 | log.Fatalf("Failed to get insert rowsAffected, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 50 | } 51 | // you can check affectedRows here 52 | fmt.Printf("Successfully inserted %d rows to example_query_geometry_native.ntb.\n", rowsAffected) 53 | // query data 54 | sql := "SELECT ts, val FROM example_query_geometry_native.ntb" 55 | rows, err := db.Query(sql) 56 | if err != nil { 57 | log.Fatalf("Failed to query data from example_query_geometry_native.ntb, sql: %s, ErrMessage: %s\n", sql, err.Error()) 58 | } 59 | for rows.Next() { 60 | // Add your data processing logic here 61 | var ( 62 | ts time.Time 63 | val []byte 64 | ) 65 | err = rows.Scan(&ts, &val) 66 | if err != nil { 67 | log.Fatalf("Failed to scan data, sql: %s, ErrMessage: %s\n", sql, err) 68 | } 69 | fmt.Printf("ts: %s, val: %v\n", ts, val) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/varbinary/query/native/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | _ "github.com/taosdata/driver-go/v3/taosSql" 10 | ) 11 | 12 | func main() { 13 | var taosDSN = "root:taosdata@tcp(localhost:6030)/" 14 | db, err := sql.Open("taosSql", taosDSN) 15 | if err != nil { 16 | log.Fatalln("Failed to connect to " + taosDSN + ", ErrMessage: " + err.Error()) 17 | } 18 | defer db.Close() 19 | // create database 20 | res, err := db.Exec("CREATE DATABASE IF NOT EXISTS example_query_varbinary_native") 21 | if err != nil { 22 | log.Fatalln("Failed to create database example_query_varbinary_native, ErrMessage: " + err.Error()) 23 | } 24 | rowsAffected, err := res.RowsAffected() 25 | if err != nil { 26 | log.Fatalln("Failed to get create database rowsAffected, ErrMessage: " + err.Error()) 27 | } 28 | // you can check rowsAffected here 29 | fmt.Println("Create database example_query_varbinary_native successfully, rowsAffected: ", rowsAffected) 30 | // create table 31 | res, err = db.Exec("CREATE TABLE IF NOT EXISTS example_query_varbinary_native.ntb (ts TIMESTAMP, val VARBINARY(100))") 32 | if err != nil { 33 | log.Fatalln("Failed to create table example_query_varbinary_native, ErrMessage: " + err.Error()) 34 | } 35 | rowsAffected, err = res.RowsAffected() 36 | if err != nil { 37 | log.Fatalln("Failed to get create table rowsAffected, ErrMessage: " + err.Error()) 38 | } 39 | // you can check rowsAffected here 40 | fmt.Println("Create table example_query_varbinary_native.ntb successfully, rowsAffected:", rowsAffected) 41 | // insert data, please make sure the database and table are created before 42 | insertQuery := "INSERT INTO example_query_varbinary_native.ntb VALUES (now, \"\\x98f46e\")" 43 | res, err = db.Exec(insertQuery) 44 | if err != nil { 45 | log.Fatalf("Failed to insert data to example_query_varbinary_native.ntb, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 46 | } 47 | rowsAffected, err = res.RowsAffected() 48 | if err != nil { 49 | log.Fatalf("Failed to get insert rowsAffected, sql: %s, ErrMessage: %s\n", insertQuery, err.Error()) 50 | } 51 | // you can check affectedRows here 52 | fmt.Printf("Successfully inserted %d rows to example_query_varbinary_native.ntb.\n", rowsAffected) 53 | // query data 54 | sql := "SELECT ts, val FROM example_query_varbinary_native.ntb" 55 | rows, err := db.Query(sql) 56 | if err != nil { 57 | log.Fatalf("Failed to query data from example_query_varbinary_native.ntb, sql: %s, ErrMessage: %s\n", sql, err.Error()) 58 | } 59 | for rows.Next() { 60 | // Add your data processing logic here 61 | var ( 62 | ts time.Time 63 | val []byte 64 | ) 65 | err = rows.Scan(&ts, &val) 66 | if err != nil { 67 | log.Fatalf("Failed to scan data, sql: %s, ErrMessage: %s\n", sql, err) 68 | } 69 | fmt.Printf("ts: %s, val: %v\n", ts, val) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ws/stmt/config.go: -------------------------------------------------------------------------------- 1 | package stmt 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | type Config struct { 10 | Url string 11 | ChanLength uint 12 | MessageTimeout time.Duration 13 | WriteWait time.Duration 14 | Timezone *time.Location 15 | ErrorHandler func(connector *Connector, err error) 16 | CloseHandler func() 17 | User string 18 | Password string 19 | DB string 20 | EnableCompression bool 21 | AutoReconnect bool 22 | ReconnectIntervalMs int 23 | ReconnectRetryCount int 24 | } 25 | 26 | func NewConfig(url string, chanLength uint) *Config { 27 | return &Config{ 28 | Url: url, 29 | ChanLength: chanLength, 30 | ReconnectRetryCount: 3, 31 | ReconnectIntervalMs: 2000, 32 | } 33 | } 34 | func (c *Config) SetConnectUser(user string) error { 35 | c.User = user 36 | return nil 37 | } 38 | 39 | func (c *Config) SetConnectPass(pass string) error { 40 | c.Password = pass 41 | return nil 42 | } 43 | func (c *Config) SetConnectDB(db string) error { 44 | c.DB = db 45 | return nil 46 | } 47 | 48 | func (c *Config) SetConnectionTimezone(timezone string) error { 49 | if timezone == "" { 50 | return errors.New("invalid timezone value: empty string") 51 | } 52 | if strings.ToLower(timezone) == "local" { 53 | return errors.New("invalid timezone value: 'local'") 54 | } 55 | loc, err := time.LoadLocation(timezone) 56 | if err != nil { 57 | return errors.New("invalid timezone value: " + timezone + ", " + err.Error()) 58 | } 59 | c.Timezone = loc 60 | return nil 61 | } 62 | 63 | func (c *Config) SetMessageTimeout(timeout time.Duration) error { 64 | if timeout < time.Second { 65 | return errors.New("message timeout cannot be less than 1 second") 66 | } 67 | c.MessageTimeout = timeout 68 | return nil 69 | } 70 | 71 | func (c *Config) SetWriteWait(writeWait time.Duration) error { 72 | if writeWait < 0 { 73 | return errors.New("write wait cannot be less than 0") 74 | } 75 | c.WriteWait = writeWait 76 | return nil 77 | } 78 | 79 | func (c *Config) SetErrorHandler(f func(connector *Connector, err error)) { 80 | c.ErrorHandler = f 81 | } 82 | 83 | func (c *Config) SetCloseHandler(f func()) { 84 | c.CloseHandler = f 85 | } 86 | 87 | func (c *Config) SetEnableCompression(enableCompression bool) { 88 | c.EnableCompression = enableCompression 89 | } 90 | 91 | func (c *Config) SetAutoReconnect(reconnect bool) { 92 | c.AutoReconnect = reconnect 93 | } 94 | 95 | func (c *Config) SetReconnectIntervalMs(reconnectIntervalMs int) { 96 | c.ReconnectIntervalMs = reconnectIntervalMs 97 | } 98 | 99 | func (c *Config) SetReconnectRetryCount(reconnectRetryCount int) { 100 | c.ReconnectRetryCount = reconnectRetryCount 101 | } 102 | -------------------------------------------------------------------------------- /examples/taosWS/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "time" 7 | 8 | _ "github.com/taosdata/driver-go/v3/taosWS" 9 | ) 10 | 11 | func main() { 12 | db, err := sql.Open("taosWS", "root:taosdata@ws(127.0.0.1:6041)/") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer db.Close() 17 | _, err = db.Exec("create database if not exists example_taos_ws") 18 | if err != nil { 19 | panic(err) 20 | } 21 | _, err = db.Exec("create table if not exists example_taos_ws.stb(ts timestamp," + 22 | "c1 bool," + 23 | "c2 tinyint," + 24 | "c3 smallint," + 25 | "c4 int," + 26 | "c5 bigint," + 27 | "c6 tinyint unsigned," + 28 | "c7 smallint unsigned," + 29 | "c8 int unsigned," + 30 | "c9 bigint unsigned," + 31 | "c10 float," + 32 | "c11 double," + 33 | "c12 binary(20)," + 34 | "c13 nchar(20)," + 35 | "c14 varbinary(20)," + 36 | "c15 geometry(100)," + 37 | "c16 decimal(20,4)" + 38 | ") tags (info json)") 39 | if err != nil { 40 | panic(err) 41 | } 42 | _, err = db.Exec("create table if not exists example_taos_ws.tb1 using example_taos_ws.stb tags ('{\"name\":\"tb1\"}')") 43 | if err != nil { 44 | panic(err) 45 | } 46 | now := time.Now() 47 | _, err = db.Exec(fmt.Sprintf("insert into example_taos_ws.tb1 values ('%s',true,2,3,4,5,6,7,8,9,10,11,'binary','nchar','varbinary','point(100 100)',123.456)", now.Format(time.RFC3339Nano))) 48 | if err != nil { 49 | panic(err) 50 | } 51 | rows, err := db.Query(fmt.Sprintf("select * from example_taos_ws.tb1 where ts = '%s'", now.Format(time.RFC3339Nano))) 52 | if err != nil { 53 | panic(err) 54 | } 55 | for rows.Next() { 56 | var ( 57 | ts time.Time 58 | c1 bool 59 | c2 int8 60 | c3 int16 61 | c4 int32 62 | c5 int64 63 | c6 uint8 64 | c7 uint16 65 | c8 uint32 66 | c9 uint64 67 | c10 float32 68 | c11 float64 69 | c12 string 70 | c13 string 71 | c14 string 72 | c15 []byte 73 | c16 string 74 | ) 75 | err = rows.Scan( 76 | &ts, 77 | &c1, 78 | &c2, 79 | &c3, 80 | &c4, 81 | &c5, 82 | &c6, 83 | &c7, 84 | &c8, 85 | &c9, 86 | &c10, 87 | &c11, 88 | &c12, 89 | &c13, 90 | &c14, 91 | &c15, 92 | &c16, 93 | ) 94 | if err != nil { 95 | panic(err) 96 | } 97 | fmt.Println("ts:", ts.Local()) 98 | fmt.Println("c1:", c1) 99 | fmt.Println("c2:", c2) 100 | fmt.Println("c3:", c3) 101 | fmt.Println("c4:", c4) 102 | fmt.Println("c5:", c5) 103 | fmt.Println("c6:", c6) 104 | fmt.Println("c7:", c7) 105 | fmt.Println("c8:", c8) 106 | fmt.Println("c9:", c9) 107 | fmt.Println("c10:", c10) 108 | fmt.Println("c11:", c11) 109 | fmt.Println("c12:", c12) 110 | fmt.Println("c13:", c13) 111 | fmt.Println("c14:", c14) 112 | fmt.Println("c15:", c15) 113 | fmt.Println("c16:", c16) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /examples/taosSql/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "time" 7 | 8 | _ "github.com/taosdata/driver-go/v3/taosSql" 9 | ) 10 | 11 | func main() { 12 | db, err := sql.Open("taosSql", "root:taosdata@tcp(localhost:6030)/") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer db.Close() 17 | _, err = db.Exec("create database if not exists example_taos_sql") 18 | if err != nil { 19 | panic(err) 20 | } 21 | _, err = db.Exec("create table if not exists example_taos_sql.stb(ts timestamp," + 22 | "c1 bool," + 23 | "c2 tinyint," + 24 | "c3 smallint," + 25 | "c4 int," + 26 | "c5 bigint," + 27 | "c6 tinyint unsigned," + 28 | "c7 smallint unsigned," + 29 | "c8 int unsigned," + 30 | "c9 bigint unsigned," + 31 | "c10 float," + 32 | "c11 double," + 33 | "c12 binary(20)," + 34 | "c13 nchar(20)," + 35 | "c14 varbinary(20)," + 36 | "c15 geometry(100)," + 37 | "c16 decimal(20,4)" + 38 | ") tags (info json)") 39 | if err != nil { 40 | panic(err) 41 | } 42 | _, err = db.Exec("create table if not exists example_taos_sql.tb1 using example_taos_sql.stb tags ('{\"name\":\"tb1\"}')") 43 | if err != nil { 44 | panic(err) 45 | } 46 | now := time.Now() 47 | _, err = db.Exec(fmt.Sprintf("insert into example_taos_sql.tb1 values ('%s',true,2,3,4,5,6,7,8,9,10,11,'binary','nchar','varbinary','point(100 100)',123.456)", now.Format(time.RFC3339Nano))) 48 | if err != nil { 49 | panic(err) 50 | } 51 | rows, err := db.Query(fmt.Sprintf("select * from example_taos_sql.tb1 where ts = '%s'", now.Format(time.RFC3339Nano))) 52 | if err != nil { 53 | panic(err) 54 | } 55 | for rows.Next() { 56 | var ( 57 | ts time.Time 58 | c1 bool 59 | c2 int8 60 | c3 int16 61 | c4 int32 62 | c5 int64 63 | c6 uint8 64 | c7 uint16 65 | c8 uint32 66 | c9 uint64 67 | c10 float32 68 | c11 float64 69 | c12 string 70 | c13 string 71 | c14 string 72 | c15 []byte 73 | c16 string 74 | ) 75 | err = rows.Scan( 76 | &ts, 77 | &c1, 78 | &c2, 79 | &c3, 80 | &c4, 81 | &c5, 82 | &c6, 83 | &c7, 84 | &c8, 85 | &c9, 86 | &c10, 87 | &c11, 88 | &c12, 89 | &c13, 90 | &c14, 91 | &c15, 92 | &c16, 93 | ) 94 | if err != nil { 95 | panic(err) 96 | } 97 | fmt.Println("ts:", ts.Local()) 98 | fmt.Println("c1:", c1) 99 | fmt.Println("c2:", c2) 100 | fmt.Println("c3:", c3) 101 | fmt.Println("c4:", c4) 102 | fmt.Println("c5:", c5) 103 | fmt.Println("c6:", c6) 104 | fmt.Println("c7:", c7) 105 | fmt.Println("c8:", c8) 106 | fmt.Println("c9:", c9) 107 | fmt.Println("c10:", c10) 108 | fmt.Println("c11:", c11) 109 | fmt.Println("c12:", c12) 110 | fmt.Println("c13:", c13) 111 | fmt.Println("c14:", c14) 112 | fmt.Println("c15:", c15) 113 | fmt.Println("c16:", c16) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /examples/restful/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "time" 7 | 8 | _ "github.com/taosdata/driver-go/v3/taosRestful" 9 | ) 10 | 11 | func main() { 12 | db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer db.Close() 17 | _, err = db.Exec("create database if not exists example_taos_rest") 18 | if err != nil { 19 | panic(err) 20 | } 21 | _, err = db.Exec("create table if not exists example_taos_rest.stb(ts timestamp," + 22 | "c1 bool," + 23 | "c2 tinyint," + 24 | "c3 smallint," + 25 | "c4 int," + 26 | "c5 bigint," + 27 | "c6 tinyint unsigned," + 28 | "c7 smallint unsigned," + 29 | "c8 int unsigned," + 30 | "c9 bigint unsigned," + 31 | "c10 float," + 32 | "c11 double," + 33 | "c12 binary(20)," + 34 | "c13 nchar(20)," + 35 | "c14 varbinary(20)," + 36 | "c15 geometry(100)," + 37 | "c16 decimal(20,4)" + 38 | ") tags (info json)") 39 | if err != nil { 40 | panic(err) 41 | } 42 | _, err = db.Exec("create table if not exists example_taos_rest.tb1 using example_taos_rest.stb tags ('{\"name\":\"tb1\"}')") 43 | if err != nil { 44 | panic(err) 45 | } 46 | now := time.Now() 47 | _, err = db.Exec(fmt.Sprintf("insert into example_taos_rest.tb1 values ('%s',true,2,3,4,5,6,7,8,9,10,11,'binary','nchar','varbinary','point(100 100)',123.456)", now.Format(time.RFC3339Nano))) 48 | if err != nil { 49 | panic(err) 50 | } 51 | rows, err := db.Query(fmt.Sprintf("select * from example_taos_rest.tb1 where ts = '%s'", now.Format(time.RFC3339Nano))) 52 | if err != nil { 53 | panic(err) 54 | } 55 | for rows.Next() { 56 | var ( 57 | ts time.Time 58 | c1 bool 59 | c2 int8 60 | c3 int16 61 | c4 int32 62 | c5 int64 63 | c6 uint8 64 | c7 uint16 65 | c8 uint32 66 | c9 uint64 67 | c10 float32 68 | c11 float64 69 | c12 string 70 | c13 string 71 | c14 string 72 | c15 []byte 73 | c16 string 74 | ) 75 | err = rows.Scan( 76 | &ts, 77 | &c1, 78 | &c2, 79 | &c3, 80 | &c4, 81 | &c5, 82 | &c6, 83 | &c7, 84 | &c8, 85 | &c9, 86 | &c10, 87 | &c11, 88 | &c12, 89 | &c13, 90 | &c14, 91 | &c15, 92 | &c16, 93 | ) 94 | if err != nil { 95 | panic(err) 96 | } 97 | fmt.Println("ts:", ts.Local()) 98 | fmt.Println("c1:", c1) 99 | fmt.Println("c2:", c2) 100 | fmt.Println("c3:", c3) 101 | fmt.Println("c4:", c4) 102 | fmt.Println("c5:", c5) 103 | fmt.Println("c6:", c6) 104 | fmt.Println("c7:", c7) 105 | fmt.Println("c8:", c8) 106 | fmt.Println("c9:", c9) 107 | fmt.Println("c10:", c10) 108 | fmt.Println("c11:", c11) 109 | fmt.Println("c12:", c12) 110 | fmt.Println("c13:", c13) 111 | fmt.Println("c14:", c14) 112 | fmt.Println("c15:", c15) 113 | fmt.Println("c16:", c16) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 5 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 6 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 8 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 9 | github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= 10 | github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 11 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 12 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 13 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 14 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 15 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 16 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 17 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 20 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 21 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 22 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 23 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 24 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 25 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 26 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 27 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 32 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 33 | -------------------------------------------------------------------------------- /examples/sqlstmt/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "time" 7 | 8 | _ "github.com/taosdata/driver-go/v3/taosSql" 9 | ) 10 | 11 | var ( 12 | driverName = "taosSql" 13 | user = "root" 14 | password = "taosdata" 15 | host = "" 16 | port = 6030 17 | dataSourceName = fmt.Sprintf("%s:%s@/tcp(%s:%d)/%s?interpolateParams=true", user, password, host, port, "") 18 | ) 19 | 20 | func main() { 21 | db, err := sql.Open(driverName, dataSourceName) 22 | if err != nil { 23 | panic(err) 24 | } 25 | defer db.Close() 26 | defer func() { 27 | db.Exec("drop database if exists test_stmt_driver") 28 | }() 29 | _, err = db.Exec("create database if not exists test_stmt_driver") 30 | if err != nil { 31 | panic(err) 32 | } 33 | _, err = db.Exec("create table if not exists test_stmt_driver.ct(ts timestamp," + 34 | "c1 bool," + 35 | "c2 tinyint," + 36 | "c3 smallint," + 37 | "c4 int," + 38 | "c5 bigint," + 39 | "c6 tinyint unsigned," + 40 | "c7 smallint unsigned," + 41 | "c8 int unsigned," + 42 | "c9 bigint unsigned," + 43 | "c10 float," + 44 | "c11 double," + 45 | "c12 binary(20)," + 46 | "c13 nchar(20)" + 47 | ")") 48 | if err != nil { 49 | panic(err) 50 | } 51 | stmt, err := db.Prepare("insert into test_stmt_driver.ct values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)") 52 | if err != nil { 53 | panic(err) 54 | } 55 | now := time.Now() 56 | result, err := stmt.Exec(now, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, "binary", "nchar") 57 | if err != nil { 58 | panic(err) 59 | } 60 | affected, err := result.RowsAffected() 61 | if err != nil { 62 | panic(err) 63 | } 64 | fmt.Println("affected", affected) 65 | stmt.Close() 66 | cr := 0 67 | err = db.QueryRow("select count(*) from test_stmt_driver.ct where ts = ?", now).Scan(&cr) 68 | if err != nil { 69 | panic(err) 70 | } 71 | fmt.Println("count", cr) 72 | stmt, err = db.Prepare("select * from test_stmt_driver.ct where ts = ?") 73 | if err != nil { 74 | panic(err) 75 | } 76 | rows, err := stmt.Query(now) 77 | if err != nil { 78 | panic(err) 79 | } 80 | columns, err := rows.Columns() 81 | if err != nil { 82 | panic(err) 83 | } 84 | fmt.Println(columns) 85 | count := 0 86 | for rows.Next() { 87 | count += 1 88 | var ( 89 | ts time.Time 90 | c1 bool 91 | c2 int8 92 | c3 int16 93 | c4 int32 94 | c5 int64 95 | c6 uint8 96 | c7 uint16 97 | c8 uint32 98 | c9 uint64 99 | c10 float32 100 | c11 float64 101 | c12 string 102 | c13 string 103 | ) 104 | err = rows.Scan(&ts, 105 | &c1, 106 | &c2, 107 | &c3, 108 | &c4, 109 | &c5, 110 | &c6, 111 | &c7, 112 | &c8, 113 | &c9, 114 | &c10, 115 | &c11, 116 | &c12, 117 | &c13) 118 | fmt.Println(ts, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) 119 | } 120 | fmt.Println("rows", count) 121 | } 122 | -------------------------------------------------------------------------------- /examples/varbinary/stmt/ws/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/taosdata/driver-go/v3/common" 10 | "github.com/taosdata/driver-go/v3/common/param" 11 | _ "github.com/taosdata/driver-go/v3/taosRestful" 12 | "github.com/taosdata/driver-go/v3/ws/stmt" 13 | ) 14 | 15 | func main() { 16 | host := "127.0.0.1" 17 | 18 | taosDSN := fmt.Sprintf("root:taosdata@http(%s:6041)/", host) 19 | db, err := sql.Open("taosRestful", taosDSN) 20 | if err != nil { 21 | log.Fatalln("Failed to connect to " + taosDSN + "; ErrMessage: " + err.Error()) 22 | } 23 | defer db.Close() 24 | // prepare database and table 25 | _, err = db.Exec("CREATE DATABASE IF NOT EXISTS example_stmt_varbinary_ws") 26 | if err != nil { 27 | log.Fatalln("Failed to create database example_stmt_varbinary_ws, ErrMessage: " + err.Error()) 28 | } 29 | _, err = db.Exec("CREATE TABLE IF NOT EXISTS example_stmt_varbinary_ws.ntb (ts TIMESTAMP, val VARBINARY(100))") 30 | if err != nil { 31 | log.Fatalln("Failed to create table example_stmt_varbinary_ws.ntb, ErrMessage: " + err.Error()) 32 | } 33 | 34 | config := stmt.NewConfig(fmt.Sprintf("ws://%s:6041", host), 0) 35 | config.SetConnectUser("root") 36 | config.SetConnectPass("taosdata") 37 | config.SetConnectDB("example_stmt_varbinary_ws") 38 | config.SetMessageTimeout(common.DefaultMessageTimeout) 39 | config.SetWriteWait(common.DefaultWriteWait) 40 | 41 | connector, err := stmt.NewConnector(config) 42 | if err != nil { 43 | log.Fatalln("Failed to create stmt connector,url: " + taosDSN + "; ErrMessage: " + err.Error()) 44 | } 45 | // prepare statement 46 | sql := "INSERT INTO ntb VALUES (?,?)" 47 | stmt, err := connector.Init() 48 | if err != nil { 49 | log.Fatalln("Failed to init stmt, sql: " + sql + ", ErrMessage: " + err.Error()) 50 | } 51 | err = stmt.Prepare(sql) 52 | if err != nil { 53 | log.Fatal("Failed to prepare sql, sql: " + sql + ", ErrMessage: " + err.Error()) 54 | } 55 | 56 | columnType := param.NewColumnType(2).AddTimestamp().AddVarBinary(100) 57 | 58 | // bind column data 59 | current := time.Now() 60 | // "\x98f46e" 61 | varbinaryData := []byte{0x98, 0xf4, 0x6e} 62 | 63 | columnData := make([]*param.Param, 2) 64 | columnData[0] = param.NewParam(1).AddTimestamp(current, common.PrecisionMilliSecond) 65 | columnData[1] = param.NewParam(1).AddVarBinary(varbinaryData) 66 | err = stmt.BindParam(columnData, columnType) 67 | if err != nil { 68 | log.Fatal("Failed to bind params, ErrMessage: " + err.Error()) 69 | } 70 | 71 | // add batch 72 | err = stmt.AddBatch() 73 | if err != nil { 74 | log.Fatal("Failed to add batch, ErrMessage: " + err.Error()) 75 | } 76 | // execute batch 77 | err = stmt.Exec() 78 | if err != nil { 79 | log.Fatal("Failed to exec, ErrMessage: " + err.Error()) 80 | } 81 | // get affected rows 82 | affected := stmt.GetAffectedRows() 83 | // you can check exeResult here 84 | fmt.Printf("Successfully inserted %d rows to example_stmt_varbinary_ws.ntb.\n", affected) 85 | 86 | err = stmt.Close() 87 | if err != nil { 88 | log.Fatal("Failed to close stmt, ErrMessage: " + err.Error()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /wrapper/notify_test.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/taosdata/driver-go/v3/common" 10 | "github.com/taosdata/driver-go/v3/wrapper/cgo" 11 | ) 12 | 13 | // @author: xftan 14 | // @date: 2023/10/13 11:28 15 | // @description: test notify callback 16 | func TestNotify(t *testing.T) { 17 | conn, err := TaosConnect("", "root", "taosdata", "", 0) 18 | if err != nil { 19 | t.Error(err) 20 | return 21 | } 22 | 23 | defer TaosClose(conn) 24 | defer func() { 25 | _ = exec(conn, "drop user t_notify") 26 | }() 27 | _ = exec(conn, "drop user t_notify") 28 | err = exec(conn, "create user t_notify pass 'notify_123'") 29 | assert.NoError(t, err) 30 | 31 | conn2, err := TaosConnect("", "t_notify", "notify_123", "", 0) 32 | if err != nil { 33 | t.Error(err) 34 | return 35 | } 36 | 37 | defer TaosClose(conn2) 38 | notify := make(chan int32, 1) 39 | handler := cgo.NewHandle(notify) 40 | errCode := TaosSetNotifyCB(conn2, handler, common.TAOS_NOTIFY_PASSVER) 41 | if errCode != 0 { 42 | errStr := TaosErrorStr(nil) 43 | t.Error(errCode, errStr) 44 | } 45 | notifyWhitelist := make(chan int64, 1) 46 | handlerWhiteList := cgo.NewHandle(notifyWhitelist) 47 | errCode = TaosSetNotifyCB(conn2, handlerWhiteList, common.TAOS_NOTIFY_WHITELIST_VER) 48 | if errCode != 0 { 49 | errStr := TaosErrorStr(nil) 50 | t.Error(errCode, errStr) 51 | } 52 | 53 | notifyDropUser := make(chan struct{}, 1) 54 | handlerDropUser := cgo.NewHandle(notifyDropUser) 55 | errCode = TaosSetNotifyCB(conn2, handlerDropUser, common.TAOS_NOTIFY_USER_DROPPED) 56 | if errCode != 0 { 57 | errStr := TaosErrorStr(nil) 58 | t.Error(errCode, errStr) 59 | } 60 | 61 | err = exec(conn, "alter user t_notify pass 'test_123'") 62 | assert.NoError(t, err) 63 | timeout, cancel := context.WithTimeout(context.Background(), time.Second*5) 64 | defer cancel() 65 | now := time.Now() 66 | select { 67 | case version := <-notify: 68 | t.Log(time.Since(now)) 69 | t.Log("password changed", version) 70 | case <-timeout.Done(): 71 | t.Error("wait for notify callback timeout") 72 | } 73 | 74 | err = exec(conn, "ALTER USER t_notify ADD HOST '192.168.1.98/0','192.168.1.98/32'") 75 | assert.NoError(t, err) 76 | timeoutWhiteList, cancelWhitelist := context.WithTimeout(context.Background(), time.Second*5) 77 | defer cancelWhitelist() 78 | now = time.Now() 79 | select { 80 | case version := <-notifyWhitelist: 81 | t.Log(time.Since(now)) 82 | t.Log("whitelist changed", version) 83 | case <-timeoutWhiteList.Done(): 84 | t.Error("wait for notifyWhitelist callback timeout") 85 | } 86 | 87 | err = exec(conn, "drop USER t_notify") 88 | assert.NoError(t, err) 89 | timeoutDropUser, cancelDropUser := context.WithTimeout(context.Background(), time.Second*5) 90 | defer cancelDropUser() 91 | now = time.Now() 92 | select { 93 | case <-notifyDropUser: 94 | t.Log(time.Since(now)) 95 | t.Log("user dropped") 96 | case <-timeoutDropUser.Done(): 97 | t.Error("wait for notifyDropUser callback timeoutDropUser") 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /examples/tmqoverws/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | 7 | "github.com/taosdata/driver-go/v3/common" 8 | tmqcommon "github.com/taosdata/driver-go/v3/common/tmq" 9 | _ "github.com/taosdata/driver-go/v3/taosRestful" 10 | "github.com/taosdata/driver-go/v3/ws/tmq" 11 | ) 12 | 13 | func main() { 14 | db, err := sql.Open("taosRestful", "root:taosdata@http(localhost:6041)/") 15 | if err != nil { 16 | panic(err) 17 | } 18 | defer db.Close() 19 | prepareEnv(db) 20 | consumer, err := tmq.NewConsumer(&tmqcommon.ConfigMap{ 21 | "ws.url": "ws://127.0.0.1:6041", 22 | "ws.message.channelLen": uint(0), 23 | "ws.message.timeout": common.DefaultMessageTimeout, 24 | "ws.message.writeWait": common.DefaultWriteWait, 25 | "td.connect.user": "root", 26 | "td.connect.pass": "taosdata", 27 | "group.id": "example", 28 | "client.id": "example_consumer", 29 | "auto.offset.reset": "earliest", 30 | }) 31 | if err != nil { 32 | panic(err) 33 | } 34 | err = consumer.Subscribe("example_ws_tmq_topic", nil) 35 | if err != nil { 36 | panic(err) 37 | } 38 | go func() { 39 | _, err := db.Exec("create table example_ws_tmq.t_all(ts timestamp," + 40 | "c1 bool," + 41 | "c2 tinyint," + 42 | "c3 smallint," + 43 | "c4 int," + 44 | "c5 bigint," + 45 | "c6 tinyint unsigned," + 46 | "c7 smallint unsigned," + 47 | "c8 int unsigned," + 48 | "c9 bigint unsigned," + 49 | "c10 float," + 50 | "c11 double," + 51 | "c12 binary(20)," + 52 | "c13 nchar(20)" + 53 | ")") 54 | if err != nil { 55 | panic(err) 56 | } 57 | _, err = db.Exec("insert into example_ws_tmq.t_all values(now,true,2,3,4,5,6,7,8,9,10.123,11.123,'binary','nchar')") 58 | if err != nil { 59 | panic(err) 60 | } 61 | }() 62 | for i := 0; i < 5; i++ { 63 | ev := consumer.Poll(500) 64 | if ev != nil { 65 | switch e := ev.(type) { 66 | case *tmqcommon.DataMessage: 67 | fmt.Printf("get message:%v\n", e) 68 | case tmqcommon.Error: 69 | fmt.Printf("%% Error: %v: %v\n", e.Code(), e) 70 | panic(e) 71 | } 72 | consumer.Commit() 73 | } 74 | } 75 | partitions, err := consumer.Assignment() 76 | if err != nil { 77 | panic(err) 78 | } 79 | for i := 0; i < len(partitions); i++ { 80 | fmt.Println(partitions[i]) 81 | err = consumer.Seek(tmqcommon.TopicPartition{ 82 | Topic: partitions[i].Topic, 83 | Partition: partitions[i].Partition, 84 | Offset: 0, 85 | }, 0) 86 | if err != nil { 87 | panic(err) 88 | } 89 | } 90 | 91 | partitions, err = consumer.Assignment() 92 | if err != nil { 93 | panic(err) 94 | } 95 | for i := 0; i < len(partitions); i++ { 96 | fmt.Println(partitions[i]) 97 | } 98 | 99 | err = consumer.Close() 100 | if err != nil { 101 | panic(err) 102 | } 103 | } 104 | 105 | func prepareEnv(db *sql.DB) { 106 | _, err := db.Exec("create database example_ws_tmq WAL_RETENTION_PERIOD 86400") 107 | if err != nil { 108 | panic(err) 109 | } 110 | _, err = db.Exec("create topic example_ws_tmq_topic as database example_ws_tmq") 111 | if err != nil { 112 | panic(err) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /benchmark/run_bench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | BENCHMARK_TIMES=$1 3 | BATCH_TABLES=$2 4 | BATCH_ROWS=$3 5 | 6 | REPORT_NAME="golang_run_${BENCHMARK_TIMES}" 7 | RESULT_FOLDER="result" 8 | INSERT_TABLE_NUM=10000 9 | 10 | echo "====== starting ..." 11 | if [ ! -d ${RESULT_FOLDER} ] 12 | then 13 | mkdir ${RESULT_FOLDER} 14 | fi 15 | echo "BENCHMARK_TIMES:${BENCHMARK_TIMES}" 16 | 17 | clear remaining result report 18 | rm ./${RESULT_FOLDER}/*.md 19 | 20 | echo "====== preparing ..." 21 | echo "=== build benchmark code " 22 | rm -f benchmark 23 | go build -o benchmark benchmark.go 24 | 25 | echo "=== create database for benchmark " 26 | taos -s 'create database if not exists benchmark' 27 | 28 | echo "===== step 1 create tables ..." 29 | taos -s 'drop stable if exists benchmark.stb' 30 | taos -s 'drop stable if exists benchmark.jtb' 31 | taosBenchmark -f ./data/only_create_table_with_normal_tag.json 32 | taosBenchmark -f ./data/only_create_table_with_json_tag.json 33 | 34 | echo "===== step 2 insert data ..." 35 | hyperfine -r ${BENCHMARK_TIMES} -L types normal,json -L tables ${INSERT_TABLE_NUM} \ 36 | './benchmark -s insert -t {types} -b {tables}' \ 37 | --time-unit millisecond \ 38 | --show-output \ 39 | --export-markdown ${RESULT_FOLDER}/${REPORT_NAME}_insert.md \ 40 | --command-name insert_{types}_${INSERT_TABLE_NUM}_tables_${BENCHMARK_TIMES}_times 41 | 42 | echo "===== step 3 clean data and create tables ..." 43 | taos -s 'drop stable if exists benchmark.stb' 44 | taos -s 'drop stable if exists benchmark.jtb' 45 | taosBenchmark -f ./data/only_create_table_with_normal_tag.json 46 | taosBenchmark -f ./data/only_create_table_with_json_tag.json 47 | 48 | echo "===== step 4 insert data with batch ..." 49 | hyperfine -r ${BENCHMARK_TIMES} -L rows ${BATCH_ROWS} -L tables ${BATCH_TABLES} \ 50 | -L types normal,json \ 51 | './benchmark -s batch -t {types} -r {rows} -b {tables}' \ 52 | --time-unit millisecond \ 53 | --show-output \ 54 | --export-markdown ${RESULT_FOLDER}/${REPORT_NAME}_bath.md \ 55 | --command-name batch_{types}_${BATCH_TABLES}_tables_${BENCHMARK_TIMES}_times 56 | 57 | echo "===== step 5 query..." 58 | hyperfine -r ${BENCHMARK_TIMES} -L types normal,json \ 59 | './benchmark -s query -t {types}' \ 60 | --time-unit millisecond \ 61 | --show-output \ 62 | --export-markdown ${RESULT_FOLDER}/${REPORT_NAME}_query.md \ 63 | --command-name query_{types}_${BENCHMARK_TIMES}_times 64 | 65 | echo "===== step 6 avg ..." 66 | hyperfine -r ${BENCHMARK_TIMES} -L types normal,json \ 67 | './benchmark -s avg -t {types}' \ 68 | --time-unit millisecond \ 69 | --show-output \ 70 | --export-markdown ${RESULT_FOLDER}/${REPORT_NAME}_avg.md \ 71 | --command-name avg_{types}_${BENCHMARK_TIMES}_times 72 | 73 | 74 | echo "| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |">>./${RESULT_FOLDER}/${REPORT_NAME}.md 75 | echo "|:---|---:|---:|---:|---:|">>./${RESULT_FOLDER}/${REPORT_NAME}.md 76 | ls ./${RESULT_FOLDER}/*.md| 77 | while read filename; 78 | do 79 | sed -n '3,4p' ${filename}>>${RESULT_FOLDER}/${REPORT_NAME}.md 80 | done 81 | 82 | echo "=== clean database and binary file ... " 83 | rm -f benchmark 84 | taos -s 'drop database benchmark' 85 | 86 | echo "=== benchmark done ... " 87 | echo "=== result file:${RESULT_FOLDER}/${REPORT_NAME}.md " 88 | -------------------------------------------------------------------------------- /examples/geometry/stmt/ws/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/taosdata/driver-go/v3/common" 10 | "github.com/taosdata/driver-go/v3/common/param" 11 | _ "github.com/taosdata/driver-go/v3/taosRestful" 12 | "github.com/taosdata/driver-go/v3/ws/stmt" 13 | ) 14 | 15 | func main() { 16 | host := "127.0.0.1" 17 | 18 | taosDSN := fmt.Sprintf("root:taosdata@http(%s:6041)/", host) 19 | db, err := sql.Open("taosRestful", taosDSN) 20 | if err != nil { 21 | log.Fatalln("Failed to connect to " + taosDSN + "; ErrMessage: " + err.Error()) 22 | } 23 | defer db.Close() 24 | // prepare database and table 25 | _, err = db.Exec("CREATE DATABASE IF NOT EXISTS example_stmt_geometry_ws") 26 | if err != nil { 27 | log.Fatalln("Failed to create database example_stmt_geometry_ws, ErrMessage: " + err.Error()) 28 | } 29 | _, err = db.Exec("CREATE TABLE IF NOT EXISTS example_stmt_geometry_ws.ntb (ts TIMESTAMP, val GEOMETRY(100))") 30 | if err != nil { 31 | log.Fatalln("Failed to create table example_stmt_geometry_ws.ntb, ErrMessage: " + err.Error()) 32 | } 33 | 34 | config := stmt.NewConfig(fmt.Sprintf("ws://%s:6041", host), 0) 35 | config.SetConnectUser("root") 36 | config.SetConnectPass("taosdata") 37 | config.SetConnectDB("example_stmt_geometry_ws") 38 | config.SetMessageTimeout(common.DefaultMessageTimeout) 39 | config.SetWriteWait(common.DefaultWriteWait) 40 | 41 | connector, err := stmt.NewConnector(config) 42 | if err != nil { 43 | log.Fatalln("Failed to create stmt connector,url: " + taosDSN + "; ErrMessage: " + err.Error()) 44 | } 45 | // prepare statement 46 | sql := "INSERT INTO ntb VALUES (?,?)" 47 | stmt, err := connector.Init() 48 | if err != nil { 49 | log.Fatalln("Failed to init stmt, sql: " + sql + ", ErrMessage: " + err.Error()) 50 | } 51 | err = stmt.Prepare(sql) 52 | if err != nil { 53 | log.Fatal("Failed to prepare sql, sql: " + sql + ", ErrMessage: " + err.Error()) 54 | } 55 | 56 | columnType := param.NewColumnType(2).AddTimestamp().AddGeometry(100) 57 | 58 | // bind column data 59 | current := time.Now() 60 | // point(100 100) 61 | geometryData := []byte{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40} 62 | 63 | columnData := make([]*param.Param, 2) 64 | columnData[0] = param.NewParam(1).AddTimestamp(current, common.PrecisionMilliSecond) 65 | columnData[1] = param.NewParam(1).AddGeometry(geometryData) 66 | err = stmt.BindParam(columnData, columnType) 67 | if err != nil { 68 | log.Fatal("Failed to bind params, ErrMessage: " + err.Error()) 69 | } 70 | 71 | // add batch 72 | err = stmt.AddBatch() 73 | if err != nil { 74 | log.Fatal("Failed to add batch, ErrMessage: " + err.Error()) 75 | } 76 | // execute batch 77 | err = stmt.Exec() 78 | if err != nil { 79 | log.Fatal("Failed to exec, ErrMessage: " + err.Error()) 80 | } 81 | // get affected rows 82 | affected := stmt.GetAffectedRows() 83 | // you can check exeResult here 84 | fmt.Printf("Successfully inserted %d rows to example_stmt_geometry_ws.ntb.\n", affected) 85 | 86 | err = stmt.Close() 87 | if err != nil { 88 | log.Fatal("Failed to close stmt, ErrMessage: " + err.Error()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /taosWS/connection_test.go: -------------------------------------------------------------------------------- 1 | package taosWS 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | taosErrors "github.com/taosdata/driver-go/v3/errors" 11 | ) 12 | 13 | // @author: xftan 14 | // @date: 2023/10/13 11:22 15 | // @description: test format bytes 16 | func Test_formatBytes(t *testing.T) { 17 | type args struct { 18 | bs []byte 19 | } 20 | tests := []struct { 21 | name string 22 | args args 23 | want string 24 | }{ 25 | { 26 | name: "nothing", 27 | args: args{ 28 | bs: nil, 29 | }, 30 | want: "", 31 | }, 32 | { 33 | name: "one byte", 34 | args: args{ 35 | bs: []byte{'a'}, 36 | }, 37 | want: "[0x61]", 38 | }, 39 | { 40 | name: "two byes", 41 | args: args{ 42 | bs: []byte{'a', 'b'}, 43 | }, 44 | want: "[0x61,0x62]", 45 | }, 46 | } 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | assert.Equalf(t, tt.want, formatBytes(tt.args.bs), "formatBytes(%v)", tt.args.bs) 50 | }) 51 | } 52 | } 53 | 54 | func TestBadConnection(t *testing.T) { 55 | defer func() { 56 | if r := recover(); r != nil { 57 | // bad connection should not panic 58 | t.Fatalf("panic: %v", r) 59 | } 60 | }() 61 | 62 | cfg, err := ParseDSN(dataSourceName) 63 | if err != nil { 64 | t.Fatalf("ParseDSN error: %v", err) 65 | } 66 | cfg.ReadTimeout = 10 * time.Second 67 | cfg.WriteTimeout = 10 * time.Second 68 | conn, err := newTaosConn(cfg) 69 | if err != nil { 70 | t.Fatalf("newTaosConn error: %v", err) 71 | } 72 | 73 | // to test bad connection, we manually close the connection 74 | err = conn.Close() 75 | if err != nil { 76 | t.Fatalf("close error: %v", err) 77 | } 78 | 79 | _, err = conn.QueryContext(context.Background(), "select 1", nil) 80 | if err == nil { 81 | t.Fatalf("query should fail") 82 | } 83 | } 84 | 85 | func TestHandleResponseError(t *testing.T) { 86 | t.Run("Error not nil", func(t *testing.T) { 87 | err := errors.New("some error") 88 | result := handleResponseError(err, 0, "ignored message") 89 | assert.Equal(t, err, result, "Expected the original error to be returned") 90 | }) 91 | 92 | t.Run("Error nil and non-zero code", func(t *testing.T) { 93 | code := 123 94 | msg := "some error message" 95 | expectedErr := taosErrors.NewError(code, msg) 96 | 97 | result := handleResponseError(nil, code, msg) 98 | assert.EqualError(t, result, expectedErr.Error(), "Expected a new error to be returned based on code and message") 99 | }) 100 | 101 | t.Run("Error nil and zero code", func(t *testing.T) { 102 | result := handleResponseError(nil, 0, "ignored message") 103 | assert.Nil(t, result, "Expected nil to be returned when there is no error and code is zero") 104 | }) 105 | } 106 | 107 | func TestBegin(t *testing.T) { 108 | cfg, err := ParseDSN(dataSourceName) 109 | if err != nil { 110 | t.Fatalf("ParseDSN error: %v", err) 111 | } 112 | cfg.ReadTimeout = 10 * time.Second 113 | cfg.WriteTimeout = 10 * time.Second 114 | conn, err := newTaosConn(cfg) 115 | if err != nil { 116 | t.Fatalf("newTaosConn error: %v", err) 117 | } 118 | defer func() { 119 | err = conn.Close() 120 | assert.NoError(t, err) 121 | }() 122 | 123 | tx, err := conn.Begin() 124 | assert.Error(t, err) 125 | assert.Nil(t, tx) 126 | } 127 | -------------------------------------------------------------------------------- /bench/stmt/insert/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | "unsafe" 8 | 9 | _ "net/http/pprof" 10 | 11 | "github.com/taosdata/driver-go/v3/common" 12 | "github.com/taosdata/driver-go/v3/common/param" 13 | "github.com/taosdata/driver-go/v3/errors" 14 | "github.com/taosdata/driver-go/v3/wrapper" 15 | ) 16 | 17 | func main() { 18 | go func() { 19 | if err := http.ListenAndServe(":6060", nil); err != nil { 20 | panic(err) 21 | } 22 | }() 23 | conn, err := wrapper.TaosConnect("", "root", "taosdata", "", 0) 24 | if err != nil { 25 | panic(err) 26 | } 27 | defer wrapper.TaosClose(conn) 28 | err = exec(conn, "create database if not exists b_stmt precision 'ns' keep 36500") 29 | if err != nil { 30 | panic(err) 31 | } 32 | err = exec(conn, "use b_stmt") 33 | if err != nil { 34 | panic(err) 35 | } 36 | err = exec(conn, "create table if not exists all_type(ts timestamp,"+ 37 | "c1 bool,"+ 38 | "c2 tinyint,"+ 39 | "c3 smallint,"+ 40 | "c4 int,"+ 41 | "c5 bigint,"+ 42 | "c6 tinyint unsigned,"+ 43 | "c7 smallint unsigned,"+ 44 | "c8 int unsigned,"+ 45 | "c9 bigint unsigned,"+ 46 | "c10 float,"+ 47 | "c11 double,"+ 48 | "c12 binary(20),"+ 49 | "c13 nchar(20))") 50 | if err != nil { 51 | panic(err) 52 | } 53 | sql := "insert into all_type values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)" 54 | for { 55 | now := time.Now() 56 | p := param.NewParam(14). 57 | AddTimestamp(now, common.PrecisionNanoSecond). 58 | AddBool(true). 59 | AddTinyint(1). 60 | AddSmallint(2). 61 | AddInt(3). 62 | AddBigint(4). 63 | AddUTinyint(5). 64 | AddUSmallint(6). 65 | AddUInt(7). 66 | AddUBigint(8). 67 | AddFloat(9). 68 | AddDouble(10). 69 | AddBinary([]byte("11")). 70 | AddNchar("12") 71 | insertStmt := wrapper.TaosStmtInit(conn) 72 | code := wrapper.TaosStmtPrepare(insertStmt, sql) 73 | if code != 0 { 74 | errStr := wrapper.TaosStmtErrStr(insertStmt) 75 | err = errors.NewError(code, errStr) 76 | panic(err) 77 | } 78 | code = wrapper.TaosStmtBindParam(insertStmt, p.GetValues()) 79 | if code != 0 { 80 | errStr := wrapper.TaosStmtErrStr(insertStmt) 81 | err = errors.NewError(code, errStr) 82 | panic(err) 83 | } 84 | code = wrapper.TaosStmtAddBatch(insertStmt) 85 | if code != 0 { 86 | errStr := wrapper.TaosStmtErrStr(insertStmt) 87 | err = errors.NewError(code, errStr) 88 | panic(err) 89 | } 90 | code = wrapper.TaosStmtExecute(insertStmt) 91 | if code != 0 { 92 | errStr := wrapper.TaosStmtErrStr(insertStmt) 93 | err = errors.NewError(code, errStr) 94 | panic(err) 95 | } 96 | affectedRows := wrapper.TaosStmtAffectedRowsOnce(insertStmt) 97 | if affectedRows != 1 { 98 | log.Fatalf("expect 1 got %d", affectedRows) 99 | } 100 | code = wrapper.TaosStmtClose(insertStmt) 101 | if code != 0 { 102 | errStr := wrapper.TaosStmtErrStr(insertStmt) 103 | err = errors.NewError(code, errStr) 104 | panic(err) 105 | } 106 | time.Sleep(time.Microsecond) 107 | } 108 | } 109 | 110 | func exec(conn unsafe.Pointer, sql string) error { 111 | res := wrapper.TaosQuery(conn, sql) 112 | defer wrapper.TaosFreeResult(res) 113 | code := wrapper.TaosError(res) 114 | if code != 0 { 115 | errStr := wrapper.TaosErrorStr(res) 116 | return errors.NewError(code, errStr) 117 | } 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /ws/client/conn_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "net/http" 7 | "net/http/httptest" 8 | "strings" 9 | "testing" 10 | "time" 11 | 12 | "github.com/gorilla/websocket" 13 | "github.com/stretchr/testify/assert" 14 | taosErrors "github.com/taosdata/driver-go/v3/errors" 15 | ) 16 | 17 | func TestEnvelopePool(t *testing.T) { 18 | pool := &EnvelopePool{} 19 | 20 | // Test Get method 21 | env := pool.Get() 22 | assert.NotNil(t, env) 23 | assert.NotNil(t, env.Msg) 24 | 25 | // Test Put method 26 | env.Msg.WriteString("test") 27 | pool.Put(env) 28 | 29 | // Test if the envelope is reset after put 30 | env = pool.Get() 31 | assert.Equal(t, 0, env.Msg.Len()) 32 | } 33 | 34 | func TestEnvelope_Reset(t *testing.T) { 35 | env := &Envelope{ 36 | Type: 1, 37 | Msg: bytes.NewBufferString("test"), 38 | } 39 | 40 | env.Reset() 41 | 42 | assert.Equal(t, 0, env.Msg.Len()) 43 | } 44 | 45 | var upgrader = websocket.Upgrader{ 46 | ReadBufferSize: 1024, 47 | WriteBufferSize: 1024, 48 | } 49 | 50 | func wsEchoServer(w http.ResponseWriter, r *http.Request) { 51 | conn, err := upgrader.Upgrade(w, r, nil) 52 | if err != nil { 53 | return 54 | } 55 | defer func() { 56 | _ = conn.Close() 57 | }() 58 | 59 | for { 60 | messageType, message, err := conn.ReadMessage() 61 | if err != nil { 62 | return 63 | } 64 | 65 | if err := conn.WriteMessage(messageType, message); err != nil { 66 | return 67 | } 68 | } 69 | } 70 | 71 | func TestClient(t *testing.T) { 72 | s := httptest.NewServer(http.HandlerFunc(wsEchoServer)) 73 | defer s.Close() 74 | t.Log(s.URL) 75 | ep := "ws" + strings.TrimPrefix(s.URL, "http") 76 | ws, _, err := websocket.DefaultDialer.Dial(ep, nil) 77 | assert.NoError(t, err) 78 | c := NewClient(ws, 1) 79 | gotMessage := make(chan struct{}) 80 | c.TextMessageHandler = func(message []byte) { 81 | assert.Equal(t, "test", string(message)) 82 | gotMessage <- struct{}{} 83 | } 84 | running := c.IsRunning() 85 | assert.True(t, running) 86 | defer c.Close() 87 | go c.ReadPump() 88 | go c.WritePump() 89 | env := c.GetEnvelope() 90 | env.Type = websocket.TextMessage 91 | env.Msg.WriteString("test") 92 | err = c.Send(env) 93 | assert.NoError(t, err) 94 | env = c.GetEnvelope() 95 | c.PutEnvelope(env) 96 | timeout := time.NewTimer(time.Second * 3) 97 | select { 98 | case <-gotMessage: 99 | t.Log("got message") 100 | case <-timeout.C: 101 | t.Error("timeout") 102 | } 103 | } 104 | 105 | func TestHandleResponseError(t *testing.T) { 106 | t.Run("Error not nil", func(t *testing.T) { 107 | err := errors.New("some error") 108 | result := HandleResponseError(err, 0, "ignored message") 109 | assert.Equal(t, err, result, "Expected the original error to be returned") 110 | }) 111 | 112 | t.Run("Error nil and non-zero code", func(t *testing.T) { 113 | code := 123 114 | msg := "some error message" 115 | expectedErr := taosErrors.NewError(code, msg) 116 | 117 | result := HandleResponseError(nil, code, msg) 118 | assert.EqualError(t, result, expectedErr.Error(), "Expected a new error to be returned based on code and message") 119 | }) 120 | 121 | t.Run("Error nil and zero code", func(t *testing.T) { 122 | result := HandleResponseError(nil, 0, "ignored message") 123 | assert.Nil(t, result, "Expected nil to be returned when there is no error and code is zero") 124 | }) 125 | } 126 | -------------------------------------------------------------------------------- /ws/tmq/config.go: -------------------------------------------------------------------------------- 1 | package tmq 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/taosdata/driver-go/v3/common" 8 | ) 9 | 10 | type config struct { 11 | Url string 12 | ChanLength uint 13 | MessageTimeout time.Duration 14 | WriteWait time.Duration 15 | Timezone *time.Location 16 | User string 17 | Password string 18 | GroupID string 19 | ClientID string 20 | OffsetRest string 21 | AutoCommit string 22 | AutoCommitIntervalMS string 23 | SnapshotEnable string 24 | WithTableName string 25 | EnableCompression bool 26 | AutoReconnect bool 27 | ReconnectIntervalMs int 28 | ReconnectRetryCount int 29 | SessionTimeoutMS string 30 | MaxPollIntervalMS string 31 | OtherOptions map[string]string 32 | } 33 | 34 | func newConfig(url string, chanLength uint) *config { 35 | return &config{ 36 | Url: url, 37 | ChanLength: chanLength, 38 | OtherOptions: make(map[string]string), 39 | } 40 | } 41 | 42 | func (c *config) setConnectUser(user string) { 43 | c.User = user 44 | } 45 | 46 | func (c *config) setConnectPass(pass string) { 47 | c.Password = pass 48 | } 49 | 50 | func (c *config) setGroupID(groupID string) { 51 | c.GroupID = groupID 52 | } 53 | 54 | func (c *config) setClientID(clientID string) { 55 | c.ClientID = clientID 56 | } 57 | 58 | func (c *config) setAutoOffsetReset(offsetReset string) { 59 | c.OffsetRest = offsetReset 60 | } 61 | 62 | func (c *config) setMessageTimeout(timeout time.Duration) error { 63 | if timeout < time.Second { 64 | return errors.New("ws.message.timeout cannot be less than 1 second") 65 | } 66 | c.MessageTimeout = timeout 67 | return nil 68 | } 69 | 70 | func (c *config) setWriteWait(writeWait time.Duration) error { 71 | if writeWait < time.Second { 72 | return errors.New("ws.message.writeWait cannot be less than 1 second") 73 | } 74 | c.WriteWait = writeWait 75 | return nil 76 | } 77 | 78 | func (c *config) setAutoCommit(enable string) { 79 | c.AutoCommit = enable 80 | } 81 | 82 | func (c *config) setAutoCommitIntervalMS(autoCommitIntervalMS string) { 83 | c.AutoCommitIntervalMS = autoCommitIntervalMS 84 | } 85 | 86 | func (c *config) setSnapshotEnable(enableSnapshot string) { 87 | c.SnapshotEnable = enableSnapshot 88 | } 89 | 90 | func (c *config) setWithTableName(withTableName string) { 91 | c.WithTableName = withTableName 92 | } 93 | 94 | func (c *config) setEnableCompression(enableCompression bool) { 95 | c.EnableCompression = enableCompression 96 | } 97 | 98 | func (c *config) setAutoReconnect(autoReconnect bool) { 99 | c.AutoReconnect = autoReconnect 100 | } 101 | 102 | func (c *config) setReconnectIntervalMs(reconnectIntervalMs int) { 103 | c.ReconnectIntervalMs = reconnectIntervalMs 104 | } 105 | 106 | func (c *config) setReconnectRetryCount(reconnectRetryCount int) { 107 | c.ReconnectRetryCount = reconnectRetryCount 108 | } 109 | 110 | func (c *config) setSessionTimeoutMS(sessionTimeoutMS string) { 111 | c.SessionTimeoutMS = sessionTimeoutMS 112 | } 113 | 114 | func (c *config) setMaxPollIntervalMS(maxPollIntervalMS string) { 115 | c.MaxPollIntervalMS = maxPollIntervalMS 116 | } 117 | 118 | func (c *config) setTimezone(timezone string) error { 119 | if timezone == "" { 120 | return nil 121 | } 122 | tz, err := common.ParseTimezone(timezone) 123 | if err != nil { 124 | return err 125 | } 126 | c.Timezone = tz 127 | return nil 128 | } 129 | -------------------------------------------------------------------------------- /benchmark/data/only_create_table_with_json_tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "filetype": "insert", 3 | "cfgdir": "/etc/taos", 4 | "host": "127.0.0.1", 5 | "port": 6030, 6 | "user": "root", 7 | "password": "taosdata", 8 | "connection_pool_size": 8, 9 | "thread_count": 16, 10 | "create_table_thread_count": 16, 11 | "confirm_parameter_prompt": "no", 12 | "insert_interval": 0, 13 | "interlace_rows": 0, 14 | "num_of_records_per_req": 30000, 15 | "prepared_rand": 10000, 16 | "chinese": "no", 17 | "databases": [ 18 | { 19 | "dbinfo": { 20 | "name": "benchmark", 21 | "drop": "no", 22 | "replica": 1, 23 | "precision": "ms", 24 | "keep": 3650, 25 | "minRows": 100, 26 | "maxRows": 4096, 27 | "comp": 2 28 | }, 29 | "super_tables": [ 30 | { 31 | "name": "jtb", 32 | "child_table_exists": "no", 33 | "childtable_count": 10000, 34 | "childtable_prefix": "jtb_", 35 | "escape_character": "yes", 36 | "auto_create_table": "no", 37 | "batch_create_tbl_num": 10000, 38 | "data_source": "rand", 39 | "insert_mode": "taosc", 40 | "non_stop_mode": "no", 41 | "line_protocol": "line", 42 | "insert_rows": 0, 43 | "childtable_limit": 10, 44 | "childtable_offset": 100, 45 | "interlace_rows": 0, 46 | "insert_interval": 0, 47 | "partial_col_num": 0, 48 | "disorder_ratio": 0, 49 | "disorder_range": 1000, 50 | "timestamp_step": 10, 51 | "start_timestamp": "2020-10-01 00:00:00.000", 52 | "sample_format": "csv", 53 | "sample_file": "./sample.csv", 54 | "use_sample_ts": "no", 55 | "tags_file": "", 56 | "columns": [ 57 | { 58 | "type": "BOOL", 59 | "name": "bl" 60 | }, 61 | { 62 | "type": "TINYINT", 63 | "name": "i8" 64 | }, 65 | { 66 | "type": "SMALLINT", 67 | "name": "i16" 68 | }, 69 | { 70 | "type": "INT", 71 | "name": "i32" 72 | }, 73 | { 74 | "type": "BIGINT", 75 | "name": "i64" 76 | }, 77 | { 78 | "type": "UTINYINT", 79 | "name": "u8" 80 | }, 81 | { 82 | "type": "USMALLINT", 83 | "name": "u16" 84 | }, 85 | { 86 | "type": "UINT", 87 | "name": "u32" 88 | }, 89 | { 90 | "type": "UBIGINT", 91 | "name": "u64" 92 | }, 93 | { 94 | "type": "FLOAT", 95 | "name": "f32" 96 | }, 97 | { 98 | "type": "DOUBLE", 99 | "name": "d64" 100 | }, 101 | { 102 | "type": "VARCHAR", 103 | "name": "bnr", 104 | "len": 20 105 | }, 106 | { 107 | "type": "NCHAR", 108 | "name": "nchr", 109 | "len": 20 110 | } 111 | ], 112 | "tags": [ 113 | { 114 | "type": "JSON", 115 | "len": 8, 116 | "count": 4 117 | } 118 | ] 119 | } 120 | ] 121 | } 122 | ] 123 | } -------------------------------------------------------------------------------- /examples/platform/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "math/rand" 7 | "strings" 8 | "time" 9 | 10 | _ "github.com/taosdata/driver-go/v3/taosRestful" 11 | "github.com/taosdata/driver-go/v3/types" 12 | ) 13 | 14 | func main() { 15 | db, err := sql.Open("taosRestful", "root:taosdata@http(192.168.1.163:8085)/?token=xxxxxx") 16 | if err != nil { 17 | panic(err) 18 | } 19 | rand.Seed(time.Now().UnixNano()) 20 | defer db.Close() 21 | _, err = db.Exec("create database if not exists restful_demo") 22 | if err != nil { 23 | panic(err) 24 | } 25 | var ( 26 | v1 = true 27 | v2 = int8(rand.Int()) 28 | v3 = int16(rand.Int()) 29 | v4 = rand.Int31() 30 | v5 = int64(rand.Int31()) 31 | v6 = uint8(rand.Uint32()) 32 | v7 = uint16(rand.Uint32()) 33 | v8 = rand.Uint32() 34 | v9 = uint64(rand.Uint32()) 35 | v10 = rand.Float32() 36 | v11 = rand.Float64() 37 | ) 38 | 39 | _, err = db.Exec("create table if not exists restful_demo.all_type(ts timestamp," + 40 | "c1 bool," + 41 | "c2 tinyint," + 42 | "c3 smallint," + 43 | "c4 int," + 44 | "c5 bigint," + 45 | "c6 tinyint unsigned," + 46 | "c7 smallint unsigned," + 47 | "c8 int unsigned," + 48 | "c9 bigint unsigned," + 49 | "c10 float," + 50 | "c11 double," + 51 | "c12 binary(20)," + 52 | "c13 nchar(20)" + 53 | ")" + 54 | "tags(t json)", 55 | ) 56 | if err != nil { 57 | panic(err) 58 | } 59 | now := time.Now().Round(time.Millisecond) 60 | _, err = db.Exec(fmt.Sprintf(`insert into restful_demo.t1 using restful_demo.all_type tags('{"a":"b"}') values('%s',%v,%v,%v,%v,%v,%v,%v,%v,%v,%v,%v,'test_binary','test_nchar')`, now.Format(time.RFC3339Nano), v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)) 61 | if err != nil { 62 | panic(err) 63 | } 64 | rows, err := db.Query(fmt.Sprintf("select * from restful_demo.all_type where ts = '%s'", now.Format(time.RFC3339Nano))) 65 | if err != nil { 66 | panic(err) 67 | } 68 | columns, err := rows.Columns() 69 | if err != nil { 70 | panic(err) 71 | } 72 | fmt.Printf("column names: %s\n", strings.Join(columns, ",")) 73 | for rows.Next() { 74 | var ( 75 | ts time.Time 76 | c1 bool 77 | c2 int8 78 | c3 int16 79 | c4 int32 80 | c5 int64 81 | c6 uint8 82 | c7 uint16 83 | c8 uint32 84 | c9 uint64 85 | c10 float32 86 | c11 float64 87 | c12 string 88 | c13 string 89 | tt types.RawMessage 90 | ) 91 | err := rows.Scan( 92 | &ts, 93 | &c1, 94 | &c2, 95 | &c3, 96 | &c4, 97 | &c5, 98 | &c6, 99 | &c7, 100 | &c8, 101 | &c9, 102 | &c10, 103 | &c11, 104 | &c12, 105 | &c13, 106 | &tt, 107 | ) 108 | if err != nil { 109 | panic(err) 110 | } 111 | fmt.Println("ts", "insert", now.Local(), "result", ts.Local()) 112 | fmt.Println("c1", "insert", v1, "result", c1) 113 | fmt.Println("c2", "insert", v2, "result", c2) 114 | fmt.Println("c3", "insert", v3, "result", c3) 115 | fmt.Println("c4", "insert", v4, "result", c4) 116 | fmt.Println("c5", "insert", v5, "result", c5) 117 | fmt.Println("c6", "insert", v6, "result", c6) 118 | fmt.Println("c7", "insert", v7, "result", c7) 119 | fmt.Println("c8", "insert", v8, "result", c8) 120 | fmt.Println("c9", "insert", v9, "result", c9) 121 | fmt.Println("c10", "insert", v10, "result", c10) 122 | fmt.Println("c11", "insert", v11, "result", c11) 123 | fmt.Println("c12", "insert", "test_binary", "result", c12) 124 | fmt.Println("c13", "insert", "test_nchar", "result", c13) 125 | fmt.Println("tag", "insert", "{\"a\":\"b\"}", "result", string(tt)) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /af/insertstmt/stmt.go: -------------------------------------------------------------------------------- 1 | package insertstmt 2 | 3 | import ( 4 | "database/sql/driver" 5 | "errors" 6 | "unsafe" 7 | 8 | "github.com/taosdata/driver-go/v3/af/locker" 9 | "github.com/taosdata/driver-go/v3/common/param" 10 | taosError "github.com/taosdata/driver-go/v3/errors" 11 | "github.com/taosdata/driver-go/v3/wrapper" 12 | ) 13 | 14 | type InsertStmt struct { 15 | stmt unsafe.Pointer 16 | } 17 | 18 | func NewInsertStmt(taosConn unsafe.Pointer) *InsertStmt { 19 | locker.Lock() 20 | stmt := wrapper.TaosStmtInit(taosConn) 21 | locker.Unlock() 22 | return &InsertStmt{stmt: stmt} 23 | } 24 | 25 | func NewInsertStmtWithReqID(taosConn unsafe.Pointer, reqID int64) *InsertStmt { 26 | locker.Lock() 27 | stmt := wrapper.TaosStmtInitWithReqID(taosConn, reqID) 28 | locker.Unlock() 29 | return &InsertStmt{stmt: stmt} 30 | } 31 | 32 | func (stmt *InsertStmt) Prepare(sql string) error { 33 | locker.Lock() 34 | code := wrapper.TaosStmtPrepare(stmt.stmt, sql) 35 | locker.Unlock() 36 | if code != 0 { 37 | return stmt.stmtErr(code) 38 | } 39 | isInsert, code := wrapper.TaosStmtIsInsert(stmt.stmt) 40 | if code != 0 { 41 | return stmt.stmtErr(code) 42 | } 43 | if !isInsert { 44 | return errors.New("only support insert") 45 | } 46 | return nil 47 | } 48 | 49 | func (stmt *InsertStmt) SetTableName(name string) error { 50 | locker.Lock() 51 | code := wrapper.TaosStmtSetTBName(stmt.stmt, name) 52 | locker.Unlock() 53 | if code != 0 { 54 | return stmt.stmtErr(code) 55 | } 56 | return nil 57 | } 58 | 59 | func (stmt *InsertStmt) SetSubTableName(name string) error { 60 | locker.Lock() 61 | code := wrapper.TaosStmtSetSubTBName(stmt.stmt, name) 62 | locker.Unlock() 63 | if code != 0 { 64 | return stmt.stmtErr(code) 65 | } 66 | return nil 67 | } 68 | 69 | func (stmt *InsertStmt) SetTableNameWithTags(tableName string, tags *param.Param) error { 70 | locker.Lock() 71 | code := wrapper.TaosStmtSetTBNameTags(stmt.stmt, tableName, tags.GetValues()) 72 | locker.Unlock() 73 | if code != 0 { 74 | return stmt.stmtErr(code) 75 | } 76 | return nil 77 | } 78 | 79 | func (stmt *InsertStmt) BindParam(params []*param.Param, bindType *param.ColumnType) error { 80 | data := make([][]driver.Value, len(params)) 81 | for columnIndex, columnData := range params { 82 | value := columnData.GetValues() 83 | data[columnIndex] = value 84 | } 85 | columnTypes, err := bindType.GetValue() 86 | if err != nil { 87 | return err 88 | } 89 | locker.Lock() 90 | code := wrapper.TaosStmtBindParamBatch(stmt.stmt, data, columnTypes) 91 | locker.Unlock() 92 | if code != 0 { 93 | return stmt.stmtErr(code) 94 | } 95 | return nil 96 | } 97 | 98 | func (stmt *InsertStmt) AddBatch() error { 99 | locker.Lock() 100 | code := wrapper.TaosStmtAddBatch(stmt.stmt) 101 | locker.Unlock() 102 | if code != 0 { 103 | return stmt.stmtErr(code) 104 | } 105 | return nil 106 | } 107 | 108 | func (stmt *InsertStmt) Execute() error { 109 | locker.Lock() 110 | code := wrapper.TaosStmtExecute(stmt.stmt) 111 | locker.Unlock() 112 | if code != 0 { 113 | return stmt.stmtErr(code) 114 | } 115 | return nil 116 | } 117 | 118 | func (stmt *InsertStmt) GetAffectedRows() int { 119 | return wrapper.TaosStmtAffectedRowsOnce(stmt.stmt) 120 | } 121 | 122 | func (stmt *InsertStmt) Close() error { 123 | locker.Lock() 124 | code := wrapper.TaosStmtClose(stmt.stmt) 125 | locker.Unlock() 126 | var err error 127 | if code != 0 { 128 | err = stmt.stmtErr(code) 129 | } 130 | stmt.stmt = nil 131 | return err 132 | } 133 | 134 | func (stmt *InsertStmt) stmtErr(code int) error { 135 | errStr := wrapper.TaosStmtErrStr(stmt.stmt) 136 | return taosError.NewError(code, errStr) 137 | } 138 | -------------------------------------------------------------------------------- /common/decimal_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestFormatI128(t *testing.T) { 10 | type args struct { 11 | hi int64 12 | lo uint64 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want string 18 | }{ 19 | { 20 | name: "-1234", 21 | args: args{ 22 | hi: -1, 23 | lo: 18446744073709550382, 24 | }, 25 | want: "-1234", 26 | }, 27 | { 28 | name: "0", 29 | args: args{ 30 | hi: 0, 31 | lo: 0, 32 | }, 33 | want: "0", 34 | }, 35 | { 36 | name: "1234", 37 | args: args{ 38 | hi: 0, 39 | lo: 1234, 40 | }, 41 | want: "1234", 42 | }, 43 | { 44 | name: "max_int64", 45 | args: args{ 46 | hi: 0, 47 | lo: 9223372036854775807, 48 | }, 49 | want: "9223372036854775807", 50 | }, 51 | { 52 | name: "min_int64", 53 | args: args{ 54 | hi: -1, 55 | lo: 9223372036854775808, 56 | }, 57 | want: "-9223372036854775808", 58 | }, 59 | { 60 | name: "max_uint64", 61 | args: args{ 62 | hi: 0, 63 | lo: 18446744073709551615, 64 | }, 65 | want: "18446744073709551615", 66 | }, 67 | { 68 | name: "max_int128", 69 | args: args{ 70 | hi: 9223372036854775807, 71 | lo: 18446744073709551615, 72 | }, 73 | want: "170141183460469231731687303715884105727", 74 | }, 75 | { 76 | name: "min_int128", 77 | args: args{ 78 | hi: -9223372036854775808, 79 | lo: 0, 80 | }, 81 | want: "-170141183460469231731687303715884105728", 82 | }, 83 | } 84 | for _, tt := range tests { 85 | t.Run(tt.name, func(t *testing.T) { 86 | assert.Equalf(t, tt.want, FormatI128(tt.args.hi, tt.args.lo), "FormatI128(%v, %v)", tt.args.hi, tt.args.lo) 87 | }) 88 | } 89 | } 90 | 91 | func TestFormatDecimal(t *testing.T) { 92 | type args struct { 93 | str string 94 | scale int 95 | } 96 | tests := []struct { 97 | name string 98 | args args 99 | want string 100 | }{ 101 | { 102 | name: "0", 103 | args: args{ 104 | str: "0", 105 | scale: 0, 106 | }, 107 | want: "0", 108 | }, 109 | { 110 | name: "0.0", 111 | args: args{ 112 | str: "0", 113 | scale: 1, 114 | }, 115 | want: "0.0", 116 | }, 117 | { 118 | name: "0.00", 119 | args: args{ 120 | str: "0", 121 | scale: 2, 122 | }, 123 | want: "0.00", 124 | }, 125 | { 126 | name: "1", 127 | args: args{ 128 | str: "1", 129 | scale: 0, 130 | }, 131 | want: "1", 132 | }, 133 | { 134 | name: "0.1", 135 | args: args{ 136 | str: "1", 137 | scale: 1, 138 | }, 139 | want: "0.1", 140 | }, 141 | { 142 | name: "-1", 143 | args: args{ 144 | str: "-1", 145 | scale: 0, 146 | }, 147 | want: "-1", 148 | }, 149 | { 150 | name: "-0.1", 151 | args: args{ 152 | str: "-1", 153 | scale: 1, 154 | }, 155 | want: "-0.1", 156 | }, 157 | { 158 | name: "170141183460469231731687303715884.105727", 159 | args: args{ 160 | str: "170141183460469231731687303715884105727", 161 | scale: 6, 162 | }, 163 | want: "170141183460469231731687303715884.105727", 164 | }, 165 | { 166 | name: "-170141183460469231731687303715884.105728", 167 | args: args{ 168 | str: "-170141183460469231731687303715884105728", 169 | scale: 6, 170 | }, 171 | want: "-170141183460469231731687303715884.105728", 172 | }, 173 | } 174 | for _, tt := range tests { 175 | t.Run(tt.name, func(t *testing.T) { 176 | assert.Equalf(t, tt.want, FormatDecimal(tt.args.str, tt.args.scale), "FormatDecimal(%v, %v)", tt.args.str, tt.args.scale) 177 | }) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /.github/workflows/compatibility-3360.yml: -------------------------------------------------------------------------------- 1 | name: compatibility-3360 2 | on: 3 | pull_request: 4 | branches: 5 | - 'main' 6 | - '3.0' 7 | push: 8 | branches: 9 | - 'main' 10 | - '3.0' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | test: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [ 'ubuntu-latest' ] 19 | go: [ '1.14', 'stable' ] 20 | name: Go-${{ matrix.os }}-${{ matrix.go }} 21 | steps: 22 | - name: get TDengine 23 | run: | 24 | wget https://github.com/taosdata/TDengine/releases/download/ver-3.3.6.0/TDengine-server-3.3.6.0-Linux-x64.tar.gz 25 | 26 | - name: install 27 | run: | 28 | tar -zxf TDengine-server-3.3.6.0-Linux-x64.tar.gz 29 | cd TDengine-server-3.3.6.0 30 | sudo ./install.sh -e no 31 | 32 | - name: checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: copy taos cfg 36 | run: | 37 | sudo mkdir -p /etc/taos 38 | sudo cp ./.github/workflows/taos.cfg /etc/taos/taos.cfg 39 | sudo cp ./.github/workflows/taosadapter.toml /etc/taos/taosadapter.toml 40 | 41 | - name: shell 42 | run: | 43 | cat >start.sh<start.sh<= rs.blockSize { 99 | err := rs.taosFetchBlock() 100 | if err != nil { 101 | return err 102 | } 103 | } 104 | if rs.blockSize == 0 { 105 | rs.block = nil 106 | return io.EOF 107 | } 108 | var err error 109 | if rs.timezone == nil { 110 | err = parser.ReadRow(dest, rs.block, rs.blockSize, rs.blockOffset, rs.rowsHeader.ColTypes, rs.precision, rs.rowsHeader.Scales) 111 | } else { 112 | err = parser.ReadRowWithTimeFormat(dest, rs.block, rs.blockSize, rs.blockOffset, rs.rowsHeader.ColTypes, rs.precision, rs.rowsHeader.Scales, rs.FormatTime) 113 | } 114 | if err != nil { 115 | return err 116 | } 117 | rs.blockOffset++ 118 | return nil 119 | } 120 | 121 | func (rs *rows) FormatTime(ts int64, precision int) driver.Value { 122 | return common.TimestampConvertToTimeWithLocation(ts, precision, rs.timezone) 123 | } 124 | 125 | func (rs *rows) taosFetchBlock() error { 126 | result := rs.asyncFetchRows() 127 | if result.N == 0 { 128 | rs.blockSize = 0 129 | return nil 130 | } 131 | if result.N < 0 { 132 | code := wrapper.TaosError(result.Res) 133 | errStr := wrapper.TaosErrorStr(result.Res) 134 | return errors.NewError(code, errStr) 135 | } 136 | rs.blockSize = result.N 137 | rs.block = wrapper.TaosGetRawBlock(result.Res) 138 | rs.blockOffset = 0 139 | return nil 140 | } 141 | 142 | func (rs *rows) asyncFetchRows() *handler.AsyncResult { 143 | locker.Lock() 144 | wrapper.TaosFetchRawBlockA(rs.result, rs.handler.Handler) 145 | locker.Unlock() 146 | r := <-rs.handler.Caller.FetchResult 147 | return r 148 | } 149 | -------------------------------------------------------------------------------- /af/rows.go: -------------------------------------------------------------------------------- 1 | package af 2 | 3 | import ( 4 | "database/sql/driver" 5 | "io" 6 | "reflect" 7 | "time" 8 | "unsafe" 9 | 10 | "github.com/taosdata/driver-go/v3/af/async" 11 | "github.com/taosdata/driver-go/v3/af/locker" 12 | "github.com/taosdata/driver-go/v3/common" 13 | "github.com/taosdata/driver-go/v3/common/parser" 14 | "github.com/taosdata/driver-go/v3/errors" 15 | "github.com/taosdata/driver-go/v3/wrapper" 16 | "github.com/taosdata/driver-go/v3/wrapper/handler" 17 | ) 18 | 19 | type rows struct { 20 | handler *handler.Handler 21 | rowsHeader *wrapper.RowsHeader 22 | done bool 23 | block unsafe.Pointer 24 | blockOffset int 25 | blockSize int 26 | result unsafe.Pointer 27 | precision int 28 | isStmt bool 29 | timezone *time.Location 30 | } 31 | 32 | func newRows(handler *handler.Handler, result unsafe.Pointer, rowsHeader *wrapper.RowsHeader, precision int, isStmt bool, timezone *time.Location) *rows { 33 | rs := &rows{ 34 | handler: handler, 35 | rowsHeader: rowsHeader, 36 | result: result, 37 | precision: precision, 38 | isStmt: isStmt, 39 | timezone: timezone, 40 | } 41 | return rs 42 | } 43 | 44 | func (rs *rows) Columns() []string { 45 | return rs.rowsHeader.ColNames 46 | } 47 | 48 | func (rs *rows) ColumnTypeDatabaseTypeName(i int) string { 49 | return rs.rowsHeader.TypeDatabaseName(i) 50 | } 51 | 52 | func (rs *rows) ColumnTypeLength(i int) (length int64, ok bool) { 53 | return rs.rowsHeader.ColLength[i], true 54 | } 55 | 56 | func (rs *rows) ColumnTypeScanType(i int) reflect.Type { 57 | return rs.rowsHeader.ScanType(i) 58 | } 59 | 60 | func (rs *rows) ColumnTypePrecisionScale(i int) (precision, scale int64, ok bool) { 61 | if rs.rowsHeader.ColTypes[i] == common.TSDB_DATA_TYPE_DECIMAL || rs.rowsHeader.ColTypes[i] == common.TSDB_DATA_TYPE_DECIMAL64 { 62 | return int64(rs.rowsHeader.Precisions[i]), int64(rs.rowsHeader.Scales[i]), true 63 | } 64 | return 0, 0, false 65 | } 66 | 67 | func (rs *rows) Close() error { 68 | rs.freeResult() 69 | rs.block = nil 70 | return nil 71 | } 72 | 73 | func (rs *rows) Next(dest []driver.Value) error { 74 | if rs.done { 75 | return io.EOF 76 | } 77 | 78 | if rs.result == nil { 79 | return &errors.TaosError{Code: 0xffff, ErrStr: "result is nil!"} 80 | } 81 | if rs.block == nil || rs.blockOffset >= rs.blockSize { 82 | if err := rs.taosFetchBlock(); err != nil { 83 | return err 84 | } 85 | } 86 | if rs.blockSize == 0 { 87 | rs.block = nil 88 | rs.freeResult() 89 | return io.EOF 90 | } 91 | var err error 92 | if rs.timezone == nil { 93 | err = parser.ReadRow(dest, rs.block, rs.blockSize, rs.blockOffset, rs.rowsHeader.ColTypes, rs.precision, rs.rowsHeader.Scales) 94 | } else { 95 | err = parser.ReadRowWithTimeFormat(dest, rs.block, rs.blockSize, rs.blockOffset, rs.rowsHeader.ColTypes, rs.precision, rs.rowsHeader.Scales, rs.FormatTime) 96 | } 97 | if err != nil { 98 | return err 99 | } 100 | rs.blockOffset++ 101 | return nil 102 | } 103 | 104 | func (rs *rows) FormatTime(ts int64, precision int) driver.Value { 105 | return common.TimestampConvertToTimeWithLocation(ts, precision, rs.timezone) 106 | } 107 | 108 | func (rs *rows) taosFetchBlock() error { 109 | result := rs.asyncFetchRows() 110 | if result.N == 0 { 111 | rs.blockSize = 0 112 | rs.done = true 113 | return nil 114 | } 115 | if result.N < 0 { 116 | code := wrapper.TaosError(result.Res) 117 | errStr := wrapper.TaosErrorStr(result.Res) 118 | return errors.NewError(code, errStr) 119 | } 120 | rs.blockSize = result.N 121 | rs.block = wrapper.TaosGetRawBlock(result.Res) 122 | rs.blockOffset = 0 123 | return nil 124 | } 125 | 126 | func (rs *rows) asyncFetchRows() *handler.AsyncResult { 127 | locker.Lock() 128 | wrapper.TaosFetchRawBlockA(rs.result, rs.handler.Handler) 129 | locker.Unlock() 130 | r := <-rs.handler.Caller.FetchResult 131 | return r 132 | } 133 | 134 | func (rs *rows) freeResult() { 135 | if rs.result != nil { 136 | if !rs.isStmt { 137 | locker.Lock() 138 | wrapper.TaosFreeResult(rs.result) 139 | locker.Unlock() 140 | } 141 | rs.result = nil 142 | } 143 | 144 | if rs.handler != nil { 145 | async.PutHandler(rs.handler) 146 | rs.handler = nil 147 | } 148 | } 149 | --------------------------------------------------------------------------------