Skip to content

Commit

Permalink
fix(database/gdb): remove support of Array/Value/Count operations for…
Browse files Browse the repository at this point in the history
… result that has multiple record fields (#3839)
  • Loading branch information
gqcn authored Oct 8, 2024
1 parent 7cbc9e8 commit 91884e7
Show file tree
Hide file tree
Showing 20 changed files with 239 additions and 65 deletions.
3 changes: 2 additions & 1 deletion contrib/drivers/mssql/mssql.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ type Driver struct {
}

const (
quoteChar = `"`
rowNumberAliasForSelect = `ROW_NUMBER__`
quoteChar = `"`
)

func init() {
Expand Down
29 changes: 29 additions & 0 deletions contrib/drivers/mssql/mssql_do_commit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package mssql

import (
"context"

"github.com/gogf/gf/v2/database/gdb"
)

// DoCommit commits current sql and arguments to underlying sql driver.
func (d *Driver) DoCommit(ctx context.Context, in gdb.DoCommitInput) (out gdb.DoCommitOutput, err error) {
out, err = d.Core.DoCommit(ctx, in)
if err != nil {
return
}
if len(out.Records) > 0 {
// remove auto added field.
for i, record := range out.Records {
delete(record, rowNumberAliasForSelect)
out.Records[i] = record
}
}
return
}
4 changes: 2 additions & 2 deletions contrib/drivers/mssql/mssql_do_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ var (
orderBySqlTmp = `SELECT %s %s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY`
withoutOrderBySqlTmp = `SELECT %s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY`
selectWithOrderSqlTmp = `
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_
WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROW_NUMBER__, %s ) as TMP_
WHERE TMP_.ROW_NUMBER__ > %d AND TMP_.ROW_NUMBER__ <= %d
`
)

Expand Down
4 changes: 3 additions & 1 deletion contrib/drivers/mysql/mysql_do_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
)

// DoFilter handles the sql before posts it to database.
func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
func (d *Driver) DoFilter(
ctx context.Context, link gdb.Link, sql string, args []interface{},
) (newSql string, newArgs []interface{}, err error) {
return d.Core.DoFilter(ctx, link, sql, args)
}
67 changes: 67 additions & 0 deletions contrib/drivers/mysql/mysql_z_unit_issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1278,3 +1278,70 @@ func Test_Issue3754(t *testing.T) {
t.Assert(oneDeleteUnscoped["update_at"].String(), "")
})
}

