fix: 将 libsql-client-go 作为普通目录提交,而非 gitlink
This commit is contained in:
22
go_modules/libsql-client-go/libsql/internal/hrana/batch.go
Normal file
22
go_modules/libsql-client-go/libsql/internal/hrana/batch.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package hrana
|
||||
|
||||
type Batch struct {
|
||||
Steps []BatchStep `json:"steps"`
|
||||
ReplicationIndex *uint64 `json:"replication_index,omitempty"`
|
||||
}
|
||||
|
||||
type BatchStep struct {
|
||||
Stmt Stmt `json:"stmt"`
|
||||
Condition *BatchCondition `json:"condition,omitempty"`
|
||||
}
|
||||
|
||||
type BatchCondition struct {
|
||||
Type string `json:"type"`
|
||||
Step *int32 `json:"step,omitempty"`
|
||||
Cond *BatchCondition `json:"cond,omitempty"`
|
||||
Conds []BatchCondition `json:"conds,omitempty"`
|
||||
}
|
||||
|
||||
func (b *Batch) Add(stmt Stmt, condition *BatchCondition) {
|
||||
b.Steps = append(b.Steps, BatchStep{Stmt: stmt, Condition: condition})
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type BatchResult struct {
|
||||
StepResults []*StmtResult `json:"step_results"`
|
||||
StepErrors []*Error `json:"step_errors"`
|
||||
ReplicationIndex *uint64 `json:"replication_index"`
|
||||
}
|
||||
|
||||
func (b *BatchResult) UnmarshalJSON(data []byte) error {
|
||||
type Alias BatchResult
|
||||
aux := &struct {
|
||||
ReplicationIndex interface{} `json:"replication_index,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(b),
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if aux.ReplicationIndex == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := aux.ReplicationIndex.(type) {
|
||||
case float64:
|
||||
repIndex := uint64(v)
|
||||
b.ReplicationIndex = &repIndex
|
||||
case string:
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
repIndex, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.ReplicationIndex = &repIndex
|
||||
default:
|
||||
return fmt.Errorf("invalid type for replication index: %T", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBatchResult_UnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
jsonData []byte
|
||||
expected *uint64
|
||||
}{
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":1}`),
|
||||
expected: uint64Ptr(1),
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":"1"}`),
|
||||
expected: uint64Ptr(1),
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":""}`),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{}`),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":"0"}`),
|
||||
expected: uint64Ptr(0),
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":0}`),
|
||||
expected: uint64Ptr(0),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
batchResult := &BatchResult{}
|
||||
err := json.Unmarshal(tc.jsonData, batchResult)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(batchResult.ReplicationIndex, tc.expected) {
|
||||
t.Errorf("ReplicationIndex field is not correctly unmarshaled got = %v, want = %v", batchResult.ReplicationIndex, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func uint64Ptr(n uint64) *uint64 {
|
||||
return &n
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package hrana
|
||||
|
||||
type PipelineRequest struct {
|
||||
Baton string `json:"baton,omitempty"`
|
||||
Requests []StreamRequest `json:"requests"`
|
||||
}
|
||||
|
||||
func (pr *PipelineRequest) Add(request StreamRequest) {
|
||||
pr.Requests = append(pr.Requests, request)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package hrana
|
||||
|
||||
type PipelineResponse struct {
|
||||
Baton string `json:"baton,omitempty"`
|
||||
BaseUrl string `json:"base_url,omitempty"`
|
||||
Results []StreamResult `json:"results"`
|
||||
}
|
||||
58
go_modules/libsql-client-go/libsql/internal/hrana/stmt.go
Normal file
58
go_modules/libsql-client-go/libsql/internal/hrana/stmt.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared"
|
||||
)
|
||||
|
||||
type Stmt struct {
|
||||
Sql *string `json:"sql,omitempty"`
|
||||
SqlId *int32 `json:"sql_id,omitempty"`
|
||||
Args []Value `json:"args,omitempty"`
|
||||
NamedArgs []NamedArg `json:"named_args,omitempty"`
|
||||
WantRows bool `json:"want_rows"`
|
||||
ReplicationIndex *uint64 `json:"replication_index,omitempty"`
|
||||
}
|
||||
|
||||
type NamedArg struct {
|
||||
Name string `json:"name"`
|
||||
Value Value `json:"value"`
|
||||
}
|
||||
|
||||
func (s *Stmt) AddArgs(params shared.Params) error {
|
||||
if len(params.Named()) > 0 {
|
||||
return s.AddNamedArgs(params.Named())
|
||||
} else {
|
||||
return s.AddPositionalArgs(params.Positional())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stmt) AddPositionalArgs(args []any) error {
|
||||
argValues := make([]Value, len(args))
|
||||
for idx := range args {
|
||||
var err error
|
||||
if argValues[idx], err = ToValue(args[idx]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.Args = argValues
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stmt) AddNamedArgs(args map[string]any) error {
|
||||
argValues := make([]NamedArg, len(args))
|
||||
idx := 0
|
||||
for key, value := range args {
|
||||
var err error
|
||||
var v Value
|
||||
if v, err = ToValue(value); err != nil {
|
||||
return err
|
||||
}
|
||||
argValues[idx] = NamedArg{
|
||||
Name: key,
|
||||
Value: v,
|
||||
}
|
||||
idx++
|
||||
}
|
||||
s.NamedArgs = argValues
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Column struct {
|
||||
Name *string `json:"name"`
|
||||
Type *string `json:"decltype"`
|
||||
}
|
||||
|
||||
type StmtResult struct {
|
||||
Cols []Column `json:"cols"`
|
||||
Rows [][]Value `json:"rows"`
|
||||
AffectedRowCount int32 `json:"affected_row_count"`
|
||||
LastInsertRowId *string `json:"last_insert_rowid"`
|
||||
ReplicationIndex *uint64 `json:"replication_index"`
|
||||
}
|
||||
|
||||
func (r *StmtResult) GetLastInsertRowId() int64 {
|
||||
if r.LastInsertRowId != nil {
|
||||
if integer, err := strconv.ParseInt(*r.LastInsertRowId, 10, 64); err == nil {
|
||||
return integer
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *StmtResult) UnmarshalJSON(data []byte) error {
|
||||
type Alias StmtResult
|
||||
aux := &struct {
|
||||
ReplicationIndex interface{} `json:"replication_index,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(r),
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if aux.ReplicationIndex == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := aux.ReplicationIndex.(type) {
|
||||
case float64:
|
||||
repIndex := uint64(v)
|
||||
r.ReplicationIndex = &repIndex
|
||||
case string:
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
repIndex, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.ReplicationIndex = &repIndex
|
||||
default:
|
||||
return fmt.Errorf("invalid type for replication index: %T", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStmtResult_UnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
jsonData []byte
|
||||
expected *uint64
|
||||
}{
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":1}`),
|
||||
expected: uint64Ptr(1),
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":"1"}`),
|
||||
expected: uint64Ptr(1),
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":""}`),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{}`),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":"0"}`),
|
||||
expected: uint64Ptr(0),
|
||||
},
|
||||
{
|
||||
jsonData: []byte(`{"replication_index":0}`),
|
||||
expected: uint64Ptr(0),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
stmtResult := &StmtResult{}
|
||||
err := json.Unmarshal(tc.jsonData, stmtResult)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(stmtResult.ReplicationIndex, tc.expected) {
|
||||
t.Errorf("ReplicationIndex field is not correctly unmarshaled got = %v, want = %v", stmtResult.ReplicationIndex, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStmtWithPositionalArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []any
|
||||
want []Value
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "int args",
|
||||
args: []any{1, 2},
|
||||
want: []Value{{Type: "integer", Value: "1"}, {Type: "integer", Value: "2"}},
|
||||
},
|
||||
{
|
||||
name: "string args",
|
||||
args: []any{"a", "b"},
|
||||
want: []Value{{Type: "text", Value: "a"}, {Type: "text", Value: "b"}},
|
||||
},
|
||||
{
|
||||
name: "invalid arg",
|
||||
args: []any{make(chan int)},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stmt := Stmt{}
|
||||
err := stmt.AddPositionalArgs(tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("StmtWithPositionalArgs() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(stmt.Args, tt.want) {
|
||||
t.Errorf("got = %v, want %v", stmt.Args, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStmtWithNamedArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args map[string]any
|
||||
want []NamedArg
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "int args",
|
||||
args: map[string]any{"arg1": 1, "arg2": int64(2)},
|
||||
want: []NamedArg{
|
||||
{Name: "arg1", Value: Value{Type: "integer", Value: "1"}},
|
||||
{Name: "arg2", Value: Value{Type: "integer", Value: "2"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "string args",
|
||||
args: map[string]any{"arg1": "a", "arg2": "b"},
|
||||
want: []NamedArg{
|
||||
{Name: "arg1", Value: Value{Type: "text", Value: "a"}},
|
||||
{Name: "arg2", Value: Value{Type: "text", Value: "b"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid arg",
|
||||
args: map[string]any{"arg1": make(chan int)},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stmt := Stmt{}
|
||||
err := stmt.AddNamedArgs(tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("StmtWithNamedArgs() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
got := make(map[NamedArg]struct{})
|
||||
want := make(map[NamedArg]struct{})
|
||||
for _, arg := range stmt.NamedArgs {
|
||||
got[arg] = struct{}{}
|
||||
}
|
||||
for _, arg := range tt.want {
|
||||
want[arg] = struct{}{}
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got = %v, want %v", stmt.NamedArgs, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared"
|
||||
)
|
||||
|
||||
type StreamRequest struct {
|
||||
Type string `json:"type"`
|
||||
Stmt *Stmt `json:"stmt,omitempty"`
|
||||
Batch *Batch `json:"batch,omitempty"`
|
||||
Sql *string `json:"sql,omitempty"`
|
||||
SqlId *int32 `json:"sql_id,omitempty"`
|
||||
}
|
||||
|
||||
func CloseStream() StreamRequest {
|
||||
return StreamRequest{Type: "close"}
|
||||
}
|
||||
|
||||
func ExecuteStream(sql string, params *shared.Params, wantRows bool) (*StreamRequest, error) {
|
||||
stmt := &Stmt{
|
||||
Sql: &sql,
|
||||
WantRows: wantRows,
|
||||
}
|
||||
if params != nil {
|
||||
if err := stmt.AddArgs(*params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &StreamRequest{Type: "execute", Stmt: stmt}, nil
|
||||
}
|
||||
|
||||
func ExecuteStoredStream(sqlId int32, params shared.Params, wantRows bool) (*StreamRequest, error) {
|
||||
stmt := &Stmt{
|
||||
SqlId: &sqlId,
|
||||
WantRows: wantRows,
|
||||
}
|
||||
if err := stmt.AddArgs(params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &StreamRequest{Type: "execute", Stmt: stmt}, nil
|
||||
}
|
||||
|
||||
func BatchStream(sqls []string, params []shared.Params, wantRows bool, transactional bool) (*StreamRequest, error) {
|
||||
size := len(sqls)
|
||||
if transactional {
|
||||
size += 1
|
||||
}
|
||||
batch := &Batch{Steps: make([]BatchStep, 0, size)}
|
||||
addArgs := len(params) > 0
|
||||
for idx, sql := range sqls {
|
||||
s := sql
|
||||
stmt := &Stmt{
|
||||
Sql: &s,
|
||||
WantRows: wantRows,
|
||||
}
|
||||
if addArgs {
|
||||
if err := stmt.AddArgs(params[idx]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var condition *BatchCondition
|
||||
if transactional {
|
||||
if idx > 0 {
|
||||
prev_idx := int32(idx - 1)
|
||||
condition = &BatchCondition{
|
||||
Type: "ok",
|
||||
Step: &prev_idx,
|
||||
}
|
||||
}
|
||||
}
|
||||
batch.Add(*stmt, condition)
|
||||
}
|
||||
if transactional {
|
||||
rollback := "ROLLBACK"
|
||||
last_idx := int32(len(sqls) - 1)
|
||||
batch.Add(Stmt{Sql: &rollback, WantRows: false},
|
||||
&BatchCondition{Type: "not", Cond: &BatchCondition{Type: "ok", Step: &last_idx}})
|
||||
}
|
||||
return &StreamRequest{Type: "batch", Batch: batch}, nil
|
||||
}
|
||||
|
||||
func StoreSqlStream(sql string, sqlId int32) StreamRequest {
|
||||
return StreamRequest{Type: "store_sql", Sql: &sql, SqlId: &sqlId}
|
||||
}
|
||||
|
||||
func CloseStoredSqlStream(sqlId int32) StreamRequest {
|
||||
return StreamRequest{Type: "close_sql", SqlId: &sqlId}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type StreamResult struct {
|
||||
Type string `json:"type"`
|
||||
Response *StreamResponse `json:"response,omitempty"`
|
||||
Error *Error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type StreamResponse struct {
|
||||
Type string `json:"type"`
|
||||
Result json.RawMessage `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
func (r *StreamResponse) ExecuteResult() (*StmtResult, error) {
|
||||
if r.Type != "execute" {
|
||||
return nil, fmt.Errorf("invalid response type: %s", r.Type)
|
||||
}
|
||||
|
||||
var res StmtResult
|
||||
if err := json.Unmarshal(r.Result, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (r *StreamResponse) BatchResult() (*BatchResult, error) {
|
||||
if r.Type != "batch" {
|
||||
return nil, fmt.Errorf("invalid response type: %s", r.Type)
|
||||
}
|
||||
|
||||
var res BatchResult
|
||||
if err := json.Unmarshal(r.Result, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, e := range res.StepErrors {
|
||||
if e != nil {
|
||||
return nil, errors.New(e.Message)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Message string `json:"message"`
|
||||
Code *string `json:"code,omitempty"`
|
||||
}
|
||||
89
go_modules/libsql-client-go/libsql/internal/hrana/value.go
Normal file
89
go_modules/libsql-client-go/libsql/internal/hrana/value.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Value struct {
|
||||
Type string `json:"type"`
|
||||
Value any `json:"value,omitempty"`
|
||||
Base64 *string `json:"base64,omitempty"`
|
||||
}
|
||||
|
||||
func (v Value) ToValue(columnType *string) any {
|
||||
if v.Type == "blob" {
|
||||
if v.Base64 == nil {
|
||||
return nil
|
||||
}
|
||||
bytes, err := base64.StdEncoding.WithPadding(base64.NoPadding).DecodeString(*v.Base64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bytes
|
||||
} else if v.Type == "integer" {
|
||||
integer, err := strconv.ParseInt(v.Value.(string), 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return integer
|
||||
} else if columnType != nil {
|
||||
if (strings.ToLower(*columnType) == "timestamp" || strings.ToLower(*columnType) == "datetime") && v.Type == "text" {
|
||||
for _, format := range []string{
|
||||
"2006-01-02 15:04:05.999999999-07:00",
|
||||
"2006-01-02T15:04:05.999999999-07:00",
|
||||
"2006-01-02 15:04:05.999999999",
|
||||
"2006-01-02T15:04:05.999999999",
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02T15:04:05",
|
||||
"2006-01-02 15:04",
|
||||
"2006-01-02T15:04",
|
||||
"2006-01-02",
|
||||
} {
|
||||
if t, err := time.ParseInLocation(format, v.Value.(string), time.UTC); err == nil {
|
||||
return t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v.Value
|
||||
}
|
||||
|
||||
func ToValue(v any) (Value, error) {
|
||||
var res Value
|
||||
if v == nil {
|
||||
res.Type = "null"
|
||||
} else if integer, ok := v.(int64); ok {
|
||||
res.Type = "integer"
|
||||
res.Value = strconv.FormatInt(integer, 10)
|
||||
} else if integer, ok := v.(int); ok {
|
||||
res.Type = "integer"
|
||||
res.Value = strconv.FormatInt(int64(integer), 10)
|
||||
} else if text, ok := v.(string); ok {
|
||||
res.Type = "text"
|
||||
res.Value = text
|
||||
} else if blob, ok := v.([]byte); ok {
|
||||
res.Type = "blob"
|
||||
b64 := base64.StdEncoding.WithPadding(base64.NoPadding).EncodeToString(blob)
|
||||
res.Base64 = &b64
|
||||
} else if float, ok := v.(float64); ok {
|
||||
res.Type = "float"
|
||||
res.Value = float
|
||||
} else if t, ok := v.(time.Time); ok {
|
||||
res.Type = "text"
|
||||
res.Value = t.Format("2006-01-02 15:04:05.999999999-07:00")
|
||||
} else if t, ok := v.(bool); ok {
|
||||
res.Type = "integer"
|
||||
res.Value = "0"
|
||||
if t {
|
||||
res.Value = "1"
|
||||
}
|
||||
} else {
|
||||
return res, fmt.Errorf("unsupported value type: %s", v)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
304
go_modules/libsql-client-go/libsql/internal/hrana/value_test.go
Normal file
304
go_modules/libsql-client-go/libsql/internal/hrana/value_test.go
Normal file
@@ -0,0 +1,304 @@
|
||||
package hrana
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
func TestValueToValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
columnType string
|
||||
value Value
|
||||
want any
|
||||
}{
|
||||
{
|
||||
name: "null",
|
||||
value: Value{
|
||||
Type: "null",
|
||||
Value: nil,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
value: Value{
|
||||
Type: "integer",
|
||||
Value: strconv.FormatInt(int64(42), 10),
|
||||
},
|
||||
want: int64(42),
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
value: Value{
|
||||
Type: "text",
|
||||
Value: "foo",
|
||||
},
|
||||
want: "foo",
|
||||
},
|
||||
{
|
||||
name: "bytes",
|
||||
value: Value{
|
||||
Type: "blob",
|
||||
Base64: toPtr("YmFy"),
|
||||
},
|
||||
want: []byte("bar"),
|
||||
},
|
||||
{
|
||||
name: "bytes",
|
||||
value: Value{
|
||||
Type: "blob",
|
||||
Base64: toPtr(""),
|
||||
},
|
||||
want: []byte{},
|
||||
},
|
||||
{
|
||||
name: "float",
|
||||
value: Value{
|
||||
Type: "float",
|
||||
Value: 3.14,
|
||||
},
|
||||
want: 3.14,
|
||||
},
|
||||
{
|
||||
name: "timestamp",
|
||||
columnType: "TIMESTAMP",
|
||||
value: Value{
|
||||
Type: "text",
|
||||
Value: "0001-01-01 01:00:00+00:00",
|
||||
},
|
||||
want: time.Time{}.Add(time.Hour),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var columnType *string = nil
|
||||
if tt.columnType != "" {
|
||||
columnType = &tt.columnType
|
||||
}
|
||||
got := tt.value.ToValue(columnType)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ToValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value any
|
||||
want Value
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "null",
|
||||
value: nil,
|
||||
want: Value{
|
||||
Type: "null",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
value: 42,
|
||||
want: Value{
|
||||
Type: "integer",
|
||||
Value: strconv.FormatInt(int64(42), 10),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
value: "foo",
|
||||
want: Value{
|
||||
Type: "text",
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bytes",
|
||||
value: []byte{},
|
||||
want: Value{
|
||||
Type: "blob",
|
||||
Base64: toPtr(""),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "float",
|
||||
value: 3.14,
|
||||
want: Value{
|
||||
Type: "float",
|
||||
Value: 3.14,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "boolean",
|
||||
value: true,
|
||||
want: Value{
|
||||
Type: "integer",
|
||||
Value: "1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "timestamp",
|
||||
value: time.Time{}.Add(time.Hour),
|
||||
want: Value{
|
||||
Type: "text",
|
||||
Value: "0001-01-01 01:00:00+00:00",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unsupported",
|
||||
value: make(chan int),
|
||||
want: Value{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ToValue(tt.value)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ToValue() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ToValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value Value
|
||||
marshaled string
|
||||
}{
|
||||
{
|
||||
name: "null",
|
||||
value: Value{
|
||||
Type: "null",
|
||||
},
|
||||
marshaled: `{"type":"null"}`,
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
value: Value{
|
||||
Type: "integer",
|
||||
Value: strconv.FormatInt(int64(42), 10),
|
||||
},
|
||||
marshaled: `{"type":"integer","value":"42"}`,
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
value: Value{
|
||||
Type: "text",
|
||||
Value: "foo",
|
||||
},
|
||||
marshaled: `{"type":"text","value":"foo"}`,
|
||||
},
|
||||
{
|
||||
name: "bytes",
|
||||
value: Value{
|
||||
Type: "blob",
|
||||
Base64: toPtr("YmFy"),
|
||||
},
|
||||
marshaled: `{"type":"blob","base64":"YmFy"}`,
|
||||
},
|
||||
{
|
||||
name: "float",
|
||||
value: Value{
|
||||
Type: "float",
|
||||
Value: 3.14,
|
||||
},
|
||||
marshaled: `{"type":"float","value":3.14}`,
|
||||
},
|
||||
{
|
||||
name: "timestamp",
|
||||
value: Value{
|
||||
Type: "text",
|
||||
Value: time.Time{}.Add(time.Hour),
|
||||
},
|
||||
marshaled: `{"type":"text","value":"0001-01-01T01:00:00Z"}`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := json.Marshal(tt.value)
|
||||
if err != nil {
|
||||
t.Errorf("json.Marshal() error = %v", err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(string(got), tt.marshaled) {
|
||||
t.Errorf("json.Marshal() = %v, want %v", string(got), tt.marshaled)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value Value
|
||||
marshaled string
|
||||
}{
|
||||
{
|
||||
name: "null",
|
||||
value: Value{
|
||||
Type: "null",
|
||||
},
|
||||
marshaled: `{"type":"null"}`,
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
value: Value{
|
||||
Type: "integer",
|
||||
Value: strconv.FormatInt(int64(42), 10),
|
||||
},
|
||||
marshaled: `{"type":"integer","value":"42"}`,
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
value: Value{
|
||||
Type: "text",
|
||||
Value: "foo",
|
||||
},
|
||||
marshaled: `{"type":"text","value":"foo"}`,
|
||||
},
|
||||
{
|
||||
name: "bytes",
|
||||
value: Value{
|
||||
Type: "blob",
|
||||
Base64: toPtr("YmFy"),
|
||||
},
|
||||
marshaled: `{"type":"blob","base64":"YmFy"}`,
|
||||
},
|
||||
{
|
||||
name: "float",
|
||||
value: Value{
|
||||
Type: "float",
|
||||
Value: 3.14,
|
||||
},
|
||||
marshaled: `{"type":"float","value":3.14}`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var got Value
|
||||
err := json.Unmarshal([]byte(tt.marshaled), &got)
|
||||
if err != nil {
|
||||
t.Errorf("json.Marshal() error = %v", err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.value) {
|
||||
t.Errorf("json.Unmarshal() = %v, want %v", got, tt.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user