diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index e114aaf04..1bb2f2745 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -38,6 +38,7 @@ type DB interface { // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. // Also see Core.Table. + // Deprecated. Table(tableNameOrStruct ...interface{}) *Model // Model creates and returns a new ORM model from given schema. @@ -51,6 +52,9 @@ type DB interface { // Also see Core.Model. Model(tableNameOrStruct ...interface{}) *Model + // Raw creates and returns a model based on a raw sql not a table. + Raw(rawSql string, args ...interface{}) *Model + // Schema creates and returns a schema. // Also see Core.Schema. Schema(schema string) *Schema diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index fb253b4a4..d793eb8e3 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -254,10 +254,7 @@ func (c *Core) doUnion(unionType int, unions ...*Model) *Model { } composedArgs = append(composedArgs, holderArgs...) } - model := c.db.Model() - model.rawSql = composedSqlStr - model.extraArgs = composedArgs - return model + return c.db.Raw(composedSqlStr, composedArgs...) } // PingMaster pings the master node to check authentication or keeps the connection alive. diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index f4a2f1131..855b1ecae 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -21,7 +21,7 @@ import ( type Model struct { db DB // Underlying DB interface. tx *TX // Underlying TX interface. - rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. + rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. schema string // Custom database schema. linkType int // Mark for operation on master or slave. tablesInit string // Table names when model initialization. @@ -80,11 +80,14 @@ func (c *Core) Table(tableNameQueryOrStruct ...interface{}) *Model { // Model creates and returns a new ORM model from given schema. // The parameter `tableNameQueryOrStruct` can be more than one table names, and also alias name, like: // 1. Model names: -// Model("user") -// Model("user u") -// Model("user, user_detail") -// Model("user u, user_detail ud") -// 2. Model name with alias: Model("user", "u") +// db.Model("user") +// db.Model("user u") +// db.Model("user, user_detail") +// db.Model("user u, user_detail ud") +// 2. Model name with alias: +// db.Model("user", "u") +// 3. Model name with sub-query: +// db.Model("? AS a, ? AS b", subQuery1, subQuery2) func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { var ( tableStr string @@ -132,6 +135,16 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { } } +// Raw creates and returns a model based on a raw sql not a table. +// Example: +// db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) +func (c *Core) Raw(rawSql string, args ...interface{}) *Model { + model := c.Model() + model.rawSql = rawSql + model.extraArgs = args + return model +} + // With creates and returns an ORM model based on meta data of given object. func (c *Core) With(objects ...interface{}) *Model { return c.db.Model().With(objects...) diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index ecfba71d6..669c2d8fd 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -421,7 +421,13 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } // Soft deletion. softDeletingCondition := m.getConditionForSoftDeleting() - if !m.unscoped && softDeletingCondition != "" { + if m.rawSql != "" && conditionWhere != "" { + if gstr.ContainsI(m.rawSql, " WHERE ") { + conditionWhere = " AND " + conditionWhere + } else { + conditionWhere = " WHERE " + conditionWhere + } + } else if !m.unscoped && softDeletingCondition != "" { if conditionWhere == "" { conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) } else { @@ -432,6 +438,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh conditionWhere = " WHERE " + conditionWhere } } + // GROUP BY. if m.groupBy != "" { conditionExtra += " GROUP BY " + m.groupBy diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 3355e7e92..3dc675272 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3696,3 +3696,22 @@ func Test_Model_OnDuplicateEx(t *testing.T) { t.Assert(one["nickname"], "name_1") }) } + +func Test_Model_Raw(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + all, err := db. + Raw(fmt.Sprintf("select * from %s where id in (?)", table), g.Slice{1, 5, 7, 8, 9, 10}). + WhereLT("id", 8). + WhereIn("id", g.Slice{1, 2, 3, 4, 5, 6, 7}). + OrderDesc("id"). + Limit(2). + All() + t.AssertNil(err) + t.Assert(len(all), 2) + t.Assert(all[0]["id"], 7) + t.Assert(all[1]["id"], 5) + }) +} diff --git a/database/gdb/gdb_z_mysql_raw_test.go b/database/gdb/gdb_z_mysql_raw_type_test.go similarity index 100% rename from database/gdb/gdb_z_mysql_raw_test.go rename to database/gdb/gdb_z_mysql_raw_type_test.go