// https://github.com/gogf/gf/issues/3626
func Test_Issue3626(t *testing.T) {
table := "issue3626"
array := gstr.SplitAndTrim(gtest.DataContent(`issue3626.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
}
}
defer dropTable(table)

// Insert.
gtest.C(t, func(t *gtest.T) {
dataInsert := g.Map{
"id": 1,
"name": "name_1",
}
r, err := db.Model(table).Data(dataInsert).Insert()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

oneInsert, err := db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(oneInsert["id"].Int(), 1)
t.Assert(oneInsert["name"].String(), "name_1")
})

var (
cacheKey = guid.S()
cacheFunc = func(duration time.Duration) gdb.HookHandler {
return gdb.HookHandler{
Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
get, err := db.GetCache().Get(ctx, cacheKey)
if err == nil && !get.IsEmpty() {
err = get.Scan(&result)
if err == nil {
return result, nil
}
}
result, err = in.Next(ctx)
if err != nil {
return nil, err
}
if result == nil || result.Len() < 1 {
result = make(gdb.Result, 0)
}
_ = db.GetCache().Set(ctx, cacheKey, result, duration)
return
},
}
}
)
gtest.C(t, func(t *gtest.T) {
defer db.GetCache().Clear(ctx)
count, err := db.Model(table).Count()
t.AssertNil(err)
t.Assert(count, 1)
count, err = db.Model(table).Hook(cacheFunc(time.Hour)).Count()
t.AssertNil(err)
t.Assert(count, 1)
count, err = db.Model(table).Hook(cacheFunc(time.Hour)).Count()
t.AssertNil(err)
t.Assert(count, 1)
})
}
6 changes: 3 additions & 3 deletions contrib/drivers/mysql/mysql_z_unit_model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ func Test_Model_Value_WithCache(t *testing.T) {
value, err := db.Model(table).Where("id", 1).Cache(gdb.CacheOption{
Duration: time.Second * 10,
Force: false,
}).Value()
}).Value("id")
t.AssertNil(err)
t.Assert(value.Int(), 1)
})
Expand Down Expand Up @@ -2965,13 +2965,13 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) {
// "create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(),

gtest.C(t, func(t *gtest.T) {
value, err := db.Model(table).FieldsEx("Passport, Password, NickName, CreateTime").Where("id", 2).Value()
value, err := db.Model(table).FieldsEx("create_date, Passport, Password, NickName, CreateTime").Where("id", 2).Value()
t.AssertNil(err)
t.Assert(value.Int(), 2)
})

gtest.C(t, func(t *gtest.T) {
value, err := db.Model(table).FieldsEx("ID, Passport, Password, CreateTime").Where("id", 2).Value()
value, err := db.Model(table).FieldsEx("create_date, ID, Passport, Password, CreateTime").Where("id", 2).Value()
t.AssertNil(err)
t.Assert(value.String(), "name_2")
})
Expand Down
5 changes: 5 additions & 0 deletions contrib/drivers/mysql/testdata/issue3626.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE `issue3626` (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3 changes: 2 additions & 1 deletion contrib/drivers/oracle/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ type Driver struct {
}

const (
quoteChar = `"`
rowNumberAliasForSelect = `ROW_NUMBER__`
quoteChar = `"`
)

func init() {
Expand Down
29 changes: 29 additions & 0 deletions contrib/drivers/oracle/oracle_do_commit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package oracle

import (
"context"

"github.com/gogf/gf/v2/database/gdb"
)

// DoCommit commits current sql and arguments to underlying sql driver.
func (d *Driver) DoCommit(ctx context.Context, in gdb.DoCommitInput) (out gdb.DoCommitOutput, err error) {
out, err = d.Core.DoCommit(ctx, in)
if err != nil {
return
}
if len(out.Records) > 0 {
// remove auto added field.
for i, record := range out.Records {
delete(record, rowNumberAliasForSelect)
out.Records[i] = record
}
}
return
}
5 changes: 2 additions & 3 deletions contrib/drivers/oracle/oracle_do_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ import (
var (
newSqlReplacementTmp = `
SELECT * FROM (
SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d
)
WHERE ROWNUM_ > %d
SELECT GFORM.*, ROWNUM ROW_NUMBER__ FROM (%s %s) GFORM WHERE ROWNUM <= %d
) WHERE ROW_NUMBER__ > %d
`
)

Expand Down
10 changes: 7 additions & 3 deletions contrib/drivers/pgsql/pgsql_do_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ func (d *Driver) DoFilter(
// Refer:
// https://github.com/gogf/gf/issues/1537
// https://www.postgresql.org/docs/12/functions-json.html
newSql, err = gregex.ReplaceStringFuncMatch(`(::jsonb([^\w\d]*)\$\d)`, newSql, func(match []string) string {
return fmt.Sprintf(`::jsonb%s?`, match[2])
})
newSql, err = gregex.ReplaceStringFuncMatch(
`(::jsonb([^\w\d]*)\$\d)`,
newSql,
func(match []string) string {
return fmt.Sprintf(`::jsonb%s?`, match[2])
},
)
if err != nil {
return "", nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion contrib/drivers/sqlite/sqlite_do_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import (
)

// DoFilter deals with the sql string before commits it to underlying sql driver.
func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
func (d *Driver) DoFilter(
ctx context.Context, link gdb.Link, sql string, args []interface{},
) (newSql string, newArgs []interface{}, err error) {
// Special insert/ignore operation for sqlite.
switch {
case gstr.HasPrefix(sql, gdb.InsertOperationIgnore):
Expand Down
4 changes: 3 additions & 1 deletion contrib/drivers/sqlitecgo/sqlitecgo_do_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import (
)

// DoFilter deals with the sql string before commits it to underlying sql driver.
func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
func (d *Driver) DoFilter(
ctx context.Context, link gdb.Link, sql string, args []interface{},
) (newSql string, newArgs []interface{}, err error) {
// Special insert/ignore operation for sqlite.
switch {
case gstr.HasPrefix(sql, gdb.InsertOperationIgnore):
Expand Down
6 changes: 5 additions & 1 deletion database/gdb/gdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,8 @@ func Instance(name ...string) (db DB, err error) {
// getConfigNodeByGroup calculates and returns a configuration node of given group. It
// calculates the value internally using weight algorithm for load balance.
//
// The returned node is a clone of configuration node, which is safe for later modification.
//
// The parameter `master` specifies whether retrieving a master node, or else a slave node
// if master-slave configured.
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
Expand Down Expand Up @@ -674,6 +676,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
}

// getConfigNodeByWeight calculates the configuration weights and randomly returns a node.
// The returned node is a clone of configuration node, which is safe for later modification.
//
// Calculation algorithm brief:
// 1. If we have 2 nodes, and their weights are both 1, then the weight range is [0, 199];
Expand Down Expand Up @@ -729,6 +732,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
configs.RLock()
defer configs.RUnlock()
// Value COPY for node.
// The returned node is a clone of configuration node, which is safe for later modification.
node, err = getConfigNodeByGroup(c.group, master)
if err != nil {
return nil, err
Expand Down Expand Up @@ -777,7 +781,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
}
return sqlDb
}
// it here uses node value not pointer as the cache key, in case of oracle ORA-12516 error.
// it here uses NODE VALUE not pointer as the cache key, in case of oracle ORA-12516 error.
instanceValue = c.links.GetOrSetFuncLock(*node, instanceCacheFunc)
)
if instanceValue != nil && sqlDb == nil {
Expand Down
2 changes: 1 addition & 1 deletion database/gdb/gdb_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ func (c *Core) FilteredLink() string {
//
// Note that this interface implements mainly for workaround for a json infinite loop bug
// of Golang version < v1.14.
func (c Core) MarshalJSON() ([]byte, error) {
func (c *Core) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`%+v`, c)), nil
}

Expand Down
2 changes: 2 additions & 0 deletions database/gdb/gdb_core_ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type internalCtxData struct {
}

// column stores column data in ctx for internal usage purpose.
// Deprecated.
// TODO remove this usage in future.
type internalColumnData struct {
// The first column in result response from database server.
// This attribute is used for Value/Count selection statement purpose,
Expand Down
25 changes: 20 additions & 5 deletions database/gdb/gdb_core_underlying.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter
}

if c.db.GetConfig().QueryTimeout > 0 {
ctx, _ = context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout)
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout)
defer cancelFunc()
}

// Sql filtering.
Expand Down Expand Up @@ -83,6 +85,9 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter
Type: SqlTypeQueryContext,
IsTransaction: link.IsTransaction(),
})
if err != nil {
return nil, err
}
return out.Records, err
}

Expand Down Expand Up @@ -144,13 +149,18 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf
Type: SqlTypeExecContext,
IsTransaction: link.IsTransaction(),
})
if err != nil {
return nil, err
}
return out.Result, err
}

