diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index be855644b..7e679c86e 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -227,10 +227,10 @@ type TableField struct { Name string // Field name. Type string // Field type. Null bool // Field can be null or not. - Key string // The index information(empty if it's not a index). + Key string // The index information(empty if it's not an index). Default interface{} // Default value for the field. Extra string // Extra information. - Comment string // Comment. + Comment string // Field comment. } // Counter is the type for update count. diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index ebed7daa8..6fb33872e 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -147,29 +147,31 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s // mappingAndFilterData automatically mappings the map key to table field and removes // all key-value pairs that are not the field of given table. func (c *Core) mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) { - if fieldsMap, err := c.db.TableFields(c.GetCtx(), c.guessPrimaryTableName(table), schema); err == nil { - fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) - for k := range fieldsMap { - fieldsKeyMap[k] = nil - } - // Automatic data key to table field name mapping. - var foundKey string - for dataKey, dataValue := range data { - if _, ok := fieldsKeyMap[dataKey]; !ok { - foundKey, _ = gutil.MapPossibleItemByKey(fieldsKeyMap, dataKey) - if foundKey != "" { - data[foundKey] = dataValue - delete(data, dataKey) - } + fieldsMap, err := c.db.TableFields(c.GetCtx(), c.guessPrimaryTableName(table), schema) + if err != nil { + return nil, err + } + fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) + for k := range fieldsMap { + fieldsKeyMap[k] = nil + } + // Automatic data key to table field name mapping. + var foundKey string + for dataKey, dataValue := range data { + if _, ok := fieldsKeyMap[dataKey]; !ok { + foundKey, _ = gutil.MapPossibleItemByKey(fieldsKeyMap, dataKey) + if foundKey != "" { + data[foundKey] = dataValue + delete(data, dataKey) } } - // Data filtering. - // It deletes all key-value pairs that has incorrect field name. - if filter { - for dataKey := range data { - if _, ok := fieldsMap[dataKey]; !ok { - delete(data, dataKey) - } + } + // Data filtering. + // It deletes all key-value pairs that has incorrect field name. + if filter { + for dataKey := range data { + if _, ok := fieldsMap[dataKey]; !ok { + delete(data, dataKey) } } } diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index 9ddfd1489..a8f555684 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -42,6 +42,10 @@ func (c *Core) SlaveLink(schema ...string) (Link, error) { // // The meaning of a `word` can be considered as a column name. func (c *Core) QuoteWord(s string) string { + s = gstr.Trim(s) + if s == "" { + return s + } charLeft, charRight := c.db.GetChars() return doQuoteWord(s, charLeft, charRight) } @@ -83,7 +87,7 @@ func (c *Core) Tables(schema ...string) (tables []string, err error) { return } -// TableFields retrieves and returns the fields information of specified table of current schema. +// TableFields retrieves and returns the fields' information of specified table of current schema. // // Note that it returns a map containing the field name and its corresponding fields. // As a map is unsorted, the TableField struct has a "Index" field marks its sequence in the fields. diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index d55ac4af4..74a5f6566 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -38,7 +38,7 @@ func (d *DriverMssql) New(core *Core, node *ConfigNode) (DB, error) { }, nil } -// Open creates and returns a underlying sql.DB object for mssql. +// Open creates and returns an underlying sql.DB object for mssql. func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) { source := "" if config.Link != "" { @@ -83,7 +83,7 @@ func (d *DriverMssql) DoCommit(ctx context.Context, link Link, sql string, args newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs) }() var index int - // Convert place holder char '?' to string "@px". + // Convert placeholder char '?' to string "@px". str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ return fmt.Sprintf("@p%d", index) @@ -219,19 +219,17 @@ func (d *DriverMssql) TableFields(ctx context.Context, table string, schema ...s if len(schema) > 0 && schema[0] != "" { useSchema = schema[0] } - tableFieldsCacheKey := fmt.Sprintf( - `mssql_table_fields_%s_%s@group:%s`, - table, useSchema, d.GetGroup(), - ) - v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { - var ( - result Result - link, err = d.SlaveLink(useSchema) - ) - if err != nil { - return nil - } - structureSql := fmt.Sprintf(` + v := tableFieldsMap.GetOrSetFuncLock( + fmt.Sprintf(`mssql_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), + func() interface{} { + var ( + result Result + link Link + ) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil + } + structureSql := fmt.Sprintf(` SELECT a.name Field, CASE b.name @@ -259,28 +257,29 @@ LEFT JOIN sys.extended_properties g ON a.id=g.major_id AND a.colid=g.minor_id LEFT JOIN sys.extended_properties f ON d.id=f.major_id AND f.minor_id =0 WHERE d.name='%s' ORDER BY a.id,a.colorder`, - strings.ToUpper(table), - ) - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.DoGetAll(ctx, link, structureSql) - if err != nil { - return nil - } - fields = make(map[string]*TableField) - for i, m := range result { - fields[strings.ToLower(m["Field"].String())] = &TableField{ - Index: i, - Name: strings.ToLower(m["Field"].String()), - Type: strings.ToLower(m["Type"].String()), - Null: m["Null"].Bool(), - Key: m["Key"].String(), - Default: m["Default"].Val(), - Extra: m["Extra"].String(), - Comment: m["Comment"].String(), + strings.ToUpper(table), + ) + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + result, err = d.DoGetAll(ctx, link, structureSql) + if err != nil { + return nil } - } - return fields - }) + fields = make(map[string]*TableField) + for i, m := range result { + fields[strings.ToLower(m["Field"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["Field"].String()), + Type: strings.ToLower(m["Type"].String()), + Null: m["Null"].Bool(), + Key: m["Key"].String(), + Default: m["Default"].Val(), + Extra: m["Extra"].String(), + Comment: m["Comment"].String(), + } + } + return fields + }, + ) if v != nil { fields = v.(map[string]*TableField) } diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index b93c6c2f0..2f399882e 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -128,37 +128,36 @@ func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...s if len(schema) > 0 && schema[0] != "" { useSchema = schema[0] } - tableFieldsCacheKey := fmt.Sprintf( - `mysql_table_fields_%s_%s@group:%s`, - table, useSchema, d.GetGroup(), - ) - v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { - var ( - result Result - link, err = d.SlaveLink(useSchema) - ) - if err != nil { - return nil - } - result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table))) - if err != nil { - return nil - } - fields = make(map[string]*TableField) - for i, m := range result { - fields[m["Field"].String()] = &TableField{ - Index: i, - Name: m["Field"].String(), - Type: m["Type"].String(), - Null: m["Null"].Bool(), - Key: m["Key"].String(), - Default: m["Default"].Val(), - Extra: m["Extra"].String(), - Comment: m["Comment"].String(), + v := tableFieldsMap.GetOrSetFuncLock( + fmt.Sprintf(`mysql_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), + func() interface{} { + var ( + result Result + link Link + ) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil } - } - return fields - }) + result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table))) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[m["Field"].String()] = &TableField{ + Index: i, + Name: m["Field"].String(), + Type: m["Type"].String(), + Null: m["Null"].Bool(), + Key: m["Key"].String(), + Default: m["Default"].Val(), + Extra: m["Extra"].String(), + Comment: m["Comment"].String(), + } + } + return fields + }, + ) if v != nil { fields = v.(map[string]*TableField) } diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index aced866f1..9d3de0617 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -193,15 +193,13 @@ func (d *DriverOracle) TableFields(ctx context.Context, table string, schema ... if len(schema) > 0 && schema[0] != "" { useSchema = schema[0] } - tableFieldsCacheKey := fmt.Sprintf( - `oracle_table_fields_%s_%s@group:%s`, - table, useSchema, d.GetGroup(), - ) - v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { - var ( - result Result - link, err = d.SlaveLink(useSchema) - structureSql = fmt.Sprintf(` + v := tableFieldsMap.GetOrSetFuncLock( + fmt.Sprintf(`oracle_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), + func() interface{} { + var ( + result Result + link Link + structureSql = fmt.Sprintf(` SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE @@ -209,27 +207,28 @@ SELECT WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')' ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, - strings.ToUpper(table), + strings.ToUpper(table), + ) ) - ) - if err != nil { - return nil - } - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.DoGetAll(ctx, link, structureSql) - if err != nil { - return nil - } - fields = make(map[string]*TableField) - for i, m := range result { - fields[strings.ToLower(m["FIELD"].String())] = &TableField{ - Index: i, - Name: strings.ToLower(m["FIELD"].String()), - Type: strings.ToLower(m["TYPE"].String()), + if link, err = d.SlaveLink(useSchema); err != nil { + return nil } - } - return fields - }) + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + result, err = d.DoGetAll(ctx, link, structureSql) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[strings.ToLower(m["FIELD"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["FIELD"].String()), + Type: strings.ToLower(m["TYPE"].String()), + } + } + return fields + }, + ) if v != nil { fields = v.(map[string]*TableField) } diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index ab1e9801e..60ce1e266 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -86,7 +86,7 @@ func (d *DriverPgsql) DoCommit(ctx context.Context, link Link, sql string, args }() var index int - // Convert place holder char '?' to string "$x". + // Convert placeholder char '?' to string "$x". sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ return fmt.Sprintf("$%d", index) @@ -119,7 +119,7 @@ func (d *DriverPgsql) Tables(ctx context.Context, schema ...string) (tables []st return } -// TableFields retrieves and returns the fields information of specified table of current schema. +// TableFields retrieves and returns the fields' information of specified table of current schema. // // Also see DriverMysql.TableFields. func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { @@ -133,15 +133,13 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s if len(schema) > 0 && schema[0] != "" { useSchema = schema[0] } - tableFieldsCacheKey := fmt.Sprintf( - `pgsql_table_fields_%s_%s@group:%s`, - table, useSchema, d.GetGroup(), - ) - v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { - var ( - result Result - link, err = d.SlaveLink(useSchema) - structureSql = fmt.Sprintf(` + v := tableFieldsMap.GetOrSetFuncLock( + fmt.Sprintf(`pgsql_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), + func() interface{} { + var ( + result Result + link Link + structureSql = fmt.Sprintf(` SELECT a.attname AS field, t.typname AS type,a.attnotnull as null, (case when d.contype is not null then 'pri' else '' end) as key ,ic.column_default as default_value,b.description as comment @@ -155,31 +153,32 @@ FROM pg_attribute a left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname WHERE c.relname = '%s' and a.attnum > 0 ORDER BY a.attnum`, - strings.ToLower(table), + strings.ToLower(table), + ) ) - ) - if err != nil { - return nil - } - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.DoGetAll(ctx, link, structureSql) - if err != nil { - return nil - } - fields = make(map[string]*TableField) - for i, m := range result { - fields[m["field"].String()] = &TableField{ - Index: i, - Name: m["field"].String(), - Type: m["type"].String(), - Null: m["null"].Bool(), - Key: m["key"].String(), - Default: m["default_value"].Val(), - Comment: m["comment"].String(), + if link, err = d.SlaveLink(useSchema); err != nil { + return nil } - } - return fields - }) + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + result, err = d.DoGetAll(ctx, link, structureSql) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[m["field"].String()] = &TableField{ + Index: i, + Name: m["field"].String(), + Type: m["type"].String(), + Null: m["null"].Bool(), + Key: m["key"].String(), + Default: m["default_value"].Val(), + Comment: m["comment"].String(), + } + } + return fields + }, + ) if v != nil { fields = v.(map[string]*TableField) } diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 15851f7f7..155270dca 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -93,7 +93,7 @@ func (d *DriverSqlite) Tables(ctx context.Context, schema ...string) (tables []s return } -// TableFields retrieves and returns the fields information of specified table of current schema. +// TableFields retrieves and returns the fields' information of specified table of current schema. // // Also see DriverMysql.TableFields. func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { @@ -106,32 +106,31 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ... if len(schema) > 0 && schema[0] != "" { useSchema = schema[0] } - tableFieldsCacheKey := fmt.Sprintf( - `sqlite_table_fields_%s_%s@group:%s`, - table, useSchema, d.GetGroup(), - ) - v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { - var ( - result Result - link, err = d.SlaveLink(useSchema) - ) - if err != nil { - return nil - } - result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) - if err != nil { - return nil - } - fields = make(map[string]*TableField) - for i, m := range result { - fields[strings.ToLower(m["name"].String())] = &TableField{ - Index: i, - Name: strings.ToLower(m["name"].String()), - Type: strings.ToLower(m["type"].String()), + v := tableFieldsMap.GetOrSetFuncLock( + fmt.Sprintf(`sqlite_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), + func() interface{} { + var ( + result Result + link Link + ) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil } - } - return fields - }) + result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[strings.ToLower(m["name"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["name"].String()), + Type: strings.ToLower(m["type"].String()), + } + } + return fields + }, + ) if v != nil { fields = v.(map[string]*TableField) } diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index a9daa9aea..9d2cd34a8 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -34,6 +34,7 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",", )) + // It needs type asserting. case length == 1: structOrMap := fieldNamesOrMapStruct[0] @@ -80,7 +81,10 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { model := m.getModel() switch { case length >= 2: - model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",") + model.fieldsEx = gstr.Join( + m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), + ",", + ) return model case length == 1: switch r := fieldNamesOrMapStruct[0].(type) { diff --git a/database/gdb/gdb_model_time.go b/database/gdb/gdb_model_time.go index e1120fe42..34ff11566 100644 --- a/database/gdb/gdb_model_time.go +++ b/database/gdb/gdb_model_time.go @@ -94,6 +94,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) { // getSoftFieldName retrieves and returns the field name of the table for possible key. func (m *Model) getSoftFieldName(table string, keys []string) (field string) { + // Ignore the error from TableFields. fieldsMap, _ := m.TableFields(table) if len(fieldsMap) > 0 { for _, key := range keys { diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 02e307396..b04b7e7a5 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -26,7 +26,7 @@ func (m *Model) QuoteWord(s string) string { return m.db.GetCore().QuoteWord(s) } -// TableFields retrieves and returns the fields information of specified table of current +// TableFields retrieves and returns the fields' information of specified table of current // schema. // // Also see DriverMysql.TableFields. @@ -57,8 +57,8 @@ func (m *Model) getModel() *Model { // ID -> id // NICK_Name -> nickname. func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string { - fieldsMap, err := m.TableFields(m.tablesInit) - if err != nil || len(fieldsMap) == 0 { + fieldsMap, _ := m.TableFields(m.tablesInit) + if len(fieldsMap) == 0 { return fields } var ( diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 1adae244b..dc2cf59c2 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -955,6 +955,7 @@ func Test_Model_StructsWithOrmTag(t *testing.T) { dbInvalid.GetLogger().SetWriter(buffer) defer dbInvalid.GetLogger().SetWriter(os.Stdout) dbInvalid.Model(table).Order("id asc").Scan(&users) + //fmt.Println(buffer.String()) t.Assert( gstr.Contains(buffer.String(), "SELECT `id`,`Passport`,`password`,`nick_name`,`create_time` FROM `user"), true,