diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 5c8bf6c6b..7d8b7e08a 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -66,6 +66,15 @@ jobs: ports: - 3306:3306 + # MariaDb backend server. + mariadb: + image: loads/mariadb:10.4 + env: + MARIADB_DATABASE: test + MARIADB_ROOT_PASSWORD: 12345678 + ports: + - 3307:3306 + # PostgreSQL backend server. # docker run -d --name postgres \ # -p 5432:5432 \ diff --git a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go index e93f59d3f..5b29ed856 100644 --- a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go +++ b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go @@ -393,3 +393,78 @@ func Test_Gen_Dao_Issue2616(t *testing.T) { t.Assert(gstr.Contains(daoUser2Content, keyStr), false) }) } + +// https://github.com/gogf/gf/issues/2746 +func Test_Gen_Dao_Issue2746(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + mdb gdb.DB + link2746 = "mariadb:root:12345678@tcp(127.0.0.1:3307)/test?loc=Local&parseTime=true" + table = "issue2746" + sqlContent = fmt.Sprintf( + gtest.DataContent(`issue`, `2746`, `sql.sql`), + table, + ) + ) + mdb, err = gdb.New(gdb.ConfigNode{ + Link: link2746, + }) + t.AssertNil(err) + + array := gstr.SplitAndTrim(sqlContent, ";") + for _, v := range array { + if _, err = mdb.Exec(ctx, v); err != nil { + t.AssertNil(err) + } + } + defer dropTableWithDb(mdb, table) + + var ( + path = gfile.Temp(guid.S()) + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: link2746, + Tables: "", + TablesEx: "", + Group: group, + Prefix: "", + RemovePrefix: "", + JsonCase: "SnakeScreaming", + ImportPrefix: "", + DaoPath: "", + DoPath: "", + EntityPath: "", + TplDaoIndexPath: "", + TplDaoInternalPath: "", + TplDaoDoPath: "", + TplDaoEntityPath: "", + StdTime: false, + WithTime: false, + GJsonSupport: true, + OverwriteDao: false, + DescriptionTag: false, + NoJsonTag: false, + NoModelComment: false, + Clear: false, + TypeMapping: nil, + } + ) + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Mkdir(path) + t.AssertNil(err) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + defer gfile.Remove(path) + + var ( + file = filepath.FromSlash(path + "/model/entity/issue_2746.go") + expectContent = gtest.DataContent(`issue`, `2746`, `issue_2746.go`) + ) + t.Assert(expectContent, gfile.GetContents(file)) + }) +} diff --git a/cmd/gf/internal/cmd/testdata/issue/2746/issue_2746.go b/cmd/gf/internal/cmd/testdata/issue/2746/issue_2746.go new file mode 100644 index 000000000..056748100 --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/issue/2746/issue_2746.go @@ -0,0 +1,18 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/encoding/gjson" +) + +// Issue2746 is the golang structure for table issue2746. +type Issue2746 struct { + Id uint `json:"ID" ` // User ID + Nickname string `json:"NICKNAME" ` // User Nickname + Tag *gjson.Json `json:"TAG" ` // + Info string `json:"INFO" ` // + Tag2 *gjson.Json `json:"TAG_2" ` // Tag2 +} diff --git a/cmd/gf/internal/cmd/testdata/issue/2746/sql.sql b/cmd/gf/internal/cmd/testdata/issue/2746/sql.sql new file mode 100644 index 000000000..99357a181 --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/issue/2746/sql.sql @@ -0,0 +1,9 @@ +CREATE TABLE %s ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID', + `nickname` varchar(45) NOT NULL COMMENT 'User Nickname', + `tag` json NOT NULL, + `info` longtext DEFAULT NULL, + `tag2` json COMMENT 'Tag2', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + diff --git a/contrib/drivers/mssql/mssql.go b/contrib/drivers/mssql/mssql.go index 045add1ed..0d65d80e4 100644 --- a/contrib/drivers/mssql/mssql.go +++ b/contrib/drivers/mssql/mssql.go @@ -15,8 +15,6 @@ import ( _ "github.com/denisenkom/go-mssqldb" "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/text/gregex" - "github.com/gogf/gf/v2/text/gstr" ) // Driver is the driver for SQL server database. @@ -34,21 +32,6 @@ func init() { } } -// formatSqlTmp formats sql template string into one line. -func formatSqlTmp(sqlTmp string) string { - var err error - // format sql template string. - sqlTmp, err = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(sqlTmp)) - if err != nil { - panic(err) - } - sqlTmp, err = gregex.ReplaceString(`\s{2,}`, " ", gstr.Trim(sqlTmp)) - if err != nil { - panic(err) - } - return sqlTmp -} - // New create and returns a driver that implements gdb.Driver, which supports operations for Mssql. func New() gdb.Driver { return &Driver{} diff --git a/contrib/drivers/mssql/mssql_do_filter.go b/contrib/drivers/mssql/mssql_do_filter.go index aa97d4584..1a1fab537 100644 --- a/contrib/drivers/mssql/mssql_do_filter.go +++ b/contrib/drivers/mssql/mssql_do_filter.go @@ -9,7 +9,6 @@ package mssql import ( "context" "fmt" - "strconv" "strings" @@ -27,7 +26,7 @@ WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d ) func init() { - selectWithOrderSqlTmp = formatSqlTmp(selectWithOrderSqlTmp) + selectWithOrderSqlTmp = gdb.FormatMultiLineSqlToSingle(selectWithOrderSqlTmp) } // DoFilter deals with the sql string before commits it to underlying sql driver. diff --git a/contrib/drivers/mssql/mssql_table_fields.go b/contrib/drivers/mssql/mssql_table_fields.go index cce605287..d15a7f009 100644 --- a/contrib/drivers/mssql/mssql_table_fields.go +++ b/contrib/drivers/mssql/mssql_table_fields.go @@ -47,7 +47,7 @@ ORDER BY a.id,a.colorder ) func init() { - tableFieldsSqlTmp = formatSqlTmp(tableFieldsSqlTmp) + tableFieldsSqlTmp = gdb.FormatMultiLineSqlToSingle(tableFieldsSqlTmp) } // TableFields retrieves and returns the fields' information of specified table of current schema. diff --git a/contrib/drivers/mysql/mysql_table_fields.go b/contrib/drivers/mysql/mysql_table_fields.go index bbd1532c2..133751f98 100644 --- a/contrib/drivers/mysql/mysql_table_fields.go +++ b/contrib/drivers/mysql/mysql_table_fields.go @@ -14,6 +14,32 @@ import ( "github.com/gogf/gf/v2/util/gutil" ) +var ( + tableFieldsSqlByMariadb = ` +SELECT + c.COLUMN_NAME AS 'Field', + ( CASE WHEN ch.CHECK_CLAUSE LIKE 'json_valid%%' THEN 'json' ELSE c.COLUMN_TYPE END ) AS 'Type', + c.COLLATION_NAME AS 'Collation', + c.IS_NULLABLE AS 'Null', + c.COLUMN_KEY AS 'Key', + ( CASE WHEN c.COLUMN_DEFAULT = 'NULL' OR c.COLUMN_DEFAULT IS NULL THEN NULL ELSE c.COLUMN_DEFAULT END) AS 'Default', + c.EXTRA AS 'Extra', + c.PRIVILEGES AS 'Privileges', + c.COLUMN_COMMENT AS 'Comment' +FROM + information_schema.COLUMNS AS c + LEFT JOIN information_schema.CHECK_CONSTRAINTS AS ch ON c.TABLE_NAME = ch.TABLE_NAME + AND c.COLUMN_NAME = ch.CONSTRAINT_NAME +WHERE + c.TABLE_SCHEMA = '%s' + AND c.TABLE_NAME = '%s' + ORDER BY c.ORDINAL_POSITION` +) + +func init() { + tableFieldsSqlByMariadb = gdb.FormatMultiLineSqlToSingle(tableFieldsSqlByMariadb) +} + // TableFields retrieves and returns the fields' information of specified table of current // schema. // @@ -28,16 +54,25 @@ import ( // process restarts. func (d *Driver) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*gdb.TableField, err error) { var ( - result gdb.Result - link gdb.Link - usedSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) + result gdb.Result + link gdb.Link + usedSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) + tableFieldsSql string ) if link, err = d.SlaveLink(usedSchema); err != nil { return nil, err } + dbType := d.GetConfig().Type + switch dbType { + case "mariadb": + tableFieldsSql = fmt.Sprintf(tableFieldsSqlByMariadb, usedSchema, table) + default: + tableFieldsSql = fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table)) + } + result, err = d.DoSelect( ctx, link, - fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table)), + tableFieldsSql, ) if err != nil { return nil, err diff --git a/contrib/drivers/oracle/oracle.go b/contrib/drivers/oracle/oracle.go index 7b174f421..ab079e2d8 100644 --- a/contrib/drivers/oracle/oracle.go +++ b/contrib/drivers/oracle/oracle.go @@ -13,8 +13,6 @@ package oracle import ( "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/text/gregex" - "github.com/gogf/gf/v2/text/gstr" ) // Driver is the driver for oracle database. @@ -32,21 +30,6 @@ func init() { } } -// formatSqlTmp formats sql template string into one line. -func formatSqlTmp(sqlTmp string) string { - var err error - // format sql template string. - sqlTmp, err = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(sqlTmp)) - if err != nil { - panic(err) - } - sqlTmp, err = gregex.ReplaceString(`\s{2,}`, " ", gstr.Trim(sqlTmp)) - if err != nil { - panic(err) - } - return sqlTmp -} - // New create and returns a driver that implements gdb.Driver, which supports operations for Oracle. func New() gdb.Driver { return &Driver{} diff --git a/contrib/drivers/oracle/oracle_do_filter.go b/contrib/drivers/oracle/oracle_do_filter.go index 7c668dfce..4c20c2cfd 100644 --- a/contrib/drivers/oracle/oracle_do_filter.go +++ b/contrib/drivers/oracle/oracle_do_filter.go @@ -27,7 +27,7 @@ SELECT * FROM ( ) func init() { - newSqlReplacementTmp = formatSqlTmp(newSqlReplacementTmp) + newSqlReplacementTmp = gdb.FormatMultiLineSqlToSingle(newSqlReplacementTmp) } // DoFilter deals with the sql string before commits it to underlying sql driver. diff --git a/contrib/drivers/oracle/oracle_table_fields.go b/contrib/drivers/oracle/oracle_table_fields.go index ca8027707..b3c90c922 100644 --- a/contrib/drivers/oracle/oracle_table_fields.go +++ b/contrib/drivers/oracle/oracle_table_fields.go @@ -29,7 +29,7 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID ) func init() { - tableFieldsSqlTmp = formatSqlTmp(tableFieldsSqlTmp) + tableFieldsSqlTmp = gdb.FormatMultiLineSqlToSingle(tableFieldsSqlTmp) } // TableFields retrieves and returns the fields' information of specified table of current schema. diff --git a/contrib/drivers/pgsql/pgsql.go b/contrib/drivers/pgsql/pgsql.go index e863d213a..b7a18a810 100644 --- a/contrib/drivers/pgsql/pgsql.go +++ b/contrib/drivers/pgsql/pgsql.go @@ -16,8 +16,6 @@ import ( "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/os/gctx" - "github.com/gogf/gf/v2/text/gregex" - "github.com/gogf/gf/v2/text/gstr" ) // Driver is the driver for postgresql database. @@ -37,21 +35,6 @@ func init() { } } -// formatSqlTmp formats sql template string into one line. -func formatSqlTmp(sqlTmp string) string { - var err error - // format sql template string. - sqlTmp, err = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(sqlTmp)) - if err != nil { - panic(err) - } - sqlTmp, err = gregex.ReplaceString(`\s{2,}`, " ", gstr.Trim(sqlTmp)) - if err != nil { - panic(err) - } - return sqlTmp -} - // New create and returns a driver that implements gdb.Driver, which supports operations for PostgreSql. func New() gdb.Driver { return &Driver{} diff --git a/contrib/drivers/pgsql/pgsql_table_fields.go b/contrib/drivers/pgsql/pgsql_table_fields.go index ed64b825c..83b344aba 100644 --- a/contrib/drivers/pgsql/pgsql_table_fields.go +++ b/contrib/drivers/pgsql/pgsql_table_fields.go @@ -32,7 +32,7 @@ ORDER BY a.attnum` ) func init() { - tableFieldsSqlTmp = formatSqlTmp(tableFieldsSqlTmp) + tableFieldsSqlTmp = gdb.FormatMultiLineSqlToSingle(tableFieldsSqlTmp) } // TableFields retrieves and returns the fields' information of specified table of current schema. diff --git a/contrib/drivers/pgsql/pgsql_tables.go b/contrib/drivers/pgsql/pgsql_tables.go index 297f7c4cc..32f2c51a2 100644 --- a/contrib/drivers/pgsql/pgsql_tables.go +++ b/contrib/drivers/pgsql/pgsql_tables.go @@ -35,7 +35,7 @@ ORDER BY ) func init() { - tablesSqlTmp = formatSqlTmp(tablesSqlTmp) + tablesSqlTmp = gdb.FormatMultiLineSqlToSingle(tablesSqlTmp) } // Tables retrieves and returns the tables of current schema. diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 149d2afc3..4c3f08740 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -30,7 +30,7 @@ type ConfigNode struct { User string `json:"user"` // Authentication username. Pass string `json:"pass"` // Authentication password. Name string `json:"name"` // Default used database name. - Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle. + Type string `json:"type"` // Database type: mysql, mariadb, sqlite, mssql, pgsql, oracle, clickhouse, dm. Link string `json:"link"` // (Optional) Custom link information for all configuration in one single string. Extra string `json:"extra"` // (Optional) Extra configuration according the registered third-party database driver. Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave. diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index b3bd2b499..411d506c2 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -929,3 +929,18 @@ func FormatSqlWithArgs(sql string, args []interface{}) string { }) return newQuery } + +// FormatMultiLineSqlToSingle formats sql template string into one line. +func FormatMultiLineSqlToSingle(sqlTmp string) string { + var err error + // format sql template string. + sqlTmp, err = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(sqlTmp)) + if err != nil { + panic(err) + } + sqlTmp, err = gregex.ReplaceString(`\s{2,}`, " ", gstr.Trim(sqlTmp)) + if err != nil { + panic(err) + } + return sqlTmp +}