// DoFilter is a hook function, which filters the sql and its arguments before it's committed to underlying driver.
// The parameter `link` specifies the current database connection operation object. You can modify the sql
// string `sql` and its arguments `args` as you wish before they're committed to driver.
func (c *Core) DoFilter(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
func (c *Core) DoFilter(
ctx context.Context, link Link, sql string, args []interface{},
) (newSql string, newArgs []interface{}, err error) {
return sql, args, nil
}

Expand Down Expand Up @@ -322,7 +332,6 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (stmt *Stmt
link = &txLink{tx.GetSqlTX()}
} else {
// Or else it creates one from master node.
var err error
if link, err = c.MasterLink(); err != nil {
return nil, err
}
Expand All @@ -336,7 +345,9 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (stmt *Stmt

if c.db.GetConfig().PrepareTimeout > 0 {
// DO NOT USE cancel function in prepare statement.
ctx, _ = context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
defer cancelFunc()
}

// Link execution.
Expand All @@ -347,6 +358,9 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (stmt *Stmt
Type: SqlTypePrepareContext,
IsTransaction: link.IsTransaction(),
})
if err != nil {
return nil, err
}
return out.Stmt, err
}

Expand Down Expand Up @@ -379,7 +393,7 @@ func (c *Core) FormatUpsert(columns []string, list List, option DoInsertOption)
}
} else {
for _, column := range columns {
// If it's SAVE operation, do not automatically update the creating time.
// If it's `SAVE` operation, do not automatically update the creating time.
if c.IsSoftCreatedFieldName(column) {
continue
}
Expand Down Expand Up @@ -474,6 +488,7 @@ func (c *Core) columnValueToLocalValue(ctx context.Context, value interface{}, c
gconv.String(value),
columnType.ScanType().String(),
), nil
default:
}
}
// Other complex types, especially custom types.
Expand Down
Loading

0 comments on commit 91884e7

Please sign in to comment.