Add support for new query API

This commit is contained in:
Patrick Pacher
2022-04-26 14:59:27 +02:00
parent e21eb16a6b
commit d098f1c137
10 changed files with 1154 additions and 43 deletions

View File

@@ -31,7 +31,7 @@ var (
// preconfigured timezone; UTC by default) or as INTEGER (the user can choose between
// unixepoch and unixnano-epoch where the nano variant is not offically supported by
// SQLITE).
sqliteTimeFormat = "2006-01-02 15:04:05"
SqliteTimeFormat = "2006-01-02 15:04:05"
)
type (
@@ -209,7 +209,7 @@ func DatetimeDecoder(loc *time.Location) DecodeFunc {
case sqlite.TypeText:
// stored ISO8601 but does not have any timezone information
// assigned so we always treat it as loc here.
t, err := time.ParseInLocation(sqliteTimeFormat, stmt.ColumnText(colIdx), loc)
t, err := time.ParseInLocation(SqliteTimeFormat, stmt.ColumnText(colIdx), loc)
if err != nil {
return nil, fmt.Errorf("failed to parse %q in %s: %w", stmt.ColumnText(colIdx), fieldDef.Name, err)
}

View File

@@ -103,6 +103,13 @@ func encodeBasic() EncodeFunc {
kind = valType.Kind()
if val.IsNil() {
if !col.Nullable {
// we need to set the zero value here since the column
// is not marked as nullable
//return reflect.New(valType).Elem().Interface(), true, nil
panic("nil pointer for not-null field")
}
return nil, true, nil
}
@@ -133,7 +140,7 @@ func encodeBasic() EncodeFunc {
}
func DatetimeEncoder(loc *time.Location) EncodeFunc {
return func(colDev *ColumnDef, valType reflect.Type, val reflect.Value) (interface{}, bool, error) {
return func(colDef *ColumnDef, valType reflect.Type, val reflect.Value) (interface{}, bool, error) {
// if fieldType holds a pointer we need to dereference the value
ft := valType.String()
if valType.Kind() == reflect.Ptr {
@@ -142,44 +149,71 @@ func DatetimeEncoder(loc *time.Location) EncodeFunc {
}
// we only care about "time.Time" here
if ft != "time.Time" {
var t time.Time
if ft == "time.Time" {
// handle the zero time as a NULL.
if !val.IsValid() || val.IsZero() {
return nil, true, nil
}
var ok bool
valInterface := val.Interface()
t, ok = valInterface.(time.Time)
if !ok {
return nil, false, fmt.Errorf("cannot convert reflect value to time.Time")
}
} else if valType.Kind() == reflect.String && colDef.IsTime {
var err error
t, err = time.Parse(time.RFC3339, val.String())
if err != nil {
return nil, false, fmt.Errorf("failed to parse time as RFC3339: %w", err)
}
} else {
// we don't care ...
return nil, false, nil
}
// handle the zero time as a NULL.
if !val.IsValid() || val.IsZero() {
return nil, true, nil
}
valInterface := val.Interface()
t, ok := valInterface.(time.Time)
if !ok {
return nil, false, fmt.Errorf("cannot convert reflect value to time.Time")
}
switch colDev.Type {
switch colDef.Type {
case sqlite.TypeInteger:
if colDev.UnixNano {
if colDef.UnixNano {
return t.UnixNano(), true, nil
}
return t.Unix(), true, nil
case sqlite.TypeText:
str := t.In(loc).Format(sqliteTimeFormat)
str := t.In(loc).Format(SqliteTimeFormat)
return str, true, nil
}
return nil, false, fmt.Errorf("cannot store time.Time in %s", colDev.Type)
return nil, false, fmt.Errorf("cannot store time.Time in %s", colDef.Type)
}
}
func runEncodeHooks(colDev *ColumnDef, valType reflect.Type, val reflect.Value, hooks []EncodeFunc) (interface{}, bool, error) {
func runEncodeHooks(colDef *ColumnDef, valType reflect.Type, val reflect.Value, hooks []EncodeFunc) (interface{}, bool, error) {
if valType == nil {
if !colDef.Nullable {
switch colDef.Type {
case sqlite.TypeBlob:
return []byte{}, true, nil
case sqlite.TypeFloat:
return 0.0, true, nil
case sqlite.TypeText:
return "", true, nil
case sqlite.TypeInteger:
return 0, true, nil
default:
return nil, false, fmt.Errorf("unsupported sqlite data type: %s", colDef.Type)
}
}
return nil, true, nil
}
for _, fn := range hooks {
res, end, err := fn(colDev, valType, val)
res, end, err := fn(colDef, valType, val)
if err != nil {
return res, false, err
}

View File

@@ -89,7 +89,7 @@ func Test_EncodeAsMap(t *testing.T) {
},
map[string]interface{}{
"TinInt": refTime.UnixNano(),
"TinString": refTime.Format(sqliteTimeFormat),
"TinString": refTime.Format(SqliteTimeFormat),
},
},
{
@@ -107,7 +107,7 @@ func Test_EncodeAsMap(t *testing.T) {
},
map[string]interface{}{
"TinInt": refTime.UnixNano(),
"TinString": refTime.Format(sqliteTimeFormat),
"TinString": refTime.Format(SqliteTimeFormat),
"Tnil1": nil,
"Tnil2": nil,
},
@@ -143,7 +143,7 @@ func Test_EncodeValue(t *testing.T) {
Type: sqlite.TypeText,
},
refTime,
refTime.Format(sqliteTimeFormat),
refTime.Format(SqliteTimeFormat),
},
{
"Special value time.Time as unix-epoch",
@@ -189,13 +189,14 @@ func Test_EncodeValue(t *testing.T) {
Type: sqlite.TypeText,
},
&refTime,
refTime.Format(sqliteTimeFormat),
refTime.Format(SqliteTimeFormat),
},
{
"Special value untyped nil",
ColumnDef{
IsTime: true,
Type: sqlite.TypeText,
Nullable: true,
IsTime: true,
Type: sqlite.TypeText,
},
nil,
nil,
@@ -209,12 +210,47 @@ func Test_EncodeValue(t *testing.T) {
(*time.Time)(nil),
nil,
},
{
"Time formated as string",
ColumnDef{
IsTime: true,
Type: sqlite.TypeText,
},
refTime.In(time.Local).Format(time.RFC3339),
refTime.Format(SqliteTimeFormat),
},
{
"Nullable integer",
ColumnDef{
Type: sqlite.TypeInteger,
Nullable: true,
},
nil,
nil,
},
{
"Not-Null integer",
ColumnDef{
Name: "test",
Type: sqlite.TypeInteger,
},
nil,
0,
},
{
"Not-Null string",
ColumnDef{
Type: sqlite.TypeText,
},
nil,
"",
},
}
for idx := range cases {
c := cases[idx]
t.Run(c.Desc, func(t *testing.T) {
// t.Parallel()
//t.Parallel()
res, err := EncodeValue(ctx, &c.Column, c.Input, DefaultEncodeConfig)
assert.NoError(t, err)

View File

@@ -53,6 +53,15 @@ type (
}
)
func (ts TableSchema) GetColumnDef(name string) *ColumnDef {
for _, def := range ts.Columns {
if def.Name == name {
return &def
}
}
return nil
}
func (ts TableSchema) CreateStatement(ifNotExists bool) string {
sql := "CREATE TABLE"
if ifNotExists {