mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 08:13:34 +00:00
fix(iotdb): align 1.3.x aggregate columns and format max_time
Closes #7306
This commit is contained in:
@@ -51,6 +51,13 @@ for TLS or mTLS connections.
|
||||
- Query results annotate timestamp columns as `TIMESTAMP(ms|us|ns)` using the
|
||||
server-reported `TimestampPrecision`. Raw timestamp integers are transported
|
||||
as decimal strings so nanosecond values are not rounded by JavaScript.
|
||||
- Tree `max_time`/`min_time` aggregates also return epoch values; they are
|
||||
annotated as `TIMESTAMP(ms|us|ns)` too so the grid renders standard times.
|
||||
- The pinned client is vendored under `agents/go-common/iotdb-client-go` with
|
||||
one patch: upstream drops the 1.3.x `ColumnNameIndexMap` fallback, which
|
||||
misaligned aggregate result values against the SELECT column order on 1.3.x
|
||||
servers (the patched client maps value columns through the server-provided
|
||||
name index when the ordered index list is absent).
|
||||
- The Agent keeps one physical IoTDB session per logical DBX session and
|
||||
invalidates that session after cancellation, timeout, or connection failure.
|
||||
|
||||
|
||||
@@ -5,3 +5,7 @@ go 1.25
|
||||
require github.com/apache/iotdb-client-go/v2 v2.0.9-0.20260807074554-e59fc7f55df1
|
||||
|
||||
require github.com/apache/thrift v0.24.0 // indirect
|
||||
|
||||
// Upstream ignores the 1.3.x ColumnNameIndexMap fallback, which misaligns
|
||||
// aggregate result columns against the SELECT list on 1.3.x servers.
|
||||
replace github.com/apache/iotdb-client-go/v2 => ../../go-common/iotdb-client-go
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
github.com/apache/iotdb-client-go/v2 v2.0.9-0.20260807074554-e59fc7f55df1 h1:UAfvet8VA2ZUCsIBusSqNDDecFGot8uDshegeDdLBRo=
|
||||
github.com/apache/iotdb-client-go/v2 v2.0.9-0.20260807074554-e59fc7f55df1/go.mod h1:Ef61z8w2tRnuAcQKESFfiH03kDvfQniVPUgOXCxWnNw=
|
||||
github.com/apache/thrift v0.24.0 h1:zy31L1a49QTNB2bG1BBfMXol3yJrTH975G3pPubQVLQ=
|
||||
github.com/apache/thrift v0.24.0/go.mod h1:zPt6WxgvTOM6hF92y8C+MkEM5LMxZuk4JcQOiU4Esvs=
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -31,6 +32,24 @@ func TestLiveIoTDBAgentTreeAndTable(t *testing.T) {
|
||||
mustExecuteNonQuery(t, treeServer, fmt.Sprintf("INSERT INTO %s(time,s1) VALUES(%d,%d)", treeDevice, index, index*10), "")
|
||||
}
|
||||
|
||||
// 1.3.x servers order aggregate TsBlock columns by their own aggregate
|
||||
// layout, not the SELECT list; values must still line up with the headers
|
||||
// (https://github.com/t8y2/dbx/issues/7306).
|
||||
aggregate, err := treeServer.executeQuery(queryOptions{
|
||||
SQL: "SELECT max_time(s1), avg(s1), max_value(s1), min_value(s1) FROM " + treeDevice,
|
||||
MaxRows: 10,
|
||||
})
|
||||
if err != nil || len(aggregate.Rows) != 1 {
|
||||
t.Fatalf("tree aggregate = %#v, %v", aggregate, err)
|
||||
}
|
||||
wantAggregateRow := []any{"4", 25.0, int64(40), int64(10)}
|
||||
if !reflect.DeepEqual(aggregate.Rows[0], wantAggregateRow) {
|
||||
t.Fatalf("tree aggregate row = %#v, want %#v", aggregate.Rows[0], wantAggregateRow)
|
||||
}
|
||||
if aggregate.ColumnTypes[0] != "TIMESTAMP(ms)" {
|
||||
t.Fatalf("tree aggregate max_time type = %#v", aggregate.ColumnTypes)
|
||||
}
|
||||
|
||||
tables, err := treeServer.listTables(treeDatabase, metadataListConstraints{})
|
||||
if err != nil || len(tables) != 1 || tables[0].Name != "d1" {
|
||||
t.Fatalf("tree listTables() = %#v, %v", tables, err)
|
||||
|
||||
@@ -178,6 +178,27 @@ func TestTreeTimeColumnPresentationDoesNotRewriteOrdinaryInt64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeTimeAggregationColumnsPresentAsTimestamps(t *testing.T) {
|
||||
columns := []string{
|
||||
"max_time(root.db.d1.s1)",
|
||||
"MIN_TIME(root.db.d1.s1)",
|
||||
" max_time(root.db.d1.s1) ",
|
||||
"avg(root.db.d1.s1)",
|
||||
"count(root.db.d1.s1)",
|
||||
}
|
||||
got := normalizedColumnTypes([]string{"INT64", "INT64", "INT64", "DOUBLE", "INT64"}, columns, client.TreeSqlDialect, "ms")
|
||||
want := []string{"TIMESTAMP(ms)", "TIMESTAMP(ms)", "TIMESTAMP(ms)", "DOUBLE", "INT64"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected tree aggregation types: %#v", got)
|
||||
}
|
||||
if name := normalizedColumnTypes([]string{"INT64"}, []string{"max_timer(root.db.d1.s1)"}, client.TreeSqlDialect, "ms"); !reflect.DeepEqual(name, []string{"INT64"}) {
|
||||
t.Fatalf("unrelated prefix must stay INT64: %#v", name)
|
||||
}
|
||||
if table := normalizedColumnTypes([]string{"INT64"}, []string{"max_time(t1.s1)"}, client.TableSqlDialect, "ms"); !reflect.DeepEqual(table, []string{"INT64"}) {
|
||||
t.Fatalf("table dialect must stay INT64: %#v", table)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyIoTDBErrors(t *testing.T) {
|
||||
canceled := classifyRPCError("execute_query", "session-1", context.Canceled)
|
||||
if canceled.Data.Category != "canceled" || canceled.Data.SessionDisposition != "quarantine" {
|
||||
|
||||
@@ -475,11 +475,27 @@ func normalizedColumnTypes(values, columns []string, dialect, timestampPrecision
|
||||
}
|
||||
if iotdbColumnTypeBase(result[index]) == "TIMESTAMP" {
|
||||
result[index] = timestampColumnType(timestampPrecision)
|
||||
continue
|
||||
}
|
||||
// max_time/min_time aggregates return INT64 epoch values in the tree
|
||||
// dialect; type them as timestamps so the grid renders standard times.
|
||||
if dialect == client.TreeSqlDialect && iotdbColumnTypeBase(result[index]) == "INT64" && isTimeAggregationColumn(columns[index]) {
|
||||
result[index] = timestampColumnType(timestampPrecision)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func isTimeAggregationColumn(column string) bool {
|
||||
trimmed := strings.TrimSpace(column)
|
||||
for _, prefix := range []string{"max_time(", "min_time("} {
|
||||
if len(trimmed) > len(prefix) && strings.EqualFold(trimmed[:len(prefix)], prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func timestampColumnType(precision string) string {
|
||||
if normalized := normalizeTimestampPrecision(precision); normalized != "" {
|
||||
return "TIMESTAMP(" + normalized + ")"
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
APACHE IOTDB SUBCOMPONENTS
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Apache IoTDB
|
||||
Copyright 2018-2020 The Apache Software Foundation.
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
============================================================================
|
||||
|
||||
IoTDB project uses 4 Chinese Patents:
|
||||
* 201711384490X
|
||||
* 201810111712.9
|
||||
* 201711322631.5
|
||||
* 201711319331.1
|
||||
|
||||
According to the Apache 2.0 License. The owner of the patents, Tsinghua University,
|
||||
grant the users the right to the use of patent under the requirement of Apache 2.0 License.
|
||||
|
||||
============================================================================
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
type BitMap struct {
|
||||
size int
|
||||
bits []byte
|
||||
}
|
||||
|
||||
var BitUtil = []byte{1, 2, 4, 8, 16, 32, 64, 128}
|
||||
var UnmarkBitUtil = []byte{
|
||||
0xFE, // 11111110
|
||||
0xFD, // 11111101
|
||||
0xFB, // 11111011
|
||||
0xF7, // 11110111
|
||||
0xEF, // 11101111
|
||||
0xDF, // 11011111
|
||||
0xBF, // 10111111
|
||||
0x7F, // 01111111
|
||||
}
|
||||
|
||||
func NewBitMap(size int) *BitMap {
|
||||
// Need to maintain consistency with the calculation method on the IoTDB side.
|
||||
bitMap := &BitMap{
|
||||
size: size,
|
||||
bits: make([]byte, size/8+1),
|
||||
}
|
||||
return bitMap
|
||||
}
|
||||
|
||||
func (b *BitMap) Mark(position int) {
|
||||
b.bits[position/8] |= BitUtil[position%8]
|
||||
}
|
||||
|
||||
func (b *BitMap) UnMark(position int) {
|
||||
b.bits[position/8] &= UnmarkBitUtil[position%8]
|
||||
}
|
||||
|
||||
func (b *BitMap) IsMarked(position int) bool {
|
||||
return (b.bits[position/8] & BitUtil[position%8]) != 0
|
||||
}
|
||||
|
||||
func (b *BitMap) IsAllUnmarked() bool {
|
||||
for i := 0; i < b.size/8; i++ {
|
||||
if b.bits[i] != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := 0; i < b.size%8; i++ {
|
||||
if (b.bits[b.size/8] & BitUtil[i]) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *BitMap) GetBits() []byte {
|
||||
return b.bits
|
||||
}
|
||||
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ColumnEncoding uint8
|
||||
|
||||
const (
|
||||
BYTE_ARRAY_COLUMN_ENCODING = ColumnEncoding(iota)
|
||||
INT32_ARRAY_COLUMN_ENCODING
|
||||
INT64_ARRAY_COLUMN_ENCODING
|
||||
BINARY_ARRAY_COLUMN_ENCODING
|
||||
RLE_COLUMN_ENCODING
|
||||
)
|
||||
|
||||
var encodingToDecoder = map[ColumnEncoding]ColumnDecoder{
|
||||
INT32_ARRAY_COLUMN_ENCODING: new(Int32ArrayColumnDecoder),
|
||||
INT64_ARRAY_COLUMN_ENCODING: new(Int64ArrayColumnDecoder),
|
||||
BYTE_ARRAY_COLUMN_ENCODING: new(ByteArrayColumnDecoder),
|
||||
BINARY_ARRAY_COLUMN_ENCODING: new(BinaryArrayColumnDecoder),
|
||||
RLE_COLUMN_ENCODING: new(RunLengthColumnDecoder),
|
||||
}
|
||||
|
||||
var byteToEncoding = map[byte]ColumnEncoding{
|
||||
0: BYTE_ARRAY_COLUMN_ENCODING,
|
||||
1: INT32_ARRAY_COLUMN_ENCODING,
|
||||
2: INT64_ARRAY_COLUMN_ENCODING,
|
||||
3: BINARY_ARRAY_COLUMN_ENCODING,
|
||||
4: RLE_COLUMN_ENCODING,
|
||||
}
|
||||
|
||||
func getColumnDecoder(encoding ColumnEncoding) (ColumnDecoder, error) {
|
||||
decoder, exists := encodingToDecoder[encoding]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unsupported column encoding: %v", encoding)
|
||||
}
|
||||
return decoder, nil
|
||||
}
|
||||
|
||||
func getColumnEncodingByByte(b byte) (ColumnEncoding, error) {
|
||||
encoding, exists := byteToEncoding[b]
|
||||
if !exists {
|
||||
return INT32_ARRAY_COLUMN_ENCODING, fmt.Errorf("invalid value: %v", b)
|
||||
}
|
||||
return encoding, nil
|
||||
}
|
||||
|
||||
type Column interface {
|
||||
GetDataType() TSDataType
|
||||
GetEncoding() ColumnEncoding
|
||||
GetBoolean(position int32) (bool, error)
|
||||
GetInt(position int32) (int32, error)
|
||||
GetLong(position int32) (int64, error)
|
||||
GetFloat(position int32) (float32, error)
|
||||
GetDouble(position int32) (float64, error)
|
||||
GetBinary(position int32) (*Binary, error)
|
||||
GetObject(position int32) (interface{}, error)
|
||||
|
||||
GetBooleans() ([]bool, error)
|
||||
GetInts() ([]int32, error)
|
||||
GetLongs() ([]int64, error)
|
||||
GetFloats() ([]float32, error)
|
||||
GetDoubles() ([]float64, error)
|
||||
GetBinaries() ([]*Binary, error)
|
||||
GetObjects() ([]interface{}, error)
|
||||
|
||||
MayHaveNull() bool
|
||||
IsNull(position int32) bool
|
||||
IsNulls() []bool
|
||||
|
||||
GetPositionCount() int32
|
||||
}
|
||||
|
||||
type baseColumn struct {
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetBoolean(_ int32) (bool, error) {
|
||||
return false, fmt.Errorf("unsupported operation: GetBoolean")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetInt(_ int32) (int32, error) {
|
||||
return 0, fmt.Errorf("unsupported operation: GetInt")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetLong(_ int32) (int64, error) {
|
||||
return 0, fmt.Errorf("unsupported operation: GetLong")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetFloat(_ int32) (float32, error) {
|
||||
return 0, fmt.Errorf("unsupported operation: GetFloat")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetDouble(_ int32) (float64, error) {
|
||||
return 0, fmt.Errorf("unsupported operation: GetDouble")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetBinary(_ int32) (*Binary, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetBinary")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetObject(_ int32) (interface{}, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetObject")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetBooleans() ([]bool, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetBooleans")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetInts() ([]int32, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetInts")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetLongs() ([]int64, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetLongs")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetFloats() ([]float32, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetFloats")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetDoubles() ([]float64, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetDoubles")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetBinaries() ([]*Binary, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetBinaries")
|
||||
}
|
||||
|
||||
func (c *baseColumn) GetObjects() ([]interface{}, error) {
|
||||
return nil, fmt.Errorf("unsupported operation: GetObjects")
|
||||
}
|
||||
|
||||
type TimeColumn struct {
|
||||
baseColumn
|
||||
arrayOffset int32
|
||||
positionCount int32
|
||||
values []int64
|
||||
}
|
||||
|
||||
func NewTimeColumn(arrayOffset int32, positionCount int32, values []int64) (*TimeColumn, error) {
|
||||
if arrayOffset < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if int32(len(values))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("values length is less than positionCount")
|
||||
}
|
||||
return &TimeColumn{
|
||||
arrayOffset: arrayOffset,
|
||||
positionCount: positionCount,
|
||||
values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetDataType() TSDataType {
|
||||
return INT64
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetEncoding() ColumnEncoding {
|
||||
return INT64_ARRAY_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetLong(position int32) (int64, error) {
|
||||
return tc.values[position+tc.arrayOffset], nil
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) MayHaveNull() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) IsNull(_ int32) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) IsNulls() []bool {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetPositionCount() int32 {
|
||||
return tc.positionCount
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetStartTime() int64 {
|
||||
return tc.values[tc.arrayOffset]
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetEndTime() int64 {
|
||||
return tc.values[tc.positionCount+tc.arrayOffset-1]
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetTimes() []int64 {
|
||||
return tc.values
|
||||
}
|
||||
|
||||
func (tc *TimeColumn) GetLongs() ([]int64, error) {
|
||||
return tc.GetTimes(), nil
|
||||
}
|
||||
|
||||
type BinaryColumn struct {
|
||||
baseColumn
|
||||
arrayOffset int32
|
||||
positionCount int32
|
||||
valueIsNull []bool
|
||||
values []*Binary
|
||||
}
|
||||
|
||||
func NewBinaryColumn(arrayOffset int32, positionCount int32, valueIsNull []bool, values []*Binary) (*BinaryColumn, error) {
|
||||
if arrayOffset < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("positionCount is negative")
|
||||
}
|
||||
if int32(len(values))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("values length is less than positionCount")
|
||||
}
|
||||
if valueIsNull != nil && int32(len(valueIsNull))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("isNull length is less than positionCount")
|
||||
}
|
||||
return &BinaryColumn{
|
||||
arrayOffset: arrayOffset,
|
||||
positionCount: positionCount,
|
||||
valueIsNull: valueIsNull,
|
||||
values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) GetDataType() TSDataType {
|
||||
return TEXT
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) GetEncoding() ColumnEncoding {
|
||||
return BINARY_ARRAY_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) GetBinary(position int32) (*Binary, error) {
|
||||
return c.values[position+c.arrayOffset], nil
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) GetBinaries() ([]*Binary, error) {
|
||||
return c.values, nil
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) GetObject(position int32) (interface{}, error) {
|
||||
return c.GetBinary(position)
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) MayHaveNull() bool {
|
||||
return c.valueIsNull != nil
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) IsNull(position int32) bool {
|
||||
return c.valueIsNull != nil && c.valueIsNull[position+c.arrayOffset]
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) IsNulls() []bool {
|
||||
if c.valueIsNull != nil {
|
||||
return c.valueIsNull
|
||||
}
|
||||
result := make([]bool, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *BinaryColumn) GetPositionCount() int32 {
|
||||
return c.positionCount
|
||||
}
|
||||
|
||||
type IntColumn struct {
|
||||
baseColumn
|
||||
arrayOffset int32
|
||||
positionCount int32
|
||||
valueIsNull []bool
|
||||
values []int32
|
||||
}
|
||||
|
||||
func NewIntColumn(arrayOffset int32, positionCount int32, valueIsNull []bool, values []int32) (*IntColumn, error) {
|
||||
if arrayOffset < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("positionCount is negative")
|
||||
}
|
||||
if int32(len(values))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("values length is less than positionCount")
|
||||
}
|
||||
if valueIsNull != nil && int32(len(valueIsNull))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("isNull length is less than positionCount")
|
||||
}
|
||||
return &IntColumn{
|
||||
arrayOffset: arrayOffset,
|
||||
positionCount: positionCount,
|
||||
valueIsNull: valueIsNull,
|
||||
values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *IntColumn) GetDataType() TSDataType {
|
||||
return INT32
|
||||
}
|
||||
|
||||
func (c *IntColumn) GetEncoding() ColumnEncoding {
|
||||
return INT32_ARRAY_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (c *IntColumn) GetInt(position int32) (int32, error) {
|
||||
return c.values[position+c.arrayOffset], nil
|
||||
}
|
||||
|
||||
func (c *IntColumn) GetInts() ([]int32, error) {
|
||||
return c.values, nil
|
||||
}
|
||||
|
||||
func (c *IntColumn) GetObject(position int32) (interface{}, error) {
|
||||
return c.GetInt(position)
|
||||
}
|
||||
|
||||
func (c *IntColumn) MayHaveNull() bool {
|
||||
return c.valueIsNull != nil
|
||||
}
|
||||
|
||||
func (c *IntColumn) IsNull(position int32) bool {
|
||||
return c.valueIsNull != nil && c.valueIsNull[position+c.arrayOffset]
|
||||
}
|
||||
|
||||
func (c *IntColumn) IsNulls() []bool {
|
||||
if c.valueIsNull != nil {
|
||||
return c.valueIsNull
|
||||
}
|
||||
result := make([]bool, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *IntColumn) GetPositionCount() int32 {
|
||||
return c.positionCount
|
||||
}
|
||||
|
||||
type FloatColumn struct {
|
||||
baseColumn
|
||||
arrayOffset int32
|
||||
positionCount int32
|
||||
valueIsNull []bool
|
||||
values []float32
|
||||
}
|
||||
|
||||
func NewFloatColumn(arrayOffset int32, positionCount int32, valueIsNull []bool, values []float32) (*FloatColumn, error) {
|
||||
if arrayOffset < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("positionCount is negative")
|
||||
}
|
||||
if int32(len(values))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("values length is less than positionCount")
|
||||
}
|
||||
if valueIsNull != nil && int32(len(valueIsNull))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("isNull length is less than positionCount")
|
||||
}
|
||||
return &FloatColumn{
|
||||
arrayOffset: arrayOffset,
|
||||
positionCount: positionCount,
|
||||
valueIsNull: valueIsNull,
|
||||
values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *FloatColumn) GetDataType() TSDataType {
|
||||
return FLOAT
|
||||
}
|
||||
|
||||
func (c *FloatColumn) GetEncoding() ColumnEncoding {
|
||||
return INT32_ARRAY_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (c *FloatColumn) GetFloat(position int32) (float32, error) {
|
||||
return c.values[position+c.arrayOffset], nil
|
||||
}
|
||||
|
||||
func (c *FloatColumn) GetFloats() ([]float32, error) {
|
||||
return c.values, nil
|
||||
}
|
||||
|
||||
func (c *FloatColumn) GetObject(position int32) (interface{}, error) {
|
||||
return c.GetFloat(position)
|
||||
}
|
||||
|
||||
func (c *FloatColumn) MayHaveNull() bool {
|
||||
return c.valueIsNull != nil
|
||||
}
|
||||
|
||||
func (c *FloatColumn) IsNull(position int32) bool {
|
||||
return c.valueIsNull != nil && c.valueIsNull[position+c.arrayOffset]
|
||||
}
|
||||
|
||||
func (c *FloatColumn) IsNulls() []bool {
|
||||
if c.valueIsNull != nil {
|
||||
return c.valueIsNull
|
||||
}
|
||||
result := make([]bool, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *FloatColumn) GetPositionCount() int32 {
|
||||
return c.positionCount
|
||||
}
|
||||
|
||||
type LongColumn struct {
|
||||
baseColumn
|
||||
arrayOffset int32
|
||||
positionCount int32
|
||||
valueIsNull []bool
|
||||
values []int64
|
||||
}
|
||||
|
||||
func NewLongColumn(arrayOffset int32, positionCount int32, valueIsNull []bool, values []int64) (*LongColumn, error) {
|
||||
if arrayOffset < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("positionCount is negative")
|
||||
}
|
||||
if int32(len(values))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("values length is less than positionCount")
|
||||
}
|
||||
if valueIsNull != nil && int32(len(valueIsNull))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("isNull length is less than positionCount")
|
||||
}
|
||||
return &LongColumn{
|
||||
arrayOffset: arrayOffset,
|
||||
positionCount: positionCount,
|
||||
valueIsNull: valueIsNull,
|
||||
values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *LongColumn) GetDataType() TSDataType {
|
||||
return INT64
|
||||
}
|
||||
|
||||
func (c *LongColumn) GetEncoding() ColumnEncoding {
|
||||
return INT64_ARRAY_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (c *LongColumn) GetLong(position int32) (int64, error) {
|
||||
return c.values[position+c.arrayOffset], nil
|
||||
}
|
||||
|
||||
func (c *LongColumn) GetLongs() ([]int64, error) {
|
||||
return c.values, nil
|
||||
}
|
||||
|
||||
func (c *LongColumn) GetObject(position int32) (interface{}, error) {
|
||||
return c.GetLong(position)
|
||||
}
|
||||
|
||||
func (c *LongColumn) MayHaveNull() bool {
|
||||
return c.valueIsNull != nil
|
||||
}
|
||||
|
||||
func (c *LongColumn) IsNull(position int32) bool {
|
||||
return c.valueIsNull != nil && c.valueIsNull[position+c.arrayOffset]
|
||||
}
|
||||
|
||||
func (c *LongColumn) IsNulls() []bool {
|
||||
if c.valueIsNull != nil {
|
||||
return c.valueIsNull
|
||||
}
|
||||
result := make([]bool, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *LongColumn) GetPositionCount() int32 {
|
||||
return c.positionCount
|
||||
}
|
||||
|
||||
type DoubleColumn struct {
|
||||
baseColumn
|
||||
arrayOffset int32
|
||||
positionCount int32
|
||||
valueIsNull []bool
|
||||
values []float64
|
||||
}
|
||||
|
||||
func NewDoubleColumn(arrayOffset int32, positionCount int32, valueIsNull []bool, values []float64) (*DoubleColumn, error) {
|
||||
if arrayOffset < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("positionCount is negative")
|
||||
}
|
||||
if int32(len(values))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("values length is less than positionCount")
|
||||
}
|
||||
if valueIsNull != nil && int32(len(valueIsNull))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("isNull length is less than positionCount")
|
||||
}
|
||||
return &DoubleColumn{
|
||||
arrayOffset: arrayOffset,
|
||||
positionCount: positionCount,
|
||||
valueIsNull: valueIsNull,
|
||||
values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) GetDataType() TSDataType {
|
||||
return DOUBLE
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) GetEncoding() ColumnEncoding {
|
||||
return INT64_ARRAY_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) GetDouble(position int32) (float64, error) {
|
||||
return c.values[position+c.arrayOffset], nil
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) GetDoubles() ([]float64, error) {
|
||||
return c.values, nil
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) GetObject(position int32) (interface{}, error) {
|
||||
return c.GetDouble(position)
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) MayHaveNull() bool {
|
||||
return c.valueIsNull != nil
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) IsNull(position int32) bool {
|
||||
return c.valueIsNull != nil && c.valueIsNull[position+c.arrayOffset]
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) IsNulls() []bool {
|
||||
if c.valueIsNull != nil {
|
||||
return c.valueIsNull
|
||||
}
|
||||
result := make([]bool, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *DoubleColumn) GetPositionCount() int32 {
|
||||
return c.positionCount
|
||||
}
|
||||
|
||||
type BooleanColumn struct {
|
||||
baseColumn
|
||||
arrayOffset int32
|
||||
positionCount int32
|
||||
valueIsNull []bool
|
||||
values []bool
|
||||
}
|
||||
|
||||
func NewBooleanColumn(arrayOffset int32, positionCount int32, valueIsNull []bool, values []bool) (*BooleanColumn, error) {
|
||||
if arrayOffset < 0 {
|
||||
return nil, fmt.Errorf("arrayOffset is negative")
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("positionCount is negative")
|
||||
}
|
||||
if int32(len(values))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("values length is less than positionCount")
|
||||
}
|
||||
if valueIsNull != nil && int32(len(valueIsNull))-arrayOffset < positionCount {
|
||||
return nil, fmt.Errorf("isNull length is less than positionCount")
|
||||
}
|
||||
return &BooleanColumn{
|
||||
arrayOffset: arrayOffset,
|
||||
positionCount: positionCount,
|
||||
valueIsNull: valueIsNull,
|
||||
values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) GetDataType() TSDataType {
|
||||
return BOOLEAN
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) GetEncoding() ColumnEncoding {
|
||||
return BYTE_ARRAY_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) GetBoolean(position int32) (bool, error) {
|
||||
return c.values[position+c.arrayOffset], nil
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) GetBooleans() ([]bool, error) {
|
||||
return c.values, nil
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) GetObject(position int32) (interface{}, error) {
|
||||
return c.GetBoolean(position)
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) MayHaveNull() bool {
|
||||
return c.valueIsNull != nil
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) IsNull(position int32) bool {
|
||||
return c.valueIsNull != nil && c.valueIsNull[position+c.arrayOffset]
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) IsNulls() []bool {
|
||||
if c.valueIsNull != nil {
|
||||
return c.valueIsNull
|
||||
}
|
||||
result := make([]bool, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *BooleanColumn) GetPositionCount() int32 {
|
||||
return c.positionCount
|
||||
}
|
||||
|
||||
type RunLengthEncodedColumn struct {
|
||||
baseColumn
|
||||
value Column
|
||||
positionCount int32
|
||||
}
|
||||
|
||||
func NewRunLengthEncodedColumn(value Column, positionCount int32) (*RunLengthEncodedColumn, error) {
|
||||
if value == nil {
|
||||
return nil, fmt.Errorf("value is null")
|
||||
}
|
||||
if value.GetPositionCount() != 1 {
|
||||
return nil, fmt.Errorf("expected value to contain a single position but has %v positions", value.GetPositionCount())
|
||||
}
|
||||
if positionCount < 0 {
|
||||
return nil, fmt.Errorf("positionCount is negative")
|
||||
}
|
||||
column := new(RunLengthEncodedColumn)
|
||||
switch (value).(type) {
|
||||
case *RunLengthEncodedColumn:
|
||||
column.value = (value.(*RunLengthEncodedColumn)).GetValue()
|
||||
default:
|
||||
column.value = value
|
||||
}
|
||||
column.positionCount = positionCount
|
||||
return column, nil
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetValue() Column {
|
||||
return c.value
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetDataType() TSDataType {
|
||||
return c.value.GetDataType()
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetEncoding() ColumnEncoding {
|
||||
return RLE_COLUMN_ENCODING
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetBoolean(_ int32) (bool, error) {
|
||||
return c.value.GetBoolean(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetInt(_ int32) (int32, error) {
|
||||
return c.value.GetInt(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetLong(_ int32) (int64, error) {
|
||||
return c.value.GetLong(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetFloat(_ int32) (float32, error) {
|
||||
return c.value.GetFloat(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetDouble(_ int32) (float64, error) {
|
||||
return c.value.GetDouble(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetBinary(_ int32) (*Binary, error) {
|
||||
return c.value.GetBinary(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetObject(_ int32) (interface{}, error) {
|
||||
return c.value.GetObject(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetBooleans() ([]bool, error) {
|
||||
v, err := c.value.GetBoolean(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]bool, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetInts() ([]int32, error) {
|
||||
v, err := c.value.GetInt(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]int32, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetLongs() ([]int64, error) {
|
||||
v, err := c.value.GetLong(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]int64, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetFloats() ([]float32, error) {
|
||||
v, err := c.value.GetFloat(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]float32, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetDoubles() ([]float64, error) {
|
||||
v, err := c.value.GetDouble(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]float64, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetBinaries() ([]*Binary, error) {
|
||||
v, err := c.value.GetBinary(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*Binary, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetObjects() ([]interface{}, error) {
|
||||
v, err := c.value.GetObject(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]interface{}, c.positionCount)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) MayHaveNull() bool {
|
||||
return c.value.MayHaveNull()
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) IsNull(_ int32) bool {
|
||||
return c.value.IsNull(0)
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) IsNulls() []bool {
|
||||
result := make([]bool, c.positionCount)
|
||||
v := c.value.IsNull(0)
|
||||
for i := int32(0); i < c.positionCount; i++ {
|
||||
result[i] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *RunLengthEncodedColumn) GetPositionCount() int32 {
|
||||
return c.positionCount
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ColumnDecoder interface {
|
||||
ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error)
|
||||
}
|
||||
|
||||
func deserializeNullIndicators(reader *bytes.Reader, positionCount int32) ([]bool, error) {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mayHaveNull := b != 0
|
||||
if !mayHaveNull {
|
||||
return nil, nil
|
||||
}
|
||||
return deserializeBooleanArray(reader, positionCount)
|
||||
}
|
||||
|
||||
func deserializeBooleanArray(reader *bytes.Reader, size int32) ([]bool, error) {
|
||||
packedSize := (size + 7) / 8
|
||||
packedBytes := make([]byte, packedSize)
|
||||
|
||||
_, err := reader.Read(packedBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// read null bits 8 at a time
|
||||
output := make([]bool, size)
|
||||
currentByte := 0
|
||||
fullGroups := int(size) & ^0b111
|
||||
for pos := 0; pos < fullGroups; pos += 8 {
|
||||
b := packedBytes[currentByte]
|
||||
currentByte++
|
||||
|
||||
output[pos+0] = (b & 0b10000000) != 0
|
||||
output[pos+1] = (b & 0b01000000) != 0
|
||||
output[pos+2] = (b & 0b00100000) != 0
|
||||
output[pos+3] = (b & 0b00010000) != 0
|
||||
output[pos+4] = (b & 0b00001000) != 0
|
||||
output[pos+5] = (b & 0b00000100) != 0
|
||||
output[pos+6] = (b & 0b00000010) != 0
|
||||
output[pos+7] = (b & 0b00000001) != 0
|
||||
}
|
||||
|
||||
// read last null bits
|
||||
if remaining := int(size) % 8; remaining > 0 {
|
||||
b := packedBytes[len(packedBytes)-1]
|
||||
mask := uint8(0b10000000)
|
||||
|
||||
for pos := fullGroups; pos < int(size); pos++ {
|
||||
output[pos] = (b & mask) != 0
|
||||
mask >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
type baseColumnDecoder struct{}
|
||||
|
||||
type Int32ArrayColumnDecoder struct {
|
||||
baseColumnDecoder
|
||||
}
|
||||
|
||||
func (decoder *Int32ArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) {
|
||||
// Serialized data layout:
|
||||
// +---------------+-----------------+-------------+
|
||||
// | may have null | null indicators | values |
|
||||
// +---------------+-----------------+-------------+
|
||||
// | byte | list[byte] | list[int32] |
|
||||
// +---------------+-----------------+-------------+
|
||||
|
||||
if positionCount == 0 {
|
||||
switch dataType {
|
||||
case INT32, DATE:
|
||||
return NewIntColumn(0, 0, nil, []int32{})
|
||||
case FLOAT:
|
||||
return NewFloatColumn(0, 0, nil, []float32{})
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid data type: %v", dataType)
|
||||
}
|
||||
}
|
||||
|
||||
nullIndicators, err := deserializeNullIndicators(reader, positionCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch dataType {
|
||||
case INT32, DATE:
|
||||
intValues := make([]int32, positionCount)
|
||||
for i := int32(0); i < positionCount; i++ {
|
||||
if nullIndicators != nil && nullIndicators[i] {
|
||||
continue
|
||||
}
|
||||
err := binary.Read(reader, binary.BigEndian, &intValues[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewIntColumn(0, positionCount, nullIndicators, intValues)
|
||||
case FLOAT:
|
||||
floatValues := make([]float32, positionCount)
|
||||
for i := int32(0); i < positionCount; i++ {
|
||||
if nullIndicators != nil && nullIndicators[i] {
|
||||
continue
|
||||
}
|
||||
err := binary.Read(reader, binary.BigEndian, &floatValues[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewFloatColumn(0, positionCount, nullIndicators, floatValues)
|
||||
}
|
||||
return nil, fmt.Errorf("invalid data type: %v", dataType)
|
||||
}
|
||||
|
||||
type Int64ArrayColumnDecoder struct {
|
||||
baseColumnDecoder
|
||||
}
|
||||
|
||||
func (decoder *Int64ArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) {
|
||||
// Serialized data layout:
|
||||
// +---------------+-----------------+-------------+
|
||||
// | may have null | null indicators | values |
|
||||
// +---------------+-----------------+-------------+
|
||||
// | byte | list[byte] | list[int64] |
|
||||
// +---------------+-----------------+-------------+
|
||||
|
||||
if positionCount == 0 {
|
||||
switch dataType {
|
||||
case INT64, TIMESTAMP:
|
||||
return NewLongColumn(0, 0, nil, []int64{})
|
||||
case DOUBLE:
|
||||
return NewDoubleColumn(0, 0, nil, []float64{})
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid data type: %v", dataType)
|
||||
}
|
||||
}
|
||||
|
||||
nullIndicators, err := deserializeNullIndicators(reader, positionCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch dataType {
|
||||
case INT64, TIMESTAMP:
|
||||
values := make([]int64, positionCount)
|
||||
for i := int32(0); i < positionCount; i++ {
|
||||
if nullIndicators != nil && nullIndicators[i] {
|
||||
continue
|
||||
}
|
||||
if err = binary.Read(reader, binary.BigEndian, &values[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewLongColumn(0, positionCount, nullIndicators, values)
|
||||
case DOUBLE:
|
||||
values := make([]float64, positionCount)
|
||||
for i := int32(0); i < positionCount; i++ {
|
||||
if nullIndicators != nil && nullIndicators[i] {
|
||||
continue
|
||||
}
|
||||
if err = binary.Read(reader, binary.BigEndian, &values[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewDoubleColumn(0, positionCount, nullIndicators, values)
|
||||
}
|
||||
return nil, fmt.Errorf("invalid data type: %v", dataType)
|
||||
}
|
||||
|
||||
type ByteArrayColumnDecoder struct {
|
||||
baseColumnDecoder
|
||||
}
|
||||
|
||||
func (decoder *ByteArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) {
|
||||
// Serialized data layout:
|
||||
// +---------------+-----------------+-------------+
|
||||
// | may have null | null indicators | values |
|
||||
// +---------------+-----------------+-------------+
|
||||
// | byte | list[byte] | list[byte] |
|
||||
// +---------------+-----------------+-------------+
|
||||
|
||||
if dataType != BOOLEAN {
|
||||
return nil, fmt.Errorf("invalid data type: %v", dataType)
|
||||
}
|
||||
|
||||
if positionCount == 0 {
|
||||
return NewBooleanColumn(0, 0, nil, []bool{})
|
||||
}
|
||||
|
||||
nullIndicators, err := deserializeNullIndicators(reader, positionCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values, err := deserializeBooleanArray(reader, positionCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewBooleanColumn(0, positionCount, nullIndicators, values)
|
||||
}
|
||||
|
||||
type BinaryArrayColumnDecoder struct {
|
||||
baseColumnDecoder
|
||||
}
|
||||
|
||||
func (decoder *BinaryArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) {
|
||||
// Serialized data layout:
|
||||
// +---------------+-----------------+-------------+
|
||||
// | may have null | null indicators | values |
|
||||
// +---------------+-----------------+-------------+
|
||||
// | byte | list[byte] | list[entry] |
|
||||
// +---------------+-----------------+-------------+
|
||||
//
|
||||
// Each entry is represented as:
|
||||
// +---------------+-------+
|
||||
// | value length | value |
|
||||
// +---------------+-------+
|
||||
// | int32 | bytes |
|
||||
// +---------------+-------+
|
||||
|
||||
if TEXT != dataType {
|
||||
return nil, fmt.Errorf("invalid data type: %v", dataType)
|
||||
}
|
||||
|
||||
if positionCount == 0 {
|
||||
return NewBinaryColumn(0, 0, nil, []*Binary{})
|
||||
}
|
||||
|
||||
nullIndicators, err := deserializeNullIndicators(reader, positionCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values := make([]*Binary, positionCount)
|
||||
for i := int32(0); i < positionCount; i++ {
|
||||
if nullIndicators != nil && nullIndicators[i] {
|
||||
continue
|
||||
}
|
||||
var length int32
|
||||
err := binary.Read(reader, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
values[i] = NewBinary([]byte{})
|
||||
} else {
|
||||
value := make([]byte, length)
|
||||
_, err = reader.Read(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values[i] = NewBinary(value)
|
||||
}
|
||||
}
|
||||
return NewBinaryColumn(0, positionCount, nullIndicators, values)
|
||||
}
|
||||
|
||||
type RunLengthColumnDecoder struct {
|
||||
baseColumnDecoder
|
||||
}
|
||||
|
||||
func (decoder *RunLengthColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) {
|
||||
// Serialized data layout:
|
||||
// +-----------+-------------------------+
|
||||
// | encoding | serialized inner column |
|
||||
// +-----------+-------------------------+
|
||||
// | byte | list[byte] |
|
||||
// +-----------+-------------------------+
|
||||
columnEncoding, err := deserializeColumnEncoding(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columnDecoder, err := getColumnDecoder(columnEncoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
column, err := columnDecoder.ReadColumn(reader, dataType, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewRunLengthEncodedColumn(column, positionCount)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/apache/iotdb-client-go/v2/common"
|
||||
)
|
||||
|
||||
// ExecutionError represents an error returned by the server via TSStatus.
|
||||
// It is NOT a connection error and should not cause session drops.
|
||||
type ExecutionError struct {
|
||||
Code int32
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *ExecutionError) Error() string {
|
||||
if e.Message != "" {
|
||||
return fmt.Sprintf("error code: %d, message: %v", e.Code, e.Message)
|
||||
}
|
||||
return fmt.Sprintf("error code: %d", e.Code)
|
||||
}
|
||||
|
||||
type BatchError struct {
|
||||
statuses []*common.TSStatus
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *BatchError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *BatchError) GetStatuses() []*common.TSStatus {
|
||||
return e.statuses
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import "time"
|
||||
|
||||
type Field struct {
|
||||
dataType TSDataType
|
||||
name string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (f *Field) IsNull() bool {
|
||||
return f.value == nil
|
||||
}
|
||||
|
||||
func (f *Field) GetDataType() TSDataType {
|
||||
return f.dataType
|
||||
}
|
||||
|
||||
func (f *Field) GetValue() interface{} {
|
||||
return f.value
|
||||
}
|
||||
|
||||
func (f *Field) GetInt32() int32 {
|
||||
if f.value == nil {
|
||||
return 0
|
||||
}
|
||||
return f.value.(int32)
|
||||
}
|
||||
|
||||
func (f *Field) GetInt64() int64 {
|
||||
if f.value == nil {
|
||||
return 0
|
||||
}
|
||||
return f.value.(int64)
|
||||
}
|
||||
|
||||
func (f *Field) GetFloat32() float32 {
|
||||
if f.value == nil {
|
||||
return 0
|
||||
}
|
||||
return f.value.(float32)
|
||||
}
|
||||
|
||||
func (f *Field) GetFloat64() float64 {
|
||||
if f.value == nil {
|
||||
return 0
|
||||
}
|
||||
return f.value.(float64)
|
||||
}
|
||||
|
||||
func (f *Field) GetName() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f *Field) GetText() string {
|
||||
if f.value == nil {
|
||||
return ""
|
||||
}
|
||||
switch f.value.(type) {
|
||||
case bool:
|
||||
if f.value.(bool) {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
case int32:
|
||||
return int32ToString(f.value.(int32))
|
||||
case int64:
|
||||
return int64ToString(f.value.(int64))
|
||||
case float32:
|
||||
return float32ToString(f.value.(float32))
|
||||
case float64:
|
||||
return float64ToString(f.value.(float64))
|
||||
case string:
|
||||
return f.value.(string)
|
||||
case []byte:
|
||||
return bytesToHexString(f.value.([]byte))
|
||||
case time.Time:
|
||||
return f.value.(time.Time).Format("2006-01-02")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import "context"
|
||||
|
||||
// Options holds the configuration for a client call.
|
||||
type Options struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Option is a functional option for configuring [Options].
|
||||
type Option func(*Options)
|
||||
|
||||
// ApplyOptions applies the given options and returns the resulting Options.
|
||||
func ApplyOptions(opts ...Option) Options {
|
||||
var result Options
|
||||
for _, opt := range opts {
|
||||
if opt == nil {
|
||||
continue
|
||||
}
|
||||
opt(&result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// WithCtx returns an Option that sets the context for a client call.
|
||||
func WithCtx(ctx context.Context) Option {
|
||||
return func(opts *Options) {
|
||||
opts.ctx = ctx
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import "fmt"
|
||||
|
||||
type TSDataType int8
|
||||
|
||||
type TSEncoding uint8
|
||||
|
||||
type TSCompressionType uint8
|
||||
|
||||
const (
|
||||
UNKNOWN TSDataType = -1
|
||||
BOOLEAN TSDataType = 0
|
||||
INT32 TSDataType = 1
|
||||
INT64 TSDataType = 2
|
||||
FLOAT TSDataType = 3
|
||||
DOUBLE TSDataType = 4
|
||||
TEXT TSDataType = 5
|
||||
TIMESTAMP TSDataType = 8
|
||||
DATE TSDataType = 9
|
||||
BLOB TSDataType = 10
|
||||
STRING TSDataType = 11
|
||||
)
|
||||
|
||||
var tsTypeMap = map[string]TSDataType{
|
||||
"BOOLEAN": BOOLEAN,
|
||||
"INT32": INT32,
|
||||
"INT64": INT64,
|
||||
"FLOAT": FLOAT,
|
||||
"DOUBLE": DOUBLE,
|
||||
"TEXT": TEXT,
|
||||
"TIMESTAMP": TIMESTAMP,
|
||||
"DATE": DATE,
|
||||
"BLOB": BLOB,
|
||||
"STRING": STRING,
|
||||
}
|
||||
|
||||
var byteToTsDataType = map[byte]TSDataType{
|
||||
0: BOOLEAN,
|
||||
1: INT32,
|
||||
2: INT64,
|
||||
3: FLOAT,
|
||||
4: DOUBLE,
|
||||
5: TEXT,
|
||||
8: TIMESTAMP,
|
||||
9: DATE,
|
||||
10: BLOB,
|
||||
11: STRING,
|
||||
}
|
||||
|
||||
func GetDataTypeByStr(name string) (TSDataType, error) {
|
||||
dataType, exists := tsTypeMap[name]
|
||||
if !exists {
|
||||
return UNKNOWN, fmt.Errorf("invalid input: %v", name)
|
||||
}
|
||||
return dataType, nil
|
||||
}
|
||||
|
||||
func getDataTypeByByte(b byte) (TSDataType, error) {
|
||||
dataType, exists := byteToTsDataType[b]
|
||||
if !exists {
|
||||
return UNKNOWN, fmt.Errorf("invalid input: %v", b)
|
||||
}
|
||||
return dataType, nil
|
||||
}
|
||||
|
||||
const (
|
||||
PLAIN TSEncoding = 0
|
||||
DICTIONARY TSEncoding = 1
|
||||
RLE TSEncoding = 2
|
||||
DIFF TSEncoding = 3
|
||||
TS_2DIFF TSEncoding = 4
|
||||
BITMAP TSEncoding = 5
|
||||
GORILLA_V1 TSEncoding = 6
|
||||
REGULAR TSEncoding = 7
|
||||
GORILLA TSEncoding = 8
|
||||
ZIGZAG TSEncoding = 9
|
||||
FREQ TSEncoding = 10
|
||||
CHIMP TSEncoding = 11
|
||||
SPRINTZ TSEncoding = 12
|
||||
RLBE TSEncoding = 13
|
||||
)
|
||||
|
||||
const (
|
||||
UNCOMPRESSED TSCompressionType = 0
|
||||
SNAPPY TSCompressionType = 1
|
||||
GZIP TSCompressionType = 2
|
||||
LZ4 TSCompressionType = 7
|
||||
ZSTD TSCompressionType = 8
|
||||
LZMA2 TSCompressionType = 9
|
||||
)
|
||||
|
||||
// TSStatusCode
|
||||
const (
|
||||
SuccessStatus int32 = 200
|
||||
IncompatibleVersion int32 = 201
|
||||
ConfigurationError int32 = 202
|
||||
StartUpError int32 = 203
|
||||
ShutDownError int32 = 204
|
||||
|
||||
UnsupportedOperation int32 = 300
|
||||
ExecuteStatementError int32 = 301
|
||||
MultipleError int32 = 302
|
||||
IllegalParameter int32 = 303
|
||||
OverlapWithExistingTask int32 = 304
|
||||
InternalServerError int32 = 305
|
||||
|
||||
RedirectionRecommend int32 = 400
|
||||
|
||||
DatabaseNotExist int32 = 500
|
||||
DatabaseAlreadyExists int32 = 501
|
||||
SeriesOverflow int32 = 502
|
||||
TimeseriesAlreadyExist int32 = 503
|
||||
TimeseriesInBlackList int32 = 504
|
||||
AliasAlreadyExist int32 = 505
|
||||
PathAlreadyExist int32 = 506
|
||||
MetadataError int32 = 507
|
||||
PathNotExist int32 = 508
|
||||
IllegalPath int32 = 509
|
||||
CreateTemplateError int32 = 510
|
||||
DuplicatedTemplate int32 = 511
|
||||
UndefinedTemplate int32 = 512
|
||||
TemplateNotSet int32 = 513
|
||||
DifferentTemplate int32 = 514
|
||||
TemplateIsInUse int32 = 515
|
||||
TemplateIncompatible int32 = 516
|
||||
SegmentNotFound int32 = 517
|
||||
PageOutOfSpace int32 = 518
|
||||
RecordDuplicated int32 = 519
|
||||
SegmentOutOfSpace int32 = 520
|
||||
SchemaFileNotExists int32 = 521
|
||||
OversizeRecord int32 = 522
|
||||
SchemaFileRedoLogBroken int32 = 523
|
||||
TemplateNotActivated int32 = 524
|
||||
|
||||
SystemReadOnly int32 = 600
|
||||
StorageEngineError int32 = 601
|
||||
StorageEngineNotReady int32 = 602
|
||||
DataregionProcessError int32 = 603
|
||||
TsfileProcessorError int32 = 604
|
||||
WriteProcessError int32 = 605
|
||||
WriteProcessReject int32 = 606
|
||||
OutOfTtl int32 = 607
|
||||
CompactionError int32 = 608
|
||||
AlignedTimeseriesError int32 = 609
|
||||
WalError int32 = 610
|
||||
DiskSpaceInsufficient int32 = 611
|
||||
|
||||
SqlParseError int32 = 700
|
||||
SemanticError int32 = 701
|
||||
GenerateTimeZoneError int32 = 702
|
||||
SetTimeZoneError int32 = 703
|
||||
QueryNotAllowed int32 = 704
|
||||
LogicalOperatorError int32 = 705
|
||||
LogicalOptimizeError int32 = 706
|
||||
UnsupportedFillType int32 = 707
|
||||
QueryProcessError int32 = 708
|
||||
MppMemoryNotEnough int32 = 709
|
||||
CloseOperationError int32 = 710
|
||||
TsblockSerializeError int32 = 711
|
||||
InternalRequestTimeOut int32 = 712
|
||||
InternalRequestRetryError int32 = 713
|
||||
|
||||
InitAuthError int32 = 800
|
||||
WrongLoginPassword int32 = 801
|
||||
NotLogin int32 = 802
|
||||
NoPermission int32 = 803
|
||||
UserNotExist int32 = 804
|
||||
UserAlreadyExist int32 = 805
|
||||
UserAlreadyHasRole int32 = 806
|
||||
UserNotHasRole int32 = 807
|
||||
RoleNotExist int32 = 808
|
||||
RoleAlreadyExist int32 = 809
|
||||
AlreadyHasPrivilege int32 = 810
|
||||
NotHasPrivilege int32 = 811
|
||||
ClearPermissionCacheError int32 = 812
|
||||
UnknownAuthPrivilege int32 = 813
|
||||
UnsupportedAuthOperation int32 = 814
|
||||
AuthIoException int32 = 815
|
||||
|
||||
MigrateRegionError int32 = 900
|
||||
CreateRegionError int32 = 901
|
||||
DeleteRegionError int32 = 902
|
||||
PartitionCacheUpdateError int32 = 903
|
||||
ConsensusNotInitialized int32 = 904
|
||||
RegionLeaderChangeError int32 = 905
|
||||
NoAvailableRegionGroup int32 = 906
|
||||
|
||||
DatanodeAlreadyRegistered int32 = 1000
|
||||
NoEnoughDatanode int32 = 1001
|
||||
AddConfignodeError int32 = 1002
|
||||
RemoveConfignodeError int32 = 1003
|
||||
DatanodeNotExist int32 = 1004
|
||||
DatanodeStopError int32 = 1005
|
||||
RemoveDatanodeError int32 = 1006
|
||||
RegisterRemovedDatanode int32 = 1007
|
||||
CanNotConnectDatanode int32 = 1008
|
||||
|
||||
LoadFileError int32 = 1100
|
||||
LoadPieceOfTsfileError int32 = 1101
|
||||
DeserializePieceOfTsfileError int32 = 1102
|
||||
SyncConnectionError int32 = 1103
|
||||
SyncFileRedirectionError int32 = 1104
|
||||
SyncFileError int32 = 1105
|
||||
CreatePipeSinkError int32 = 1106
|
||||
PipeError int32 = 1107
|
||||
PipeserverError int32 = 1108
|
||||
VerifyMetadataError int32 = 1109
|
||||
|
||||
UdfLoadClassError int32 = 1200
|
||||
UdfDownloadError int32 = 1201
|
||||
CreateUdfOnDatanodeError int32 = 1202
|
||||
DropUdfOnDatanodeError int32 = 1203
|
||||
|
||||
CreateTriggerError int32 = 1300
|
||||
DropTriggerError int32 = 1301
|
||||
TriggerFireError int32 = 1302
|
||||
TriggerLoadClassError int32 = 1303
|
||||
TriggerDownloadError int32 = 1304
|
||||
CreateTriggerInstanceError int32 = 1305
|
||||
ActiveTriggerInstanceError int32 = 1306
|
||||
DropTriggerInstanceError int32 = 1307
|
||||
UpdateTriggerLocationError int32 = 1308
|
||||
|
||||
NoSuchCq int32 = 1400
|
||||
CqAlreadyActive int32 = 1401
|
||||
CqAlreadyExist int32 = 1402
|
||||
CqUpdateLastExecTimeError int32 = 1403
|
||||
)
|
||||
|
||||
const (
|
||||
TimestampColumnName = "Time"
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
type RowRecord struct {
|
||||
timestamp int64
|
||||
fields []*Field
|
||||
}
|
||||
|
||||
func (r *RowRecord) GetFields() []*Field {
|
||||
return r.fields
|
||||
}
|
||||
|
||||
func (r *RowRecord) GetTimestamp() int64 {
|
||||
return r.timestamp
|
||||
}
|
||||
@@ -0,0 +1,829 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/apache/iotdb-client-go/v2/common"
|
||||
"github.com/apache/iotdb-client-go/v2/rpc"
|
||||
)
|
||||
|
||||
const startIndex = int32(2)
|
||||
|
||||
type IoTDBRpcDataSet struct {
|
||||
sql string
|
||||
isClosed bool
|
||||
client *rpc.IClientRPCServiceClient
|
||||
columnNameList []string // no deduplication
|
||||
columnTypeList []string // no deduplication
|
||||
columnOrdinalMap map[string]int32 // used because the server returns deduplicated columns
|
||||
columnName2TsBlockColumnIndexMap map[string]int32
|
||||
|
||||
// column index -> TsBlock column index
|
||||
columnIndex2TsBlockColumnIndexList []int32
|
||||
|
||||
dataTypeForTsBlockColumn []TSDataType
|
||||
fetchSize int32
|
||||
timeout *int64
|
||||
hasCachedRecord bool
|
||||
lastReadWasNull bool
|
||||
|
||||
columnSize int32
|
||||
|
||||
sessionId int64
|
||||
queryId int64
|
||||
statementId int64
|
||||
time int64
|
||||
ignoreTimestamp bool
|
||||
// indicates that there is still more data in server side and we can call fetchResult to get more
|
||||
moreData bool
|
||||
|
||||
queryResult [][]byte
|
||||
curTsBlock *TsBlock
|
||||
queryResultSize int32 // the length of queryResult
|
||||
queryResultIndex int32 // the index of bytebuffer in queryResult
|
||||
tsBlockSize int32 // the size of current tsBlock
|
||||
tsBlockIndex int32 // the row index in current tsBlock
|
||||
|
||||
zoneId *time.Location
|
||||
timeFormat string
|
||||
timeFactor int32
|
||||
timePrecision string
|
||||
}
|
||||
|
||||
func NewIoTDBRpcDataSet(sql string, columnNameList []string, columnTypeList []string, columnNameIndex map[string]int32, ignoreTimestamp bool, moreData bool, queryId int64, statementId int64, client *rpc.IClientRPCServiceClient, sessionId int64, queryResult [][]byte, fetchSize int32, timeout *int64, zoneId string, timeFormat string, timeFactor int32, columnIndex2TsBlockColumnIndexList []int32) (rpcDataSet *IoTDBRpcDataSet, err error) {
|
||||
ds := &IoTDBRpcDataSet{
|
||||
sessionId: sessionId,
|
||||
statementId: statementId,
|
||||
ignoreTimestamp: ignoreTimestamp,
|
||||
sql: sql,
|
||||
queryId: queryId,
|
||||
client: client,
|
||||
fetchSize: fetchSize,
|
||||
timeout: timeout,
|
||||
moreData: moreData,
|
||||
columnSize: int32(len(columnNameList)),
|
||||
columnNameList: make([]string, 0, len(columnNameList)+1),
|
||||
columnTypeList: make([]string, 0, len(columnTypeList)+1),
|
||||
columnOrdinalMap: make(map[string]int32),
|
||||
columnName2TsBlockColumnIndexMap: make(map[string]int32),
|
||||
}
|
||||
columnStartIndex := int32(1)
|
||||
resultSetColumnSize := int32(len(columnNameList))
|
||||
// newly generated or updated columnIndex2TsBlockColumnIndexList.size() may not be equal to
|
||||
// columnNameList.size()
|
||||
// so we need startIndexForColumnIndex2TsBlockColumnIndexList to adjust the mapping relation
|
||||
startIndexForColumnIndex2TsBlockColumnIndexList := int32(0)
|
||||
|
||||
// for Time Column in tree model which should always be the first column and its index for
|
||||
// TsBlockColumn is -1
|
||||
if !ignoreTimestamp {
|
||||
ds.columnNameList = append(ds.columnNameList, TimestampColumnName)
|
||||
ds.columnTypeList = append(ds.columnTypeList, "INT64")
|
||||
ds.columnName2TsBlockColumnIndexMap[TimestampColumnName] = -1
|
||||
ds.columnOrdinalMap[TimestampColumnName] = 1
|
||||
if columnIndex2TsBlockColumnIndexList != nil {
|
||||
newColumnIndex2TsBlockColumnIndexList := make([]int32, 0, len(columnIndex2TsBlockColumnIndexList)+1)
|
||||
newColumnIndex2TsBlockColumnIndexList = append(newColumnIndex2TsBlockColumnIndexList, -1)
|
||||
newColumnIndex2TsBlockColumnIndexList = append(newColumnIndex2TsBlockColumnIndexList, columnIndex2TsBlockColumnIndexList...)
|
||||
columnIndex2TsBlockColumnIndexList = newColumnIndex2TsBlockColumnIndexList
|
||||
startIndexForColumnIndex2TsBlockColumnIndexList = 1
|
||||
}
|
||||
columnStartIndex += 1
|
||||
resultSetColumnSize += 1
|
||||
}
|
||||
|
||||
ds.columnNameList = append(ds.columnNameList, columnNameList...)
|
||||
ds.columnTypeList = append(ds.columnTypeList, columnTypeList...)
|
||||
|
||||
if columnIndex2TsBlockColumnIndexList == nil {
|
||||
columnIndex2TsBlockColumnIndexList = make([]int32, 0, resultSetColumnSize)
|
||||
if !ignoreTimestamp {
|
||||
startIndexForColumnIndex2TsBlockColumnIndexList = 1
|
||||
columnIndex2TsBlockColumnIndexList = append(columnIndex2TsBlockColumnIndexList, -1)
|
||||
}
|
||||
// IoTDB 1.3.x servers send columnNameIndex (column name -> deduplicated
|
||||
// value column index) instead of the ordered index list, because their
|
||||
// aggregate TsBlock column order may differ from the SELECT list. Prefer
|
||||
// that mapping so values stay aligned with column names.
|
||||
for i, columnName := range columnNameList {
|
||||
if index, ok := columnNameIndex[columnName]; ok {
|
||||
columnIndex2TsBlockColumnIndexList = append(columnIndex2TsBlockColumnIndexList, index)
|
||||
} else {
|
||||
columnIndex2TsBlockColumnIndexList = append(columnIndex2TsBlockColumnIndexList, int32(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tsBlockColumnSize := int32(0)
|
||||
for _, value := range columnIndex2TsBlockColumnIndexList {
|
||||
if value > tsBlockColumnSize {
|
||||
tsBlockColumnSize = value
|
||||
}
|
||||
}
|
||||
tsBlockColumnSize += 1
|
||||
ds.dataTypeForTsBlockColumn = make([]TSDataType, tsBlockColumnSize)
|
||||
|
||||
for i, columnName := range columnNameList {
|
||||
tsBlockColumnIndex := columnIndex2TsBlockColumnIndexList[startIndexForColumnIndex2TsBlockColumnIndexList+int32(i)]
|
||||
if tsBlockColumnIndex != -1 {
|
||||
columnType, err := GetDataTypeByStr(columnTypeList[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds.dataTypeForTsBlockColumn[tsBlockColumnIndex] = columnType
|
||||
}
|
||||
if _, exists := ds.columnName2TsBlockColumnIndexMap[columnName]; !exists {
|
||||
ds.columnOrdinalMap[columnName] = int32(i) + columnStartIndex
|
||||
ds.columnName2TsBlockColumnIndexMap[columnName] = tsBlockColumnIndex
|
||||
}
|
||||
}
|
||||
|
||||
ds.queryResult = queryResult
|
||||
if queryResult != nil {
|
||||
ds.queryResultSize = int32(len(queryResult))
|
||||
} else {
|
||||
ds.queryResultSize = 0
|
||||
}
|
||||
ds.queryResultIndex = 0
|
||||
ds.tsBlockSize = 0
|
||||
ds.tsBlockIndex = -1
|
||||
if ds.zoneId, err = time.LoadLocation(zoneId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds.timeFormat = timeFormat
|
||||
ds.timeFactor = timeFactor
|
||||
if ds.timePrecision, err = getTimePrecision(timeFactor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(columnIndex2TsBlockColumnIndexList) != len(ds.columnNameList) {
|
||||
return nil, fmt.Errorf("size of columnIndex2TsBlockColumnIndexList %v doesn't equal to size of columnNameList %v", len(columnIndex2TsBlockColumnIndexList), len(ds.columnNameList))
|
||||
}
|
||||
ds.columnIndex2TsBlockColumnIndexList = columnIndex2TsBlockColumnIndexList
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) Close() (err error) {
|
||||
if s.isClosed {
|
||||
return nil
|
||||
}
|
||||
closeRequest := &rpc.TSCloseOperationReq{
|
||||
SessionId: s.sessionId,
|
||||
StatementId: &s.statementId,
|
||||
QueryId: &s.queryId,
|
||||
}
|
||||
|
||||
var status *common.TSStatus
|
||||
status, err = s.client.CloseOperation(context.Background(), closeRequest)
|
||||
if err == nil {
|
||||
err = VerifySuccess(status)
|
||||
}
|
||||
s.client = nil
|
||||
s.isClosed = true
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) Next() (result bool, err error) {
|
||||
if s.hasCachedBlock() {
|
||||
s.lastReadWasNull = false
|
||||
err = s.constructOneRow()
|
||||
return true, err
|
||||
}
|
||||
if s.hasCachedByteBuffer() {
|
||||
if err = s.constructOneTsBlock(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = s.constructOneRow()
|
||||
return true, err
|
||||
}
|
||||
|
||||
if s.moreData {
|
||||
hasResultSet, err := s.fetchResults()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if hasResultSet && s.hasCachedByteBuffer() {
|
||||
if err = s.constructOneTsBlock(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = s.constructOneRow()
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
err = s.Close()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) fetchResults() (bool, error) {
|
||||
if s.isClosed {
|
||||
return false, fmt.Errorf("this data set is already closed")
|
||||
}
|
||||
req := rpc.TSFetchResultsReq{
|
||||
SessionId: s.sessionId,
|
||||
Statement: s.sql,
|
||||
FetchSize: s.fetchSize,
|
||||
QueryId: s.queryId,
|
||||
IsAlign: true,
|
||||
}
|
||||
req.Timeout = s.timeout
|
||||
|
||||
resp, err := s.client.FetchResultsV2(context.Background(), &req)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err = VerifySuccess(resp.Status); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
s.moreData = resp.GetMoreData()
|
||||
|
||||
if !resp.HasResultSet {
|
||||
err = s.Close()
|
||||
} else {
|
||||
s.queryResult = resp.GetQueryResult_()
|
||||
s.queryResultIndex = 0
|
||||
if s.queryResult != nil {
|
||||
s.queryResultSize = int32(len(s.queryResult))
|
||||
} else {
|
||||
s.queryResultSize = 0
|
||||
}
|
||||
s.tsBlockSize = 0
|
||||
s.tsBlockIndex = -1
|
||||
}
|
||||
return resp.HasResultSet, err
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) hasCachedBlock() bool {
|
||||
return s.curTsBlock != nil && s.tsBlockIndex < s.tsBlockSize-1
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) hasCachedByteBuffer() bool {
|
||||
return s.queryResult != nil && s.queryResultIndex < s.queryResultSize
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) constructOneRow() (err error) {
|
||||
s.tsBlockIndex++
|
||||
s.hasCachedRecord = true
|
||||
s.time, err = s.curTsBlock.GetTimeColumn().GetLong(s.tsBlockIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) constructOneTsBlock() (err error) {
|
||||
s.lastReadWasNull = false
|
||||
curTsBlockBytes := s.queryResult[s.queryResultIndex]
|
||||
s.queryResultIndex = s.queryResultIndex + 1
|
||||
s.curTsBlock, err = DeserializeTsBlock(curTsBlockBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.tsBlockIndex = -1
|
||||
s.tsBlockSize = s.curTsBlock.GetPositionCount()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) isNullByIndex(columnIndex int32) (bool, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return s.isNull(index, s.tsBlockIndex), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) isNullByColumnName(columnName string) (bool, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return s.isNull(index, s.tsBlockIndex), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) isNull(index int32, rowNum int32) bool {
|
||||
// -1 for time column which will never be null
|
||||
return index >= 0 && s.curTsBlock.GetColumn(index).IsNull(rowNum)
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getBooleanByIndex(columnIndex int32) (bool, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return s.getBooleanByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getBoolean(columnName string) (bool, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return s.getBooleanByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getBooleanByTsBlockColumnIndex(tsBlockColumnIndex int32) (bool, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = false
|
||||
return s.curTsBlock.GetColumn(tsBlockColumnIndex).GetBoolean(s.tsBlockIndex)
|
||||
} else {
|
||||
s.lastReadWasNull = true
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getDoubleByIndex(columnIndex int32) (float64, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getDoubleByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getDouble(columnName string) (float64, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getDoubleByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getDoubleByTsBlockColumnIndex(tsBlockColumnIndex int32) (float64, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = false
|
||||
return s.curTsBlock.GetColumn(tsBlockColumnIndex).GetDouble(s.tsBlockIndex)
|
||||
} else {
|
||||
s.lastReadWasNull = true
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getFloatByIndex(columnIndex int32) (float32, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getFloatByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getFloat(columnName string) (float32, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getFloatByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getFloatByTsBlockColumnIndex(tsBlockColumnIndex int32) (float32, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = false
|
||||
return s.curTsBlock.GetColumn(tsBlockColumnIndex).GetFloat(s.tsBlockIndex)
|
||||
} else {
|
||||
s.lastReadWasNull = true
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getIntByIndex(columnIndex int32) (int32, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getIntByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getInt(columnName string) (int32, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getIntByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getIntByTsBlockColumnIndex(tsBlockColumnIndex int32) (int32, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = false
|
||||
dataType := s.curTsBlock.GetColumn(tsBlockColumnIndex).GetDataType()
|
||||
if dataType == INT64 {
|
||||
if v, err := s.curTsBlock.GetColumn(tsBlockColumnIndex).GetLong(s.tsBlockIndex); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return int32(v), nil
|
||||
}
|
||||
}
|
||||
return s.curTsBlock.GetColumn(tsBlockColumnIndex).GetInt(s.tsBlockIndex)
|
||||
} else {
|
||||
s.lastReadWasNull = true
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getLongByIndex(columnIndex int32) (int64, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getLongByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getLong(columnName string) (int64, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return s.getLongByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getLongByTsBlockColumnIndex(tsBlockColumnIndex int32) (int64, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if tsBlockColumnIndex < 0 {
|
||||
s.lastReadWasNull = false
|
||||
return s.curTsBlock.GetTimeByIndex(s.tsBlockIndex)
|
||||
}
|
||||
if !s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = false
|
||||
dataType := s.curTsBlock.GetColumn(tsBlockColumnIndex).GetDataType()
|
||||
if dataType == INT32 {
|
||||
if v, err := s.curTsBlock.GetColumn(tsBlockColumnIndex).GetInt(s.tsBlockIndex); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return int64(v), nil
|
||||
}
|
||||
}
|
||||
return s.curTsBlock.GetColumn(tsBlockColumnIndex).GetLong(s.tsBlockIndex)
|
||||
} else {
|
||||
s.lastReadWasNull = true
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getBinaryByIndex(columnIndex int32) (*Binary, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return s.getBinaryByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getBinary(columnName string) (*Binary, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return s.getBinaryByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getBinaryByTsBlockColumnIndex(tsBlockColumnIndex int32) (*Binary, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = false
|
||||
return s.curTsBlock.GetColumn(tsBlockColumnIndex).GetBinary(s.tsBlockIndex)
|
||||
} else {
|
||||
s.lastReadWasNull = true
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getObjectByIndex(columnIndex int32) (interface{}, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return s.getObjectByTsBlockIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getObject(columnName string) (interface{}, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return s.getObjectByTsBlockIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getObjectByTsBlockIndex(tsBlockColumnIndex int32) (interface{}, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = true
|
||||
return nil, nil
|
||||
}
|
||||
s.lastReadWasNull = false
|
||||
dataType := s.getDataTypeByTsBlockColumnIndex(tsBlockColumnIndex)
|
||||
switch dataType {
|
||||
case BOOLEAN, INT32, INT64, FLOAT, DOUBLE:
|
||||
return s.curTsBlock.GetColumn(tsBlockColumnIndex).GetObject(s.tsBlockIndex)
|
||||
case TIMESTAMP:
|
||||
var timestamp int64
|
||||
var err error
|
||||
if tsBlockColumnIndex == -1 {
|
||||
timestamp, err = s.curTsBlock.GetTimeByIndex(s.tsBlockIndex)
|
||||
} else {
|
||||
timestamp, err = s.curTsBlock.GetColumn(tsBlockColumnIndex).GetLong(s.tsBlockIndex)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertToTimestamp(timestamp, s.timeFactor), nil
|
||||
case TEXT, STRING:
|
||||
if binary, err := s.curTsBlock.GetColumn(tsBlockColumnIndex).GetBinary(s.tsBlockIndex); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return binary.GetStringValue(), nil
|
||||
}
|
||||
case BLOB:
|
||||
if binary, err := s.curTsBlock.GetColumn(tsBlockColumnIndex).GetBinary(s.tsBlockIndex); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return binary.GetValues(), nil
|
||||
}
|
||||
case DATE:
|
||||
if value, err := s.curTsBlock.GetColumn(tsBlockColumnIndex).GetInt(s.tsBlockIndex); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return Int32ToDate(value)
|
||||
}
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getStringByIndex(columnIndex int32) (string, error) {
|
||||
columnIndex, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.getStringByTsBlockColumnIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getString(columnName string) (string, error) {
|
||||
columnIndex, err := s.getTsBlockColumnIndexForColumnName(columnName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.getStringByTsBlockColumnIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getStringByTsBlockColumnIndex(tsBlockColumnIndex int32) (string, error) {
|
||||
if err := s.checkRecord(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// to keep compatibility, tree model should return a long value for time column
|
||||
if tsBlockColumnIndex == -1 {
|
||||
timestamp, err := s.curTsBlock.GetTimeByIndex(s.tsBlockIndex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return int64ToString(timestamp), nil
|
||||
}
|
||||
if s.isNull(tsBlockColumnIndex, s.tsBlockIndex) {
|
||||
s.lastReadWasNull = true
|
||||
return "", nil
|
||||
}
|
||||
s.lastReadWasNull = false
|
||||
return s.getStringByTsBlockColumnIndexAndDataType(tsBlockColumnIndex, s.getDataTypeByTsBlockColumnIndex(tsBlockColumnIndex))
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getStringByTsBlockColumnIndexAndDataType(index int32, tsDataType TSDataType) (string, error) {
|
||||
switch tsDataType {
|
||||
case BOOLEAN:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetBoolean(s.tsBlockIndex); err != nil {
|
||||
return "", nil
|
||||
} else {
|
||||
return strconv.FormatBool(v), nil
|
||||
}
|
||||
case INT32:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetInt(s.tsBlockIndex); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return int32ToString(v), nil
|
||||
}
|
||||
case INT64:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetLong(s.tsBlockIndex); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return int64ToString(v), nil
|
||||
}
|
||||
case TIMESTAMP:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetLong(s.tsBlockIndex); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return formatDatetime(DEFAULT_TIME_FORMAT, s.timePrecision, v, s.zoneId), nil
|
||||
}
|
||||
case FLOAT:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetFloat(s.tsBlockIndex); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return float32ToString(v), nil
|
||||
}
|
||||
case DOUBLE:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetDouble(s.tsBlockIndex); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return float64ToString(v), nil
|
||||
}
|
||||
case TEXT, STRING:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetBinary(s.tsBlockIndex); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return v.GetStringValue(), nil
|
||||
}
|
||||
case BLOB:
|
||||
if v, err := s.curTsBlock.GetColumn(index).GetBinary(s.tsBlockIndex); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return bytesToHexString(v.values), nil
|
||||
}
|
||||
case DATE:
|
||||
v, err := s.curTsBlock.GetColumn(index).GetInt(s.tsBlockIndex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
t, err := Int32ToDate(v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return t.Format("2006-01-02"), nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getTimestampByIndex(columnIndex int32) (time.Time, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return time.Time{}, err
|
||||
} else {
|
||||
return s.getTimestampByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getTimestamp(columnName string) (time.Time, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return time.Time{}, err
|
||||
} else {
|
||||
return s.getTimestampByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getTimestampByTsBlockColumnIndex(tsBlockColumnIndex int32) (time.Time, error) {
|
||||
if value, err := s.getLongByTsBlockColumnIndex(tsBlockColumnIndex); err != nil {
|
||||
return time.Time{}, err
|
||||
} else {
|
||||
return convertToTimestamp(value, s.timeFactor), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetDateByIndex(columnIndex int32) (time.Time, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return time.Time{}, err
|
||||
} else {
|
||||
return s.GetDateByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetDate(columnName string) (time.Time, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return time.Time{}, err
|
||||
} else {
|
||||
return s.GetDateByTsBlockColumnIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetDateByTsBlockColumnIndex(tsBlockColumnIndex int32) (time.Time, error) {
|
||||
if value, err := s.getIntByTsBlockColumnIndex(tsBlockColumnIndex); err != nil {
|
||||
return time.Time{}, err
|
||||
} else {
|
||||
return Int32ToDate(value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getDataTypeByIndex(columnIndex int32) (TSDataType, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnIndex(columnIndex); err != nil {
|
||||
return UNKNOWN, err
|
||||
} else {
|
||||
return s.getDataTypeByTsBlockColumnIndex(index), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getDataType(columnName string) (TSDataType, error) {
|
||||
if index, err := s.getTsBlockColumnIndexForColumnName(columnName); err != nil {
|
||||
return UNKNOWN, err
|
||||
} else {
|
||||
return s.getDataTypeByTsBlockColumnIndex(index), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getDataTypeByTsBlockColumnIndex(tsBlockColumnIndex int32) TSDataType {
|
||||
if tsBlockColumnIndex < 0 {
|
||||
return TIMESTAMP
|
||||
} else {
|
||||
return s.dataTypeForTsBlockColumn[tsBlockColumnIndex]
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) findColumn(columnName string) int32 {
|
||||
return s.columnOrdinalMap[columnName]
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) findColumnNameByIndex(columnIndex int32) (string, error) {
|
||||
if columnIndex <= 0 {
|
||||
return "", fmt.Errorf("column index should start from 1")
|
||||
}
|
||||
if columnIndex > int32(len(s.columnNameList)) {
|
||||
return "", fmt.Errorf("column index %d out of range %d", columnIndex, len(s.columnNameList))
|
||||
}
|
||||
return s.columnNameList[columnIndex-1], nil
|
||||
}
|
||||
|
||||
// return -1 for time column of tree model
|
||||
func (s *IoTDBRpcDataSet) getTsBlockColumnIndexForColumnName(columnName string) (int32, error) {
|
||||
if index, exists := s.columnName2TsBlockColumnIndexMap[columnName]; !exists {
|
||||
return 0, fmt.Errorf("unknown column name: %v", columnName)
|
||||
} else {
|
||||
return index, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) getTsBlockColumnIndexForColumnIndex(columnIndex int32) (int32, error) {
|
||||
if columnIndex-1 >= int32(len(s.columnIndex2TsBlockColumnIndexList)) || int32(columnIndex-1) < 0 {
|
||||
return 0, fmt.Errorf("index %v out of range %v", columnIndex-1, len(s.columnIndex2TsBlockColumnIndexList))
|
||||
}
|
||||
return s.columnIndex2TsBlockColumnIndexList[columnIndex-1], nil
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) checkRecord() (err error) {
|
||||
if s.queryResultIndex > s.queryResultSize || s.tsBlockIndex >= s.tsBlockSize || s.queryResult == nil || s.curTsBlock == nil {
|
||||
err = fmt.Errorf("no record remains")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetValueColumnStartIndex() int32 {
|
||||
if s.ignoreTimestamp {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetColumnSize() int32 {
|
||||
return int32(len(s.columnNameList))
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetColumnTypeList() []string {
|
||||
return s.columnTypeList
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetColumnNameTypeList() []string {
|
||||
return s.columnTypeList
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) IsClosed() bool {
|
||||
return s.isClosed
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetFetchSize() int32 {
|
||||
return s.fetchSize
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) SetFetchSize(fetchSize int32) {
|
||||
s.fetchSize = fetchSize
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) HasCachedRecord() bool {
|
||||
return s.hasCachedRecord
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) SetHasCachedRecord(hasCachedRecord bool) {
|
||||
s.hasCachedRecord = hasCachedRecord
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) IsLastReadWasNull() bool {
|
||||
return s.lastReadWasNull
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) GetCurrentRowTime() int64 {
|
||||
return s.time
|
||||
}
|
||||
|
||||
func (s *IoTDBRpcDataSet) IsIgnoreTimestamp() bool {
|
||||
return s.ignoreTimestamp
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/apache/iotdb-client-go/v2/rpc"
|
||||
)
|
||||
|
||||
type SessionDataSet struct {
|
||||
ioTDBRpcDataSet *IoTDBRpcDataSet
|
||||
}
|
||||
|
||||
func NewSessionDataSet(sql string, columnNameList []string, columnTypeList []string, columnNameIndex map[string]int32, queryId int64, statementId int64, client *rpc.IClientRPCServiceClient, sessionId int64, queryResult [][]byte, ignoreTimestamp bool, timeout *int64, moreData bool, fetchSize int32, zoneId string, timeFactor int32, columnIndex2TsBlockColumnIndexList []int32) (*SessionDataSet, error) {
|
||||
rpcDataSet, err := NewIoTDBRpcDataSet(sql, columnNameList, columnTypeList, columnNameIndex, ignoreTimestamp, moreData, queryId, statementId, client, sessionId, queryResult, fetchSize, timeout, zoneId, DEFAULT_TIME_FORMAT, timeFactor, columnIndex2TsBlockColumnIndexList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SessionDataSet{ioTDBRpcDataSet: rpcDataSet}, nil
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) Next() (bool, error) {
|
||||
return s.ioTDBRpcDataSet.Next()
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) Close() error {
|
||||
return s.ioTDBRpcDataSet.Close()
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) IsNull(columnName string) (bool, error) {
|
||||
return s.ioTDBRpcDataSet.isNullByColumnName(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) IsNullByIndex(columnIndex int32) (bool, error) {
|
||||
return s.ioTDBRpcDataSet.isNullByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetBooleanByIndex(columnIndex int32) (bool, error) {
|
||||
return s.ioTDBRpcDataSet.getBooleanByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetBoolean(columnName string) (bool, error) {
|
||||
return s.ioTDBRpcDataSet.getBoolean(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetDoubleByIndex(columnIndex int32) (float64, error) {
|
||||
return s.ioTDBRpcDataSet.getDoubleByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetDouble(columnName string) (float64, error) {
|
||||
return s.ioTDBRpcDataSet.getDouble(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetFloatByIndex(columnIndex int32) (float32, error) {
|
||||
return s.ioTDBRpcDataSet.getFloatByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetFloat(columnName string) (float32, error) {
|
||||
return s.ioTDBRpcDataSet.getFloat(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetIntByIndex(columnIndex int32) (int32, error) {
|
||||
return s.ioTDBRpcDataSet.getIntByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetInt(columnName string) (int32, error) {
|
||||
return s.ioTDBRpcDataSet.getInt(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetLongByIndex(columnIndex int32) (int64, error) {
|
||||
return s.ioTDBRpcDataSet.getLongByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetLong(columnName string) (int64, error) {
|
||||
return s.ioTDBRpcDataSet.getLong(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetObjectByIndex(columnIndex int32) (interface{}, error) {
|
||||
return s.ioTDBRpcDataSet.getObjectByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetObject(columnName string) (interface{}, error) {
|
||||
return s.ioTDBRpcDataSet.getObject(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetStringByIndex(columnIndex int32) (string, error) {
|
||||
return s.ioTDBRpcDataSet.getStringByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetString(columnName string) (string, error) {
|
||||
return s.ioTDBRpcDataSet.getString(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetTimestampByIndex(columnIndex int32) (time.Time, error) {
|
||||
return s.ioTDBRpcDataSet.getTimestampByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetTimestamp(columnName string) (time.Time, error) {
|
||||
return s.ioTDBRpcDataSet.getTimestamp(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetDateByIndex(columnIndex int32) (time.Time, error) {
|
||||
return s.ioTDBRpcDataSet.GetDateByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetDate(columnName string) (time.Time, error) {
|
||||
return s.ioTDBRpcDataSet.GetDate(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetBlobByIndex(columnIndex int32) (*Binary, error) {
|
||||
return s.ioTDBRpcDataSet.getBinaryByIndex(columnIndex)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetBlob(columnName string) (*Binary, error) {
|
||||
return s.ioTDBRpcDataSet.getBinary(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) FindColumn(columnName string) int32 {
|
||||
return s.ioTDBRpcDataSet.findColumn(columnName)
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetColumnNames() []string {
|
||||
return s.ioTDBRpcDataSet.columnNameList
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetColumnTypes() []string {
|
||||
return s.ioTDBRpcDataSet.columnTypeList
|
||||
}
|
||||
|
||||
func (s *SessionDataSet) GetCurrentRowTime() time.Time {
|
||||
return convertToTimestamp(s.ioTDBRpcDataSet.GetCurrentRowTime(), s.ioTDBRpcDataSet.timeFactor)
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errTimeout = errors.New("get session timeout")
|
||||
var errPoolClosed = errors.New("sessionPool has closed")
|
||||
var defaultMultiple = 5
|
||||
|
||||
type SessionPool struct {
|
||||
config *PoolConfig
|
||||
maxSize int
|
||||
waitToGetSessionTimeoutInMs int
|
||||
enableCompression bool
|
||||
connectionTimeoutInMs int
|
||||
ch chan Session
|
||||
sem chan int8
|
||||
}
|
||||
|
||||
type PoolConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
NodeUrls []string
|
||||
UserName string
|
||||
Password string
|
||||
FetchSize int32
|
||||
TimeZone string
|
||||
ConnectRetryMax int
|
||||
Database string
|
||||
TLSConfig *TLSConfig
|
||||
sqlDialect string
|
||||
}
|
||||
|
||||
func NewSessionPool(conf *PoolConfig, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs int,
|
||||
enableCompression bool) SessionPool {
|
||||
return newSessionPoolWithSqlDialect(conf, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs, enableCompression, TreeSqlDialect)
|
||||
}
|
||||
|
||||
func newSessionPoolWithSqlDialect(conf *PoolConfig, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs int,
|
||||
enableCompression bool, sqlDialect string) SessionPool {
|
||||
conf.sqlDialect = sqlDialect
|
||||
if maxSize <= 0 {
|
||||
maxSize = runtime.NumCPU() * defaultMultiple
|
||||
}
|
||||
var ch = make(chan Session, maxSize)
|
||||
var sem = make(chan int8, maxSize)
|
||||
return SessionPool{
|
||||
config: conf,
|
||||
maxSize: maxSize,
|
||||
waitToGetSessionTimeoutInMs: waitToGetSessionTimeoutInMs,
|
||||
connectionTimeoutInMs: connectionTimeoutInMs,
|
||||
enableCompression: enableCompression,
|
||||
ch: ch,
|
||||
sem: sem,
|
||||
}
|
||||
}
|
||||
|
||||
func (spool *SessionPool) GetSession() (session Session, err error) {
|
||||
for {
|
||||
select {
|
||||
case spool.sem <- 1:
|
||||
select {
|
||||
case session, ok := <-spool.ch:
|
||||
if ok {
|
||||
return session, nil
|
||||
} else {
|
||||
log.Println("sessionPool has closed")
|
||||
return session, errPoolClosed
|
||||
}
|
||||
default:
|
||||
config := spool.config
|
||||
session, err = spool.ConstructSession(config)
|
||||
if err != nil {
|
||||
<-spool.sem
|
||||
}
|
||||
return session, err
|
||||
}
|
||||
case <-time.After(time.Millisecond * time.Duration(spool.waitToGetSessionTimeoutInMs)):
|
||||
log.Println("get session timeout")
|
||||
return session, errTimeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (spool *SessionPool) getTableSession() (ITableSession, error) {
|
||||
tableSession := PooledTableSession{}
|
||||
session, err := spool.GetSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tableSession.session = session
|
||||
tableSession.sessionPool = spool
|
||||
return &tableSession, nil
|
||||
}
|
||||
|
||||
func (spool *SessionPool) ConstructSession(config *PoolConfig) (session Session, err error) {
|
||||
if len(config.NodeUrls) > 0 {
|
||||
session, err = newClusterSessionWithSqlDialect(getClusterSessionConfig(config))
|
||||
if err != nil {
|
||||
return session, err
|
||||
}
|
||||
if err = session.OpenCluster(spool.enableCompression); err != nil {
|
||||
log.Print(err)
|
||||
return session, err
|
||||
}
|
||||
} else {
|
||||
session = newSessionWithSpecifiedSqlDialect(getSessionConfig(config))
|
||||
if err := session.Open(spool.enableCompression, spool.connectionTimeoutInMs); err != nil {
|
||||
log.Print(err)
|
||||
return session, err
|
||||
}
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func getSessionConfig(config *PoolConfig) *Config {
|
||||
return &Config{
|
||||
Host: config.Host,
|
||||
Port: config.Port,
|
||||
UserName: config.UserName,
|
||||
Password: config.Password,
|
||||
FetchSize: config.FetchSize,
|
||||
TimeZone: config.TimeZone,
|
||||
ConnectRetryMax: config.ConnectRetryMax,
|
||||
sqlDialect: config.sqlDialect,
|
||||
Database: config.Database,
|
||||
TLSConfig: config.TLSConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func getClusterSessionConfig(config *PoolConfig) *ClusterConfig {
|
||||
return &ClusterConfig{
|
||||
NodeUrls: config.NodeUrls,
|
||||
UserName: config.UserName,
|
||||
Password: config.Password,
|
||||
FetchSize: config.FetchSize,
|
||||
TimeZone: config.TimeZone,
|
||||
ConnectRetryMax: config.ConnectRetryMax,
|
||||
sqlDialect: config.sqlDialect,
|
||||
Database: config.Database,
|
||||
TLSConfig: config.TLSConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (spool *SessionPool) PutBack(session Session) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
session.Close()
|
||||
}
|
||||
}()
|
||||
if session.trans != nil && session.trans.IsOpen() {
|
||||
spool.ch <- session
|
||||
}
|
||||
<-spool.sem
|
||||
}
|
||||
|
||||
func (spool *SessionPool) dropSession(session Session) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
if session.trans != nil && session.trans.IsOpen() {
|
||||
session.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
err := session.Close()
|
||||
if err != nil {
|
||||
log.Println("Failed to close session ", session)
|
||||
}
|
||||
<-spool.sem
|
||||
}
|
||||
|
||||
func (spool *SessionPool) Close() {
|
||||
close(spool.ch)
|
||||
for s := range spool.ch {
|
||||
s.Close()
|
||||
}
|
||||
close(spool.sem)
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
// ITableSession defines an interface for interacting with IoTDB tables.
|
||||
// It supports operations such as data insertion, executing queries, and closing the session.
|
||||
// Implementations of this interface are expected to manage connections and ensure
|
||||
// proper resource cleanup.
|
||||
//
|
||||
// Each method may return an error to indicate issues such as connection errors
|
||||
// or execution failures.
|
||||
//
|
||||
// Since this interface includes a Close method, it is recommended to use
|
||||
// defer to ensure the session is properly closed.
|
||||
type ITableSession interface {
|
||||
|
||||
// Insert inserts a Tablet into the database.
|
||||
//
|
||||
// Parameters:
|
||||
// - tablet: A pointer to a Tablet containing time-series data to be inserted.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if an issue occurs during the operation, such as a connection error or execution failure.
|
||||
Insert(tablet *Tablet) error
|
||||
|
||||
// ExecuteNonQueryStatement executes a non-query SQL statement, such as a DDL or DML command.
|
||||
//
|
||||
// Parameters:
|
||||
// - sql: The SQL statement to execute.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if an issue occurs during the operation, such as a connection error or execution failure.
|
||||
ExecuteNonQueryStatement(sql string) error
|
||||
|
||||
// ExecuteQueryStatement executes a query SQL statement and returns the result set.
|
||||
//
|
||||
// Parameters:
|
||||
// - sql: The SQL query statement to execute.
|
||||
// - timeoutInMs: A pointer to the timeout duration in milliseconds for the query execution.
|
||||
//
|
||||
// Returns:
|
||||
// - result: A pointer to SessionDataSet containing the query results.
|
||||
// - err: An error if an issue occurs during the operation, such as a connection error or execution failure.
|
||||
ExecuteQueryStatement(sql string, timeoutInMs *int64) (*SessionDataSet, error)
|
||||
|
||||
// Close closes the session, releasing any held resources.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if there is an issue with closing the IoTDB connection.
|
||||
Close() (err error)
|
||||
}
|
||||
|
||||
// TableSession represents a session for interacting with IoTDB in relational mode.
|
||||
// It wraps a Session instance, providing methods for executing SQL statements
|
||||
// and managing the lifecycle of the session.
|
||||
type TableSession struct {
|
||||
session Session
|
||||
}
|
||||
|
||||
// NewTableSession creates a new TableSession instance using the provided configuration.
|
||||
//
|
||||
// Parameters:
|
||||
// - config: The configuration for the session.
|
||||
// - enableRPCCompression: A boolean indicating whether RPC compression is enabled.
|
||||
// - connectionTimeoutInMs: The timeout in milliseconds for establishing a connection.
|
||||
//
|
||||
// Returns:
|
||||
// - An ITableSession instance if the session is successfully created.
|
||||
// - An error if there is an issue during session initialization.
|
||||
func NewTableSession(config *Config, enableRPCCompression bool, connectionTimeoutInMs int) (ITableSession, error) {
|
||||
config.sqlDialect = TableSqlDialect
|
||||
session := newSessionWithSpecifiedSqlDialect(config)
|
||||
|
||||
if err := session.Open(enableRPCCompression, connectionTimeoutInMs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TableSession{session: session}, nil
|
||||
}
|
||||
|
||||
// NewClusterTableSession creates a new TableSession instance for a cluster setup.
|
||||
//
|
||||
// Parameters:
|
||||
// - clusterConfig: The configuration for the cluster session.
|
||||
// - enableRPCCompression: A boolean indicating whether RPC compression is enabled.
|
||||
//
|
||||
// Returns:
|
||||
// - An ITableSession instance if the session is successfully created.
|
||||
// - An error if there is an issue during session initialization.
|
||||
func NewClusterTableSession(clusterConfig *ClusterConfig, enableRPCCompression bool) (ITableSession, error) {
|
||||
clusterConfig.sqlDialect = TableSqlDialect
|
||||
session, err := newClusterSessionWithSqlDialect(clusterConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = session.OpenCluster(enableRPCCompression); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TableSession{session: session}, nil
|
||||
}
|
||||
|
||||
// Insert inserts a Tablet into the IoTDB.
|
||||
//
|
||||
// Parameters:
|
||||
// - tablet: A pointer to a Tablet containing the data to be inserted.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if the operation fails.
|
||||
func (s *TableSession) Insert(tablet *Tablet) error {
|
||||
return s.session.insertRelationalTablet(tablet)
|
||||
}
|
||||
|
||||
// ExecuteNonQueryStatement executes a non-query SQL statement, such as a DDL or DML command.
|
||||
//
|
||||
// Parameters:
|
||||
// - sql: The SQL statement to be executed.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if the operation fails.
|
||||
func (s *TableSession) ExecuteNonQueryStatement(sql string) error {
|
||||
return s.session.ExecuteNonQueryStatement(sql)
|
||||
}
|
||||
|
||||
// ExecuteQueryStatement executes a query SQL statement and retrieves the result set.
|
||||
//
|
||||
// Parameters:
|
||||
// - sql: The SQL query to be executed.
|
||||
// - timeoutInMs: (Optional) A pointer to the timeout duration in milliseconds for the query.
|
||||
//
|
||||
// Returns:
|
||||
// - result: A pointer to SessionDataSet containing the query results.
|
||||
// - err: An error if the operation fails.
|
||||
func (s *TableSession) ExecuteQueryStatement(sql string, timeoutInMs *int64) (*SessionDataSet, error) {
|
||||
return s.session.ExecuteQueryStatement(sql, timeoutInMs)
|
||||
}
|
||||
|
||||
// Close closes the TableSession, releasing any associated resources.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if the session fails to close properly.
|
||||
func (s *TableSession) Close() error {
|
||||
return s.session.Close()
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var ErrTableSessionClosed = errors.New("table session has been closed")
|
||||
|
||||
// TableSessionPool manages a pool of ITableSession instances, enabling efficient
|
||||
// reuse and management of resources. It provides methods to acquire a session
|
||||
// from the pool and to close the pool, releasing all held resources.
|
||||
//
|
||||
// This implementation ensures proper lifecycle management of sessions,
|
||||
// including efficient reuse and cleanup of resources.
|
||||
type TableSessionPool struct {
|
||||
sessionPool SessionPool
|
||||
}
|
||||
|
||||
// NewTableSessionPool creates a new TableSessionPool with the specified configuration.
|
||||
//
|
||||
// Parameters:
|
||||
// - conf: PoolConfig defining the configuration for the pool.
|
||||
// - maxSize: The maximum number of sessions the pool can hold.
|
||||
// - connectionTimeoutInMs: Timeout for establishing a connection in milliseconds.
|
||||
// - waitToGetSessionTimeoutInMs: Timeout for waiting to acquire a session in milliseconds.
|
||||
// - enableCompression: A boolean indicating whether to enable compression.
|
||||
//
|
||||
// Returns:
|
||||
// - A TableSessionPool instance.
|
||||
func NewTableSessionPool(conf *PoolConfig, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs int,
|
||||
enableCompression bool) TableSessionPool {
|
||||
return TableSessionPool{sessionPool: newSessionPoolWithSqlDialect(conf, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs, enableCompression, TableSqlDialect)}
|
||||
}
|
||||
|
||||
// GetSession acquires an ITableSession instance from the pool.
|
||||
//
|
||||
// Returns:
|
||||
// - A usable ITableSession instance for interacting with IoTDB.
|
||||
// - An error if a session cannot be acquired.
|
||||
func (spool *TableSessionPool) GetSession() (ITableSession, error) {
|
||||
return spool.sessionPool.getTableSession()
|
||||
}
|
||||
|
||||
// Close closes the TableSessionPool, releasing all held resources.
|
||||
// Once closed, no further sessions can be acquired from the pool.
|
||||
func (spool *TableSessionPool) Close() {
|
||||
spool.sessionPool.Close()
|
||||
}
|
||||
|
||||
// PooledTableSession represents a session managed within a TableSessionPool.
|
||||
// It ensures proper cleanup and reusability of the session.
|
||||
type PooledTableSession struct {
|
||||
session Session
|
||||
sessionPool *SessionPool
|
||||
closed int32
|
||||
}
|
||||
|
||||
// isConnectionError returns true if the error is a connection-level error
|
||||
// (i.e., not a server-side execution error indicated by TSStatus).
|
||||
func isConnectionError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var exeErr *ExecutionError
|
||||
if errors.As(err, &exeErr) {
|
||||
return false
|
||||
}
|
||||
var batchErr *BatchError
|
||||
if errors.As(err, &batchErr) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Insert inserts a Tablet into the database.
|
||||
//
|
||||
// Parameters:
|
||||
// - tablet: A pointer to a Tablet containing time-series data to be inserted.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if an issue occurs during the operation.
|
||||
func (s *PooledTableSession) Insert(tablet *Tablet) error {
|
||||
if atomic.LoadInt32(&s.closed) == 1 {
|
||||
return ErrTableSessionClosed
|
||||
}
|
||||
err := s.session.insertRelationalTablet(tablet)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if isConnectionError(err) {
|
||||
if atomic.CompareAndSwapInt32(&s.closed, 0, 1) {
|
||||
s.sessionPool.dropSession(s.session)
|
||||
s.session = Session{}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecuteNonQueryStatement executes a non-query SQL statement, such as a DDL or DML command.
|
||||
//
|
||||
// Parameters:
|
||||
// - sql: The SQL statement to execute.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if an issue occurs during the operation.
|
||||
func (s *PooledTableSession) ExecuteNonQueryStatement(sql string) error {
|
||||
if atomic.LoadInt32(&s.closed) == 1 {
|
||||
return ErrTableSessionClosed
|
||||
}
|
||||
err := s.session.ExecuteNonQueryStatement(sql)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if isConnectionError(err) {
|
||||
if atomic.CompareAndSwapInt32(&s.closed, 0, 1) {
|
||||
s.sessionPool.dropSession(s.session)
|
||||
s.session = Session{}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecuteQueryStatement executes a query SQL statement and returns the result set.
|
||||
//
|
||||
// Parameters:
|
||||
// - sql: The SQL query statement to execute.
|
||||
// - timeoutInMs: A pointer to the timeout duration in milliseconds for query execution.
|
||||
//
|
||||
// Returns:
|
||||
// - result: A pointer to SessionDataSet containing the query results.
|
||||
// - err: An error if an issue occurs during the operation.
|
||||
func (s *PooledTableSession) ExecuteQueryStatement(sql string, timeoutInMs *int64) (*SessionDataSet, error) {
|
||||
if atomic.LoadInt32(&s.closed) == 1 {
|
||||
return nil, ErrTableSessionClosed
|
||||
}
|
||||
sessionDataSet, err := s.session.ExecuteQueryStatement(sql, timeoutInMs)
|
||||
if err == nil {
|
||||
return sessionDataSet, nil
|
||||
}
|
||||
if isConnectionError(err) {
|
||||
if atomic.CompareAndSwapInt32(&s.closed, 0, 1) {
|
||||
s.sessionPool.dropSession(s.session)
|
||||
s.session = Session{}
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Close closes the PooledTableSession, releasing it back to the pool.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if there is an issue with session closure or cleanup.
|
||||
func (s *PooledTableSession) Close() error {
|
||||
if atomic.CompareAndSwapInt32(&s.closed, 0, 1) {
|
||||
if s.session.config.Database != s.sessionPool.config.Database && s.sessionPool.config.Database != "" {
|
||||
err := s.session.ExecuteNonQueryStatement("use " + s.sessionPool.config.Database)
|
||||
if err != nil {
|
||||
log.Println("Failed to change back database by executing: use ", s.sessionPool.config.Database)
|
||||
s.sessionPool.dropSession(s.session)
|
||||
s.session = Session{}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
s.sessionPool.PutBack(s.session)
|
||||
s.session = Session{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MeasurementSchema struct {
|
||||
Measurement string
|
||||
DataType TSDataType
|
||||
}
|
||||
|
||||
type ColumnCategory int8
|
||||
|
||||
const (
|
||||
TAG = iota
|
||||
FIELD
|
||||
ATTRIBUTE
|
||||
)
|
||||
|
||||
type Tablet struct {
|
||||
insertTargetName string
|
||||
measurementSchemas []*MeasurementSchema
|
||||
columnCategories []ColumnCategory
|
||||
timestamps []int64
|
||||
values []interface{}
|
||||
bitMaps []*BitMap
|
||||
maxRowNumber int
|
||||
RowSize int
|
||||
}
|
||||
|
||||
func (t *Tablet) Len() int {
|
||||
return t.RowSize
|
||||
}
|
||||
|
||||
func (t *Tablet) Swap(i, j int) {
|
||||
for index, schema := range t.measurementSchemas {
|
||||
switch schema.DataType {
|
||||
case BOOLEAN:
|
||||
sortedSlice := t.values[index].([]bool)
|
||||
sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i]
|
||||
case INT32, DATE:
|
||||
sortedSlice := t.values[index].([]int32)
|
||||
sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i]
|
||||
case INT64, TIMESTAMP:
|
||||
sortedSlice := t.values[index].([]int64)
|
||||
sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i]
|
||||
case FLOAT:
|
||||
sortedSlice := t.values[index].([]float32)
|
||||
sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i]
|
||||
case DOUBLE:
|
||||
sortedSlice := t.values[index].([]float64)
|
||||
sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i]
|
||||
case TEXT, BLOB, STRING:
|
||||
sortedSlice := t.values[index].([][]byte)
|
||||
sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i]
|
||||
}
|
||||
}
|
||||
if t.bitMaps != nil {
|
||||
for _, bitMap := range t.bitMaps {
|
||||
if bitMap != nil {
|
||||
isNilI := bitMap.IsMarked(i)
|
||||
isNilJ := bitMap.IsMarked(j)
|
||||
if isNilI {
|
||||
bitMap.Mark(j)
|
||||
} else {
|
||||
bitMap.UnMark(j)
|
||||
}
|
||||
if isNilJ {
|
||||
bitMap.Mark(i)
|
||||
} else {
|
||||
bitMap.UnMark(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
t.timestamps[i], t.timestamps[j] = t.timestamps[j], t.timestamps[i]
|
||||
}
|
||||
|
||||
func (t *Tablet) Less(i, j int) bool {
|
||||
return t.timestamps[i] < t.timestamps[j]
|
||||
}
|
||||
|
||||
func (t *Tablet) SetTimestamp(timestamp int64, rowIndex int) {
|
||||
t.timestamps[rowIndex] = timestamp
|
||||
}
|
||||
|
||||
// SetTimestampAt is a bounds-checked alternative to SetTimestamp. It validates
|
||||
// rowIndex and returns an "illegal argument rowIndex" error instead of
|
||||
// panicking with an index-out-of-range when rowIndex is negative or beyond the
|
||||
// tablet's capacity, consistent with SetValueAt and GetValueAt. The existing
|
||||
// SetTimestamp is preserved unchanged for source compatibility.
|
||||
func (t *Tablet) SetTimestampAt(timestamp int64, rowIndex int) error {
|
||||
if rowIndex < 0 || rowIndex >= t.maxRowNumber {
|
||||
return fmt.Errorf("illegal argument rowIndex %d", rowIndex)
|
||||
}
|
||||
t.timestamps[rowIndex] = timestamp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tablet) SetValueAt(value interface{}, columnIndex, rowIndex int) error {
|
||||
|
||||
if columnIndex < 0 || columnIndex >= len(t.measurementSchemas) {
|
||||
return fmt.Errorf("illegal argument columnIndex %d", columnIndex)
|
||||
}
|
||||
|
||||
if rowIndex < 0 || rowIndex >= t.maxRowNumber {
|
||||
return fmt.Errorf("illegal argument rowIndex %d", rowIndex)
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
// Init the bitMap to mark nil value
|
||||
if t.bitMaps == nil {
|
||||
t.bitMaps = make([]*BitMap, len(t.values))
|
||||
}
|
||||
if t.bitMaps[columnIndex] == nil {
|
||||
t.bitMaps[columnIndex] = NewBitMap(t.maxRowNumber)
|
||||
}
|
||||
// Mark the nil value position
|
||||
t.bitMaps[columnIndex].Mark(rowIndex)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch t.measurementSchemas[columnIndex].DataType {
|
||||
case BOOLEAN:
|
||||
values := t.values[columnIndex].([]bool)
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
values[rowIndex] = v
|
||||
case *bool:
|
||||
values[rowIndex] = *v
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
case INT32:
|
||||
values := t.values[columnIndex].([]int32)
|
||||
switch v := value.(type) {
|
||||
case int32:
|
||||
values[rowIndex] = v
|
||||
case *int32:
|
||||
values[rowIndex] = *v
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
case INT64, TIMESTAMP:
|
||||
values := t.values[columnIndex].([]int64)
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
values[rowIndex] = v
|
||||
case *int64:
|
||||
values[rowIndex] = *v
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
case FLOAT:
|
||||
values := t.values[columnIndex].([]float32)
|
||||
switch v := value.(type) {
|
||||
case float32:
|
||||
values[rowIndex] = v
|
||||
case *float32:
|
||||
values[rowIndex] = *v
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
case DOUBLE:
|
||||
values := t.values[columnIndex].([]float64)
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
values[rowIndex] = v
|
||||
case *float64:
|
||||
values[rowIndex] = *v
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
case TEXT, STRING:
|
||||
values := t.values[columnIndex].([][]byte)
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
values[rowIndex] = []byte(v)
|
||||
case []byte:
|
||||
values[rowIndex] = v
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
case BLOB:
|
||||
values := t.values[columnIndex].([][]byte)
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
values[rowIndex] = v
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
case DATE:
|
||||
values := t.values[columnIndex].([]int32)
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
val, err := DateToInt32(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values[rowIndex] = val
|
||||
default:
|
||||
return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tablet) GetMaxRowNumber() int {
|
||||
return t.maxRowNumber
|
||||
}
|
||||
|
||||
func (t *Tablet) GetValueAt(columnIndex, rowIndex int) (interface{}, error) {
|
||||
if columnIndex < 0 || columnIndex >= len(t.measurementSchemas) {
|
||||
return nil, fmt.Errorf("illegal argument columnIndex %d", columnIndex)
|
||||
}
|
||||
|
||||
if rowIndex < 0 || rowIndex >= t.maxRowNumber {
|
||||
return nil, fmt.Errorf("illegal argument rowIndex %d", rowIndex)
|
||||
}
|
||||
|
||||
schema := t.measurementSchemas[columnIndex]
|
||||
|
||||
if t.bitMaps != nil && t.bitMaps[columnIndex] != nil && t.bitMaps[columnIndex].IsMarked(rowIndex) {
|
||||
return nil, nil
|
||||
}
|
||||
switch schema.DataType {
|
||||
case BOOLEAN:
|
||||
return t.values[columnIndex].([]bool)[rowIndex], nil
|
||||
case INT32:
|
||||
return t.values[columnIndex].([]int32)[rowIndex], nil
|
||||
case INT64, TIMESTAMP:
|
||||
return t.values[columnIndex].([]int64)[rowIndex], nil
|
||||
case FLOAT:
|
||||
return t.values[columnIndex].([]float32)[rowIndex], nil
|
||||
case DOUBLE:
|
||||
return t.values[columnIndex].([]float64)[rowIndex], nil
|
||||
case TEXT, STRING:
|
||||
return string(t.values[columnIndex].([][]byte)[rowIndex]), nil
|
||||
case BLOB:
|
||||
return t.values[columnIndex].([][]byte)[rowIndex], nil
|
||||
case DATE:
|
||||
return Int32ToDate(t.values[columnIndex].([]int32)[rowIndex])
|
||||
default:
|
||||
return nil, fmt.Errorf("illegal datatype %v", schema.DataType)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tablet) GetTimestampBytes() []byte {
|
||||
buff := &bytes.Buffer{}
|
||||
binary.Write(buff, binary.BigEndian, t.timestamps[0:t.RowSize])
|
||||
return buff.Bytes()
|
||||
}
|
||||
|
||||
func (t *Tablet) GetMeasurements() []string {
|
||||
measurements := make([]string, len(t.measurementSchemas))
|
||||
for i, s := range t.measurementSchemas {
|
||||
measurements[i] = s.Measurement
|
||||
}
|
||||
return measurements
|
||||
}
|
||||
|
||||
func (t *Tablet) getDataTypes() []int32 {
|
||||
types := make([]int32, len(t.measurementSchemas))
|
||||
for i, s := range t.measurementSchemas {
|
||||
types[i] = int32(s.DataType)
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func (t *Tablet) getColumnCategories() []int8 {
|
||||
columnCategories := make([]int8, len(t.columnCategories))
|
||||
for i := range t.columnCategories {
|
||||
columnCategories[i] = int8(t.columnCategories[i])
|
||||
}
|
||||
return columnCategories
|
||||
}
|
||||
|
||||
func (t *Tablet) getValuesBytes() ([]byte, error) {
|
||||
buff := &bytes.Buffer{}
|
||||
for i, schema := range t.measurementSchemas {
|
||||
switch schema.DataType {
|
||||
case BOOLEAN:
|
||||
binary.Write(buff, binary.BigEndian, t.values[i].([]bool)[0:t.RowSize])
|
||||
case INT32, DATE:
|
||||
binary.Write(buff, binary.BigEndian, t.values[i].([]int32)[0:t.RowSize])
|
||||
case INT64, TIMESTAMP:
|
||||
binary.Write(buff, binary.BigEndian, t.values[i].([]int64)[0:t.RowSize])
|
||||
case FLOAT:
|
||||
binary.Write(buff, binary.BigEndian, t.values[i].([]float32)[0:t.RowSize])
|
||||
case DOUBLE:
|
||||
binary.Write(buff, binary.BigEndian, t.values[i].([]float64)[0:t.RowSize])
|
||||
case TEXT, STRING, BLOB:
|
||||
for _, s := range t.values[i].([][]byte)[0:t.RowSize] {
|
||||
binary.Write(buff, binary.BigEndian, int32(len(s)))
|
||||
binary.Write(buff, binary.BigEndian, s)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("illegal datatype %v", schema.DataType)
|
||||
}
|
||||
}
|
||||
if t.bitMaps != nil {
|
||||
for _, bitMap := range t.bitMaps {
|
||||
columnHasNil := bitMap != nil && !bitMap.IsAllUnmarked()
|
||||
binary.Write(buff, binary.BigEndian, columnHasNil)
|
||||
if columnHasNil {
|
||||
// Need to maintain consistency with the calculation method on the IoTDB side.
|
||||
binary.Write(buff, binary.BigEndian, bitMap.GetBits()[0:t.RowSize/8+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return buff.Bytes(), nil
|
||||
}
|
||||
|
||||
func (t *Tablet) Sort() error {
|
||||
sort.Sort(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tablet) Reset() {
|
||||
t.RowSize = 0
|
||||
t.bitMaps = nil
|
||||
}
|
||||
|
||||
func NewTablet(insertTargetName string, measurementSchemas []*MeasurementSchema, maxRowNumber int) (*Tablet, error) {
|
||||
tablet := &Tablet{
|
||||
insertTargetName: insertTargetName,
|
||||
measurementSchemas: measurementSchemas,
|
||||
maxRowNumber: maxRowNumber,
|
||||
}
|
||||
tablet.timestamps = make([]int64, maxRowNumber)
|
||||
tablet.values = make([]interface{}, len(measurementSchemas))
|
||||
for i, schema := range tablet.measurementSchemas {
|
||||
switch schema.DataType {
|
||||
case BOOLEAN:
|
||||
tablet.values[i] = make([]bool, maxRowNumber)
|
||||
case INT32, DATE:
|
||||
tablet.values[i] = make([]int32, maxRowNumber)
|
||||
case INT64, TIMESTAMP:
|
||||
tablet.values[i] = make([]int64, maxRowNumber)
|
||||
case FLOAT:
|
||||
tablet.values[i] = make([]float32, maxRowNumber)
|
||||
case DOUBLE:
|
||||
tablet.values[i] = make([]float64, maxRowNumber)
|
||||
case TEXT, STRING, BLOB:
|
||||
tablet.values[i] = make([][]byte, maxRowNumber)
|
||||
default:
|
||||
return nil, fmt.Errorf("illegal datatype %v", schema.DataType)
|
||||
}
|
||||
}
|
||||
return tablet, nil
|
||||
}
|
||||
|
||||
func NewRelationalTablet(tableName string, measurementSchemas []*MeasurementSchema, columnCategories []ColumnCategory, maxRowNumber int) (*Tablet, error) {
|
||||
tablet, err := NewTablet(tableName, measurementSchemas, maxRowNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tablet.columnCategories = columnCategories
|
||||
return tablet, nil
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/apache/thrift/lib/go/thrift"
|
||||
)
|
||||
|
||||
// TLSConfig enables TLS for an IoTDB client connection. Set CertFile and
|
||||
// KeyFile together to enable mTLS client authentication.
|
||||
type TLSConfig struct {
|
||||
// Config is an optional base tls.Config. It is cloned before use.
|
||||
Config *tls.Config
|
||||
|
||||
// CAFile is an optional PEM encoded CA certificate file used to verify the server.
|
||||
CAFile string
|
||||
|
||||
// CertFile and KeyFile are optional PEM encoded client certificate and key files for mTLS.
|
||||
CertFile string
|
||||
KeyFile string
|
||||
}
|
||||
|
||||
func newTransport(host string, port string, connectionTimeoutInMs int, tlsConfig *TLSConfig) (thrift.TTransport, error) {
|
||||
conf := &thrift.TConfiguration{
|
||||
ConnectTimeout: time.Duration(connectionTimeoutInMs) * time.Millisecond,
|
||||
MaxFrameSize: thrift.DEFAULT_MAX_FRAME_SIZE,
|
||||
}
|
||||
hostPort := net.JoinHostPort(host, port)
|
||||
|
||||
var base thrift.TTransport
|
||||
if tlsConfig == nil {
|
||||
base = thrift.NewTSocketConf(hostPort, conf)
|
||||
} else {
|
||||
cfg, err := buildTLSConfig(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf.TLSConfig = cfg
|
||||
base = thrift.NewTSSLSocketConf(hostPort, conf)
|
||||
}
|
||||
|
||||
return thrift.NewTFramedTransportConf(base, conf), nil
|
||||
}
|
||||
|
||||
func buildTLSConfig(config *TLSConfig) (*tls.Config, error) {
|
||||
if config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
if config.Config != nil {
|
||||
tlsConfig = config.Config.Clone()
|
||||
}
|
||||
if config.CAFile != "" {
|
||||
rootCAs, err := loadCertPool(tlsConfig.RootCAs, config.CAFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.RootCAs = rootCAs
|
||||
}
|
||||
if config.CertFile != "" || config.KeyFile != "" {
|
||||
if config.CertFile == "" || config.KeyFile == "" {
|
||||
return nil, fmt.Errorf("both TLS CertFile and KeyFile must be set")
|
||||
}
|
||||
certificate, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load TLS client certificate/key: %w", err)
|
||||
}
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, certificate)
|
||||
}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
func loadCertPool(base *x509.CertPool, caFile string) (*x509.CertPool, error) {
|
||||
rootCAs := base
|
||||
if rootCAs != nil {
|
||||
rootCAs = rootCAs.Clone()
|
||||
} else {
|
||||
systemPool, err := x509.SystemCertPool()
|
||||
if err == nil && systemPool != nil {
|
||||
rootCAs = systemPool
|
||||
} else {
|
||||
rootCAs = x509.NewCertPool()
|
||||
}
|
||||
}
|
||||
|
||||
caCert, err := os.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read TLS CA file %q: %w", caFile, err)
|
||||
}
|
||||
if !rootCAs.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("append TLS CA file %q: no certificates found", caFile)
|
||||
}
|
||||
return rootCAs, nil
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type TsBlock struct {
|
||||
timeColumn Column
|
||||
valueColumns []Column
|
||||
positionCount int32
|
||||
}
|
||||
|
||||
func NewTsBlock(positionCount int32, timeColumn Column, valueColumns ...Column) (*TsBlock, error) {
|
||||
if valueColumns == nil {
|
||||
return nil, fmt.Errorf("blocks is null")
|
||||
}
|
||||
return &TsBlock{
|
||||
timeColumn: timeColumn,
|
||||
valueColumns: valueColumns,
|
||||
positionCount: positionCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DeserializeTsBlock(data []byte) (*TsBlock, error) {
|
||||
// Serialized tsblock:
|
||||
// +-------------+---------------+---------+------------+-----------+----------+
|
||||
// | val col cnt | val col types | pos cnt | encodings | time col | val col |
|
||||
// +-------------+---------------+---------+------------+-----------+----------+
|
||||
// | int32 | list[byte] | int32 | list[byte] | bytes | bytes |
|
||||
// +-------------+---------------+---------+------------+-----------+----------+
|
||||
|
||||
reader := bytes.NewReader(data)
|
||||
// value column count
|
||||
var valueColumnCount int32
|
||||
if err := binary.Read(reader, binary.BigEndian, &valueColumnCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// value column data types
|
||||
valueColumnDataTypes := make([]TSDataType, valueColumnCount)
|
||||
for i := int32(0); i < valueColumnCount; i++ {
|
||||
dataType, err := deserializeDataType(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueColumnDataTypes[i] = dataType
|
||||
}
|
||||
|
||||
// position count
|
||||
var positionCount int32
|
||||
if err := binary.Read(reader, binary.BigEndian, &positionCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// column encodings
|
||||
columnEncodings := make([]ColumnEncoding, valueColumnCount+1)
|
||||
for i := int32(0); i < valueColumnCount+1; i++ {
|
||||
columnEncoding, err := deserializeColumnEncoding(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columnEncodings[i] = columnEncoding
|
||||
}
|
||||
|
||||
// time column
|
||||
timeColumnDecoder, err := getColumnDecoder(columnEncodings[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeColumn, err := timeColumnDecoder.ReadColumn(reader, INT64, positionCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// value columns
|
||||
valueColumns := make([]Column, valueColumnCount)
|
||||
for i := int32(0); i < valueColumnCount; i++ {
|
||||
valueColumnDecoder, err := getColumnDecoder(columnEncodings[i+1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueColumn, err := valueColumnDecoder.ReadColumn(reader, valueColumnDataTypes[i], positionCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueColumns[i] = valueColumn
|
||||
}
|
||||
return NewTsBlock(positionCount, timeColumn, valueColumns...)
|
||||
}
|
||||
|
||||
func deserializeDataType(reader *bytes.Reader) (TSDataType, error) {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return UNKNOWN, err
|
||||
}
|
||||
return getDataTypeByByte(b)
|
||||
}
|
||||
|
||||
func deserializeColumnEncoding(reader *bytes.Reader) (ColumnEncoding, error) {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return RLE_COLUMN_ENCODING, err
|
||||
}
|
||||
return getColumnEncodingByByte(b)
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetPositionCount() int32 {
|
||||
return t.positionCount
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetStartTime() (int64, error) {
|
||||
return t.timeColumn.GetLong(0)
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetEndTime() (int64, error) {
|
||||
return t.timeColumn.GetLong(t.positionCount - 1)
|
||||
}
|
||||
|
||||
func (t *TsBlock) IsEmpty() bool {
|
||||
return t.positionCount == 0
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetTimeByIndex(index int32) (int64, error) {
|
||||
return t.timeColumn.GetLong(index)
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetValueColumnCount() int32 {
|
||||
return int32(len(t.valueColumns))
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetTimeColumn() Column {
|
||||
return t.timeColumn
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetValueColumns() *[]Column {
|
||||
return &t.valueColumns
|
||||
}
|
||||
|
||||
func (t *TsBlock) GetColumn(columnIndex int32) Column {
|
||||
return t.valueColumns[columnIndex]
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/apache/iotdb-client-go/v2/common"
|
||||
"github.com/apache/iotdb-client-go/v2/rpc"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_TIME_FORMAT = "default"
|
||||
TIME_PRECISION = "timestamp_precision"
|
||||
MILLISECOND = "ms"
|
||||
MICROSECOND = "us"
|
||||
NANOSECOND = "ns"
|
||||
)
|
||||
|
||||
func getTimeFactor(openResp *rpc.TSOpenSessionResp) (int32, error) {
|
||||
if !openResp.IsSetConfiguration() {
|
||||
return 1_000, nil
|
||||
}
|
||||
precision, exists := openResp.GetConfiguration()[TIME_PRECISION]
|
||||
if !exists {
|
||||
return 1_000, nil
|
||||
}
|
||||
switch precision {
|
||||
case MILLISECOND:
|
||||
return 1_000, nil
|
||||
case MICROSECOND:
|
||||
return 1_000_000, nil
|
||||
case NANOSECOND:
|
||||
return 1_000_000_000, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown time precision: %v", precision)
|
||||
}
|
||||
}
|
||||
|
||||
func getTimePrecision(timeFactor int32) (string, error) {
|
||||
switch timeFactor {
|
||||
case 1_000:
|
||||
return MILLISECOND, nil
|
||||
case 1_000_000:
|
||||
return MICROSECOND, nil
|
||||
case 1_000_000_000:
|
||||
return NANOSECOND, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unknown time factor: %v", timeFactor)
|
||||
}
|
||||
}
|
||||
|
||||
func formatDatetime(timeFormat, timePrecision string, timestamp int64, zone *time.Location) string {
|
||||
switch timeFormat {
|
||||
case "long", "number":
|
||||
return strconv.FormatInt(timestamp, 10)
|
||||
case "default", "iso8601":
|
||||
return parseLongToDateWithPrecision(timestamp, zone, timePrecision)
|
||||
default:
|
||||
sec := timestamp / 1000
|
||||
nsec := (timestamp % 1000) * int64(time.Millisecond)
|
||||
t := time.Unix(sec, nsec).In(zone)
|
||||
return t.Format(timeFormat)
|
||||
}
|
||||
}
|
||||
|
||||
func getMilliSecond(timeValue int64, timeFactor int32) int64 {
|
||||
return (timeValue / int64(timeFactor)) * 1_000
|
||||
}
|
||||
|
||||
func getNanoSecond(timeValue int64, timeFactor int32) int {
|
||||
return int((timeValue % int64(timeFactor)) * (1_000_000_000 / int64(timeFactor)))
|
||||
}
|
||||
|
||||
func convertToTimestamp(timeValue int64, timeFactor int32) time.Time {
|
||||
millis := getMilliSecond(timeValue, timeFactor)
|
||||
nanos := getNanoSecond(timeValue, timeFactor)
|
||||
return time.Unix(millis/1e3, (millis%1e3)*1e6).Add(time.Duration(nanos) * time.Nanosecond)
|
||||
}
|
||||
|
||||
func parseLongToDateWithPrecision(timestamp int64, zone *time.Location, precision string) string {
|
||||
var divisor int64
|
||||
var digits int
|
||||
|
||||
switch precision {
|
||||
case MILLISECOND:
|
||||
divisor = 1000
|
||||
digits = 3
|
||||
case MICROSECOND:
|
||||
divisor = 1_000_000
|
||||
digits = 6
|
||||
case NANOSECOND:
|
||||
divisor = 1_000_000_000
|
||||
digits = 9
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
quotient := timestamp / divisor
|
||||
remainder := timestamp % divisor
|
||||
|
||||
if timestamp < 0 && remainder != 0 {
|
||||
quotient--
|
||||
remainder += divisor
|
||||
}
|
||||
|
||||
t := time.Unix(quotient, 0).In(zone)
|
||||
year, month, day := t.Date()
|
||||
hour, min, sec := t.Clock()
|
||||
|
||||
_, offset := t.Zone()
|
||||
offsetSeconds := offset
|
||||
sign := "+"
|
||||
if offsetSeconds < 0 {
|
||||
sign = "-"
|
||||
offsetSeconds = -offsetSeconds
|
||||
}
|
||||
hours := offsetSeconds / 3600
|
||||
minutes := (offsetSeconds % 3600) / 60
|
||||
|
||||
zoneOffset := fmt.Sprintf("%s%02d:%02d", sign, hours, minutes)
|
||||
if offset == 0 {
|
||||
zoneOffset = "Z"
|
||||
}
|
||||
|
||||
formatStr := fmt.Sprintf("%%0%dd", digits)
|
||||
fraction := fmt.Sprintf(formatStr, remainder)
|
||||
|
||||
isoStr := fmt.Sprintf("%04d-%02d-%02dT%02d:%02d:%02d.%s%s",
|
||||
year, month, day, hour, min, sec, fraction, zoneOffset)
|
||||
|
||||
return isoStr
|
||||
}
|
||||
|
||||
func int32ToString(n int32) string {
|
||||
return strconv.Itoa(int(n))
|
||||
}
|
||||
|
||||
func int64ToString(n int64) string {
|
||||
return strconv.FormatInt(n, 10)
|
||||
}
|
||||
|
||||
func float32ToString(val float32) string {
|
||||
return strconv.FormatFloat(float64(val), 'f', -1, 32)
|
||||
}
|
||||
|
||||
func float64ToString(val float64) string {
|
||||
return strconv.FormatFloat(val, 'f', -1, 64)
|
||||
}
|
||||
|
||||
func int32ToBytes(n int32) []byte {
|
||||
bytesBuffer := bytes.NewBuffer([]byte{})
|
||||
binary.Write(bytesBuffer, binary.BigEndian, n)
|
||||
return bytesBuffer.Bytes()
|
||||
}
|
||||
|
||||
func int64ToBytes(n int64) []byte {
|
||||
bytesBuffer := bytes.NewBuffer([]byte{})
|
||||
binary.Write(bytesBuffer, binary.BigEndian, n)
|
||||
return bytesBuffer.Bytes()
|
||||
}
|
||||
|
||||
func bytesToInt32(bys []byte) int32 {
|
||||
bytesBuffer := bytes.NewBuffer(bys)
|
||||
var data int32
|
||||
binary.Read(bytesBuffer, binary.BigEndian, &data)
|
||||
return data
|
||||
}
|
||||
|
||||
func bytesToInt64(bys []byte) int64 {
|
||||
bytesBuffer := bytes.NewBuffer(bys)
|
||||
var data int64
|
||||
binary.Read(bytesBuffer, binary.BigEndian, &data)
|
||||
return data
|
||||
}
|
||||
|
||||
func bytesToHexString(input []byte) string {
|
||||
hexString := "0x"
|
||||
if input != nil {
|
||||
for _, b := range input {
|
||||
hexString += fmt.Sprintf("%02x", b)
|
||||
}
|
||||
}
|
||||
return hexString
|
||||
}
|
||||
|
||||
func DateToInt32(localDate time.Time) (int32, error) {
|
||||
if localDate.IsZero() {
|
||||
return 0, errors.New("date expression is null or empty")
|
||||
}
|
||||
|
||||
year := localDate.Year()
|
||||
if year < 1000 || year > 9999 {
|
||||
return 0, errors.New("year must be between 1000 and 9999")
|
||||
}
|
||||
|
||||
// Convert to YYYY/MM/DD format
|
||||
result := year*10000 + int(localDate.Month())*100 + localDate.Day()
|
||||
return int32(result), nil
|
||||
}
|
||||
|
||||
func Int32ToDate(val int32) (time.Time, error) {
|
||||
date := int(val)
|
||||
year := date / 10000
|
||||
month := (date / 100) % 100
|
||||
day := date % 100
|
||||
|
||||
localDate := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
if localDate.Year() != year || int(localDate.Month()) != month || localDate.Day() != day {
|
||||
return time.Time{}, errors.New("invalid date format")
|
||||
}
|
||||
|
||||
return localDate, nil
|
||||
}
|
||||
|
||||
func bytesToDate(bys []byte) (time.Time, error) {
|
||||
return Int32ToDate(bytesToInt32(bys))
|
||||
}
|
||||
|
||||
func verifySuccesses(statuses []*common.TSStatus) error {
|
||||
buff := bytes.Buffer{}
|
||||
for _, status := range statuses {
|
||||
if status.Code != SuccessStatus && status.Code != RedirectionRecommend {
|
||||
buff.WriteString(*status.Message + ";")
|
||||
}
|
||||
}
|
||||
errMsg := buff.String()
|
||||
if len(errMsg) > 0 {
|
||||
return &BatchError{statuses, errMsg}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifySuccess(status *common.TSStatus) error {
|
||||
if status.Code == RedirectionRecommend {
|
||||
return nil
|
||||
}
|
||||
|
||||
if status.Code == MultipleError {
|
||||
if err := verifySuccesses(status.GetSubStatus()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if status.Code != SuccessStatus {
|
||||
msg := ""
|
||||
if status.Message != nil {
|
||||
msg = *status.Message
|
||||
}
|
||||
return &ExecutionError{Code: status.Code, Message: msg}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Binary struct {
|
||||
values []byte
|
||||
}
|
||||
|
||||
func NewBinary(v []byte) *Binary {
|
||||
return &Binary{v}
|
||||
}
|
||||
|
||||
func (b *Binary) GetStringValue() string {
|
||||
return string(b.values)
|
||||
}
|
||||
|
||||
func (b *Binary) GetValues() []byte {
|
||||
return b.values
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Code generated by Thrift Compiler (0.23.0). DO NOT EDIT.
|
||||
|
||||
package common
|
||||
|
||||
var GoUnusedProtection__ int;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Code generated by Thrift Compiler (0.23.0). DO NOT EDIT.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"iter"
|
||||
"log/slog"
|
||||
"time"
|
||||
thrift "github.com/apache/thrift/lib/go/thrift"
|
||||
"strings"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// (needed to ensure safety because of naive import list construction.)
|
||||
var _ = bytes.Equal
|
||||
var _ = context.Background
|
||||
var _ = errors.New
|
||||
var _ = fmt.Printf
|
||||
var _ = iter.Pull[int]
|
||||
var _ = slog.Log
|
||||
var _ = time.Now
|
||||
var _ = thrift.ZERO
|
||||
// (needed by validator.)
|
||||
var _ = strings.Contains
|
||||
var _ = regexp.MatchString
|
||||
|
||||
|
||||
func init() {
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
module github.com/apache/iotdb-client-go/v2
|
||||
|
||||
go 1.25
|
||||
|
||||
require github.com/apache/thrift v0.24.0
|
||||
@@ -0,0 +1,2 @@
|
||||
github.com/apache/thrift v0.24.0 h1:zy31L1a49QTNB2bG1BBfMXol3yJrTH975G3pPubQVLQ=
|
||||
github.com/apache/thrift v0.24.0/go.mod h1:zPt6WxgvTOM6hF92y8C+MkEM5LMxZuk4JcQOiU4Esvs=
|
||||
@@ -0,0 +1,6 @@
|
||||
// Code generated by Thrift Compiler (0.23.0). DO NOT EDIT.
|
||||
|
||||
package rpc
|
||||
|
||||
var GoUnusedProtection__ int;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Code generated by Thrift Compiler (0.23.0). DO NOT EDIT.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"iter"
|
||||
"log/slog"
|
||||
"time"
|
||||
thrift "github.com/apache/thrift/lib/go/thrift"
|
||||
"strings"
|
||||
"regexp"
|
||||
"github.com/apache/iotdb-client-go/v2/common"
|
||||
|
||||
)
|
||||
|
||||
// (needed to ensure safety because of naive import list construction.)
|
||||
var _ = bytes.Equal
|
||||
var _ = context.Background
|
||||
var _ = errors.New
|
||||
var _ = fmt.Printf
|
||||
var _ = iter.Pull[int]
|
||||
var _ = slog.Log
|
||||
var _ = time.Now
|
||||
var _ = thrift.ZERO
|
||||
// (needed by validator.)
|
||||
var _ = strings.Contains
|
||||
var _ = regexp.MatchString
|
||||
|
||||
var _ = common.GoUnusedProtection__
|
||||
|
||||
func init() {
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user