diff --git a/netquery/orm/decoder.go b/netquery/orm/decoder.go index 24126e97..5b87df82 100644 --- a/netquery/orm/decoder.go +++ b/netquery/orm/decoder.go @@ -289,7 +289,7 @@ func decodeBasic() DecodeFunc { // if we have the column definition available we // use the target go type from there. if colDef != nil { - valueKind = normalizeKind(colDef.GoType.Kind()) + valueKind = NormalizeKind(colDef.GoType.Kind()) // if we have a column definition we try to convert the value to // the actual Go-type that was used in the model. @@ -458,10 +458,10 @@ func runDecodeHooks(colIdx int, colDef *ColumnDef, stmt Stmt, fieldDef reflect.S // to their base type. func getKind(val reflect.Value) reflect.Kind { kind := val.Kind() - return normalizeKind(kind) + return NormalizeKind(kind) } -func normalizeKind(kind reflect.Kind) reflect.Kind { +func NormalizeKind(kind reflect.Kind) reflect.Kind { switch { case kind >= reflect.Int && kind <= reflect.Int64: return reflect.Int diff --git a/netquery/orm/encoder.go b/netquery/orm/encoder.go index ff30a14a..a1b880b7 100644 --- a/netquery/orm/encoder.go +++ b/netquery/orm/encoder.go @@ -123,7 +123,7 @@ func encodeBasic() EncodeFunc { val = val.Elem() } - switch normalizeKind(kind) { //nolint:exhaustive + switch NormalizeKind(kind) { //nolint:exhaustive case reflect.String, reflect.Float64, reflect.Bool, @@ -156,7 +156,7 @@ func DatetimeEncoder(loc *time.Location) EncodeFunc { val = reflect.Indirect(val) } - normalizedKind := normalizeKind(valType.Kind()) + normalizedKind := NormalizeKind(valType.Kind()) // we only care about "time.Time" here var t time.Time diff --git a/netquery/orm/schema_builder.go b/netquery/orm/schema_builder.go index 6aba2a1f..018a55e1 100644 --- a/netquery/orm/schema_builder.go +++ b/netquery/orm/schema_builder.go @@ -176,7 +176,7 @@ func getColumnDef(fieldType reflect.StructField) (*ColumnDef, error) { } def.GoType = ft - kind := normalizeKind(ft.Kind()) + kind := NormalizeKind(ft.Kind()) switch kind { //nolint:exhaustive case reflect.Int, reflect.Uint: diff --git a/netquery/query.go b/netquery/query.go index 8ff99fe3..33a9ae40 100644 --- a/netquery/query.go +++ b/netquery/query.go @@ -5,10 +5,12 @@ import ( "encoding/json" "fmt" "io" + "reflect" "sort" "strings" "github.com/hashicorp/go-multierror" + "golang.org/x/exp/slices" "zombiezen.com/go/sqlite" "github.com/safing/portmaster/netquery/orm" @@ -338,8 +340,24 @@ func (match Matcher) toSQLConditionClause(ctx context.Context, suffix string, co params[uniqKey] = encodedValue } + // NOTE(ppacher): for now we assume that the type of each member of values + // is the same. We also can be sure that there is always at least one value. + // + // FIXME(ppacher): if we start supporting values of different types here + // we need to revisit the whole behavior as we might need to do more boolean + // expression nesting to support that. + kind := orm.NormalizeKind(reflect.TypeOf(values[0]).Kind()) + isNumber := slices.Contains([]reflect.Kind{ + reflect.Uint, + reflect.Int, + reflect.Float64, + }, kind) + + // if this is a time column that is stored in "text" format and the provided + // value is a number type, we need to wrap the property in a strftime() method + // call. nameStmt := colDef.Name - if colDef.IsTime && colDef.Type == sqlite.TypeText { + if colDef.IsTime && colDef.Type == sqlite.TypeText && isNumber { nameStmt = fmt.Sprintf("strftime('%%s', %s)+0", nameStmt) }