diff --git a/backends/clickhouse/column.go b/backends/clickhouse/column.go index 6628be6..d06585c 100644 --- a/backends/clickhouse/column.go +++ b/backends/clickhouse/column.go @@ -20,13 +20,11 @@ import ( "fmt" "reflect" "strconv" - "strings" "time" "yunion.io/x/log" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" - "yunion.io/x/pkg/util/timeutils" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -157,8 +155,8 @@ func (c *SBooleanColumn) DefinitionString() string { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + switch sqlchemy.ConvertValueToBool(str) { + case true: return uint8(1) default: return uint8(0) @@ -167,11 +165,12 @@ func (c *SBooleanColumn) ConvertFromString(str string) interface{} { // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} { - bVal := val.(bool) - if bVal { + switch sqlchemy.ConvertValueToBool(val) { + case true: return uint8(1) + default: + return uint8(0) } - return uint8(0) } // IsZero implementation of SBooleanColumn for IColumnSpec @@ -207,24 +206,24 @@ func (c *STristateColumn) DefinitionString() string { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + switch sqlchemy.ConvertValueToTriState(str) { + case tristate.True: return uint8(1) - case "none", "null", "unknown": - return sql.NullInt32{} - default: + case tristate.False: return uint8(0) + default: + return sql.NullInt32{} } } // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromValue(val interface{}) interface{} { - bVal := val.(tristate.TriState) - if bVal == tristate.True { + switch sqlchemy.ConvertValueToTriState(val) { + case tristate.True: return uint8(1) - } else if bVal == tristate.False { + case tristate.False: return uint8(0) - } else { + default: return sql.NullInt32{} } } @@ -282,37 +281,26 @@ func (c *SIntegerColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *SIntegerColumn) ConvertFromString(str string) interface{} { - ctype := c.SBaseColumn.ColType() - if ctype[0] == 'U' { - // unsigned - val, _ := strconv.ParseUint(str, 10, 64) - switch ctype { - case "UInt8": - return uint8(val) - case "UInt16": - return uint16(val) - case "UInt32": - return uint32(val) - case "UInt64": - return val - default: - panic(fmt.Sprintf("unsupported type %s", ctype)) - } - } else { - val, _ := strconv.ParseInt(str, 10, 64) - switch ctype { - case "Int8": - return int8(val) - case "Int16": - return int16(val) - case "Int32": - return int32(val) - case "Int64": - return val - default: - panic(fmt.Sprintf("unsupported type %s", ctype)) - } + val := sqlchemy.ConvertValueToInteger(str) + switch c.ColType() { + case "UInt8": + return uint8(val) + case "UInt16": + return uint16(val) + case "UInt32": + return uint32(val) + case "UInt64": + return val + case "Int8": + return int8(val) + case "Int16": + return int16(val) + case "Int32": + return int32(val) + case "Int64": + return val } + panic(fmt.Sprintf("unsupported type %s", c.ColType())) } // IsAutoVersion implements IsAutoVersion for IColumnSpec @@ -374,16 +362,14 @@ func (c *SFloatColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *SFloatColumn) ConvertFromString(str string) interface{} { - ctype := c.SBaseColumn.ColType() - val, _ := strconv.ParseFloat(str, 64) - switch ctype { + floatVal := sqlchemy.ConvertValueToFloat(str) + switch c.ColType() { case "Float32": - return float32(val) + return float32(floatVal) case "Float64": - return val - default: - panic(fmt.Sprintf("unsupported type %s", ctype)) + return floatVal } + panic(fmt.Sprintf("unsupported type %s", c.ColType())) } // NewFloatColumn returns an instance of SFloatColumn @@ -442,8 +428,7 @@ func (c *SDecimalColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *SDecimalColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseFloat(str, 64) - return val + return sqlchemy.ConvertValueToFloat(str) } // NewDecimalColumn returns an instance of SDecimalColumn @@ -564,8 +549,12 @@ func (c *STimeTypeColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *STimeTypeColumn) ConvertFromString(str string) interface{} { - tm, _ := timeutils.ParseTimeStr(str) - return tm + return sqlchemy.ConvertValueToTime(str) +} + +// ConvertFromValue implementation of STimeTypeColumn for IColumnSpec +func (c *STimeTypeColumn) ConvertFromValue(val interface{}) interface{} { + return sqlchemy.ConvertValueToTime(val) } func (c *STimeTypeColumn) GetTTL() (int, string) { diff --git a/backends/clickhouse/column_test.go b/backends/clickhouse/column_test.go index 56f0418..778a198 100644 --- a/backends/clickhouse/column_test.go +++ b/backends/clickhouse/column_test.go @@ -17,6 +17,7 @@ package clickhouse import ( "database/sql" "testing" + "time" "yunion.io/x/jsonutils" "yunion.io/x/pkg/tristate" @@ -268,6 +269,11 @@ func TestConvertString(t *testing.T) { want: float32(0.01), col: &float32Col, }, + { + in: "2025-03-27 12:00:00", + want: time.Date(2025, 3, 27, 12, 0, 0, 0, time.UTC), + col: &dateCol, + }, } for _, c := range cases { got := c.col.ConvertFromString(c.in) diff --git a/backends/clickhouse/sync.go b/backends/clickhouse/sync.go index 3fe1f91..761b113 100644 --- a/backends/clickhouse/sync.go +++ b/backends/clickhouse/sync.go @@ -164,7 +164,7 @@ func (clickhouse *SClickhouseBackend) CommitTableChangeSQL(ts sqlchemy.ITableSpe if oldTtlSpec != newTtlSpec { if oldTtlSpec.Count > 0 && newTtlSpec.Count == 0 { // remove - sql := fmt.Sprintf("REMOVE TTL") + sql := "REMOVE TTL" alters = append(alters, sql) } else { // alter diff --git a/backends/dameng/column.go b/backends/dameng/column.go index 225ba3c..e7f8274 100644 --- a/backends/dameng/column.go +++ b/backends/dameng/column.go @@ -20,13 +20,11 @@ import ( "fmt" "reflect" "strconv" - "strings" "time" "yunion.io/x/log" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" - "yunion.io/x/pkg/util/timeutils" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -85,8 +83,8 @@ func (c *SBooleanColumn) DefinitionString() string { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + switch sqlchemy.ConvertValueToBool(str) { + case true: return 1 default: return 0 @@ -95,16 +93,12 @@ func (c *SBooleanColumn) ConvertFromString(str string) interface{} { // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} { - var bVal bool - if c.IsPointer() { - bVal = *val.(*bool) - } else { - bVal = val.(bool) - } - if bVal { + switch sqlchemy.ConvertValueToBool(val) { + case true: return 1 + default: + return 0 } - return 0 } // IsZero implementation of SBooleanColumn for IColumnSpec @@ -140,24 +134,24 @@ func (c *STristateColumn) DefinitionString() string { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + switch sqlchemy.ConvertValueToTriState(str) { + case tristate.True: return 1 - case "none", "null", "unknown": - return sql.NullInt32{} - default: + case tristate.False: return 0 + default: + return sql.NullInt32{} } } // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromValue(val interface{}) interface{} { - bVal := val.(tristate.TriState) - if bVal == tristate.True { + switch sqlchemy.ConvertValueToTriState(val) { + case tristate.True: return 1 - } else if bVal == tristate.False { + case tristate.False: return 0 - } else { + default: return sql.NullInt32{} } } @@ -234,8 +228,18 @@ func (c *SIntegerColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SIntegerColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseInt(str, 10, 64) - return val + intval := sqlchemy.ConvertValueToInteger(str) + switch c.ColType() { + case "TINYINT": + return int8(intval) + case "SMALLINT": + return int16(intval) + case "INT": + return int(intval) + case "BIGINT": + return int64(intval) + } + panic(fmt.Sprintf("unsupported type %s", c.ColType())) } func (c *SIntegerColumn) IsAutoVersion() bool { @@ -335,8 +339,14 @@ func (c *SFloatColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SFloatColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseFloat(str, 64) - return val + floatVal := sqlchemy.ConvertValueToFloat(str) + switch c.ColType() { + case "FLOAT", "REAL": + return float32(floatVal) + case "DOUBLE": + return floatVal + } + panic(fmt.Sprintf("unsupported type %s", c.ColType())) } // NewFloatColumn returns an instance of SFloatColumn @@ -392,8 +402,7 @@ func (c *SDecimalColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SDecimalColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseFloat(str, 64) - return val + return sqlchemy.ConvertValueToFloat(str) } // NewDecimalColumn returns an instance of SDecimalColumn @@ -498,8 +507,11 @@ func (c *STimeTypeColumn) ColType() string { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *STimeTypeColumn) ConvertFromString(str string) interface{} { - tm, _ := timeutils.ParseTimeStr(str) - return tm + return sqlchemy.ConvertValueToTime(str) +} + +func (c *STimeTypeColumn) ConvertFromValue(val interface{}) interface{} { + return sqlchemy.ConvertValueToTime(val) } // NewTimeTypeColumn return an instance of STimeTypeColumn diff --git a/backends/dameng/column_test.go b/backends/dameng/column_test.go index fbe4cf7..b8e1e05 100644 --- a/backends/dameng/column_test.go +++ b/backends/dameng/column_test.go @@ -17,6 +17,7 @@ package dameng import ( "database/sql" "testing" + "time" "yunion.io/x/jsonutils" "yunion.io/x/pkg/tristate" @@ -166,6 +167,11 @@ func TestConvertValue(t *testing.T) { want: `{}`, col: &compCol, }, + { + in: "2025-03-27 12:00:00", + want: time.Date(2025, 3, 27, 12, 0, 0, 0, time.UTC), + col: &dateCol, + }, } for _, c := range cases { got := c.col.ConvertFromValue(c.in) @@ -207,12 +213,12 @@ func TestConvertString(t *testing.T) { }, { in: "23", - want: int64(23), + want: 23, col: &intCol, }, { in: "0.01", - want: 0.01, + want: float32(0.01), col: &floatCol, }, } diff --git a/backends/dameng/dameng.go b/backends/dameng/dameng.go index 8afe7b6..3a86d47 100644 --- a/backends/dameng/dameng.go +++ b/backends/dameng/dameng.go @@ -257,7 +257,7 @@ func (dameng *SDamengBackend) GetColumnSpecByFieldType(table *sqlchemy.STableSpe col := NewDecimalColumn(fieldname, tagmap, isPointer) return &col } - colType := "REAL" + colType := "FLOAT" if fieldType == gotypes.Float64Type { colType = "DOUBLE" } diff --git a/backends/mysql/column.go b/backends/mysql/column.go index ff42acd..dad35e3 100644 --- a/backends/mysql/column.go +++ b/backends/mysql/column.go @@ -26,7 +26,6 @@ import ( "yunion.io/x/log" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" - "yunion.io/x/pkg/util/timeutils" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -85,8 +84,8 @@ func (c *SBooleanColumn) DefinitionString() string { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + switch sqlchemy.ConvertValueToBool(str) { + case true: return 1 default: return 0 @@ -95,16 +94,12 @@ func (c *SBooleanColumn) ConvertFromString(str string) interface{} { // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} { - var bVal bool - if c.IsPointer() { - bVal = *val.(*bool) - } else { - bVal = val.(bool) - } - if bVal { + switch sqlchemy.ConvertValueToBool(val) { + case true: return 1 + default: + return 0 } - return 0 } // IsZero implementation of SBooleanColumn for IColumnSpec @@ -140,24 +135,24 @@ func (c *STristateColumn) DefinitionString() string { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + switch sqlchemy.ConvertValueToTriState(str) { + case tristate.True: return 1 - case "none", "null", "unknown": - return sql.NullInt32{} - default: + case tristate.False: return 0 + default: + return sql.NullInt32{} } } // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromValue(val interface{}) interface{} { - bVal := val.(tristate.TriState) - if bVal == tristate.True { + switch sqlchemy.ConvertValueToTriState(val) { + case tristate.True: return 1 - } else if bVal == tristate.False { + case tristate.False: return 0 - } else { + default: return sql.NullInt32{} } } @@ -232,15 +227,34 @@ func (c *SIntegerColumn) IsZero(val interface{}) bool { return true } +func (c *SIntegerColumn) int2int(intval int64) interface{} { + colType := c.ColType() + parts := strings.Split(colType, "(") + switch parts[0] { + case "TINYINT": + return int8(intval) + case "SMALLINT": + return int16(intval) + case "INT": + return int(intval) + case "BIGINT": + return int64(intval) + case "UNSIGNED TINYINT": + return uint8(intval) + case "UNSIGNED SMALLINT": + return uint16(intval) + case "UNSIGNED INT": + return uint(intval) + case "UNSIGNED BIGINT": + return uint64(intval) + } + panic(fmt.Sprintf("unsupported type %s", c.ColType())) +} + // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SIntegerColumn) ConvertFromString(str string) interface{} { - if c.isUnsigned { - val, _ := strconv.ParseUint(str, 10, 64) - return val - } else { - val, _ := strconv.ParseInt(str, 10, 64) - return val - } + intval := sqlchemy.ConvertValueToInteger(str) + return c.int2int(intval) } // ColType implementation of SIntegerColumn for IColumnSpec @@ -350,8 +364,7 @@ func (c *SFloatColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SFloatColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseFloat(str, 64) - return val + return sqlchemy.ConvertValueToFloat(str) } // NewFloatColumn returns an instance of SFloatColumn @@ -407,8 +420,7 @@ func (c *SDecimalColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SDecimalColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseFloat(str, 64) - return val + return sqlchemy.ConvertValueToFloat(str) } // NewDecimalColumn returns an instance of SDecimalColumn @@ -541,8 +553,12 @@ func (c *STimeTypeColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *STimeTypeColumn) ConvertFromString(str string) interface{} { - tm, _ := timeutils.ParseTimeStr(str) - return tm + return sqlchemy.ConvertValueToTime(str) +} + +// ConvertFromValue implementation of STimeTypeColumn for IColumnSpec +func (c *STimeTypeColumn) ConvertFromValue(val interface{}) interface{} { + return sqlchemy.ConvertValueToTime(val) } // NewTimeTypeColumn return an instance of STimeTypeColumn diff --git a/backends/mysql/column_test.go b/backends/mysql/column_test.go index 25ee62c..251280c 100644 --- a/backends/mysql/column_test.go +++ b/backends/mysql/column_test.go @@ -223,7 +223,7 @@ func TestConvertString(t *testing.T) { }, { in: "23", - want: int64(23), + want: 23, col: &intCol, }, { diff --git a/backends/sqlite/column.go b/backends/sqlite/column.go index a74d78d..fa95a73 100644 --- a/backends/sqlite/column.go +++ b/backends/sqlite/column.go @@ -20,12 +20,10 @@ import ( "fmt" "reflect" "strconv" - "strings" "time" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" - "yunion.io/x/pkg/util/timeutils" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -88,18 +86,15 @@ func (c *SBooleanColumn) DefinitionString() string { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + if sqlchemy.ConvertValueToBool(str) { return 1 - default: - return 0 } + return 0 } // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} { - bVal := val.(bool) - if bVal { + if sqlchemy.ConvertValueToBool(val) { return 1 } return 0 @@ -138,10 +133,10 @@ func (c *STristateColumn) DefinitionString() string { // ConvertFromString implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromString(str string) interface{} { - switch strings.ToLower(str) { - case "true", "yes", "on", "ok", "1": + switch sqlchemy.ConvertValueToTriState(str) { + case tristate.True: return 1 - case "none", "null", "unknown": + case tristate.None: return sql.NullInt32{} default: return 0 @@ -150,12 +145,12 @@ func (c *STristateColumn) ConvertFromString(str string) interface{} { // ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromValue(val interface{}) interface{} { - bVal := val.(tristate.TriState) - if bVal == tristate.True { + switch sqlchemy.ConvertValueToTriState(val) { + case tristate.True: return 1 - } else if bVal == tristate.None { + case tristate.None: return sql.NullInt32{} - } else { + default: return 0 } } @@ -219,8 +214,7 @@ func (c *SIntegerColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SIntegerColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseInt(str, 10, 64) - return val + return sqlchemy.ConvertValueToInteger(str) } func (c *SIntegerColumn) IsAutoVersion() bool { @@ -316,8 +310,7 @@ func (c *SFloatColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SFloatColumn) ConvertFromString(str string) interface{} { - val, _ := strconv.ParseFloat(str, 64) - return val + return sqlchemy.ConvertValueToFloat(str) } // NewFloatColumn returns an instance of SFloatColumn @@ -403,8 +396,12 @@ func (c *STimeTypeColumn) IsZero(val interface{}) bool { // ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *STimeTypeColumn) ConvertFromString(str string) interface{} { - tm, _ := timeutils.ParseTimeStr(str) - return tm + return sqlchemy.ConvertValueToTime(str) +} + +// ConvertFromValue implementation of STimeTypeColumn for IColumnSpec +func (c *STimeTypeColumn) ConvertFromValue(val interface{}) interface{} { + return sqlchemy.ConvertValueToTime(val) } // NewTimeTypeColumn return an instance of STimeTypeColumn diff --git a/conditions.go b/conditions.go index 6860ccf..91f4080 100644 --- a/conditions.go +++ b/conditions.go @@ -328,7 +328,7 @@ func IsFalse(f IQueryField) ICondition { return &c } -// SNoLaterThanCondition coompares a DATETIME field with current time and ensure the field is no later than now, e.g. a <= NOW() +// SNoLaterThanCondition compares a DATETIME field with current time and ensure the field is no later than now, e.g. a <= NOW() type SNoLaterThanCondition struct { SSingleCondition } @@ -430,15 +430,13 @@ func VarConditionWhereClause(v interface{}) string { } func varConditionVariables(v interface{}) []interface{} { - switch v.(type) { + switch vv := v.(type) { case IQueryField: return []interface{}{} case *SQuery: - q := v.(*SQuery) - return q.Variables() + return vv.Variables() case *SSubQuery: - q := v.(*SSubQuery) - return q.query.Variables() + return vv.query.Variables() default: return reflectutils.ExpandInterface(v) } @@ -462,7 +460,11 @@ func (t *STupleCondition) Variables() []interface{} { if isFieldRequireAscii(t.left) && !isVariableAscii(t.right) { return []interface{}{} } - return varConditionVariables(t.right) + vars := varConditionVariables(t.right) + for i := range vars { + vars[i] = t.left.ConvertFromValue(vars[i]) + } + return vars } // database implementation of STupleCondition for ICondition @@ -710,9 +712,13 @@ type STripleCondition struct { func (t *STripleCondition) Variables() []interface{} { ret := make([]interface{}, 0) vars := varConditionVariables(t.right) - ret = append(ret, vars...) + for i := range vars { + ret = append(ret, t.left.ConvertFromValue(vars[i])) + } vars = varConditionVariables(t.right2) - ret = append(ret, vars...) + for i := range vars { + ret = append(ret, t.left.ConvertFromValue(vars[i])) + } return ret } diff --git a/functions.go b/functions.go index 0cfc095..9542b67 100644 --- a/functions.go +++ b/functions.go @@ -51,6 +51,8 @@ type SFunctionFieldBase struct { alias string aggregate bool + + convertFunc func(interface{}) interface{} } func (ff *SFunctionFieldBase) getQuoteChar() string { @@ -97,6 +99,14 @@ func (ff *SFunctionFieldBase) Variables() []interface{} { return ff.variables() } +// ConvertFromValue implementation of SFunctionFieldBase for IQueryField +func (ff *SFunctionFieldBase) ConvertFromValue(val interface{}) interface{} { + if ff.convertFunc != nil { + return ff.convertFunc(val) + } + return val +} + func (ff *SFunctionFieldBase) IsAggregate() bool { return ff.aggregate } @@ -140,16 +150,21 @@ func (ff *sExprFunction) queryFields() []IQueryField { return ff.fields } -// NewFunctionField returns an instance of query field by calling a SQL embedded function func NewFunctionField(name string, isAggr bool, funcexp string, fields ...IQueryField) IQueryField { + return NewFunctionFieldWithConvert(name, isAggr, funcexp, nil, fields...) +} + +// NewFunctionField returns an instance of query field by calling a SQL embedded function +func NewFunctionFieldWithConvert(name string, isAggr bool, funcexp string, convertFunc func(interface{}) interface{}, fields ...IQueryField) IQueryField { funcBase := &sExprFunction{ fields: fields, function: funcexp, } return &SFunctionFieldBase{ - IFunction: funcBase, - alias: name, - aggregate: isAggr, + IFunction: funcBase, + alias: name, + aggregate: isAggr, + convertFunc: convertFunc, } } @@ -238,6 +253,11 @@ func (s *SConstField) Label(label string) IQueryField { return s } +// ConvertFromValue implementation of SConstField for IQueryField +func (s *SConstField) ConvertFromValue(val interface{}) interface{} { + return val +} + // database implementation of SConstField for IQueryField func (s *SConstField) database() *SDatabase { return nil @@ -287,6 +307,11 @@ func (s *SStringField) Label(label string) IQueryField { return s } +// ConvertFromValue implementation of SStringField for IQueryField +func (s *SStringField) ConvertFromValue(val interface{}) interface{} { + return val +} + // database implementation of SStringField for IQueryField func (s *SStringField) database() *SDatabase { return nil diff --git a/querydefs.go b/querydefs.go index f739985..e9dbb06 100644 --- a/querydefs.go +++ b/querydefs.go @@ -81,6 +81,9 @@ type IQueryField interface { // return variables Variables() []interface{} + // ConvertFromValue returns the SQL representation of a value for this + ConvertFromValue(val interface{}) interface{} + // Database returns the database of this IQuerySource database() *SDatabase } diff --git a/queryfield.go b/queryfield.go index ea72a13..382a3fd 100644 --- a/queryfield.go +++ b/queryfield.go @@ -53,6 +53,15 @@ func (sqf *sQueryField) Variables() []interface{} { return nil } +// ConvertFromValue returns the SQL representation of a value for this +func (sqf *sQueryField) ConvertFromValue(val interface{}) interface{} { + field := sqf.from.Field(sqf.name) + if field == nil { + return val + } + return field.ConvertFromValue(val) +} + // Database returns the database of this IQuerySource func (sqf *sQueryField) database() *SDatabase { return sqf.from.database() diff --git a/rawquery.go b/rawquery.go index 854513c..8d411cb 100644 --- a/rawquery.go +++ b/rawquery.go @@ -48,6 +48,11 @@ func (rqf *SRawQueryField) Variables() []interface{} { return nil } +// ConvertFromValue implementation of SRawQueryField for IQueryField +func (rqf *SRawQueryField) ConvertFromValue(val interface{}) interface{} { + return val +} + func (rqf *SRawQueryField) database() *SDatabase { return rqf.db } diff --git a/reflect.go b/reflect.go index 2dd74fc..ddba638 100644 --- a/reflect.go +++ b/reflect.go @@ -202,3 +202,517 @@ func setValueBySQLString(value reflect.Value, val string) error { } } } + +func ConvertValueToTime(val interface{}) time.Time { + switch v := val.(type) { + case string: + tm, _ := timeutils.ParseTimeStr(v) + return tm + case time.Time: + return v + case int: + return time.Unix(int64(v), 0) + case int32: + return time.Unix(int64(v), 0) + case int64: + return time.Unix(int64(v), 0) + case uint: + return time.Unix(int64(v), 0) + case uint32: + return time.Unix(int64(v), 0) + case uint64: + return time.Unix(int64(v), 0) + case float32: + return time.Unix(int64(v), int64((v-float32(int64(v)))*1000000000)) + case float64: + return time.Unix(int64(v), int64((v-float64(int64(v)))*1000000000)) + case *string: + tm, _ := timeutils.ParseTimeStr(*v) + return tm + case *time.Time: + return *v + case *int: + return time.Unix(int64(*v), 0) + case *int32: + return time.Unix(int64(*v), 0) + case *int64: + return time.Unix(int64(*v), 0) + case *uint: + return time.Unix(int64(*v), 0) + case *uint32: + return time.Unix(int64(*v), 0) + case *uint64: + return time.Unix(int64(*v), 0) + case *float32: + return time.Unix(int64(*v), int64((*v-float32(int64(*v)))*1000000000)) + case *float64: + return time.Unix(int64(*v), int64((*v-float64(int64(*v)))*1000000000)) + } + return time.Time{} +} + +func ConvertValueToInteger(val interface{}) int64 { + switch v := val.(type) { + case string: + intv, _ := strconv.ParseInt(v, 10, 64) + return intv + case int: + return int64(v) + case int32: + return int64(v) + case int64: + return v + case uint: + return int64(v) + case uint32: + return int64(v) + case uint64: + return int64(v) + case float32: + return int64(v) + case float64: + return int64(v) + case time.Time: + return v.Unix() + case *string: + intv, _ := strconv.ParseInt(*v, 10, 64) + return intv + case *int: + return int64(*v) + case *int32: + return int64(*v) + case *int64: + return *v + case *uint: + return int64(*v) + case *uint32: + return int64(*v) + case *uint64: + return int64(*v) + case *float32: + return int64(*v) + case *float64: + return int64(*v) + case *time.Time: + return v.Unix() + } + return 0 +} + +func ConvertValueToFloat(val interface{}) float64 { + switch v := val.(type) { + case string: + intv, _ := strconv.ParseFloat(v, 64) + return intv + case int: + return float64(v) + case int32: + return float64(v) + case int64: + return float64(v) + case uint: + return float64(v) + case uint32: + return float64(v) + case uint64: + return float64(v) + case float32: + return float64(v) + case float64: + return v + case time.Time: + return float64(v.Unix()) + case *string: + intv, _ := strconv.ParseFloat(*v, 64) + return intv + case *int: + return float64(*v) + case *int32: + return float64(*v) + case *int64: + return float64(*v) + case *uint: + return float64(*v) + case *uint32: + return float64(*v) + case *uint64: + return float64(*v) + case *float32: + return float64(*v) + case *float64: + return *v + case *time.Time: + return float64(v.Unix()) + } + + return 0 +} + +func ConvertValueToBool(val interface{}) bool { + switch v := val.(type) { + case string: + v = strings.ToLower(v) + return v == "1" || v == "true" || v == "ok" || v == "yes" || v == "on" + case bool: + return v + case tristate.TriState: + return v == tristate.True + case int8: + return v > 0 + case int16: + return v > 0 + case int: + return v > 0 + case int32: + return v > 0 + case int64: + return v > 0 + case uint8: + return v > 0 + case uint16: + return v > 0 + case uint: + return v > 0 + case uint32: + return v > 0 + case uint64: + return v > 0 + case float32: + return v > 0 + case float64: + return v > 0 + case *string: + nv := strings.ToLower(*v) + return nv == "1" || nv == "true" || nv == "ok" || nv == "yes" || nv == "on" + case *bool: + return *v + case *tristate.TriState: + return *v == tristate.True + case *int8: + return *v > 0 + case *int16: + return *v > 0 + case *int: + return *v > 0 + case *int32: + return *v > 0 + case *int64: + return *v > 0 + case *uint8: + return *v > 0 + case *uint16: + return *v > 0 + case *uint: + return *v > 0 + case *uint32: + return *v > 0 + case *uint64: + return *v > 0 + case *float32: + return *v > 0 + case *float64: + return *v > 0 + } + return false +} + +func ConvertValueToTriState(val interface{}) tristate.TriState { + switch v := val.(type) { + case tristate.TriState: + return v + case string: + switch strings.ToLower(v) { + case "true", "yes", "on", "ok", "1": + return tristate.True + case "none", "null", "unknown": + return tristate.None + default: + return tristate.False + } + case bool: + if v { + return tristate.True + } else { + return tristate.False + } + case int8: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case int16: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case int: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case int32: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case int64: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case uint8: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case uint16: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case uint: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case uint32: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case uint64: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case float32: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case float64: + switch { + case v > 0: + return tristate.True + case v < 0: + return tristate.False + default: + return tristate.None + } + case *string: + switch strings.ToLower(*v) { + case "true", "yes", "on", "ok", "1": + return tristate.True + case "none", "null", "unknown": + return tristate.None + default: + return tristate.False + } + case *bool: + if *v { + return tristate.True + } else { + return tristate.False + } + case *tristate.TriState: + return *v + case *int8: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *int: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *int32: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *int64: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *uint8: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *uint16: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *uint: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *uint32: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *uint64: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *float32: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + case *float64: + switch { + case *v > 0: + return tristate.True + case *v < 0: + return tristate.False + default: + return tristate.None + } + } + return tristate.None +} + +func ConvertValueToString(val interface{}) string { + if gotypes.IsNil(val) { + return "" + } + switch v := val.(type) { + case string: + return v + case int8, int16, int, int32, int64, uint8, uint16, uint, uint32, uint64: + return fmt.Sprintf("%d", v) + case float32, float64: + return fmt.Sprintf("%f", v) + case bool: + return fmt.Sprintf("%v", v) + case time.Time: + return timeutils.IsoTime(v) + case *string: + return *v + case *int8: + return fmt.Sprintf("%d", *v) + case *int16: + return fmt.Sprintf("%d", *v) + case *int: + return fmt.Sprintf("%d", *v) + case *int32: + return fmt.Sprintf("%d", *v) + case *int64: + return fmt.Sprintf("%d", *v) + case *uint8: + return fmt.Sprintf("%d", *v) + case *uint16: + return fmt.Sprintf("%d", *v) + case *uint: + return fmt.Sprintf("%d", *v) + case *uint32: + return fmt.Sprintf("%d", *v) + case *uint64: + return fmt.Sprintf("%d", *v) + case *float32: + return fmt.Sprintf("%f", *v) + case *float64: + return fmt.Sprintf("%f", *v) + case *time.Time: + return timeutils.IsoTime(*v) + case tristate.TriState: + return v.String() + case *tristate.TriState: + return (*v).String() + } + if reflect.ValueOf(val).Kind() == reflect.String { + return val.(string) + } + return jsonutils.Marshal(val).String() +} diff --git a/subquery.go b/subquery.go index ee60682..46e8687 100644 --- a/subquery.go +++ b/subquery.go @@ -62,6 +62,11 @@ func (sqf *SSubQueryField) Variables() []interface{} { return nil } +// ConvertFromValue implementation of SSubQueryField for IQueryField +func (sqf *SSubQueryField) ConvertFromValue(val interface{}) interface{} { + return sqf.field.ConvertFromValue(val) +} + // database implementation of SSubQueryField for IQueryField func (sqf *SSubQueryField) database() *SDatabase { return sqf.query.database() diff --git a/table.go b/table.go index d3fb891..7eac721 100644 --- a/table.go +++ b/table.go @@ -424,6 +424,11 @@ func (c *STableField) Variables() []interface{} { return nil } +// ConvertFromValue implementation of STableField for IQueryField +func (c *STableField) ConvertFromValue(val interface{}) interface{} { + return c.spec.ConvertFromValue(val) +} + // database implementation of STableField for IQueryField func (c *STableField) database() *SDatabase { return c.table.database() diff --git a/union.go b/union.go index 0289817..32edcb0 100644 --- a/union.go +++ b/union.go @@ -67,6 +67,17 @@ func (sqf *SUnionQueryField) Variables() []interface{} { return nil } +// ConvertFromValue implementation of SUnionQueryField for IQueryField +func (sqf *SUnionQueryField) ConvertFromValue(val interface{}) interface{} { + for _, query := range sqf.union.queries { + field := query.Field(sqf.name) + if field != nil { + return field.ConvertFromValue(val) + } + } + return val +} + func (sqf *SUnionQueryField) database() *SDatabase { return sqf.union.database() }