diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 609505c..0ea2913 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -17,7 +17,7 @@ env: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Build outputs: commit_id: ${{ steps.get_commit_id.outputs.commit_id }} @@ -110,7 +110,7 @@ jobs: tar -zcvf server.tar.gz ./release test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: build strategy: matrix: diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index fe46a29..64f795c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -11,7 +11,7 @@ env: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Build outputs: commit_id: ${{ steps.get_commit_id.outputs.commit_id }} @@ -83,7 +83,7 @@ jobs: tar -zcvf server.tar.gz ./release test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: build strategy: matrix: diff --git a/common/column.go b/common/column.go index 0408b77..aad229c 100644 --- a/common/column.go +++ b/common/column.go @@ -42,4 +42,5 @@ var ColumnTypeMap = map[int]reflect.Type{ TSDB_DATA_TYPE_TIMESTAMP: NullTime, TSDB_DATA_TYPE_JSON: NullJson, TSDB_DATA_TYPE_VARBINARY: Bytes, + TSDB_DATA_TYPE_GEOMETRY: Bytes, } diff --git a/common/const.go b/common/const.go index 93d7e0d..ea2b7c9 100644 --- a/common/const.go +++ b/common/const.go @@ -23,90 +23,6 @@ const ( TSDB_OPTION_USE_ADAPTER ) -const ( - TSDB_DATA_TYPE_NULL = 0 // 1 bytes - TSDB_DATA_TYPE_BOOL = 1 // 1 bytes - TSDB_DATA_TYPE_TINYINT = 2 // 1 byte - TSDB_DATA_TYPE_SMALLINT = 3 // 2 bytes - TSDB_DATA_TYPE_INT = 4 // 4 bytes - TSDB_DATA_TYPE_BIGINT = 5 // 8 bytes - TSDB_DATA_TYPE_FLOAT = 6 // 4 bytes - TSDB_DATA_TYPE_DOUBLE = 7 // 8 bytes - TSDB_DATA_TYPE_BINARY = 8 // string - TSDB_DATA_TYPE_TIMESTAMP = 9 // 8 bytes - TSDB_DATA_TYPE_NCHAR = 10 // unicode string - TSDB_DATA_TYPE_UTINYINT = 11 // 1 byte - TSDB_DATA_TYPE_USMALLINT = 12 // 2 bytes - TSDB_DATA_TYPE_UINT = 13 // 4 bytes - TSDB_DATA_TYPE_UBIGINT = 14 // 8 bytes - TSDB_DATA_TYPE_JSON = 15 - TSDB_DATA_TYPE_VARBINARY = 16 - TSDB_DATA_TYPE_DECIMAL = 17 - TSDB_DATA_TYPE_BLOB = 18 - TSDB_DATA_TYPE_MEDIUMBLOB = 19 - TSDB_DATA_TYPE_MAX = 20 -) - -const ( - TSDB_DATA_TYPE_NULL_Str = "NULL" - TSDB_DATA_TYPE_BOOL_Str = "BOOL" - TSDB_DATA_TYPE_TINYINT_Str = "TINYINT" - TSDB_DATA_TYPE_SMALLINT_Str = "SMALLINT" - TSDB_DATA_TYPE_INT_Str = "INT" - TSDB_DATA_TYPE_BIGINT_Str = "BIGINT" - TSDB_DATA_TYPE_FLOAT_Str = "FLOAT" - TSDB_DATA_TYPE_DOUBLE_Str = "DOUBLE" - TSDB_DATA_TYPE_BINARY_Str = "VARCHAR" - TSDB_DATA_TYPE_TIMESTAMP_Str = "TIMESTAMP" - TSDB_DATA_TYPE_NCHAR_Str = "NCHAR" - TSDB_DATA_TYPE_UTINYINT_Str = "TINYINT UNSIGNED" - TSDB_DATA_TYPE_USMALLINT_Str = "SMALLINT UNSIGNED" - TSDB_DATA_TYPE_UINT_Str = "INT UNSIGNED" - TSDB_DATA_TYPE_UBIGINT_Str = "BIGINT UNSIGNED" - TSDB_DATA_TYPE_JSON_Str = "JSON" - TSDB_DATA_TYPE_VARBINARY_Str = "VARBINARY" -) - -var TypeNameMap = map[int]string{ - TSDB_DATA_TYPE_NULL: TSDB_DATA_TYPE_NULL_Str, - TSDB_DATA_TYPE_BOOL: TSDB_DATA_TYPE_BOOL_Str, - TSDB_DATA_TYPE_TINYINT: TSDB_DATA_TYPE_TINYINT_Str, - TSDB_DATA_TYPE_SMALLINT: TSDB_DATA_TYPE_SMALLINT_Str, - TSDB_DATA_TYPE_INT: TSDB_DATA_TYPE_INT_Str, - TSDB_DATA_TYPE_BIGINT: TSDB_DATA_TYPE_BIGINT_Str, - TSDB_DATA_TYPE_FLOAT: TSDB_DATA_TYPE_FLOAT_Str, - TSDB_DATA_TYPE_DOUBLE: TSDB_DATA_TYPE_DOUBLE_Str, - TSDB_DATA_TYPE_BINARY: TSDB_DATA_TYPE_BINARY_Str, - TSDB_DATA_TYPE_TIMESTAMP: TSDB_DATA_TYPE_TIMESTAMP_Str, - TSDB_DATA_TYPE_NCHAR: TSDB_DATA_TYPE_NCHAR_Str, - TSDB_DATA_TYPE_UTINYINT: TSDB_DATA_TYPE_UTINYINT_Str, - TSDB_DATA_TYPE_USMALLINT: TSDB_DATA_TYPE_USMALLINT_Str, - TSDB_DATA_TYPE_UINT: TSDB_DATA_TYPE_UINT_Str, - TSDB_DATA_TYPE_UBIGINT: TSDB_DATA_TYPE_UBIGINT_Str, - TSDB_DATA_TYPE_JSON: TSDB_DATA_TYPE_JSON_Str, - TSDB_DATA_TYPE_VARBINARY: TSDB_DATA_TYPE_VARBINARY_Str, -} - -var NameTypeMap = map[string]int{ - TSDB_DATA_TYPE_NULL_Str: TSDB_DATA_TYPE_NULL, - TSDB_DATA_TYPE_BOOL_Str: TSDB_DATA_TYPE_BOOL, - TSDB_DATA_TYPE_TINYINT_Str: TSDB_DATA_TYPE_TINYINT, - TSDB_DATA_TYPE_SMALLINT_Str: TSDB_DATA_TYPE_SMALLINT, - TSDB_DATA_TYPE_INT_Str: TSDB_DATA_TYPE_INT, - TSDB_DATA_TYPE_BIGINT_Str: TSDB_DATA_TYPE_BIGINT, - TSDB_DATA_TYPE_FLOAT_Str: TSDB_DATA_TYPE_FLOAT, - TSDB_DATA_TYPE_DOUBLE_Str: TSDB_DATA_TYPE_DOUBLE, - TSDB_DATA_TYPE_BINARY_Str: TSDB_DATA_TYPE_BINARY, - TSDB_DATA_TYPE_TIMESTAMP_Str: TSDB_DATA_TYPE_TIMESTAMP, - TSDB_DATA_TYPE_NCHAR_Str: TSDB_DATA_TYPE_NCHAR, - TSDB_DATA_TYPE_UTINYINT_Str: TSDB_DATA_TYPE_UTINYINT, - TSDB_DATA_TYPE_USMALLINT_Str: TSDB_DATA_TYPE_USMALLINT, - TSDB_DATA_TYPE_UINT_Str: TSDB_DATA_TYPE_UINT, - TSDB_DATA_TYPE_UBIGINT_Str: TSDB_DATA_TYPE_UBIGINT, - TSDB_DATA_TYPE_JSON_Str: TSDB_DATA_TYPE_JSON, - TSDB_DATA_TYPE_VARBINARY_Str: TSDB_DATA_TYPE_VARBINARY, -} - const ( TMQ_RES_INVALID = -1 TMQ_RES_DATA = 1 diff --git a/common/datatype.go b/common/datatype.go new file mode 100644 index 0000000..ea85688 --- /dev/null +++ b/common/datatype.go @@ -0,0 +1,299 @@ +package common + +import ( + "errors" + "reflect" +) + +type DBType struct { + IsVarData bool + ID int + Length int + Name string + ReflectType reflect.Type +} + +var NullType = DBType{ + ID: TSDB_DATA_TYPE_NULL, + Name: TSDB_DATA_TYPE_NULL_Str, + Length: 0, + ReflectType: UnknownType, + IsVarData: false, +} + +var BoolType = DBType{ + ID: TSDB_DATA_TYPE_BOOL, + Name: TSDB_DATA_TYPE_BOOL_Str, + Length: 1, + ReflectType: NullBool, + IsVarData: false, +} + +var TinyIntType = DBType{ + ID: TSDB_DATA_TYPE_TINYINT, + Name: TSDB_DATA_TYPE_TINYINT_Str, + Length: 1, + ReflectType: NullInt8, + IsVarData: false, +} + +var SmallIntType = DBType{ + ID: TSDB_DATA_TYPE_SMALLINT, + Name: TSDB_DATA_TYPE_SMALLINT_Str, + Length: 2, + ReflectType: NullInt16, + IsVarData: false, +} + +var IntType = DBType{ + ID: TSDB_DATA_TYPE_INT, + Name: TSDB_DATA_TYPE_INT_Str, + Length: 4, + ReflectType: NullInt32, + IsVarData: false, +} + +var BigIntType = DBType{ + ID: TSDB_DATA_TYPE_BIGINT, + Name: TSDB_DATA_TYPE_BIGINT_Str, + Length: 8, + ReflectType: NullInt64, + IsVarData: false, +} + +var UTinyIntType = DBType{ + ID: TSDB_DATA_TYPE_UTINYINT, + Name: TSDB_DATA_TYPE_UTINYINT_Str, + Length: 1, + ReflectType: NullUInt8, + IsVarData: false, +} + +var USmallIntType = DBType{ + ID: TSDB_DATA_TYPE_USMALLINT, + Name: TSDB_DATA_TYPE_USMALLINT_Str, + Length: 2, + ReflectType: NullUInt16, + IsVarData: false, +} + +var UIntType = DBType{ + ID: TSDB_DATA_TYPE_UINT, + Name: TSDB_DATA_TYPE_UINT_Str, + Length: 4, + ReflectType: NullUInt32, + IsVarData: false, +} + +var UBigIntType = DBType{ + ID: TSDB_DATA_TYPE_UBIGINT, + Name: TSDB_DATA_TYPE_UBIGINT_Str, + Length: 8, + ReflectType: NullUInt64, + IsVarData: false, +} + +var FloatType = DBType{ + ID: TSDB_DATA_TYPE_FLOAT, + Name: TSDB_DATA_TYPE_FLOAT_Str, + Length: 4, + ReflectType: NullFloat32, + IsVarData: false, +} + +var DoubleType = DBType{ + ID: TSDB_DATA_TYPE_DOUBLE, + Name: TSDB_DATA_TYPE_DOUBLE_Str, + Length: 8, + ReflectType: NullFloat64, + IsVarData: false, +} + +var BinaryType = DBType{ + ID: TSDB_DATA_TYPE_BINARY, + Name: TSDB_DATA_TYPE_BINARY_Str, + Length: 0, + ReflectType: NullString, + IsVarData: true, +} + +var NcharType = DBType{ + ID: TSDB_DATA_TYPE_NCHAR, + Name: TSDB_DATA_TYPE_NCHAR_Str, + Length: 0, + ReflectType: NullString, + IsVarData: true, +} + +var TimestampType = DBType{ + ID: TSDB_DATA_TYPE_TIMESTAMP, + Name: TSDB_DATA_TYPE_TIMESTAMP_Str, + Length: 8, + ReflectType: NullTime, + IsVarData: false, +} + +var JsonType = DBType{ + ID: TSDB_DATA_TYPE_JSON, + Name: TSDB_DATA_TYPE_JSON_Str, + Length: 0, + ReflectType: NullJson, + IsVarData: true, +} + +var VarBinaryType = DBType{ + ID: TSDB_DATA_TYPE_VARBINARY, + Name: TSDB_DATA_TYPE_VARBINARY_Str, + Length: 0, + ReflectType: NullString, + IsVarData: true, +} + +var GeometryType = DBType{ + ID: TSDB_DATA_TYPE_GEOMETRY, + Name: TSDB_DATA_TYPE_GEOMETRY_Str, + Length: 0, + ReflectType: NullString, + IsVarData: true, +} + +var allType = [21]*DBType{ + //TSDB_DATA_TYPE_NULL = 0 + &NullType, + //TSDB_DATA_TYPE_BOOL = 1 + &BoolType, + //TSDB_DATA_TYPE_TINYINT = 2 + &TinyIntType, + //TSDB_DATA_TYPE_SMALLINT = 3 + &SmallIntType, + //TSDB_DATA_TYPE_INT = 4 + &IntType, + //TSDB_DATA_TYPE_BIGINT = 5 + &BigIntType, + //TSDB_DATA_TYPE_FLOAT = 6 + &FloatType, + //TSDB_DATA_TYPE_DOUBLE = 7 + &DoubleType, + //TSDB_DATA_TYPE_BINARY = 8 + &BinaryType, + //TSDB_DATA_TYPE_TIMESTAMP = 9 + &TimestampType, + //TSDB_DATA_TYPE_NCHAR = 10 + &NcharType, + //TSDB_DATA_TYPE_UTINYINT = 11 + &UTinyIntType, + //TSDB_DATA_TYPE_USMALLINT = 12 + &USmallIntType, + //TSDB_DATA_TYPE_UINT = 13 + &UIntType, + //TSDB_DATA_TYPE_UBIGINT = 14 + &UBigIntType, + //TSDB_DATA_TYPE_JSON = 15 + &JsonType, + //TSDB_DATA_TYPE_VARBINARY = 16 + &VarBinaryType, + //TSDB_DATA_TYPE_DECIMAL = 17 + nil, + //TSDB_DATA_TYPE_BLOB = 18 + nil, + //TSDB_DATA_TYPE_MEDIUMBLOB = 19 + nil, + //TSDB_DATA_TYPE_GEOMETRY = 20 + &GeometryType, +} + +const ( + TSDB_DATA_TYPE_NULL = 0 // 1 bytes + TSDB_DATA_TYPE_BOOL = 1 // 1 bytes + TSDB_DATA_TYPE_TINYINT = 2 // 1 byte + TSDB_DATA_TYPE_SMALLINT = 3 // 2 bytes + TSDB_DATA_TYPE_INT = 4 // 4 bytes + TSDB_DATA_TYPE_BIGINT = 5 // 8 bytes + TSDB_DATA_TYPE_FLOAT = 6 // 4 bytes + TSDB_DATA_TYPE_DOUBLE = 7 // 8 bytes + TSDB_DATA_TYPE_BINARY = 8 // string + TSDB_DATA_TYPE_TIMESTAMP = 9 // 8 bytes + TSDB_DATA_TYPE_NCHAR = 10 // unicode string + TSDB_DATA_TYPE_UTINYINT = 11 // 1 byte + TSDB_DATA_TYPE_USMALLINT = 12 // 2 bytes + TSDB_DATA_TYPE_UINT = 13 // 4 bytes + TSDB_DATA_TYPE_UBIGINT = 14 // 8 bytes + TSDB_DATA_TYPE_JSON = 15 + TSDB_DATA_TYPE_VARBINARY = 16 + TSDB_DATA_TYPE_DECIMAL = 17 + TSDB_DATA_TYPE_BLOB = 18 + TSDB_DATA_TYPE_MEDIUMBLOB = 19 + TSDB_DATA_TYPE_GEOMETRY = 20 +) + +const ( + TSDB_DATA_TYPE_NULL_Str = "NULL" + TSDB_DATA_TYPE_BOOL_Str = "BOOL" + TSDB_DATA_TYPE_TINYINT_Str = "TINYINT" + TSDB_DATA_TYPE_SMALLINT_Str = "SMALLINT" + TSDB_DATA_TYPE_INT_Str = "INT" + TSDB_DATA_TYPE_BIGINT_Str = "BIGINT" + TSDB_DATA_TYPE_FLOAT_Str = "FLOAT" + TSDB_DATA_TYPE_DOUBLE_Str = "DOUBLE" + TSDB_DATA_TYPE_BINARY_Str = "VARCHAR" + TSDB_DATA_TYPE_TIMESTAMP_Str = "TIMESTAMP" + TSDB_DATA_TYPE_NCHAR_Str = "NCHAR" + TSDB_DATA_TYPE_UTINYINT_Str = "TINYINT UNSIGNED" + TSDB_DATA_TYPE_USMALLINT_Str = "SMALLINT UNSIGNED" + TSDB_DATA_TYPE_UINT_Str = "INT UNSIGNED" + TSDB_DATA_TYPE_UBIGINT_Str = "BIGINT UNSIGNED" + TSDB_DATA_TYPE_JSON_Str = "JSON" + TSDB_DATA_TYPE_VARBINARY_Str = "VARBINARY" + TSDB_DATA_TYPE_GEOMETRY_Str = "GEOMETRY" +) + +var TypeNameMap = map[int]string{ + TSDB_DATA_TYPE_NULL: TSDB_DATA_TYPE_NULL_Str, + TSDB_DATA_TYPE_BOOL: TSDB_DATA_TYPE_BOOL_Str, + TSDB_DATA_TYPE_TINYINT: TSDB_DATA_TYPE_TINYINT_Str, + TSDB_DATA_TYPE_SMALLINT: TSDB_DATA_TYPE_SMALLINT_Str, + TSDB_DATA_TYPE_INT: TSDB_DATA_TYPE_INT_Str, + TSDB_DATA_TYPE_BIGINT: TSDB_DATA_TYPE_BIGINT_Str, + TSDB_DATA_TYPE_FLOAT: TSDB_DATA_TYPE_FLOAT_Str, + TSDB_DATA_TYPE_DOUBLE: TSDB_DATA_TYPE_DOUBLE_Str, + TSDB_DATA_TYPE_BINARY: TSDB_DATA_TYPE_BINARY_Str, + TSDB_DATA_TYPE_TIMESTAMP: TSDB_DATA_TYPE_TIMESTAMP_Str, + TSDB_DATA_TYPE_NCHAR: TSDB_DATA_TYPE_NCHAR_Str, + TSDB_DATA_TYPE_UTINYINT: TSDB_DATA_TYPE_UTINYINT_Str, + TSDB_DATA_TYPE_USMALLINT: TSDB_DATA_TYPE_USMALLINT_Str, + TSDB_DATA_TYPE_UINT: TSDB_DATA_TYPE_UINT_Str, + TSDB_DATA_TYPE_UBIGINT: TSDB_DATA_TYPE_UBIGINT_Str, + TSDB_DATA_TYPE_JSON: TSDB_DATA_TYPE_JSON_Str, + TSDB_DATA_TYPE_VARBINARY: TSDB_DATA_TYPE_VARBINARY_Str, + TSDB_DATA_TYPE_GEOMETRY: TSDB_DATA_TYPE_GEOMETRY_Str, +} + +var NameTypeMap = map[string]int{ + TSDB_DATA_TYPE_NULL_Str: TSDB_DATA_TYPE_NULL, + TSDB_DATA_TYPE_BOOL_Str: TSDB_DATA_TYPE_BOOL, + TSDB_DATA_TYPE_TINYINT_Str: TSDB_DATA_TYPE_TINYINT, + TSDB_DATA_TYPE_SMALLINT_Str: TSDB_DATA_TYPE_SMALLINT, + TSDB_DATA_TYPE_INT_Str: TSDB_DATA_TYPE_INT, + TSDB_DATA_TYPE_BIGINT_Str: TSDB_DATA_TYPE_BIGINT, + TSDB_DATA_TYPE_FLOAT_Str: TSDB_DATA_TYPE_FLOAT, + TSDB_DATA_TYPE_DOUBLE_Str: TSDB_DATA_TYPE_DOUBLE, + TSDB_DATA_TYPE_BINARY_Str: TSDB_DATA_TYPE_BINARY, + TSDB_DATA_TYPE_TIMESTAMP_Str: TSDB_DATA_TYPE_TIMESTAMP, + TSDB_DATA_TYPE_NCHAR_Str: TSDB_DATA_TYPE_NCHAR, + TSDB_DATA_TYPE_UTINYINT_Str: TSDB_DATA_TYPE_UTINYINT, + TSDB_DATA_TYPE_USMALLINT_Str: TSDB_DATA_TYPE_USMALLINT, + TSDB_DATA_TYPE_UINT_Str: TSDB_DATA_TYPE_UINT, + TSDB_DATA_TYPE_UBIGINT_Str: TSDB_DATA_TYPE_UBIGINT, + TSDB_DATA_TYPE_JSON_Str: TSDB_DATA_TYPE_JSON, + TSDB_DATA_TYPE_VARBINARY_Str: TSDB_DATA_TYPE_VARBINARY, + TSDB_DATA_TYPE_GEOMETRY_Str: TSDB_DATA_TYPE_GEOMETRY, +} + +var NotSupportType = errors.New("not support type") + +func GetColType(colType int) (*DBType, error) { + if colType > len(allType) || colType < 0 { + return nil, NotSupportType + } + return allType[colType], nil +} diff --git a/common/param/column.go b/common/param/column.go index a11839c..de5dedc 100644 --- a/common/param/column.go +++ b/common/param/column.go @@ -196,6 +196,18 @@ func (c *ColumnType) AddJson(strMaxLen int) *ColumnType { return c } +func (c *ColumnType) AddGeometry(strMaxLen int) *ColumnType { + if c.column >= c.size { + return c + } + c.value[c.column] = &types.ColumnType{ + Type: types.TaosGeometryType, + MaxLen: strMaxLen, + } + c.column += 1 + return c +} + func (c *ColumnType) GetValue() ([]*types.ColumnType, error) { if c.size != c.column { return nil, fmt.Errorf("incomplete column expect %d columns set %d columns", c.size, c.column) diff --git a/common/param/param.go b/common/param/param.go index 680d5f1..a14854b 100644 --- a/common/param/param.go +++ b/common/param/param.go @@ -142,6 +142,13 @@ func (p *Param) SetJson(offset int, value []byte) { p.value[offset] = taosTypes.TaosJson(value) } +func (p *Param) SetGeometry(offset int, value []byte) { + if offset >= p.size { + return + } + p.value[offset] = taosTypes.TaosGeometry(value) +} + func (p *Param) AddBool(value bool) *Param { if p.offset >= p.size { return p @@ -298,6 +305,15 @@ func (p *Param) AddJson(value []byte) *Param { return p } +func (p *Param) AddGeometry(value []byte) *Param { + if p.offset >= p.size { + return p + } + p.value[p.offset] = taosTypes.TaosGeometry(value) + p.offset += 1 + return p +} + func (p *Param) GetValues() []driver.Value { return p.value } diff --git a/common/parser/block.go b/common/parser/block.go index d3bea86..7228e90 100644 --- a/common/parser/block.go +++ b/common/parser/block.go @@ -81,7 +81,11 @@ func RawBlockGetColDataOffset(colCount int) uintptr { type FormatTimeFunc func(ts int64, precision int) driver.Value func IsVarDataType(colType uint8) bool { - return colType == common.TSDB_DATA_TYPE_BINARY || colType == common.TSDB_DATA_TYPE_NCHAR || colType == common.TSDB_DATA_TYPE_JSON || colType == common.TSDB_DATA_TYPE_VARBINARY + return colType == common.TSDB_DATA_TYPE_BINARY || + colType == common.TSDB_DATA_TYPE_NCHAR || + colType == common.TSDB_DATA_TYPE_JSON || + colType == common.TSDB_DATA_TYPE_VARBINARY || + colType == common.TSDB_DATA_TYPE_GEOMETRY } func BitmapLen(n int) int { @@ -124,6 +128,7 @@ var rawConvertVarDataMap = map[uint8]rawConvertVarDataFunc{ uint8(common.TSDB_DATA_TYPE_NCHAR): rawConvertNchar, uint8(common.TSDB_DATA_TYPE_JSON): rawConvertJson, uint8(common.TSDB_DATA_TYPE_VARBINARY): rawConvertVarBinary, + uint8(common.TSDB_DATA_TYPE_GEOMETRY): rawConvertGeometry, } func ItemIsNull(pHeader unsafe.Pointer, row int) bool { @@ -191,7 +196,24 @@ func rawConvertTime(pStart unsafe.Pointer, row int, arg ...interface{}) driver.V } func rawConvertVarBinary(pHeader, pStart unsafe.Pointer, row int) driver.Value { - return rawConvertBinary(pHeader, pStart, row) + offset := *((*int32)(pointer.AddUintptr(pHeader, uintptr(row*4)))) + if offset == -1 { + return nil + } + currentRow := pointer.AddUintptr(pStart, uintptr(offset)) + clen := *((*uint16)(currentRow)) + currentRow = unsafe.Pointer(uintptr(currentRow) + 2) + + binaryVal := make([]byte, clen) + + for index := uint16(0); index < clen; index++ { + binaryVal[index] = *((*byte)(unsafe.Pointer(uintptr(currentRow) + uintptr(index)))) + } + return binaryVal[:] +} + +func rawConvertGeometry(pHeader, pStart unsafe.Pointer, row int) driver.Value { + return rawConvertVarBinary(pHeader, pStart, row) } func rawConvertBinary(pHeader, pStart unsafe.Pointer, row int) driver.Value { diff --git a/common/parser/block_test.go b/common/parser/block_test.go index f62d7b7..5b7232a 100644 --- a/common/parser/block_test.go +++ b/common/parser/block_test.go @@ -627,7 +627,9 @@ func TestParseBlock(t *testing.T) { "c10 float,"+ "c11 double,"+ "c12 binary(20),"+ - "c13 nchar(20)"+ + "c13 nchar(20),"+ + "c14 varbinary(20),"+ + "c15 geometry(100)"+ ") tags (info json)") code = wrapper.TaosError(res) if code != 0 { @@ -639,7 +641,9 @@ func TestParseBlock(t *testing.T) { wrapper.TaosFreeResult(res) now := time.Now() after1s := now.Add(time.Second) - sql := fmt.Sprintf("insert into parse_block.t0 using parse_block.all_type tags('{\"a\":1}') values('%s',1,1,1,1,1,1,1,1,1,1,1,'test_binary','test_nchar')('%s',null,null,null,null,null,null,null,null,null,null,null,null,null)", now.Format(time.RFC3339Nano), after1s.Format(time.RFC3339Nano)) + sql := fmt.Sprintf("insert into parse_block.t0 using parse_block.all_type tags('{\"a\":1}') "+ + "values('%s',1,1,1,1,1,1,1,1,1,1,1,'test_binary','test_nchar','test_varbinary','POINT(100 100)')"+ + "('%s',null,null,null,null,null,null,null,null,null,null,null,null,null,null,null)", now.Format(time.RFC3339Nano), after1s.Format(time.RFC3339Nano)) res = wrapper.TaosQuery(conn, sql) code = wrapper.TaosError(res) if code != 0 { @@ -682,11 +686,11 @@ func TestParseBlock(t *testing.T) { version := RawBlockGetVersion(block) assert.Equal(t, int32(1), version) length := RawBlockGetLength(block) - assert.Equal(t, int32(374), length) + assert.Equal(t, int32(447), length) rows := RawBlockGetNumOfRows(block) assert.Equal(t, int32(2), rows) columns := RawBlockGetNumOfCols(block) - assert.Equal(t, int32(15), columns) + assert.Equal(t, int32(17), columns) hasColumnSegment := RawBlockGetHasColumnSegment(block) assert.Equal(t, int32(-2147483648), hasColumnSegment) groupId := RawBlockGetGroupID(block) @@ -752,6 +756,14 @@ func TestParseBlock(t *testing.T) { ColType: 10, Bytes: 82, }, + { + ColType: 16, + Bytes: 22, + }, + { + ColType: 20, + Bytes: 102, + }, { ColType: 15, Bytes: 16384, @@ -779,11 +791,13 @@ func TestParseBlock(t *testing.T) { assert.Equal(t, float64(1), row1[11].(float64)) assert.Equal(t, "test_binary", row1[12].(string)) assert.Equal(t, "test_nchar", row1[13].(string)) - assert.Equal(t, []byte(`{"a":1}`), row1[14].([]byte)) + assert.Equal(t, []byte("test_varbinary"), row1[14].([]byte)) + assert.Equal(t, []byte{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40}, row1[15].([]byte)) + assert.Equal(t, []byte(`{"a":1}`), row1[16].([]byte)) row2 := data[1] assert.Equal(t, after1s.UnixNano()/1e6, row2[0].(time.Time).UnixNano()/1e6) - for i := 1; i < 14; i++ { + for i := 1; i < 16; i++ { assert.Nil(t, row2[i]) } - assert.Equal(t, []byte(`{"a":1}`), row2[14].([]byte)) + assert.Equal(t, []byte(`{"a":1}`), row2[16].([]byte)) } diff --git a/common/serializer/block.go b/common/serializer/block.go index 830708e..03a53f2 100644 --- a/common/serializer/block.go +++ b/common/serializer/block.go @@ -394,6 +394,34 @@ func SerializeRawBlock(params []*param.Param, colType *param.ColumnType) ([]byte } lengthData = appendUint32(lengthData, uint32(length)) data = append(data, dataTmp...) + case taosTypes.TaosGeometryType: + colInfoData = append(colInfoData, common.TSDB_DATA_TYPE_GEOMETRY) + colInfoData = appendUint32(colInfoData, uint32(0)) + length := 0 + dataTmp := make([]byte, Int32Size*rows) + rowData := params[colIndex].GetValues() + for rowIndex := 0; rowIndex < rows; rowIndex++ { + offset := Int32Size * rowIndex + if rowData[rowIndex] == nil { + for i := 0; i < Int32Size; i++ { + // -1 + dataTmp[offset+i] = byte(255) + } + } else { + v, is := rowData[rowIndex].(taosTypes.TaosGeometry) + if !is { + return nil, DataTypeWrong + } + for i := 0; i < Int32Size; i++ { + dataTmp[offset+i] = byte(length >> (8 * i)) + } + dataTmp = appendUint16(dataTmp, uint16(len(v))) + dataTmp = append(dataTmp, v...) + length += len(v) + Int16Size + } + } + lengthData = appendUint32(lengthData, uint32(length)) + data = append(data, dataTmp...) case taosTypes.TaosNcharType: colInfoData = append(colInfoData, common.TSDB_DATA_TYPE_NCHAR) colInfoData = appendUint32(colInfoData, uint32(0)) diff --git a/common/serializer/block_test.go b/common/serializer/block_test.go index 9f6811b..c59d988 100644 --- a/common/serializer/block_test.go +++ b/common/serializer/block_test.go @@ -146,9 +146,55 @@ func TestSerializeRawBlock(t *testing.T) { param.NewParam(3).AddDouble(1).AddNull().AddDouble(1), param.NewParam(3).AddBinary([]byte("test_binary")).AddNull().AddBinary([]byte("test_binary")), param.NewParam(3).AddNchar("test_nchar").AddNull().AddNchar("test_nchar"), + param.NewParam(3).AddVarBinary([]byte("test_varbinary")).AddNull().AddVarBinary([]byte("test_varbinary")), + param.NewParam(3).AddGeometry([]byte{ + 0x01, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x59, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x59, + 0x40, + }).AddNull().AddGeometry([]byte{ + 0x01, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x59, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x59, + 0x40, + }), param.NewParam(3).AddJson([]byte("{\"a\":1}")).AddNull().AddJson([]byte("{\"a\":1}")), }, - colType: param.NewColumnType(15). + colType: param.NewColumnType(17). AddTimestamp(). AddBool(). AddTinyint(). @@ -163,13 +209,15 @@ func TestSerializeRawBlock(t *testing.T) { AddDouble(). AddBinary(0). AddNchar(0). + AddVarBinary(0). + AddGeometry(0). AddJson(0), }, want: []byte{ 0x01, 0x00, 0x00, 0x00, - 0xec, 0x01, 0x00, 0x00, + 0x64, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //types @@ -187,6 +235,8 @@ func TestSerializeRawBlock(t *testing.T) { 0x07, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, //lengths 0x18, 0x00, 0x00, 0x00, @@ -203,6 +253,8 @@ func TestSerializeRawBlock(t *testing.T) { 0x18, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, // ts 0x40, @@ -300,6 +352,24 @@ func TestSerializeRawBlock(t *testing.T) { 0x6e, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + //varbinary + 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, + 0x0e, 0x00, + 0x74, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, + 0x0e, 0x00, + 0x74, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, + + //geometry + 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0x17, 0x00, 0x00, 0x00, + 0x15, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, + 0x15, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, + //json 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, diff --git a/common/stmt/field.go b/common/stmt/field.go index c8a46d4..20c51b9 100644 --- a/common/stmt/field.go +++ b/common/stmt/field.go @@ -49,6 +49,8 @@ func (s *StmtField) GetType() (*types.ColumnType, error) { return &types.ColumnType{Type: types.TaosTimestampType}, nil case common.TSDB_DATA_TYPE_JSON: return &types.ColumnType{Type: types.TaosJsonType}, nil + case common.TSDB_DATA_TYPE_GEOMETRY: + return &types.ColumnType{Type: types.TaosGeometryType}, nil } return nil, fmt.Errorf("unsupported type: %d, name %s", s.FieldType, s.Name) } diff --git a/examples/sqlstmt/main.go b/examples/sqlstmt/main.go new file mode 100644 index 0000000..1215d42 --- /dev/null +++ b/examples/sqlstmt/main.go @@ -0,0 +1,121 @@ +package main + +import ( + "database/sql" + "fmt" + "time" + + _ "github.com/taosdata/driver-go/v3/taosSql" +) + +var ( + driverName = "taosSql" + user = "root" + password = "taosdata" + host = "" + port = 6030 + dataSourceName = fmt.Sprintf("%s:%s@/tcp(%s:%d)/%s?interpolateParams=true", user, password, host, port, "") +) + +func main() { + db, err := sql.Open(driverName, dataSourceName) + if err != nil { + panic(err) + } + defer db.Close() + defer func() { + db.Exec("drop database if exists test_stmt_driver") + }() + _, err = db.Exec("create database if not exists test_stmt_driver") + if err != nil { + panic(err) + } + _, err = db.Exec("create table if not exists test_stmt_driver.ct(ts timestamp," + + "c1 bool," + + "c2 tinyint," + + "c3 smallint," + + "c4 int," + + "c5 bigint," + + "c6 tinyint unsigned," + + "c7 smallint unsigned," + + "c8 int unsigned," + + "c9 bigint unsigned," + + "c10 float," + + "c11 double," + + "c12 binary(20)," + + "c13 nchar(20)" + + ")") + if err != nil { + panic(err) + } + stmt, err := db.Prepare("insert into test_stmt_driver.ct values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)") + if err != nil { + panic(err) + } + now := time.Now() + result, err := stmt.Exec(now, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, "binary", "nchar") + if err != nil { + panic(err) + } + affected, err := result.RowsAffected() + if err != nil { + panic(err) + } + fmt.Println("affected", affected) + stmt.Close() + cr := 0 + err = db.QueryRow("select count(*) from test_stmt_driver.ct where ts = ?", now).Scan(&cr) + if err != nil { + panic(err) + } + fmt.Println("count", cr) + stmt, err = db.Prepare("select * from test_stmt_driver.ct where ts = ?") + if err != nil { + panic(err) + } + rows, err := stmt.Query(now) + if err != nil { + panic(err) + } + columns, err := rows.Columns() + if err != nil { + panic(err) + } + fmt.Println(columns) + count := 0 + for rows.Next() { + count += 1 + var ( + ts time.Time + c1 bool + c2 int8 + c3 int16 + c4 int32 + c5 int64 + c6 uint8 + c7 uint16 + c8 uint32 + c9 uint64 + c10 float32 + c11 float64 + c12 string + c13 string + ) + err = rows.Scan(&ts, + &c1, + &c2, + &c3, + &c4, + &c5, + &c6, + &c7, + &c8, + &c9, + &c10, + &c11, + &c12, + &c13) + fmt.Println(ts, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) + } + fmt.Println("rows", count) +} diff --git a/taosSql/connection.go b/taosSql/connection.go index 086ee09..288a74f 100644 --- a/taosSql/connection.go +++ b/taosSql/connection.go @@ -3,7 +3,6 @@ package taosSql import ( "context" "database/sql/driver" - errors2 "errors" "unsafe" "github.com/taosdata/driver-go/v3/common" @@ -49,6 +48,7 @@ func (tc *taosConn) Prepare(query string) (driver.Stmt, error) { } locker.Lock() isInsert, code := wrapper.TaosStmtIsInsert(stmtP) + locker.Unlock() if code != 0 { errStr := wrapper.TaosStmtErrStr(stmtP) err := errors.NewError(code, errStr) @@ -57,13 +57,6 @@ func (tc *taosConn) Prepare(query string) (driver.Stmt, error) { locker.Unlock() return nil, err } - if !isInsert { - locker.Lock() - wrapper.TaosStmtClose(stmtP) - locker.Unlock() - return nil, errors2.New("only supports insert statements") - } - locker.Unlock() stmt := &Stmt{ tc: tc, pSql: query, diff --git a/taosSql/rows.go b/taosSql/rows.go index 685f66f..aaf684d 100644 --- a/taosSql/rows.go +++ b/taosSql/rows.go @@ -22,6 +22,7 @@ type rows struct { lengthList []int result unsafe.Pointer precision int + isStmt bool } func (rs *rows) Columns() []string { @@ -41,7 +42,16 @@ func (rs *rows) ColumnTypeScanType(i int) reflect.Type { } func (rs *rows) Close() error { - rs.freeResult() + if rs.handler != nil { + asyncHandlerPool.Put(rs.handler) + rs.handler = nil + } + if !rs.isStmt && rs.result != nil { + locker.Lock() + wrapper.TaosFreeResult(rs.result) + locker.Unlock() + } + rs.result = nil rs.block = nil return nil } @@ -82,6 +92,8 @@ func (rs *rows) Next(dest []driver.Value) error { } func (rs *rows) taosFetchBlock() error { + //rs.blockSize, rs.block = wrapper.TaosFetchBlock(rs.result) + //return nil result := rs.asyncFetchRows() if result.N == 0 { rs.blockSize = 0 @@ -107,16 +119,3 @@ func (rs *rows) asyncFetchRows() *handler.AsyncResult { r := <-rs.handler.Caller.FetchResult return r } - -func (rs *rows) freeResult() { - if rs.handler != nil { - asyncHandlerPool.Put(rs.handler) - rs.handler = nil - } - if rs.result != nil { - locker.Lock() - wrapper.TaosFreeResult(rs.result) - locker.Unlock() - rs.result = nil - } -} diff --git a/taosSql/statement.go b/taosSql/statement.go index c2219ad..e103a0e 100644 --- a/taosSql/statement.go +++ b/taosSql/statement.go @@ -2,7 +2,6 @@ package taosSql import ( "database/sql/driver" - errors2 "errors" "fmt" "reflect" "strconv" @@ -25,7 +24,6 @@ type Stmt struct { pSql string isInsert bool cols []*stmtCommon.StmtField - //tags []*wrapper.StmtField } func (stmt *Stmt) Close() error { @@ -47,7 +45,7 @@ func (stmt *Stmt) NumInput() int { func (stmt *Stmt) Exec(args []driver.Value) (driver.Result, error) { if stmt.tc == nil || stmt.tc.taos == nil { - return nil, errors.ErrTscInvalidConnection + return nil, driver.ErrBadConn } if len(args) != len(stmt.cols) { return nil, fmt.Errorf("stmt exec error: wrong number of parameters") @@ -74,42 +72,42 @@ func (stmt *Stmt) Exec(args []driver.Value) (driver.Result, error) { } func (stmt *Stmt) Query(args []driver.Value) (driver.Rows, error) { - return nil, errors2.New("unsupported") - //if stmt.tc == nil || stmt.tc.taos == nil { - // return nil, errors.ErrTscInvalidConnection - //} - //locker.Lock() - //defer locker.Unlock() - //code := wrapper.TaosStmtBindParam(stmt.stmt, args) - //if code != 0 { - // errStr := wrapper.TaosStmtErrStr(stmt.stmt) - // return nil, errors.NewError(code, errStr) - //} - //code = wrapper.TaosStmtAddBatch(stmt.stmt) - //if code != 0 { - // errStr := wrapper.TaosStmtErrStr(stmt.stmt) - // return nil, errors.NewError(code, errStr) - //} - //code = wrapper.TaosStmtExecute(stmt.stmt) - //if code != 0 { - // errStr := wrapper.TaosStmtErrStr(stmt.stmt) - // return nil, errors.NewError(code, errStr) - //} - //res := wrapper.TaosStmtUseResult(stmt.stmt) - //handler := asyncHandlerPool.Get() - //numFields := wrapper.TaosNumFields(res) - //rowsHeader, err := wrapper.ReadColumn(res, numFields) - //if err != nil { - // return nil, err - //} - //precision := wrapper.TaosResultPrecision(res) - //rs := &rows{ - // handler: handler, - // rowsHeader: rowsHeader, - // result: res, - // precision: precision, - //} - //return rs, nil + if stmt.tc == nil || stmt.tc.taos == nil { + return nil, driver.ErrBadConn + } + locker.Lock() + defer locker.Unlock() + code := wrapper.TaosStmtBindParam(stmt.stmt, args) + if code != 0 { + errStr := wrapper.TaosStmtErrStr(stmt.stmt) + return nil, errors.NewError(code, errStr) + } + code = wrapper.TaosStmtAddBatch(stmt.stmt) + if code != 0 { + errStr := wrapper.TaosStmtErrStr(stmt.stmt) + return nil, errors.NewError(code, errStr) + } + code = wrapper.TaosStmtExecute(stmt.stmt) + if code != 0 { + errStr := wrapper.TaosStmtErrStr(stmt.stmt) + return nil, errors.NewError(code, errStr) + } + res := wrapper.TaosStmtUseResult(stmt.stmt) + handler := asyncHandlerPool.Get() + numFields := wrapper.TaosNumFields(res) + rowsHeader, err := wrapper.ReadColumn(res, numFields) + if err != nil { + return nil, err + } + precision := wrapper.TaosResultPrecision(res) + rs := &rows{ + handler: handler, + rowsHeader: rowsHeader, + result: res, + precision: precision, + isStmt: true, + } + return rs, nil } func (stmt *Stmt) CheckNamedValue(v *driver.NamedValue) error { @@ -316,6 +314,17 @@ func (stmt *Stmt) CheckNamedValue(v *driver.NamedValue) error { default: return fmt.Errorf("CheckNamedValue:%v can not convert to varbinary", v) } + + case common.TSDB_DATA_TYPE_GEOMETRY: + switch v.Value.(type) { + case string: + v.Value = types.TaosGeometry(v.Value.(string)) + case []byte: + v.Value = types.TaosGeometry(v.Value.([]byte)) + default: + return fmt.Errorf("CheckNamedValue:%v can not convert to geometry", v) + } + case common.TSDB_DATA_TYPE_TIMESTAMP: t, is := v.Value.(time.Time) if is { @@ -491,7 +500,6 @@ func (stmt *Stmt) CheckNamedValue(v *driver.NamedValue) error { v.Value = types.TaosBinary(rv.Bytes()) } else { return fmt.Errorf("CheckNamedValue: can not convert query value %v", v) - } default: return fmt.Errorf("CheckNamedValue: can not convert query value %v", v) diff --git a/taosSql/statement_test.go b/taosSql/statement_test.go index 55a7517..7230d78 100644 --- a/taosSql/statement_test.go +++ b/taosSql/statement_test.go @@ -68,125 +68,125 @@ func TestStmtExec(t *testing.T) { assert.Equal(t, int64(1), affected) } -//func TestStmtQuery(t *testing.T) { -// db, err := sql.Open(driverName, dataSourceName) -// if err != nil { -// t.Error(err) -// return -// } -// defer db.Close() -// defer func() { -// db.Exec("drop database if exists test_stmt_driver") -// }() -// _, err = db.Exec("create database if not exists test_stmt_driver") -// if err != nil { -// t.Error(err) -// return -// } -// _, err = db.Exec("create table if not exists test_stmt_driver.ct(ts timestamp," + -// "c1 bool," + -// "c2 tinyint," + -// "c3 smallint," + -// "c4 int," + -// "c5 bigint," + -// "c6 tinyint unsigned," + -// "c7 smallint unsigned," + -// "c8 int unsigned," + -// "c9 bigint unsigned," + -// "c10 float," + -// "c11 double," + -// "c12 binary(20)," + -// "c13 nchar(20)" + -// ")") -// if err != nil { -// t.Error(err) -// return -// } -// stmt, err := db.Prepare("insert into test_stmt_driver.ct values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)") -// if err != nil { -// t.Error(err) -// return -// } -// now := time.Now() -// result, err := stmt.Exec(now, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, "binary", "nchar") -// if err != nil { -// t.Error(err) -// return -// } -// affected, err := result.RowsAffected() -// if err != nil { -// t.Error(err) -// return -// } -// assert.Equal(t, int64(1), affected) -// stmt.Close() -// stmt, err = db.Prepare("select * from test_stmt_driver.ct where ts = ?") -// if err != nil { -// t.Error(err) -// return -// } -// rows, err := stmt.Query(now) -// if err != nil { -// t.Error(err) -// return -// } -// columns, err := rows.Columns() -// if err != nil { -// t.Error(err) -// return -// } -// assert.Equal(t, []string{"ts", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12", "c13"}, columns) -// count := 0 -// for rows.Next() { -// count += 1 -// var ( -// ts time.Time -// c1 bool -// c2 int8 -// c3 int16 -// c4 int32 -// c5 int64 -// c6 uint8 -// c7 uint16 -// c8 uint32 -// c9 uint64 -// c10 float32 -// c11 float64 -// c12 string -// c13 string -// ) -// err = rows.Scan(&ts, -// &c1, -// &c2, -// &c3, -// &c4, -// &c5, -// &c6, -// &c7, -// &c8, -// &c9, -// &c10, -// &c11, -// &c12, -// &c13) -// assert.NoError(t, err) -// assert.Equal(t, now.UnixNano()/1e6, ts.UnixNano()/1e6) -// assert.Equal(t, true, c1) -// assert.Equal(t, int8(2), c2) -// assert.Equal(t, int16(3), c3) -// assert.Equal(t, int32(4), c4) -// assert.Equal(t, int64(5), c5) -// assert.Equal(t, uint8(6), c6) -// assert.Equal(t, uint16(7), c7) -// assert.Equal(t, uint32(8), c8) -// assert.Equal(t, uint64(9), c9) -// assert.Equal(t, float32(10), c10) -// assert.Equal(t, float64(11), c11) -// assert.Equal(t, "binary", c12) -// assert.Equal(t, "nchar", c13) -// } -// assert.Equal(t, 1, count) -//} +func TestStmtQuery(t *testing.T) { + db, err := sql.Open(driverName, dataSourceName) + if err != nil { + t.Error(err) + return + } + defer db.Close() + defer func() { + db.Exec("drop database if exists test_stmt_driver_q") + }() + _, err = db.Exec("create database if not exists test_stmt_driver_q") + if err != nil { + t.Error(err) + return + } + _, err = db.Exec("create table if not exists test_stmt_driver_q.ct(ts timestamp," + + "c1 bool," + + "c2 tinyint," + + "c3 smallint," + + "c4 int," + + "c5 bigint," + + "c6 tinyint unsigned," + + "c7 smallint unsigned," + + "c8 int unsigned," + + "c9 bigint unsigned," + + "c10 float," + + "c11 double," + + "c12 binary(20)," + + "c13 nchar(20)" + + ")") + if err != nil { + t.Error(err) + return + } + stmt, err := db.Prepare("insert into test_stmt_driver_q.ct values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)") + if err != nil { + t.Error(err) + return + } + now := time.Now() + result, err := stmt.Exec(now, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, "binary", "nchar") + if err != nil { + t.Error(err) + return + } + affected, err := result.RowsAffected() + if err != nil { + t.Error(err) + return + } + assert.Equal(t, int64(1), affected) + stmt.Close() + stmt, err = db.Prepare("select * from test_stmt_driver_q.ct where ts = ?") + if err != nil { + t.Error(err) + return + } + rows, err := stmt.Query(now) + if err != nil { + t.Error(err) + return + } + columns, err := rows.Columns() + if err != nil { + t.Error(err) + return + } + assert.Equal(t, []string{"ts", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12", "c13"}, columns) + count := 0 + for rows.Next() { + count += 1 + var ( + ts time.Time + c1 bool + c2 int8 + c3 int16 + c4 int32 + c5 int64 + c6 uint8 + c7 uint16 + c8 uint32 + c9 uint64 + c10 float32 + c11 float64 + c12 string + c13 string + ) + err = rows.Scan(&ts, + &c1, + &c2, + &c3, + &c4, + &c5, + &c6, + &c7, + &c8, + &c9, + &c10, + &c11, + &c12, + &c13) + assert.NoError(t, err) + assert.Equal(t, now.UnixNano()/1e6, ts.UnixNano()/1e6) + assert.Equal(t, true, c1) + assert.Equal(t, int8(2), c2) + assert.Equal(t, int16(3), c3) + assert.Equal(t, int32(4), c4) + assert.Equal(t, int64(5), c5) + assert.Equal(t, uint8(6), c6) + assert.Equal(t, uint16(7), c7) + assert.Equal(t, uint32(8), c8) + assert.Equal(t, uint64(9), c9) + assert.Equal(t, float32(10), c10) + assert.Equal(t, float64(11), c11) + assert.Equal(t, "binary", c12) + assert.Equal(t, "nchar", c13) + } + assert.Equal(t, 1, count) +} // @author: xftan // @date: 2023/10/13 11:22 @@ -1094,151 +1094,1066 @@ func TestStmtConvertExec(t *testing.T) { } } -//func TestStmtConvertQuery(t *testing.T) { -// db, err := sql.Open(driverName, dataSourceName) -// if err != nil { -// t.Error(err) -// return -// } -// defer db.Close() -// _, err = db.Exec("drop database if exists test_stmt_driver_convert_q") -// if err != nil { -// t.Error(err) -// return -// } -// defer func() { -// _, err = db.Exec("drop database if exists test_stmt_driver_convert_q") -// if err != nil { -// t.Error(err) -// return -// } -// }() -// _, err = db.Exec("create database test_stmt_driver_convert_q") -// if err != nil { -// t.Error(err) -// return -// } -// _, err = db.Exec("use test_stmt_driver_convert_q") -// if err != nil { -// t.Error(err) -// return -// } -// _, err = db.Exec("create table t0 (ts timestamp," + -// "c1 bool," + -// "c2 tinyint," + -// "c3 smallint," + -// "c4 int," + -// "c5 bigint," + -// "c6 tinyint unsigned," + -// "c7 smallint unsigned," + -// "c8 int unsigned," + -// "c9 bigint unsigned," + -// "c10 float," + -// "c11 double," + -// "c12 binary(20)," + -// "c13 nchar(20)" + -// ")") -// if err != nil { -// t.Error(err) -// return -// } -// now := time.Now() -// after1s := now.Add(time.Second) -// _, err = db.Exec(fmt.Sprintf("insert into t0 values('%s',true,2,3,4,5,6,7,8,9,10,11,'binary','nchar')", now.Format(time.RFC3339Nano))) -// if err != nil { -// t.Error(err) -// return -// } -// _, err = db.Exec(fmt.Sprintf("insert into t0 values('%s',null,null,null,null,null,null,null,null,null,null,null,null,null)", after1s.Format(time.RFC3339Nano))) -// if err != nil { -// t.Error(err) -// return -// } -// tests := []struct { -// name string -// field string -// where string -// bind interface{} -// expectNoValue bool -// expectValue driver.Value -// expectError bool -// }{ -// { -// name: "bool_true", -// field: "c1", -// where: "c1 = ?", -// bind: true, -// expectValue: true, -// }, -// { -// name: "bool_false", -// field: "c1", -// where: "c1 = ?", -// bind: false, -// expectNoValue: true, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// sql := fmt.Sprintf("select %s from t0 where %s", tt.field, tt.where) -// -// stmt, err := db.Prepare(sql) -// if err != nil { -// t.Error(err) -// return -// } -// rows, err := stmt.Query(tt.bind) -// if tt.expectError { -// assert.NotNil(t, err) -// stmt.Close() -// return -// } -// if err != nil { -// t.Error(err) -// return -// } -// tts, err := rows.ColumnTypes() -// typesL := make([]reflect.Type, 1) -// for i, tp := range tts { -// st := tp.ScanType() -// if st == nil { -// t.Errorf("scantype is null for column %q", tp.Name()) -// continue -// } -// typesL[i] = st -// } -// var data []driver.Value -// for rows.Next() { -// values := make([]interface{}, 1) -// for i := range values { -// values[i] = reflect.New(typesL[i]).Interface() -// } -// err = rows.Scan(values...) -// if err != nil { -// t.Error(err) -// return -// } -// v, err := values[0].(driver.Valuer).Value() -// if err != nil { -// t.Error(err) -// } -// data = append(data, v) -// } -// if tt.expectNoValue { -// if len(data) > 0 { -// t.Errorf("expect no value got %#v", data) -// return -// } -// return -// } -// if len(data) != 1 { -// t.Errorf("expect %d got %d", 1, len(data)) -// return -// } -// if data[0] != tt.expectValue { -// t.Errorf("expect %v got %v", tt.expectValue, data[0]) -// return -// } -// }) -// } -//} +func TestStmtConvertQuery(t *testing.T) { + db, err := sql.Open(driverName, dataSourceName) + if err != nil { + t.Error(err) + return + } + defer db.Close() + _, err = db.Exec("drop database if exists test_stmt_driver_convert_q") + if err != nil { + t.Error(err) + return + } + defer func() { + _, err = db.Exec("drop database if exists test_stmt_driver_convert_q") + if err != nil { + t.Error(err) + return + } + }() + _, err = db.Exec("create database test_stmt_driver_convert_q") + if err != nil { + t.Error(err) + return + } + _, err = db.Exec("use test_stmt_driver_convert_q") + if err != nil { + t.Error(err) + return + } + _, err = db.Exec("create table t0 (ts timestamp," + + "c1 bool," + + "c2 tinyint," + + "c3 smallint," + + "c4 int," + + "c5 bigint," + + "c6 tinyint unsigned," + + "c7 smallint unsigned," + + "c8 int unsigned," + + "c9 bigint unsigned," + + "c10 float," + + "c11 double," + + "c12 binary(20)," + + "c13 nchar(20)" + + ")") + if err != nil { + t.Error(err) + return + } + now := time.Now() + after1s := now.Add(time.Second) + _, err = db.Exec(fmt.Sprintf("insert into t0 values('%s',true,2,3,4,5,6,7,8,9,10,11,'binary','nchar')", now.Format(time.RFC3339Nano))) + if err != nil { + t.Error(err) + return + } + _, err = db.Exec(fmt.Sprintf("insert into t0 values('%s',null,null,null,null,null,null,null,null,null,null,null,null,null)", after1s.Format(time.RFC3339Nano))) + if err != nil { + t.Error(err) + return + } + tests := []struct { + name string + field string + where string + bind interface{} + expectNoValue bool + expectValue driver.Value + expectError bool + }{ + //ts + { + name: "ts", + field: "ts", + where: "ts = ?", + bind: now, + expectValue: time.Unix(now.Unix(), int64((now.Nanosecond()/1e6)*1e6)).Local(), + }, + + //bool + { + name: "bool_true", + field: "c1", + where: "c1 = ?", + bind: true, + expectValue: true, + }, + { + name: "bool_false", + field: "c1", + where: "c1 = ?", + bind: false, + expectNoValue: true, + }, + { + name: "tinyint_int8", + field: "c2", + where: "c2 = ?", + bind: int8(2), + expectValue: int8(2), + }, + { + name: "tinyint_iny16", + field: "c2", + where: "c2 = ?", + bind: int16(2), + expectValue: int8(2), + }, + { + name: "tinyint_int32", + field: "c2", + where: "c2 = ?", + bind: int32(2), + expectValue: int8(2), + }, + { + name: "tinyint_int64", + field: "c2", + where: "c2 = ?", + bind: int64(2), + expectValue: int8(2), + }, + { + name: "tinyint_uint8", + field: "c2", + where: "c2 = ?", + bind: uint8(2), + expectValue: int8(2), + }, + { + name: "tinyint_uint16", + field: "c2", + where: "c2 = ?", + bind: uint16(2), + expectValue: int8(2), + }, + { + name: "tinyint_uint32", + field: "c2", + where: "c2 = ?", + bind: uint32(2), + expectValue: int8(2), + }, + { + name: "tinyint_uint64", + field: "c2", + where: "c2 = ?", + bind: uint64(2), + expectValue: int8(2), + }, + { + name: "tinyint_float32", + field: "c2", + where: "c2 = ?", + bind: float32(2), + expectValue: int8(2), + }, + { + name: "tinyint_float64", + field: "c2", + where: "c2 = ?", + bind: float64(2), + expectValue: int8(2), + }, + { + name: "tinyint_int", + field: "c2", + where: "c2 = ?", + bind: int(2), + expectValue: int8(2), + }, + { + name: "tinyint_uint", + field: "c2", + where: "c2 = ?", + bind: uint(2), + expectValue: int8(2), + }, + + // smallint + { + name: "smallint_int8", + field: "c3", + where: "c3 = ?", + bind: int8(3), + expectValue: int16(3), + }, + { + name: "smallint_iny16", + field: "c3", + where: "c3 = ?", + bind: int16(3), + expectValue: int16(3), + }, + { + name: "smallint_int32", + field: "c3", + where: "c3 = ?", + bind: int32(3), + expectValue: int16(3), + }, + { + name: "smallint_int64", + field: "c3", + where: "c3 = ?", + bind: int64(3), + expectValue: int16(3), + }, + { + name: "smallint_uint8", + field: "c3", + where: "c3 = ?", + bind: uint8(3), + expectValue: int16(3), + }, + { + name: "smallint_uint16", + field: "c3", + where: "c3 = ?", + bind: uint16(3), + expectValue: int16(3), + }, + { + name: "smallint_uint32", + field: "c3", + where: "c3 = ?", + bind: uint32(3), + expectValue: int16(3), + }, + { + name: "smallint_uint64", + field: "c3", + where: "c3 = ?", + bind: uint64(3), + expectValue: int16(3), + }, + { + name: "smallint_float32", + field: "c3", + where: "c3 = ?", + bind: float32(3), + expectValue: int16(3), + }, + { + name: "smallint_float64", + field: "c3", + where: "c3 = ?", + bind: float64(3), + expectValue: int16(3), + }, + { + name: "smallint_int", + field: "c3", + where: "c3 = ?", + bind: int(3), + expectValue: int16(3), + }, + { + name: "smallint_uint", + field: "c3", + where: "c3 = ?", + bind: uint(3), + expectValue: int16(3), + }, + + //int + { + name: "int_int8", + field: "c4", + where: "c4 = ?", + bind: int8(4), + expectValue: int32(4), + }, + { + name: "int_iny16", + field: "c4", + where: "c4 = ?", + bind: int16(4), + expectValue: int32(4), + }, + { + name: "int_int32", + field: "c4", + where: "c4 = ?", + bind: int32(4), + expectValue: int32(4), + }, + { + name: "int_int64", + field: "c4", + where: "c4 = ?", + bind: int64(4), + expectValue: int32(4), + }, + { + name: "int_uint8", + field: "c4", + where: "c4 = ?", + bind: uint8(4), + expectValue: int32(4), + }, + { + name: "int_uint16", + field: "c4", + where: "c4 = ?", + bind: uint16(4), + expectValue: int32(4), + }, + { + name: "int_uint32", + field: "c4", + where: "c4 = ?", + bind: uint32(4), + expectValue: int32(4), + }, + { + name: "int_uint64", + field: "c4", + where: "c4 = ?", + bind: uint64(4), + expectValue: int32(4), + }, + { + name: "int_float32", + field: "c4", + where: "c4 = ?", + bind: float32(4), + expectValue: int32(4), + }, + { + name: "int_float64", + field: "c4", + where: "c4 = ?", + bind: float64(4), + expectValue: int32(4), + }, + { + name: "int_int", + field: "c4", + where: "c4 = ?", + bind: int(4), + expectValue: int32(4), + }, + { + name: "int_uint", + field: "c4", + where: "c4 = ?", + bind: uint(4), + expectValue: int32(4), + }, + + //bigint + { + name: "bigint_int8", + field: "c5", + where: "c5 = ?", + bind: int8(5), + expectValue: int64(5), + }, + { + name: "bigint_iny16", + field: "c5", + where: "c5 = ?", + bind: int16(5), + expectValue: int64(5), + }, + { + name: "bigint_int32", + field: "c5", + where: "c5 = ?", + bind: int32(5), + expectValue: int64(5), + }, + { + name: "bigint_int64", + field: "c5", + where: "c5 = ?", + bind: int64(5), + expectValue: int64(5), + }, + { + name: "bigint_uint8", + field: "c5", + where: "c5 = ?", + bind: uint8(5), + expectValue: int64(5), + }, + { + name: "bigint_uint16", + field: "c5", + where: "c5 = ?", + bind: uint16(5), + expectValue: int64(5), + }, + { + name: "bigint_uint32", + field: "c5", + where: "c5 = ?", + bind: uint32(5), + expectValue: int64(5), + }, + { + name: "bigint_uint64", + field: "c5", + where: "c5 = ?", + bind: uint64(5), + expectValue: int64(5), + }, + { + name: "bigint_float32", + field: "c5", + where: "c5 = ?", + bind: float32(5), + expectValue: int64(5), + }, + { + name: "bigint_float64", + field: "c5", + where: "c5 = ?", + bind: float64(5), + expectValue: int64(5), + }, + { + name: "bigint_int", + field: "c5", + where: "c5 = ?", + bind: int(5), + expectValue: int64(5), + }, + { + name: "bigint_uint", + field: "c5", + where: "c5 = ?", + bind: uint(5), + expectValue: int64(5), + }, + + //utinyint + { + name: "utinyint_int8", + field: "c6", + where: "c6 = ?", + bind: int8(6), + expectValue: uint8(6), + }, + { + name: "utinyint_iny16", + field: "c6", + where: "c6 = ?", + bind: int16(6), + expectValue: uint8(6), + }, + { + name: "utinyint_int32", + field: "c6", + where: "c6 = ?", + bind: int32(6), + expectValue: uint8(6), + }, + { + name: "utinyint_int64", + field: "c6", + where: "c6 = ?", + bind: int64(6), + expectValue: uint8(6), + }, + { + name: "utinyint_uint8", + field: "c6", + where: "c6 = ?", + bind: uint8(6), + expectValue: uint8(6), + }, + { + name: "utinyint_uint16", + field: "c6", + where: "c6 = ?", + bind: uint16(6), + expectValue: uint8(6), + }, + { + name: "utinyint_uint32", + field: "c6", + where: "c6 = ?", + bind: uint32(6), + expectValue: uint8(6), + }, + { + name: "utinyint_uint64", + field: "c6", + where: "c6 = ?", + bind: uint64(6), + expectValue: uint8(6), + }, + { + name: "utinyint_float32", + field: "c6", + where: "c6 = ?", + bind: float32(6), + expectValue: uint8(6), + }, + { + name: "utinyint_float64", + field: "c6", + where: "c6 = ?", + bind: float64(6), + expectValue: uint8(6), + }, + { + name: "utinyint_int", + field: "c6", + where: "c6 = ?", + bind: int(6), + expectValue: uint8(6), + }, + { + name: "utinyint_uint", + field: "c6", + where: "c6 = ?", + bind: uint(6), + expectValue: uint8(6), + }, + + //usmallint + { + name: "usmallint_int8", + field: "c7", + where: "c7 = ?", + bind: int8(7), + expectValue: uint16(7), + }, + { + name: "usmallint_iny16", + field: "c7", + where: "c7 = ?", + bind: int16(7), + expectValue: uint16(7), + }, + { + name: "usmallint_int32", + field: "c7", + where: "c7 = ?", + bind: int32(7), + expectValue: uint16(7), + }, + { + name: "usmallint_int64", + field: "c7", + where: "c7 = ?", + bind: int64(7), + expectValue: uint16(7), + }, + { + name: "usmallint_uint8", + field: "c7", + where: "c7 = ?", + bind: uint8(7), + expectValue: uint16(7), + }, + { + name: "usmallint_uint16", + field: "c7", + where: "c7 = ?", + bind: uint16(7), + expectValue: uint16(7), + }, + { + name: "usmallint_uint32", + field: "c7", + where: "c7 = ?", + bind: uint32(7), + expectValue: uint16(7), + }, + { + name: "usmallint_uint64", + field: "c7", + where: "c7 = ?", + bind: uint64(7), + expectValue: uint16(7), + }, + { + name: "usmallint_float32", + field: "c7", + where: "c7 = ?", + bind: float32(7), + expectValue: uint16(7), + }, + { + name: "usmallint_float64", + field: "c7", + where: "c7 = ?", + bind: float64(7), + expectValue: uint16(7), + }, + { + name: "usmallint_int", + field: "c7", + where: "c7 = ?", + bind: int(7), + expectValue: uint16(7), + }, + { + name: "usmallint_uint", + field: "c7", + where: "c7 = ?", + bind: uint(7), + expectValue: uint16(7), + }, + + //uint + { + name: "uint_int8", + field: "c8", + where: "c8 = ?", + bind: int8(8), + expectValue: uint32(8), + }, + { + name: "uint_iny16", + field: "c8", + where: "c8 = ?", + bind: int16(8), + expectValue: uint32(8), + }, + { + name: "uint_int32", + field: "c8", + where: "c8 = ?", + bind: int32(8), + expectValue: uint32(8), + }, + { + name: "uint_int64", + field: "c8", + where: "c8 = ?", + bind: int64(8), + expectValue: uint32(8), + }, + { + name: "uint_uint8", + field: "c8", + where: "c8 = ?", + bind: uint8(8), + expectValue: uint32(8), + }, + { + name: "uint_uint16", + field: "c8", + where: "c8 = ?", + bind: uint16(8), + expectValue: uint32(8), + }, + { + name: "uint_uint32", + field: "c8", + where: "c8 = ?", + bind: uint32(8), + expectValue: uint32(8), + }, + { + name: "uint_uint64", + field: "c8", + where: "c8 = ?", + bind: uint64(8), + expectValue: uint32(8), + }, + { + name: "uint_float32", + field: "c8", + where: "c8 = ?", + bind: float32(8), + expectValue: uint32(8), + }, + { + name: "uint_float64", + field: "c8", + where: "c8 = ?", + bind: float64(8), + expectValue: uint32(8), + }, + { + name: "uint_int", + field: "c8", + where: "c8 = ?", + bind: int(8), + expectValue: uint32(8), + }, + { + name: "uint_uint", + field: "c8", + where: "c8 = ?", + bind: uint(8), + expectValue: uint32(8), + }, + + //ubigint + { + name: "ubigint_int8", + field: "c9", + where: "c9 = ?", + bind: int8(9), + expectValue: uint64(9), + }, + { + name: "ubigint_iny16", + field: "c9", + where: "c9 = ?", + bind: int16(9), + expectValue: uint64(9), + }, + { + name: "ubigint_int32", + field: "c9", + where: "c9 = ?", + bind: int32(9), + expectValue: uint64(9), + }, + { + name: "ubigint_int64", + field: "c9", + where: "c9 = ?", + bind: int64(9), + expectValue: uint64(9), + }, + { + name: "ubigint_uint8", + field: "c9", + where: "c9 = ?", + bind: uint8(9), + expectValue: uint64(9), + }, + { + name: "ubigint_uint16", + field: "c9", + where: "c9 = ?", + bind: uint16(9), + expectValue: uint64(9), + }, + { + name: "ubigint_uint32", + field: "c9", + where: "c9 = ?", + bind: uint32(9), + expectValue: uint64(9), + }, + { + name: "ubigint_uint64", + field: "c9", + where: "c9 = ?", + bind: uint64(9), + expectValue: uint64(9), + }, + { + name: "ubigint_float32", + field: "c9", + where: "c9 = ?", + bind: float32(9), + expectValue: uint64(9), + }, + { + name: "ubigint_float64", + field: "c9", + where: "c9 = ?", + bind: float64(9), + expectValue: uint64(9), + }, + { + name: "ubigint_int", + field: "c9", + where: "c9 = ?", + bind: int(9), + expectValue: uint64(9), + }, + { + name: "ubigint_uint", + field: "c9", + where: "c9 = ?", + bind: uint(9), + expectValue: uint64(9), + }, + + //float + { + name: "float_int8", + field: "c10", + where: "c10 = ?", + bind: int8(10), + expectValue: float32(10), + }, + { + name: "float_iny16", + field: "c10", + where: "c10 = ?", + bind: int16(10), + expectValue: float32(10), + }, + { + name: "float_int32", + field: "c10", + where: "c10 = ?", + bind: int32(10), + expectValue: float32(10), + }, + { + name: "float_int64", + field: "c10", + where: "c10 = ?", + bind: int64(10), + expectValue: float32(10), + }, + { + name: "float_uint8", + field: "c10", + where: "c10 = ?", + bind: uint8(10), + expectValue: float32(10), + }, + { + name: "float_uint16", + field: "c10", + where: "c10 = ?", + bind: uint16(10), + expectValue: float32(10), + }, + { + name: "float_uint32", + field: "c10", + where: "c10 = ?", + bind: uint32(10), + expectValue: float32(10), + }, + { + name: "float_uint64", + field: "c10", + where: "c10 = ?", + bind: uint64(10), + expectValue: float32(10), + }, + { + name: "float_float32", + field: "c10", + where: "c10 = ?", + bind: float32(10), + expectValue: float32(10), + }, + { + name: "float_float64", + field: "c10", + where: "c10 = ?", + bind: float64(10), + expectValue: float32(10), + }, + { + name: "float_int", + field: "c10", + where: "c10 = ?", + bind: int(10), + expectValue: float32(10), + }, + { + name: "float_uint", + field: "c10", + where: "c10 = ?", + bind: uint(10), + expectValue: float32(10), + }, + + //double + { + name: "double_int8", + field: "c11", + where: "c11 = ?", + bind: int8(11), + expectValue: float64(11), + }, + { + name: "double_iny16", + field: "c11", + where: "c11 = ?", + bind: int16(11), + expectValue: float64(11), + }, + { + name: "double_int32", + field: "c11", + where: "c11 = ?", + bind: int32(11), + expectValue: float64(11), + }, + { + name: "double_int64", + field: "c11", + where: "c11 = ?", + bind: int64(11), + expectValue: float64(11), + }, + { + name: "double_uint8", + field: "c11", + where: "c11 = ?", + bind: uint8(11), + expectValue: float64(11), + }, + { + name: "double_uint16", + field: "c11", + where: "c11 = ?", + bind: uint16(11), + expectValue: float64(11), + }, + { + name: "double_uint32", + field: "c11", + where: "c11 = ?", + bind: uint32(11), + expectValue: float64(11), + }, + { + name: "double_uint64", + field: "c11", + where: "c11 = ?", + bind: uint64(11), + expectValue: float64(11), + }, + { + name: "double_float32", + field: "c11", + where: "c11 = ?", + bind: float32(11), + expectValue: float64(11), + }, + { + name: "double_float64", + field: "c11", + where: "c11 = ?", + bind: float64(11), + expectValue: float64(11), + }, + { + name: "double_int", + field: "c11", + where: "c11 = ?", + bind: int(11), + expectValue: float64(11), + }, + { + name: "double_uint", + field: "c11", + where: "c11 = ?", + bind: uint(11), + expectValue: float64(11), + }, + + // binary + { + name: "binary_string", + field: "c12", + where: "c12 = ?", + bind: "binary", + expectValue: "binary", + }, + { + name: "binary_bytes", + field: "c12", + where: "c12 = ?", + bind: []byte("binary"), + expectValue: "binary", + }, + { + name: "binary_string_like", + field: "c12", + where: "c12 like ?", + bind: "bin%", + expectValue: "binary", + }, + + // nchar + { + name: "nchar_string", + field: "c13", + where: "c13 = ?", + bind: "nchar", + expectValue: "nchar", + }, + { + name: "nchar_bytes", + field: "c13", + where: "c13 = ?", + bind: []byte("nchar"), + expectValue: "nchar", + }, + { + name: "nchar_string", + field: "c13", + where: "c13 like ?", + bind: "nch%", + expectValue: "nchar", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sql := fmt.Sprintf("select %s from t0 where %s", tt.field, tt.where) + + stmt, err := db.Prepare(sql) + if err != nil { + t.Error(err) + return + } + defer stmt.Close() + rows, err := stmt.Query(tt.bind) + if tt.expectError { + assert.NotNil(t, err) + stmt.Close() + return + } + if err != nil { + t.Error(err) + return + } + tts, err := rows.ColumnTypes() + typesL := make([]reflect.Type, 1) + for i, tp := range tts { + st := tp.ScanType() + if st == nil { + t.Errorf("scantype is null for column %q", tp.Name()) + continue + } + typesL[i] = st + } + var data []driver.Value + for rows.Next() { + values := make([]interface{}, 1) + for i := range values { + values[i] = reflect.New(typesL[i]).Interface() + } + err = rows.Scan(values...) + if err != nil { + t.Error(err) + return + } + v, err := values[0].(driver.Valuer).Value() + if err != nil { + t.Error(err) + } + data = append(data, v) + } + if tt.expectNoValue { + if len(data) > 0 { + t.Errorf("expect no value got %#v", data) + return + } + return + } + if len(data) != 1 { + t.Errorf("expect %d got %d", 1, len(data)) + return + } + if data[0] != tt.expectValue { + t.Errorf("expect %v got %v", tt.expectValue, data[0]) + return + } + }) + } +} diff --git a/types/taostype.go b/types/taostype.go index e6c7947..f1bbcc2 100644 --- a/types/taostype.go +++ b/types/taostype.go @@ -24,7 +24,8 @@ type ( T time.Time Precision int } - TaosJson []byte + TaosJson []byte + TaosGeometry []byte ) var ( @@ -44,6 +45,7 @@ var ( TaosNcharType = reflect.TypeOf(TaosNchar("")) TaosTimestampType = reflect.TypeOf(TaosTimestamp{}) TaosJsonType = reflect.TypeOf(TaosJson("")) + TaosGeometryType = reflect.TypeOf(TaosGeometry(nil)) ) type ColumnType struct { diff --git a/wrapper/row.go b/wrapper/row.go index 042d4b3..38422f8 100644 --- a/wrapper/row.go +++ b/wrapper/row.go @@ -51,7 +51,7 @@ func FetchRow(row unsafe.Pointer, offset int, colType uint8, length int, arg ... return *((*float32)(p)) case C.TSDB_DATA_TYPE_DOUBLE: return *((*float64)(p)) - case C.TSDB_DATA_TYPE_BINARY, C.TSDB_DATA_TYPE_NCHAR, C.TSDB_DATA_TYPE_VARBINARY: + case C.TSDB_DATA_TYPE_BINARY, C.TSDB_DATA_TYPE_NCHAR: data := make([]byte, length) for i := 0; i < length; i++ { data[i] = *((*byte)(pointer.AddUintptr(p, uintptr(i)))) @@ -65,7 +65,7 @@ func FetchRow(row unsafe.Pointer, offset int, colType uint8, length int, arg ... } else { panic("convertTime error") } - case C.TSDB_DATA_TYPE_JSON: + case C.TSDB_DATA_TYPE_JSON, C.TSDB_DATA_TYPE_VARBINARY, C.TSDB_DATA_TYPE_GEOMETRY: data := make([]byte, length) for i := 0; i < length; i++ { data[i] = *((*byte)(pointer.AddUintptr(p, uintptr(i)))) diff --git a/wrapper/row_test.go b/wrapper/row_test.go index e38e04e..92f9425 100644 --- a/wrapper/row_test.go +++ b/wrapper/row_test.go @@ -539,7 +539,9 @@ func TestFetchRowAllType(t *testing.T) { "c10 float,"+ "c11 double,"+ "c12 binary(20),"+ - "c13 nchar(20)"+ + "c13 nchar(20),"+ + "c14 varbinary(20),"+ + "c15 geometry(100)"+ ")"+ "tags(t json)", db)) code = TaosError(res) @@ -563,7 +565,7 @@ func TestFetchRowAllType(t *testing.T) { } TaosFreeResult(res) now := time.Now() - res = TaosQuery(conn, fmt.Sprintf("insert into %s.tb1 values('%s',true,2,3,4,5,6,7,8,9,10,11,'binary','nchar');", db, now.Format(time.RFC3339Nano))) + res = TaosQuery(conn, fmt.Sprintf("insert into %s.tb1 values('%s',true,2,3,4,5,6,7,8,9,10,11,'binary','nchar','varbinary','POINT(100 100)');", db, now.Format(time.RFC3339Nano))) code = TaosError(res) if code != int(errors.SUCCESS) { errStr := TaosErrorStr(res) @@ -620,5 +622,7 @@ func TestFetchRowAllType(t *testing.T) { assert.Equal(t, float64(11), result[11].(float64)) assert.Equal(t, "binary", result[12].(string)) assert.Equal(t, "nchar", result[13].(string)) - assert.Equal(t, []byte(`{"a":1}`), result[14].([]byte)) + assert.Equal(t, []byte("varbinary"), result[14].([]byte)) + assert.Equal(t, []byte{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40}, result[15].([]byte)) + assert.Equal(t, []byte(`{"a":1}`), result[16].([]byte)) } diff --git a/wrapper/stmt.go b/wrapper/stmt.go index 5bba031..e2bb2c0 100644 --- a/wrapper/stmt.go +++ b/wrapper/stmt.go @@ -246,6 +246,17 @@ func generateTaosBindList(params []driver.Value) ([]C.TAOS_MULTI_BIND, []unsafe. *(bind.length) = C.int32_t(clen) needFreePointer = append(needFreePointer, p) bind.buffer_length = C.uintptr_t(clen) + case taosTypes.TaosGeometry: + bind.buffer_type = C.TSDB_DATA_TYPE_GEOMETRY + cbuf := C.CString(string(value)) + needFreePointer = append(needFreePointer, unsafe.Pointer(cbuf)) + bind.buffer = unsafe.Pointer(cbuf) + clen := int32(len(value)) + p := C.malloc(C.size_t(unsafe.Sizeof(clen))) + bind.length = (*C.int32_t)(p) + *(bind.length) = C.int32_t(clen) + needFreePointer = append(needFreePointer, p) + bind.buffer_length = C.uintptr_t(clen) case taosTypes.TaosNchar: bind.buffer_type = C.TSDB_DATA_TYPE_NCHAR p := unsafe.Pointer(C.CString(string(value))) @@ -581,6 +592,24 @@ func TaosStmtBindParamBatch(stmt unsafe.Pointer, multiBind [][]driver.Value, bin *(*C.int32_t)(l) = C.int32_t(len(value)) } } + case taosTypes.TaosGeometryType: + p = unsafe.Pointer(C.malloc(C.size_t(C.uint(columnType.MaxLen * rowLen)))) + bind.buffer_type = C.TSDB_DATA_TYPE_GEOMETRY + bind.buffer_length = C.uintptr_t(columnType.MaxLen) + for i, rowData := range columnData { + currentNull := unsafe.Pointer(uintptr(nullList) + uintptr(i)) + if rowData == nil { + *(*C.char)(currentNull) = C.char(1) + } else { + *(*C.char)(currentNull) = C.char(0) + value := rowData.(taosTypes.TaosGeometry) + for j := 0; j < len(value); j++ { + *(*C.char)(unsafe.Pointer(uintptr(p) + uintptr(columnType.MaxLen*i+j))) = (C.char)(value[j]) + } + l := unsafe.Pointer(uintptr(lengthList) + uintptr(4*i)) + *(*C.int32_t)(l) = C.int32_t(len(value)) + } + } case taosTypes.TaosNcharType: p = unsafe.Pointer(C.malloc(C.size_t(C.uint(columnType.MaxLen * rowLen)))) bind.buffer_type = C.TSDB_DATA_TYPE_NCHAR diff --git a/wrapper/stmt_test.go b/wrapper/stmt_test.go index a3af59f..d924645 100644 --- a/wrapper/stmt_test.go +++ b/wrapper/stmt_test.go @@ -146,7 +146,17 @@ func TestStmt(t *testing.T) { Type: taosTypes.TaosVarBinaryType, MaxLen: 3, }}, - expectValue: "yes", + expectValue: []byte("yes"), + }, //3 + { + tbType: "ts timestamp, v geometry(100)", + pos: "?, ?", + params: [][]driver.Value{{taosTypes.TaosTimestamp{T: now, Precision: common.PrecisionMilliSecond}}, {taosTypes.TaosGeometry{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40}}}, + bindType: []*taosTypes.ColumnType{{Type: taosTypes.TaosTimestampType}, { + Type: taosTypes.TaosGeometryType, + MaxLen: 3, + }}, + expectValue: []byte{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40}, }, //3 { tbType: "ts timestamp, v nchar(8)", @@ -231,10 +241,7 @@ func TestStmt(t *testing.T) { t.Errorf("expect %d got %d", 1, len(result)) return } - if result[0][0] != tc.expectValue { - t.Errorf("expect %v got %v", tc.expectValue, result[0][0]) - return - } + assert.Equal(t, tc.expectValue, result[0][0]) }) } @@ -350,7 +357,13 @@ func TestStmtExec(t *testing.T) { tbType: "ts timestamp, v varbinary(8)", pos: "?, ?", params: []driver.Value{taosTypes.TaosTimestamp{T: now, Precision: common.PrecisionMilliSecond}, taosTypes.TaosVarBinary("yes")}, - expectValue: "yes", + expectValue: []byte("yes"), + }, //3 + { + tbType: "ts timestamp, v geometry(100)", + pos: "?, ?", + params: []driver.Value{taosTypes.TaosTimestamp{T: now, Precision: common.PrecisionMilliSecond}, taosTypes.TaosGeometry{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40}}, + expectValue: []byte{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40}, }, //3 { tbType: "ts timestamp, v nchar(8)", @@ -432,10 +445,7 @@ func TestStmtExec(t *testing.T) { t.Errorf("expect %d got %d", 1, len(result)) return } - if result[0][0] != tc.expectValue { - t.Errorf("expect %v got %v", tc.expectValue, result[0][0]) - return - } + assert.Equal(t, tc.expectValue, result[0][0]) }) } }