diff --git a/database.go b/database.go index ed9d832..0da11c9 100644 --- a/database.go +++ b/database.go @@ -5,7 +5,7 @@ import ( "database/sql" "sync" - "git.fsdpf.net/go/db/v2/exec" + "git.fsdpf.net/go/db/exec" ) type ( @@ -432,6 +432,9 @@ type ( Tx SQLTx qf exec.QueryFactory qfOnce sync.Once + + // [fix] commands out of sync. Did you run multiple statements at once? + mu sync.Mutex } ) @@ -496,6 +499,9 @@ func (td *TxDatabase) Exec(query string, args ...interface{}) (sql.Result, error // See Database#ExecContext func (td *TxDatabase) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { + td.mu.Lock() + defer td.mu.Unlock() + td.Trace("EXEC", query, args...) return td.Tx.ExecContext(ctx, query, args...) } @@ -518,6 +524,9 @@ func (td *TxDatabase) Query(query string, args ...interface{}) (*sql.Rows, error // See Database#QueryContext func (td *TxDatabase) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { + td.mu.Lock() + defer td.mu.Unlock() + td.Trace("QUERY", query, args...) return td.Tx.QueryContext(ctx, query, args...) } diff --git a/database_example_test.go b/database_example_test.go index 06254e9..43ffa6b 100644 --- a/database_example_test.go +++ b/database_example_test.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - dbv2 "git.fsdpf.net/go/db/v2" + dbv2 "git.fsdpf.net/go/db" ) func ExampleDatabase_Begin() { diff --git a/database_test.go b/database_test.go index b7048b2..477124c 100644 --- a/database_test.go +++ b/database_test.go @@ -6,8 +6,8 @@ import ( "sync" "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/internal/errors" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/internal/errors" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/suite" ) diff --git a/db.go b/db.go index ca58feb..d9de389 100644 --- a/db.go +++ b/db.go @@ -3,8 +3,8 @@ package db import ( "time" - "git.fsdpf.net/go/db/v2/internal/util" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/internal/util" + "git.fsdpf.net/go/db/sqlgen" ) type DialectWrapper struct { diff --git a/db_example_test.go b/db_example_test.go index 213f046..4471b7a 100644 --- a/db_example_test.go +++ b/db_example_test.go @@ -4,16 +4,16 @@ import ( "fmt" "time" - dbv2 "git.fsdpf.net/go/db/v2" - _ "git.fsdpf.net/go/db/v2/dialect/mysql" - _ "git.fsdpf.net/go/db/v2/dialect/postgres" - _ "git.fsdpf.net/go/db/v2/dialect/sqlite3" + dbv2 "git.fsdpf.net/go/db" + _ "git.fsdpf.net/go/db/dialect/mysql" + _ "git.fsdpf.net/go/db/dialect/postgres" + _ "git.fsdpf.net/go/db/dialect/sqlite3" "github.com/DATA-DOG/go-sqlmock" ) // Creating a mysql dataset. Be sure to import the mysql adapter. func ExampleDialect_datasetMysql() { - // import _ "git.fsdpf.net/go/db/v2/dialect/mysql" + // import _ "git.fsdpf.net/go/db/dialect/mysql" d := dbv2.Dialect("mysql") ds := d.From("test").Where(dbv2.Ex{ @@ -34,7 +34,7 @@ func ExampleDialect_datasetMysql() { // Creating a mysql database. Be sure to import the mysql adapter. func ExampleDialect_dbMysql() { - // import _ "git.fsdpf.net/go/db/v2/dialect/mysql" + // import _ "git.fsdpf.net/go/db/dialect/mysql" type item struct { ID int64 `db:"id"` @@ -84,7 +84,7 @@ func ExampleDialect_dbMysql() { // Creating a mysql dataset. Be sure to import the postgres adapter func ExampleDialect_datasetPostgres() { - // import _ "git.fsdpf.net/go/db/v2/dialect/postgres" + // import _ "git.fsdpf.net/go/db/dialect/postgres" d := dbv2.Dialect("postgres") ds := d.From("test").Where(dbv2.Ex{ @@ -105,7 +105,7 @@ func ExampleDialect_datasetPostgres() { // Creating a postgres dataset. Be sure to import the postgres adapter func ExampleDialect_dbPostgres() { - // import _ "git.fsdpf.net/go/db/v2/dialect/postgres" + // import _ "git.fsdpf.net/go/db/dialect/postgres" type item struct { ID int64 `db:"id"` @@ -155,7 +155,7 @@ func ExampleDialect_dbPostgres() { // Creating a mysql dataset. Be sure to import the sqlite3 adapter func ExampleDialect_datasetSqlite3() { - // import _ "git.fsdpf.net/go/db/v2/dialect/sqlite3" + // import _ "git.fsdpf.net/go/db/dialect/sqlite3" d := dbv2.Dialect("sqlite3") ds := d.From("test").Where(dbv2.Ex{ @@ -176,7 +176,7 @@ func ExampleDialect_datasetSqlite3() { // Creating a sqlite3 database. Be sure to import the sqlite3 adapter func ExampleDialect_dbSqlite3() { - // import _ "git.fsdpf.net/go/db/v2/dialect/sqlite3" + // import _ "git.fsdpf.net/go/db/dialect/sqlite3" type item struct { ID int64 `db:"id"` Address string `db:"address"` diff --git a/db_test.go b/db_test.go index b48da49..85bac92 100644 --- a/db_test.go +++ b/db_test.go @@ -3,7 +3,7 @@ package db_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" + dbv2 "git.fsdpf.net/go/db" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/suite" ) diff --git a/delete_dataset.go b/delete_dataset.go index 5685371..d7e1b0f 100644 --- a/delete_dataset.go +++ b/delete_dataset.go @@ -1,10 +1,10 @@ package db import ( - "git.fsdpf.net/go/db/v2/exec" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exec" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) var ErrBadFromArgument = errors.New("unsupported DeleteDataset#From argument, a string or identifier expression is required") @@ -15,6 +15,7 @@ type DeleteDataset struct { isPrepared prepared queryFactory exec.QueryFactory err error + hooks exec.Hooks } // used internally by database to create a database with a specific adapter @@ -87,6 +88,7 @@ func (dd *DeleteDataset) copy(clauses exp.DeleteClauses) *DeleteDataset { isPrepared: dd.isPrepared, queryFactory: dd.queryFactory, err: dd.err, + hooks: dd.hooks, } } @@ -119,14 +121,16 @@ func (dd *DeleteDataset) WithRecursive(name string, subquery exp.Expression) *De // Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased // LiteralExpression: (See Literal) Will use the literal SQL func (dd *DeleteDataset) From(table interface{}) *DeleteDataset { - switch t := table.(type) { - case exp.IdentifierExpression: - return dd.copy(dd.clauses.SetFrom(t)) - case string: - return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t))) - default: - panic(ErrBadFromArgument) - } + return dd.copy(dd.clauses.SetFrom(exp.NewColumnListExpression(table))) + + // switch t := table.(type) { + // case exp.IdentifierExpression: + // return dd.copy(dd.clauses.SetFrom(t)) + // case string: + // return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t))) + // default: + // panic(ErrBadFromArgument) + // } } // Adds a WHERE clause. See examples. @@ -232,8 +236,17 @@ func (dd *DeleteDataset) ReturnsColumns() bool { // db.Delete("test").Exec() // // See Dataset#ToUpdateSQL for arguments -func (dd *DeleteDataset) Executor() exec.QueryExecutor { - return dd.queryFactory.FromSQLBuilder(dd.deleteSQLBuilder()) +func (dd *DeleteDataset) Executor() (executor exec.QueryExecutor) { + if dd.hooks != nil { + dd.SetError(dd.hooks.Before(dd)) + } + executor = dd.queryFactory.FromSQLBuilder(dd.deleteSQLBuilder()) + if dd.hooks != nil { + executor.Hook(func(result interface{}) error { + return dd.hooks.After(dd, result) + }) + } + return executor } func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder { diff --git a/delete_dataset_example_test.go b/delete_dataset_example_test.go index 7960485..0eb185b 100644 --- a/delete_dataset_example_test.go +++ b/delete_dataset_example_test.go @@ -3,8 +3,8 @@ package db_test import ( "fmt" - dbv2 "git.fsdpf.net/go/db/v2" - _ "git.fsdpf.net/go/db/v2/dialect/mysql" + dbv2 "git.fsdpf.net/go/db" + _ "git.fsdpf.net/go/db/dialect/mysql" ) func ExampleDelete() { diff --git a/delete_dataset_test.go b/delete_dataset_test.go index 4b2e4de..4a61005 100644 --- a/delete_dataset_test.go +++ b/delete_dataset_test.go @@ -3,11 +3,11 @@ package db_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/mocks" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/mocks" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" @@ -100,7 +100,7 @@ func (dds *deleteDatasetSuite) TestPrepared() { func (dds *deleteDatasetSuite) TestGetClauses() { ds := dbv2.Delete("test") - ce := exp.NewDeleteClauses().SetFrom(dbv2.I("test")) + ce := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")) dds.Equal(ce, ds.GetClauses()) } @@ -110,12 +110,12 @@ func (dds *deleteDatasetSuite) TestWith() { dds.assertCases( deleteTestCase{ ds: bd.With("test-cte", from), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")). + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")). CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")), }, ) } @@ -126,12 +126,12 @@ func (dds *deleteDatasetSuite) TestWithRecursive() { dds.assertCases( deleteTestCase{ ds: bd.WithRecursive("test-cte", from), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")). + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")). CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from)), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")), }, ) } @@ -141,23 +141,23 @@ func (dds *deleteDatasetSuite) TestFrom_withIdentifier() { dds.assertCases( deleteTestCase{ ds: bd.From("items2"), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items2")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")), }, deleteTestCase{ ds: bd.From(dbv2.C("items2")), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items2")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")), }, deleteTestCase{ ds: bd.From(dbv2.T("items2")), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.T("items2")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")), }, deleteTestCase{ ds: bd.From("schema.table"), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.I("schema.table")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("schema.table")), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")), }, ) @@ -172,19 +172,19 @@ func (dds *deleteDatasetSuite) TestWhere() { deleteTestCase{ ds: bd.Where(dbv2.Ex{"a": 1}), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). WhereAppend(dbv2.Ex{"a": 1}), }, deleteTestCase{ ds: bd.Where(dbv2.Ex{"a": 1}).Where(dbv2.C("b").Eq("c")), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). WhereAppend(dbv2.Ex{"a": 1}). WhereAppend(dbv2.C("b").Eq("c")), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")), }, ) } @@ -195,12 +195,12 @@ func (dds *deleteDatasetSuite) TestClearWhere() { deleteTestCase{ ds: bd.ClearWhere(), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")), + SetFrom(exp.NewColumnListExpression("items")), }, deleteTestCase{ ds: bd, clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). WhereAppend(dbv2.Ex{"a": 1}), }, ) @@ -212,24 +212,24 @@ func (dds *deleteDatasetSuite) TestOrder() { deleteTestCase{ ds: bd.Order(dbv2.C("a").Asc()), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("a").Asc()), }, deleteTestCase{ ds: bd.Order(dbv2.C("a").Asc()).Order(dbv2.C("b").Desc()), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("b").Desc()), }, deleteTestCase{ ds: bd.Order(dbv2.C("a").Asc(), dbv2.C("b").Desc()), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")), }, ) } @@ -240,13 +240,13 @@ func (dds *deleteDatasetSuite) TestOrderAppend() { deleteTestCase{ ds: bd.OrderAppend(dbv2.C("b").Desc()), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()), }, deleteTestCase{ ds: bd, clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("a").Asc()), }, ) @@ -258,13 +258,13 @@ func (dds *deleteDatasetSuite) TestOrderPrepend() { deleteTestCase{ ds: bd.OrderPrepend(dbv2.C("b").Desc()), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("b").Desc(), dbv2.C("a").Asc()), }, deleteTestCase{ ds: bd, clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("a").Asc()), }, ) @@ -275,12 +275,12 @@ func (dds *deleteDatasetSuite) TestClearOrder() { dds.assertCases( deleteTestCase{ ds: bd.ClearOrder(), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")), }, deleteTestCase{ ds: bd, clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetOrder(dbv2.C("a").Asc()), }, ) @@ -292,26 +292,26 @@ func (dds *deleteDatasetSuite) TestLimit() { deleteTestCase{ ds: bd.Limit(10), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("test")). + SetFrom(exp.NewColumnListExpression("test")). SetLimit(uint(10)), }, deleteTestCase{ ds: bd.Limit(0), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")), }, deleteTestCase{ ds: bd.Limit(10).Limit(2), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("test")). + SetFrom(exp.NewColumnListExpression("test")). SetLimit(uint(2)), }, deleteTestCase{ ds: bd.Limit(10).Limit(0), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")), }, ) } @@ -322,18 +322,18 @@ func (dds *deleteDatasetSuite) TestLimitAll() { deleteTestCase{ ds: bd.LimitAll(), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("test")). + SetFrom(exp.NewColumnListExpression("test")). SetLimit(dbv2.L("ALL")), }, deleteTestCase{ ds: bd.Limit(10).LimitAll(), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("test")). + SetFrom(exp.NewColumnListExpression("test")). SetLimit(dbv2.L("ALL")), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")), }, ) } @@ -343,11 +343,11 @@ func (dds *deleteDatasetSuite) TestClearLimit() { dds.assertCases( deleteTestCase{ ds: bd.ClearLimit(), - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")).SetLimit(uint(10)), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")).SetLimit(uint(10)), }, ) } @@ -358,30 +358,30 @@ func (dds *deleteDatasetSuite) TestReturning() { deleteTestCase{ ds: bd.Returning("a"), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetReturning(exp.NewColumnListExpression("a")), }, deleteTestCase{ ds: bd.Returning(), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetReturning(exp.NewColumnListExpression()), }, deleteTestCase{ ds: bd.Returning(nil), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetReturning(exp.NewColumnListExpression()), }, deleteTestCase{ ds: bd.Returning("a").Returning("b"), clauses: exp.NewDeleteClauses(). - SetFrom(dbv2.C("items")). + SetFrom(exp.NewColumnListExpression("items")). SetReturning(exp.NewColumnListExpression("b")), }, deleteTestCase{ ds: bd, - clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), + clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")), }, ) } diff --git a/dialect/mysql/mysql.go b/dialect/mysql/mysql.go index 69c97ea..ac2f89b 100644 --- a/dialect/mysql/mysql.go +++ b/dialect/mysql/mysql.go @@ -1,8 +1,8 @@ package mysql import ( - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" ) func DialectOptions() *db.SQLDialectOptions { @@ -16,11 +16,11 @@ func DialectOptions() *db.SQLDialectOptions { opts.SupportsConflictUpdateWhere = false opts.SupportsInsertIgnoreSyntax = true opts.SupportsConflictTarget = false - opts.SupportsWithCTE = false - opts.SupportsWithCTERecursive = false + opts.SupportsWithCTE = true + opts.SupportsWithCTERecursive = true opts.SupportsDistinctOn = false - opts.SupportsWindowFunction = false - opts.SupportsDeleteTableHint = true + opts.SupportsWindowFunction = true + opts.SupportsDeleteTableHint = false opts.UseFromClauseForMultipleUpdateTables = false diff --git a/dialect/mysql/mysql_dialect_test.go b/dialect/mysql/mysql_dialect_test.go index 2787f89..c79351d 100644 --- a/dialect/mysql/mysql_dialect_test.go +++ b/dialect/mysql/mysql_dialect_test.go @@ -4,8 +4,8 @@ import ( "regexp" "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/dialect/mysql/mysql_test.go b/dialect/mysql/mysql_test.go index 7c003f2..8699c29 100644 --- a/dialect/mysql/mysql_test.go +++ b/dialect/mysql/mysql_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/dialect/mysql" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/dialect/mysql" _ "github.com/go-sql-driver/mysql" "github.com/stretchr/testify/suite" ) diff --git a/dialect/postgres/postgres.go b/dialect/postgres/postgres.go index 92e9d77..965529c 100644 --- a/dialect/postgres/postgres.go +++ b/dialect/postgres/postgres.go @@ -1,6 +1,6 @@ package postgres -import "git.fsdpf.net/go/db/v2" +import "git.fsdpf.net/go/db" func DialectOptions() *db.SQLDialectOptions { do := db.DefaultDialectOptions() diff --git a/dialect/postgres/postgres_test.go b/dialect/postgres/postgres_test.go index dd91f35..cb9b371 100644 --- a/dialect/postgres/postgres_test.go +++ b/dialect/postgres/postgres_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - dbv2 "git.fsdpf.net/go/db/v2" + dbv2 "git.fsdpf.net/go/db" "github.com/lib/pq" "github.com/stretchr/testify/suite" diff --git a/dialect/sqlite3/sqlite3.go b/dialect/sqlite3/sqlite3.go index e2f7e0b..cbbc7ba 100644 --- a/dialect/sqlite3/sqlite3.go +++ b/dialect/sqlite3/sqlite3.go @@ -3,8 +3,8 @@ package sqlite3 import ( "time" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" ) func DialectOptions() *db.SQLDialectOptions { diff --git a/dialect/sqlite3/sqlite3_dialect_test.go b/dialect/sqlite3/sqlite3_dialect_test.go index bfe6495..ee8d7a8 100644 --- a/dialect/sqlite3/sqlite3_dialect_test.go +++ b/dialect/sqlite3/sqlite3_dialect_test.go @@ -4,8 +4,8 @@ import ( "regexp" "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/dialect/sqlite3/sqlite3_test.go b/dialect/sqlite3/sqlite3_test.go index 3aa207d..03cf313 100644 --- a/dialect/sqlite3/sqlite3_test.go +++ b/dialect/sqlite3/sqlite3_test.go @@ -8,9 +8,9 @@ import ( _ "github.com/mattn/go-sqlite3" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/dialect/mysql" - "git.fsdpf.net/go/db/v2/dialect/sqlite3" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/dialect/mysql" + "git.fsdpf.net/go/db/dialect/sqlite3" "github.com/stretchr/testify/suite" ) diff --git a/dialect/sqlserver/sqlserver.go b/dialect/sqlserver/sqlserver.go index 9e28702..125571e 100644 --- a/dialect/sqlserver/sqlserver.go +++ b/dialect/sqlserver/sqlserver.go @@ -1,9 +1,9 @@ package sqlserver import ( - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/sqlgen" ) func DialectOptions() *db.SQLDialectOptions { diff --git a/dialect/sqlserver/sqlserver_dialect_test.go b/dialect/sqlserver/sqlserver_dialect_test.go index da0cd58..4781664 100644 --- a/dialect/sqlserver/sqlserver_dialect_test.go +++ b/dialect/sqlserver/sqlserver_dialect_test.go @@ -3,8 +3,8 @@ package sqlserver_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/dialect/sqlserver/sqlserver_test.go b/dialect/sqlserver/sqlserver_test.go index f934cb9..72296c9 100644 --- a/dialect/sqlserver/sqlserver_test.go +++ b/dialect/sqlserver/sqlserver_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "git.fsdpf.net/go/db/v2/dialect/mysql" + "git.fsdpf.net/go/db/dialect/mysql" - dbv2 "git.fsdpf.net/go/db/v2" - _ "git.fsdpf.net/go/db/v2/dialect/sqlserver" + dbv2 "git.fsdpf.net/go/db" + _ "git.fsdpf.net/go/db/dialect/sqlserver" _ "github.com/denisenkom/go-mssqldb" "github.com/stretchr/testify/suite" ) diff --git a/engine/engine.go b/engine/engine.go index a4e4d8a..ec3e12a 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -1,6 +1,7 @@ package engine import ( + "context" "database/sql" "fmt" @@ -8,15 +9,16 @@ import ( _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" + "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3" - _ "git.fsdpf.net/go/db/v2/dialect/mysql" - _ "git.fsdpf.net/go/db/v2/dialect/postgres" - _ "git.fsdpf.net/go/db/v2/dialect/sqlite3" - _ "git.fsdpf.net/go/db/v2/dialect/sqlserver" + _ "git.fsdpf.net/go/db/dialect/mysql" + _ "git.fsdpf.net/go/db/dialect/postgres" + _ "git.fsdpf.net/go/db/dialect/sqlite3" + _ "git.fsdpf.net/go/db/dialect/sqlserver" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/internal/errors" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/internal/errors" ) type Engine struct { @@ -50,12 +52,31 @@ func (e Engine) Connection(name string) *db.Database { return db.New(cfg.Driver, _db) } -func (e Engine) MakeConnection(cfg DBConfig) *sql.DB { +func (e Engine) MakeConnection(cfg DBConfig) (db *sql.DB) { dsn := cfg.ToDSN() switch cfg.Driver { case "mysql": case "sqlite3": + defer func() { + if r := recover(); r != nil { + panic(r) + } + if cfg.SQLite.Raw == nil { + return + } + conn, err := db.Conn(context.Background()) + if err != nil { + panic(err) + } + err = conn.Raw(func(driverConn any) error { + sqliteConn := driverConn.(*sqlite3.SQLiteConn) + return cfg.SQLite.Raw(sqliteConn) + }) + if err != nil { + panic(err) + } + }() case "sqlserver": case "postgres": if url, err := pq.ParseURL(dsn); err == nil { @@ -80,10 +101,18 @@ func (e Engine) MakeConnection(cfg DBConfig) *sql.DB { return db } -func Open(cfgs map[string]DBConfig) *Engine { +func Open(cfgs map[string]DBConfig) Engine { for n, cfg := range cfgs { _engine.configs[n] = cfg } - return _engine + return *_engine +} + +func Mock(cfgs map[string]MockDBConfig) Engine { + for k, cfg := range cfgs { + _engine.dbs[k] = cfg.Mock + _engine.configs[k] = DBConfig{Driver: cfg.Driver} + } + return *_engine } diff --git a/engine/engine_config.go b/engine/engine_config.go index 827ec9d..966af3c 100644 --- a/engine/engine_config.go +++ b/engine/engine_config.go @@ -1,11 +1,19 @@ package engine import ( + "database/sql" "fmt" "net/url" "time" + + "github.com/mattn/go-sqlite3" ) +type MockDBConfig struct { + Driver string + Mock *sql.DB +} + // DBConfig 数据库配置结构体 type DBConfig struct { Driver string @@ -48,6 +56,7 @@ type DBConfig struct { Synchronous int Cache string BusyTimeout int + Raw func(*sqlite3.SQLiteConn) error } SQLServer struct { @@ -379,6 +388,16 @@ func WithSQLiteFile(file string) Option { } } +// SQLite 注册函数 +func WithSQLiteRaw(raw func(*sqlite3.SQLiteConn) error) Option { + return func(c *DBConfig) { + if c.Driver != "sqlite3" { + panic("WithSQLiteRegFn is only valid for sqlite3 driver") + } + c.SQLite.Raw = raw + } +} + func WithSQLiteJournal(journal string) Option { return func(c *DBConfig) { if c.Driver != "sqlite3" { diff --git a/engine/engine_config_test.go b/engine/engine_config_test.go index 388c297..b9f13ad 100644 --- a/engine/engine_config_test.go +++ b/engine/engine_config_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "git.fsdpf.net/go/db/v2/engine" + "git.fsdpf.net/go/db/engine" ) // TestMySQLConfig 测试 MySQL 驱动的配置 diff --git a/exec/query_executor.go b/exec/query_executor.go index e83885b..61b5ddd 100644 --- a/exec/query_executor.go +++ b/exec/query_executor.go @@ -5,12 +5,13 @@ import ( gsql "database/sql" "reflect" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/util" ) type ( QueryExecutor struct { + hook func(result interface{}) error de DbExecutor err error query string @@ -19,6 +20,8 @@ type ( ) var ( + errUnsupportedScanMapType = errors.New("type must be a pointer to a map when scanning into a map") + errUnsupportedScanMapsType = errors.New("type must be a pointer to a slice when scanning into a map") errUnsupportedScanStructType = errors.New("type must be a pointer to a struct when scanning into a struct") errUnsupportedScanStructsType = errors.New("type must be a pointer to a slice when scanning into structs") errUnsupportedScanValsType = errors.New("type must be a pointer to a slice when scanning into vals") @@ -38,22 +41,34 @@ func (q QueryExecutor) Exec() (gsql.Result, error) { return q.ExecContext(context.Background()) } -func (q QueryExecutor) ExecContext(ctx context.Context) (gsql.Result, error) { +func (q QueryExecutor) ExecContext(ctx context.Context) (result gsql.Result, err error) { if q.err != nil { return nil, q.err } - return q.de.ExecContext(ctx, q.query, q.args...) + result, err = q.de.ExecContext(ctx, q.query, q.args...) + defer func() { + if err == nil && q.hook != nil { + err = q.hook(result) + } + }() + return result, err } func (q QueryExecutor) Query() (*gsql.Rows, error) { return q.QueryContext(context.Background()) } -func (q QueryExecutor) QueryContext(ctx context.Context) (*gsql.Rows, error) { +func (q QueryExecutor) QueryContext(ctx context.Context) (result *gsql.Rows, err error) { if q.err != nil { return nil, q.err } - return q.de.QueryContext(ctx, q.query, q.args...) + result, err = q.de.QueryContext(ctx, q.query, q.args...) + defer func() { + if err == nil && q.hook != nil { + err = q.hook(result) + } + }() + return result, err } // This will execute the SQL and append results to the slice @@ -238,6 +253,35 @@ func (q QueryExecutor) ScanValContext(ctx context.Context, i interface{}) (bool, return false, scanner.Err() } +func (q QueryExecutor) GetRecord() (map[string]any, error) { + return q.GetRecordContext(context.Background()) +} + +func (q QueryExecutor) GetRecordContext(ctx context.Context) (map[string]any, error) { + scanner, err := q.ScannerContext(ctx) + if err != nil { + return nil, err + } + defer func() { _ = scanner.Close() }() + if scanner.Next() { + return scanner.GetRecord() + } + return nil, scanner.Err() +} + +func (q QueryExecutor) GetRecords() ([]map[string]any, error) { + return q.GetRecordsContext(context.Background()) +} + +func (q QueryExecutor) GetRecordsContext(ctx context.Context) ([]map[string]any, error) { + scanner, err := q.ScannerContext(ctx) + if err != nil { + return nil, err + } + defer func() { _ = scanner.Close() }() + return scanner.GetRecords() +} + // Scanner will return a Scanner that can be used for manually scanning rows. func (q QueryExecutor) Scanner() (Scanner, error) { return q.ScannerContext(context.Background()) @@ -251,3 +295,7 @@ func (q QueryExecutor) ScannerContext(ctx context.Context) (Scanner, error) { } return NewScanner(rows), nil } + +func (q *QueryExecutor) Hook(hook func(dataset interface{}) error) { + q.hook = hook +} diff --git a/exec/query_factory.go b/exec/query_factory.go index d900872..a0928c4 100644 --- a/exec/query_factory.go +++ b/exec/query_factory.go @@ -4,7 +4,7 @@ import ( "context" "database/sql" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/internal/sb" ) type ( diff --git a/exec/query_hooks.go b/exec/query_hooks.go new file mode 100644 index 0000000..a8812d3 --- /dev/null +++ b/exec/query_hooks.go @@ -0,0 +1,7 @@ +package exec + +// Hooks 钩子实例 +type Hooks interface { + Before(dataset interface{}) error + After(dataset interface{}, result interface{}) error +} diff --git a/exec/scanner.go b/exec/scanner.go index 4489a45..1a7d430 100644 --- a/exec/scanner.go +++ b/exec/scanner.go @@ -2,11 +2,11 @@ package exec import ( "database/sql" + "encoding/json" "reflect" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/util" ) type ( @@ -17,6 +17,8 @@ type ( ScanStructs(i interface{}) error ScanVal(i interface{}) error ScanVals(i interface{}) error + GetRecord() (map[string]any, error) + GetRecords() ([]map[string]any, error) Close() error Err() error } @@ -49,6 +51,86 @@ func (s *scanner) Err() error { return s.rows.Err() } +func (s *scanner) GetRecords() ([]map[string]any, error) { + records := []map[string]any{} + for s.Next() { + row, err := s.GetRecord() + if err != nil { + return records, err + } + records = append(records, row) + } + + return records, s.Err() +} + +func (s *scanner) GetRecord() (record map[string]any, err error) { + // Setup columns, but only once. + if s.columns == nil || s.columnMap == nil { + colsType, err := s.rows.ColumnTypes() + if err != nil { + return nil, err + } + + s.columns = make([]string, len(colsType)) + s.columnMap = util.ColumnMap{} + for i := range colsType { + s.columns[i] = colsType[i].Name() + + typ := colsType[i].ScanType() + + // 强制日期时间为字符串 + if typ == reflect.TypeOf(sql.NullTime{}) { + typ = reflect.TypeOf(sql.Null[[]uint8]{}) + } + + s.columnMap[colsType[i].Name()] = util.ColumnData{ + ColumnName: colsType[i].Name(), + GoType: typ, + } + } + } + + scans := make([]interface{}, len(s.columns)) + + for i, col := range s.columns { + scans[i] = reflect.New(s.columnMap[col].GoType).Interface() + } + + if err := s.rows.Scan(scans...); err != nil { + return nil, err + } + + record = make(map[string]interface{}, len(s.columns)) + for i, col := range s.columns { + var vv any + switch v := scans[i].(type) { + case *sql.Null[[]uint8]: // 强制日期时间为字符串 + if vv, err = v.Value(); err == nil && vv != nil { + vv = string(vv.([]uint8)) + } + case *sql.RawBytes: + if len(*v) > 1 { + if rune((*v)[0]) == rune('[') || rune((*v)[0]) == rune('{') { + err = json.Unmarshal(*v, &vv) + } else { + vv = string(*v) + } + } else { + vv = string(*v) + } + default: + vv = reflect.Indirect(reflect.ValueOf(v)).Interface() + } + if err != nil { + return + } + record[col] = vv + } + + return record, s.Err() +} + // ScanStruct will scan the current row into i. func (s *scanner) ScanStruct(i interface{}) error { // Setup columnMap and columns, but only once. @@ -63,35 +145,37 @@ func (s *scanner) ScanStruct(i interface{}) error { return err } + // 补全未知字段类型 + if len(cols) != len(cm) { + colTypes, err := s.rows.ColumnTypes() + if err != nil { + return err + } + for _, t := range colTypes { + if _, ok := cm[t.Name()]; !ok { + cm[t.Name()] = util.ColumnData{ + ColumnName: t.Name(), + GoType: t.ScanType(), + } + } + } + } + s.columnMap = cm s.columns = cols } - scans := make([]interface{}, 0, len(s.columns)) - for _, col := range s.columns { - data, ok := s.columnMap[col] + scans, err := createColumnScans(s.columns, s.columnMap) - if !ok { - return unableToFindFieldError(col) - } - // 处理 converting NULL to string is unsupported - // 前面将 string 和 int 类型转为了 *string 和 *int - switch data.GoType.Kind() { - case reflect.String, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Float32, reflect.Float64, - reflect.Bool: - scans = append(scans, reflect.New(reflect.PointerTo(data.GoType)).Interface()) - default: - scans = append(scans, reflect.New(data.GoType).Interface()) - } + if err != nil { + return err } if err := s.rows.Scan(scans...); err != nil { return err } - record := exp.Record{} + record := map[string]interface{}{} for index, col := range s.columns { record[col] = scans[index] } @@ -175,3 +259,30 @@ func checkScanValsTarget(i interface{}) (reflect.Value, error) { } return val, nil } + +func createColumnScans(cols []string, cm util.ColumnMap) (scans []interface{}, err error) { + scans = make([]interface{}, 0, len(cols)) + + for _, col := range cols { + data, ok := cm[col] + + if !ok { + return scans, unableToFindFieldError(col) + } + // 处理 converting NULL to string is unsupported + // 前面将 string 和 int 类型转为了 *string 和 *int + switch data.GoType.Kind() { + case reflect.String, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Float32, reflect.Float64, + reflect.Bool: + scans = append(scans, reflect.New(reflect.PointerTo(data.GoType)).Interface()) + case reflect.Map, reflect.Slice, reflect.Struct: + scans = append(scans, reflect.New(reflect.PointerTo(reflect.TypeOf(json.RawMessage{}))).Interface()) + default: + scans = append(scans, reflect.New(data.GoType).Interface()) + } + } + + return scans, nil +} diff --git a/exec/scanner_internal_test.go b/exec/scanner_internal_test.go index 0a73b1f..a2bf487 100644 --- a/exec/scanner_internal_test.go +++ b/exec/scanner_internal_test.go @@ -3,6 +3,7 @@ package exec import ( "testing" + "git.fsdpf.net/go/db/exp" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/suite" ) @@ -67,3 +68,27 @@ func (s *scannerSuite) TestScanVals() { s.Require().NoError(err) s.Require().ElementsMatch([]int{1, 2}, result) } + +func (s *scannerSuite) TestGetRecords() { + db, mock, err := sqlmock.New() + s.Require().NoError(err) + + mock.ExpectQuery("SELECT \\* FROM `items`"). + WithArgs(). + WillReturnRows( + sqlmock.NewRows([]string{"address", "name"}). + AddRow("111 Test Addr", "Test1"). + AddRow("111 Test Addr", "Test1"), + ) + + rows, err := db.Query("SELECT \\* FROM `items`") + s.Require().NoError(err) + + result, err := NewScanner(rows).GetRecords() + s.Require().NoError(err) + + s.Equal([]exp.Record{ + {"address": "111 Test Addr", "name": "Test1"}, + {"address": "111 Test Addr", "name": "Test1"}, + }, result) +} diff --git a/exp/alias_test.go b/exp/alias_test.go index 571ed47..d2dd8a6 100644 --- a/exp/alias_test.go +++ b/exp/alias_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/bitwise_test.go b/exp/bitwise_test.go index b24347c..46c049d 100644 --- a/exp/bitwise_test.go +++ b/exp/bitwise_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/case_test.go b/exp/case_test.go index 0cb23b2..1537f6b 100644 --- a/exp/case_test.go +++ b/exp/case_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/cast_test.go b/exp/cast_test.go index 212c51d..e2e82f2 100644 --- a/exp/cast_test.go +++ b/exp/cast_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/col.go b/exp/col.go index 4a88315..4c039a5 100644 --- a/exp/col.go +++ b/exp/col.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/internal/util" ) type columnList struct { @@ -23,7 +23,8 @@ func NewColumnListExpression(vals ...interface{}) ColumnListExpression { case Expression: cols = append(cols, t) default: - _, valKind := util.GetTypeInfo(val, reflect.Indirect(reflect.ValueOf(val))) + refVal := reflect.Indirect(reflect.ValueOf(val)) + _, valKind := util.GetTypeInfo(val, refVal) if valKind == reflect.Struct { cm, err := util.GetColumnMap(val) @@ -39,6 +40,12 @@ func NewColumnListExpression(vals ...interface{}) ColumnListExpression { } cols = append(cols, sc) } + } else if refVal.Kind() == reflect.Slice { + items := make([]any, refVal.Len()) + for i := 0; i < refVal.Len(); i++ { + items[i] = refVal.Index(i).Interface() + } + return NewColumnListExpression(items...) } else { panic(fmt.Sprintf("Cannot create expression from %+v", val)) } diff --git a/exp/delete_clauses.go b/exp/delete_clauses.go index 25af851..c9f5fef 100644 --- a/exp/delete_clauses.go +++ b/exp/delete_clauses.go @@ -8,8 +8,8 @@ type ( CommonTables() []CommonTableExpression CommonTablesAppend(cte CommonTableExpression) DeleteClauses - From() IdentifierExpression - SetFrom(table IdentifierExpression) DeleteClauses + From() ColumnListExpression + SetFrom(table ColumnListExpression) DeleteClauses Where() ExpressionList ClearWhere() DeleteClauses @@ -33,7 +33,7 @@ type ( } deleteClauses struct { commonTables []CommonTableExpression - from IdentifierExpression + from ColumnListExpression where ExpressionList order ColumnListExpression limit interface{} @@ -71,11 +71,11 @@ func (dc *deleteClauses) CommonTablesAppend(cte CommonTableExpression) DeleteCla return ret } -func (dc *deleteClauses) From() IdentifierExpression { +func (dc *deleteClauses) From() ColumnListExpression { return dc.from } -func (dc *deleteClauses) SetFrom(table IdentifierExpression) DeleteClauses { +func (dc *deleteClauses) SetFrom(table ColumnListExpression) DeleteClauses { ret := dc.clone() ret.from = table return ret diff --git a/exp/delete_clauses_test.go b/exp/delete_clauses_test.go index 0a04f6f..ce72755 100644 --- a/exp/delete_clauses_test.go +++ b/exp/delete_clauses_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) @@ -17,7 +17,7 @@ func TestDeleteClausesSuite(t *testing.T) { func (dcs *deleteClausesSuite) TestHasFrom() { c := exp.NewDeleteClauses() - c2 := c.SetFrom(exp.NewIdentifierExpression("", "test", "")) + c2 := c.SetFrom(exp.NewColumnListExpression("test")) dcs.False(c.HasFrom()) @@ -26,7 +26,7 @@ func (dcs *deleteClausesSuite) TestHasFrom() { func (dcs *deleteClausesSuite) TestFrom() { c := exp.NewDeleteClauses() - ti := exp.NewIdentifierExpression("", "a", "") + ti := exp.NewColumnListExpression("a") c2 := c.SetFrom(ti) dcs.Nil(c.From()) @@ -36,7 +36,7 @@ func (dcs *deleteClausesSuite) TestFrom() { func (dcs *deleteClausesSuite) TestSetFrom() { c := exp.NewDeleteClauses() - ti := exp.NewIdentifierExpression("", "a", "") + ti := exp.NewColumnListExpression("a") c2 := c.SetFrom(ti) dcs.Nil(c.From()) diff --git a/exp/exp.go b/exp/exp.go index 83b533c..7748c01 100644 --- a/exp/exp.go +++ b/exp/exp.go @@ -3,7 +3,7 @@ package exp import ( "fmt" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/internal/sb" ) // Behaviors diff --git a/exp/exp_map.go b/exp/exp_map.go index 38fc8e3..8b2db24 100644 --- a/exp/exp_map.go +++ b/exp/exp_map.go @@ -4,7 +4,7 @@ import ( "sort" "strings" - "git.fsdpf.net/go/db/v2/internal/errors" + "git.fsdpf.net/go/db/internal/errors" ) type ( diff --git a/exp/exp_map_test.go b/exp/exp_map_test.go index 938a950..487d494 100644 --- a/exp/exp_map_test.go +++ b/exp/exp_map_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/func_test.go b/exp/func_test.go index c95ec75..46fc351 100644 --- a/exp/func_test.go +++ b/exp/func_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/ident_test.go b/exp/ident_test.go index 0b64c9e..90cdeb2 100644 --- a/exp/ident_test.go +++ b/exp/ident_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/insert.go b/exp/insert.go index 8a03663..b3aaabc 100644 --- a/exp/insert.go +++ b/exp/insert.go @@ -4,8 +4,8 @@ import ( "reflect" "sort" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/util" ) type ( diff --git a/exp/insert_clauses_test.go b/exp/insert_clauses_test.go index 77a673b..8dd79bf 100644 --- a/exp/insert_clauses_test.go +++ b/exp/insert_clauses_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/insert_test.go b/exp/insert_test.go index a073e72..4bbf3cb 100644 --- a/exp/insert_test.go +++ b/exp/insert_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/lateral_test.go b/exp/lateral_test.go index ffec863..7ef75f5 100644 --- a/exp/lateral_test.go +++ b/exp/lateral_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/literal_test.go b/exp/literal_test.go index 29db7f5..7073133 100644 --- a/exp/literal_test.go +++ b/exp/literal_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/record.go b/exp/record.go index 6a2d55f..d1be89a 100644 --- a/exp/record.go +++ b/exp/record.go @@ -4,7 +4,7 @@ import ( "reflect" "sort" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/internal/util" ) // Alternative to writing map[string]interface{}. Can be used for Inserts, Updates or Deletes diff --git a/exp/select_clauses.go b/exp/select_clauses.go index f802a88..c565175 100644 --- a/exp/select_clauses.go +++ b/exp/select_clauses.go @@ -22,6 +22,7 @@ type ( Joins() JoinExpressions JoinsAppend(jc JoinExpression) SelectClauses + JoinsPrepend(jc JoinExpression) SelectClauses Where() ExpressionList ClearWhere() SelectClauses @@ -41,6 +42,7 @@ type ( GroupBy() ColumnListExpression SetGroupBy(cl ColumnListExpression) SelectClauses GroupByAppend(cl ColumnListExpression) SelectClauses + ClearGroupBy() SelectClauses Limit() interface{} HasLimit() bool @@ -197,6 +199,12 @@ func (c *selectClauses) JoinsAppend(jc JoinExpression) SelectClauses { return ret } +func (c *selectClauses) JoinsPrepend(jc JoinExpression) SelectClauses { + ret := c.clone() + ret.joins = append(JoinExpressions{jc}, ret.joins...) + return ret +} + func (c *selectClauses) Where() ExpressionList { return c.where } @@ -310,6 +318,12 @@ func (c *selectClauses) SetGroupBy(cl ColumnListExpression) SelectClauses { return ret } +func (c *selectClauses) ClearGroupBy() SelectClauses { + ret := c.clone() + ret.groupBy = nil + return ret +} + func (c *selectClauses) Limit() interface{} { return c.limit } diff --git a/exp/select_clauses_test.go b/exp/select_clauses_test.go index b3a5490..d102922 100644 --- a/exp/select_clauses_test.go +++ b/exp/select_clauses_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/truncate_clauses_test.go b/exp/truncate_clauses_test.go index 13bdc4e..f32a33e 100644 --- a/exp/truncate_clauses_test.go +++ b/exp/truncate_clauses_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/update.go b/exp/update.go index 11b7ee8..418c638 100644 --- a/exp/update.go +++ b/exp/update.go @@ -4,8 +4,8 @@ import ( "reflect" "sort" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/util" ) type ( diff --git a/exp/update_clauses_test.go b/exp/update_clauses_test.go index c032d96..1ff236f 100644 --- a/exp/update_clauses_test.go +++ b/exp/update_clauses_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/update_test.go b/exp/update_test.go index 8aa9eb9..95e484a 100644 --- a/exp/update_test.go +++ b/exp/update_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/window_func_test.go b/exp/window_func_test.go index 914def2..c44d53b 100644 --- a/exp/window_func_test.go +++ b/exp/window_func_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/exp/window_test.go b/exp/window_test.go index ac5eb71..46f78a7 100644 --- a/exp/window_test.go +++ b/exp/window_test.go @@ -3,7 +3,7 @@ package exp_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/expressions.go b/expressions.go index 9edfd7b..403f117 100644 --- a/expressions.go +++ b/expressions.go @@ -1,7 +1,7 @@ package db import ( - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" ) type ( diff --git a/expressions_example_test.go b/expressions_example_test.go index 3c526e9..02b9d08 100644 --- a/expressions_example_test.go +++ b/expressions_example_test.go @@ -5,8 +5,8 @@ import ( "fmt" "regexp" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" ) func ExampleAVG() { diff --git a/expressions_test.go b/expressions_test.go index 2d052cd..9b7d928 100644 --- a/expressions_test.go +++ b/expressions_test.go @@ -3,8 +3,8 @@ package db_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" "github.com/stretchr/testify/suite" ) diff --git a/go.mod b/go.mod index 1cbfe8c..abdef50 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.fsdpf.net/go/db/v2 +module git.fsdpf.net/go/db go 1.19 @@ -18,6 +18,7 @@ require ( github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/crypto v0.11.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index c1b87f4..65f6afd 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= diff --git a/insert_dataset.go b/insert_dataset.go index 3698c54..62bfbef 100644 --- a/insert_dataset.go +++ b/insert_dataset.go @@ -2,11 +2,12 @@ package db import ( "fmt" + "reflect" - "git.fsdpf.net/go/db/v2/exec" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exec" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) type InsertDataset struct { @@ -15,6 +16,7 @@ type InsertDataset struct { isPrepared prepared queryFactory exec.QueryFactory err error + hooks exec.Hooks } var ErrUnsupportedIntoType = errors.New("unsupported table type, a string or identifier expression is required") @@ -88,6 +90,7 @@ func (id *InsertDataset) copy(clauses exp.InsertClauses) *InsertDataset { isPrepared: id.isPrepared, queryFactory: id.queryFactory, err: id.err, + hooks: id.hooks, } } @@ -170,9 +173,65 @@ func (id *InsertDataset) ClearVals() *InsertDataset { return id.copy(id.clauses.SetVals(nil)) } -// Insert rows. Rows can be a map, db.Record or struct. See examples. +// Insert rows. Rows can be a map, db.Record, struct, slice or array. See examples. func (id *InsertDataset) Rows(rows ...interface{}) *InsertDataset { - return id.copy(id.clauses.SetRows(rows)) + // If a single argument is a slice or array, expand it as rows + if len(rows) == 1 { + val := reflect.ValueOf(rows[0]) + kind := val.Kind() + if kind == reflect.Slice || kind == reflect.Array { + expanded := make([]interface{}, val.Len()) + for i := 0; i < val.Len(); i++ { + expanded[i] = val.Index(i).Interface() + } + // 递归调用 Rows 展开切片或数组 + return id.Rows(expanded...) + } + } + + converted := make([]interface{}, len(rows)) + for i, row := range rows { + switch v := row.(type) { + case map[string]interface{}: + converted[i] = v + + case Record: + converted[i] = map[string]interface{}(v) + + default: + val := reflect.ValueOf(row) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if val.Kind() == reflect.Slice || val.Kind() == reflect.Array { + panic("Rows: nested slice/array not supported, pass as top-level argument") + } + if val.Kind() != reflect.Struct { + panic("Rows: unsupported row type, must be map, Record, struct, slice or array") + } + + result := make(map[string]interface{}) + typ := val.Type() + for j := 0; j < val.NumField(); j++ { + field := typ.Field(j) + // Skip unexported fields + if field.PkgPath != "" { + continue + } + // Use db tag if present, otherwise use field name + key := field.Name + if dbTag, ok := field.Tag.Lookup("db"); ok && dbTag != "" { + if dbTag == "-" { + continue // Skip fields with db tag "-" + } + key = dbTag + } + result[key] = val.Field(j).Interface() + } + converted[i] = result + } + } + return id.copy(id.clauses.SetRows(converted)) } // Clears the rows for this insert dataset. See examples. @@ -259,8 +318,24 @@ func (id *InsertDataset) ReturnsColumns() bool { // Generates the INSERT sql, and returns an QueryExecutor struct with the sql set to the INSERT statement // // db.Insert("test").Rows(Record{"name":"Bob"}).Executor().Exec() -func (id *InsertDataset) Executor() exec.QueryExecutor { - return id.queryFactory.FromSQLBuilder(id.insertSQLBuilder()) +func (id *InsertDataset) Executor() (executor exec.QueryExecutor) { + if id.hooks != nil { + id.SetError(id.hooks.Before(id)) + } + executor = id.queryFactory.FromSQLBuilder(id.insertSQLBuilder()) + if id.hooks != nil { + executor.Hook(func(result interface{}) error { + return id.hooks.After(id, result) + }) + } + return executor +} + +func (id *InsertDataset) WithHook(hooks exec.Hooks) *InsertDataset { + if id.hooks == nil { + id.hooks = hooks + } + return id } func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder { diff --git a/insert_dataset_example_test.go b/insert_dataset_example_test.go index 97648d2..a8f411f 100644 --- a/insert_dataset_example_test.go +++ b/insert_dataset_example_test.go @@ -6,8 +6,8 @@ import ( "fmt" "time" - dbv2 "git.fsdpf.net/go/db/v2" - _ "git.fsdpf.net/go/db/v2/dialect/postgres" + dbv2 "git.fsdpf.net/go/db" + _ "git.fsdpf.net/go/db/dialect/postgres" ) func ExampleInsert_dbv2Record() { diff --git a/insert_dataset_test.go b/insert_dataset_test.go index a0f1da1..8beafed 100644 --- a/insert_dataset_test.go +++ b/insert_dataset_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/mocks" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/mocks" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" diff --git a/internal/util/column_map.go b/internal/util/column_map.go index 23cf2f6..7b26e56 100644 --- a/internal/util/column_map.go +++ b/internal/util/column_map.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - "git.fsdpf.net/go/db/v2/internal/tag" + "git.fsdpf.net/go/db/internal/tag" ) type ( @@ -37,13 +37,14 @@ func newColumnMap(t reflect.Type, fieldIndex []int, prefixes []string) ColumnMap // if PkgPath is empty then it is an exported field columnName := getColumnName(&f, dbTag) if !shouldIgnoreField(dbTag) { - if !implementsScanner(f.Type) { - subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes) - if len(subCm) != 0 { - subColMaps = append(subColMaps, subCm) - continue - } - } + // 移除原来的关联结构,并 scans 字段出现 table.col + // if !implementsScanner(f.Type) { + // subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes) + // if len(subCm) != 0 { + // subColMaps = append(subColMaps, subCm) + // continue + // } + // } ffTag := tag.New("ff", f.Tag) columnName = strings.Join(append(prefixes, columnName), ".") cm[columnName] = newColumnData(&f, columnName, fieldIndex, ffTag) diff --git a/internal/util/reflect.go b/internal/util/reflect.go index f29d646..4e9ee19 100644 --- a/internal/util/reflect.go +++ b/internal/util/reflect.go @@ -2,11 +2,12 @@ package util import ( "database/sql" + "encoding/json" "reflect" "strings" "sync" - "git.fsdpf.net/go/db/v2/internal/errors" + "git.fsdpf.net/go/db/internal/errors" ) const ( @@ -56,6 +57,10 @@ func IsStruct(k reflect.Kind) bool { return k == reflect.Struct } +func IsMap(k reflect.Kind) bool { + return k == reflect.Map +} + func IsInvalid(k reflect.Kind) bool { return k == reflect.Invalid } @@ -117,6 +122,15 @@ func GetSliceElementType(val reflect.Value) reflect.Type { return elemType } +func GetMapElementType(val reflect.Value) reflect.Type { + elemType := val.Type().Elem() + if elemType.Kind() == reflect.Ptr { + elemType = elemType.Elem() + } + + return elemType +} + // AppendSliceElement will append val to slice. Handles slice of pointers and // not pointers. Val needs to be a pointer. func AppendSliceElement(slice, val reflect.Value) { @@ -167,16 +181,8 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re return v case 1: f := v.FieldByIndex(fieldIndex) - srcVal := reflect.ValueOf(src).Elem() - // 处理 converting NULL to string is unsupported - // 前面将 string 和 int 类型转为了 *string 和 *int - // 这里做还原 - if srcVal.IsNil() { - f.Set(reflect.Zero(f.Type())) - } else if f.Type().ConvertibleTo(srcVal.Type().Elem()) { - f.Set(srcVal.Elem()) - } else { - f.Set(srcVal) + if err := SafeSetVarValue(f, src); err != nil { + panic(err) } default: f := v.Field(fieldIndex[0]) @@ -196,6 +202,28 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re return v } +func SafeSetVarValue(v reflect.Value, src interface{}) error { + f := reflect.Indirect(v) + srcVal := reflect.ValueOf(src).Elem() + + // 处理 converting NULL to string is unsupported + // 前面将 string 和 int 类型转为了 *string 和 *int + // 这里做还原 + if srcVal.IsNil() { + f.Set(reflect.Zero(f.Type())) + } else if f.Type().ConvertibleTo(srcVal.Type().Elem()) { + f.Set(srcVal.Elem()) + } else if f.Type().ConvertibleTo(srcVal.Type()) { + f.Set(srcVal) + } else if u, ok := srcVal.Interface().(*json.RawMessage); ok && len(*u) >= 2 { + if err := json.Unmarshal(*u, f.Addr().Interface()); err != nil { + return err + } + } + + return nil +} + type rowData = map[string]interface{} // AssignStructVals will assign the data from rd to i. diff --git a/internal/util/reflect_test.go b/internal/util/reflect_test.go index f3eb10e..eaccc3b 100644 --- a/internal/util/reflect_test.go +++ b/internal/util/reflect_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/internal/util" "github.com/stretchr/testify/suite" ) diff --git a/issues_test.go b/issues_test.go index bed1d8f..0c6669d 100644 --- a/issues_test.go +++ b/issues_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/suite" ) diff --git a/mocks/SQLDialect.go b/mocks/SQLDialect.go index f191e61..2c874ef 100644 --- a/mocks/SQLDialect.go +++ b/mocks/SQLDialect.go @@ -2,10 +2,10 @@ package mocks -import exp "git.fsdpf.net/go/db/v2/exp" +import exp "git.fsdpf.net/go/db/exp" import mock "github.com/stretchr/testify/mock" -import sb "git.fsdpf.net/go/db/v2/internal/sb" +import sb "git.fsdpf.net/go/db/internal/sb" // SQLDialect is an autogenerated mock type for the SQLDialect type type SQLDialect struct { diff --git a/schema/blueprint.go b/schema/blueprint.go index 9cf3fd8..e833487 100755 --- a/schema/blueprint.go +++ b/schema/blueprint.go @@ -10,10 +10,11 @@ type Blueprint struct { table string // the table the blueprint describes. columns []*ColumnDefinition // columns that should be added to the table commands []*Command // - temporary bool // Whether to make the table temporary. - charset string // The default character set that should be used for the table. - collation string // The collation that should be used for the table. - engine string // The engine that should be used for the table. + Temporary bool // Whether to make the table temporary. + Charset string // The default character set that should be used for the table. + Collation string // The collation that should be used for the table. + Engine string // The engine that should be used for the table. + Comment string } type Command struct { @@ -30,7 +31,7 @@ type CommandOptions struct { } func NewBlueprint(table string) *Blueprint { - return &Blueprint{table: table, charset: "utf8mb4", collation: "utf8mb4_general_ci"} + return &Blueprint{table: table} } // 字符串 @@ -263,21 +264,17 @@ func (this *Blueprint) Create() *Command { return this.addCommand("create", CommandOptions{}) } -// 设置临时表标记 -func (this *Blueprint) Temporary() { - this.temporary = true -} - -// 设置表字符集 -func (this *Blueprint) Charset(charset string) { - this.charset = charset -} - // 修改表名 func (this *Blueprint) Rename(to string) *Command { return this.addCommand("rename", CommandOptions{To: to}) } +// 修改表备注 +func (this *Blueprint) ModifyComment(comment string) *Command { + this.Comment = comment + return this.addCommand("modifyComment", CommandOptions{}) +} + // 删除表 func (this *Blueprint) Drop() *Command { return this.addCommand("drop", CommandOptions{}) @@ -307,6 +304,8 @@ func (this *Blueprint) ToSql(sc Schema) (statements []string) { statements = append(statements, sc.CompileDropColumn(this)...) case "rename": statements = append(statements, sc.CompileRename(this)...) + case "modifyComment": + statements = append(statements, sc.CompileModifyComment(this)...) } } @@ -420,26 +419,10 @@ func (this *Blueprint) GetCommands() []*Command { return this.commands } -func (this *Blueprint) IsTemporary() bool { - return this.temporary -} - func (this *Blueprint) GetTable() string { return this.table } -func (this *Blueprint) GetCharset() string { - return this.charset -} - -func (this *Blueprint) GetEngine() string { - return this.engine -} - -func (this *Blueprint) GetCollation() string { - return this.collation -} - // 命令类型 func (this *Command) Command() string { return this.Type diff --git a/schema/builder.go b/schema/builder.go index 9911773..8636856 100755 --- a/schema/builder.go +++ b/schema/builder.go @@ -1,7 +1,7 @@ package schema import ( - "git.fsdpf.net/go/db/v2" + "git.fsdpf.net/go/db" ) type Builder struct { @@ -48,6 +48,13 @@ func (this Builder) Rename(from, to string) error { return this.Build(bp) } +// 修改表备注 +func (this Builder) ModifyComment(table, comment string) error { + bp := NewBlueprint(table) + bp.ModifyComment(comment) + return this.Build(bp) +} + // 删除表 func (this Builder) Drop(table string) error { bp := NewBlueprint(table) diff --git a/schema/dialect/mysql/mysql.go b/schema/dialect/mysql/mysql.go index 014451b..bd85e6e 100644 --- a/schema/dialect/mysql/mysql.go +++ b/schema/dialect/mysql/mysql.go @@ -3,11 +3,11 @@ package mysql import ( "strings" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/dialect/mysql" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/schema" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/dialect/mysql" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/schema" + "git.fsdpf.net/go/db/sqlgen" ) type Mysql struct { @@ -16,6 +16,7 @@ type Mysql struct { } var mysqlDefaultModifiers = []string{ + "Unsigned", "Charset", "Collate", "VirtualAs", "StoredAs", "Nullable", "Default", "Increment", "Comment", "After", "First", } @@ -47,29 +48,33 @@ func (this Mysql) TableExists(table string) (bool, error) { // 创建表 func (this Mysql) CompileCreate(bp *schema.Blueprint) []string { temporary := db.L("CREATE") - if bp.IsTemporary() { + if bp.Temporary { temporary = db.L("CREATE TEMPORARY") } columns := strings.Join(this.getAddedColumns(bp), ",\n") sql := this.GenerateSQL("? TABLE ? (\n?\n)", temporary, db.T(bp.GetTable()), db.L(columns)) - charset := bp.GetCharset() + charset := bp.Charset if charset == "" { charset = "utf8mb4" } - collation := bp.GetCollation() + collation := bp.Collation if collation == "" { collation = "utf8mb4_general_ci" } sql += this.GenerateSQL(" default charset=? collate=?", charset, collation) - if engine := bp.GetEngine(); engine != "" { + if engine := bp.Engine; engine != "" { sql += this.GenerateSQL(" engine=?", engine) } + if comment := bp.Comment; comment != "" { + sql += this.GenerateSQL(" comment=?", comment) + } + return []string{sql} } @@ -121,13 +126,18 @@ func (this Mysql) CompileRename(bp *schema.Blueprint) []string { if len(commands) == 0 { panic("new table undefined") } - toName = bp.GetCommands()[0].To + toName = commands[0].To if toName == "" { panic("new table undefined") } return []string{this.GenerateSQL("RENAME TABLE ? TO ?", db.T(bp.GetTable()), db.T(toName))} } +// 修改表备注 +func (this Mysql) CompileModifyComment(bp *schema.Blueprint) []string { + return []string{this.GenerateSQL("ALTER TABLE ? COMMENT = ?", db.T(bp.GetTable()), db.V(bp.Comment))} +} + func (this Mysql) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string { for _, modifier := range mysqlDefaultModifiers { sql += this.GetColumnModifier(modifier, bp, column) diff --git a/schema/dialect/mysql/mysql_builder_test.go b/schema/dialect/mysql/mysql_builder_test.go index e6677b8..1a33626 100644 --- a/schema/dialect/mysql/mysql_builder_test.go +++ b/schema/dialect/mysql/mysql_builder_test.go @@ -3,9 +3,9 @@ package mysql_test import ( "os" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/engine" - "git.fsdpf.net/go/db/v2/schema" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/engine" + "git.fsdpf.net/go/db/schema" "github.com/stretchr/testify/suite" ) @@ -40,7 +40,7 @@ func (t *mysqlBuilderTest) TestGetColumnListing() { // 创建表 func (t *mysqlBuilderTest) TestCreateTable() { if err := t.builder.Create("users", func(table *schema.Blueprint) { - table.Charset("utf8mb4") + table.Charset = "utf8mb4" table.BigIncrements("id").Comment("ID") table.Boolean("enabled").Default("1").Comment("是否有效") diff --git a/schema/dialect/mysql/mysql_test.go b/schema/dialect/mysql/mysql_test.go index 3005bd2..1c732e5 100644 --- a/schema/dialect/mysql/mysql_test.go +++ b/schema/dialect/mysql/mysql_test.go @@ -4,9 +4,9 @@ import ( "os" "testing" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/engine" - "git.fsdpf.net/go/db/v2/schema" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/engine" + "git.fsdpf.net/go/db/schema" "github.com/stretchr/testify/suite" _ "github.com/go-sql-driver/mysql" @@ -71,7 +71,7 @@ func (t *mysqlTest) TestTableExists() { func (t *mysqlTest) TestCompileCreate() { bp := schema.NewBlueprint("users") bp.Create() - bp.Charset("utf8mb4") + bp.Charset = "utf8mb4" bp.BigIncrements("id").AutoIncrement().Comment("ID") bp.Boolean("enabled").Default("1").Comment("是否有效") @@ -188,3 +188,5 @@ func (t *mysqlTest) TestCompileRename() { t.T().Log(sql) } + +// 修改表备注 diff --git a/schema/dialect/postgres/postgres.go b/schema/dialect/postgres/postgres.go index 4a71df1..36eab03 100644 --- a/schema/dialect/postgres/postgres.go +++ b/schema/dialect/postgres/postgres.go @@ -3,11 +3,11 @@ package postgres import ( "strings" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/dialect/postgres" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/schema" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/dialect/postgres" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/schema" + "git.fsdpf.net/go/db/sqlgen" ) type Postgres struct { @@ -41,7 +41,7 @@ func (this Postgres) TableExists(table string) (bool, error) { func (this Postgres) CompileCreate(bp *schema.Blueprint) []string { temporary := db.L("CREATE") - if bp.IsTemporary() { + if bp.Temporary { temporary = db.L("CREATE TEMPORARY") } columns := strings.Join(this.getAddedColumns(bp), ",\n") @@ -96,6 +96,11 @@ func (this Postgres) CompileRename(bp *schema.Blueprint) []string { return []string{this.GenerateSQL("ALTER TABLE ? RENAME TO ?", db.T(bp.GetTable()), db.T(toName))} } +// 修改表备注 +func (this Postgres) CompileModifyComment(bp *schema.Blueprint) []string { + return []string{this.GenerateSQL("COMMENT ON TABLE ? IS ?", db.T(bp.GetTable()), db.V(bp.Comment))} +} + func (this Postgres) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string { for _, modifier := range pgDefaultModifiers { sql += this.GetColumnModifier(modifier, bp, column) diff --git a/schema/dialect/postgres/postgres_test.go b/schema/dialect/postgres/postgres_test.go index 562760c..cac2b64 100644 --- a/schema/dialect/postgres/postgres_test.go +++ b/schema/dialect/postgres/postgres_test.go @@ -4,8 +4,8 @@ import ( "os" "testing" - "git.fsdpf.net/go/db/v2/engine" - "git.fsdpf.net/go/db/v2/schema" + "git.fsdpf.net/go/db/engine" + "git.fsdpf.net/go/db/schema" "github.com/stretchr/testify/suite" _ "github.com/lib/pq" diff --git a/schema/dialect/sqlite3/sqlite3.go b/schema/dialect/sqlite3/sqlite3.go index 749743e..d70493a 100644 --- a/schema/dialect/sqlite3/sqlite3.go +++ b/schema/dialect/sqlite3/sqlite3.go @@ -5,11 +5,11 @@ import ( "strings" "time" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/dialect/sqlite3" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/schema" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/dialect/sqlite3" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/schema" + "git.fsdpf.net/go/db/sqlgen" ) var ( @@ -72,8 +72,15 @@ func (this Sqlite3) CompileCreate(bp *schema.Blueprint) (sqls []string) { } columns = append(columns, sql) } + + prefix := "? TABLE ? (\n?\n)" + + if comment := bp.Comment; comment != "" { + prefix = "? TABLE ? ( -- " + comment + "\n?\n)" + } + sqls = append(sqls, - this.GenerateSQL("? TABLE ? (\n?\n)", + this.GenerateSQL(prefix, temporary, db.T(bp.GetTable()), db.L(strings.Join(columns, "\n")), @@ -152,6 +159,11 @@ func (this Sqlite3) CompileRename(bp *schema.Blueprint) []string { return []string{this.GenerateSQL("RENAME TABLE ? TO ?", db.T(bp.GetTable()), db.T(toName))} } +// 修改表备注 +func (this Sqlite3) CompileModifyComment(bp *schema.Blueprint) []string { + return []string{} +} + func (this Sqlite3) GetColumnModifier(modifier string, bp *schema.Blueprint, column *schema.ColumnDefinition) string { switch modifier { case "Collate": diff --git a/schema/dialect/sqlite3/sqlite3_test.go b/schema/dialect/sqlite3/sqlite3_test.go index c5c1122..a33739b 100644 --- a/schema/dialect/sqlite3/sqlite3_test.go +++ b/schema/dialect/sqlite3/sqlite3_test.go @@ -4,9 +4,9 @@ import ( "os" "testing" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/engine" - "git.fsdpf.net/go/db/v2/schema" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/engine" + "git.fsdpf.net/go/db/schema" "github.com/stretchr/testify/suite" _ "github.com/mattn/go-sqlite3" @@ -68,7 +68,7 @@ func (t *sqlite3Test) TestGetColumnListing() { func (t *sqlite3Test) TestCompileCreate() { bp := schema.NewBlueprint("users") bp.Create() - bp.Charset("utf8mb4") + bp.Charset = "utf8mb4" bp.BigIncrements("id").AutoIncrement().Comment("ID") bp.Boolean("enabled").Default("1").Comment("是否有效") diff --git a/schema/dialect/sqlserver/sqlserver.go b/schema/dialect/sqlserver/sqlserver.go index 607a49c..1a63e42 100644 --- a/schema/dialect/sqlserver/sqlserver.go +++ b/schema/dialect/sqlserver/sqlserver.go @@ -3,11 +3,11 @@ package sqlserver import ( "strings" - "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/dialect/sqlserver" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/schema" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/dialect/sqlserver" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/schema" + "git.fsdpf.net/go/db/sqlgen" ) type SQLServer struct { @@ -39,7 +39,7 @@ func (this SQLServer) TableExists(table string) (bool, error) { func (this SQLServer) CompileCreate(bp *schema.Blueprint) []string { temporary := db.L("CREATE") - if bp.IsTemporary() { + if bp.Temporary { temporary = db.L("CREATE") } columns := strings.Join(this.getAddedColumns(bp), ",\n") @@ -94,6 +94,35 @@ func (this SQLServer) CompileRename(bp *schema.Blueprint) []string { return []string{this.GenerateSQL("EXEC sp_rename ?, ?", db.T(bp.GetTable()), db.T(toName))} } +// 修改表备注 +func (this SQLServer) CompileModifyComment(bp *schema.Blueprint) []string { + return []string{ + this.GenerateSQL(` +IF EXISTS ( + SELECT 1 FROM sys.extended_properties + WHERE major_id = OBJECT_ID(?) + AND name = 'MS_Description' + AND minor_id = 0 +) +BEGIN + EXEC sp_updateextendedproperty + @name = N'MS_Description', + @value = ?, + @level0type = N'SCHEMA', @level0name = 'dbo', + @level1type = N'TABLE', @level1name = ? +END +ELSE +BEGIN + EXEC sp_addextendedproperty + @name = N'MS_Description', + @value = ?, + @level0type = N'SCHEMA', @level0name = 'dbo', + @level1type = N'TABLE', @level1name = ? +END +`, db.T(bp.GetTable()), db.V(bp.Comment), db.T(bp.GetTable()), db.V(bp.Comment), db.T(bp.GetTable())), + } +} + func (this SQLServer) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string { for _, modifier := range sqlServerDefaultModifiers { sql += this.GetColumnModifier(modifier, bp, column) diff --git a/schema/dialect/sqlserver/sqlserver_test.go b/schema/dialect/sqlserver/sqlserver_test.go index d76a785..283d392 100644 --- a/schema/dialect/sqlserver/sqlserver_test.go +++ b/schema/dialect/sqlserver/sqlserver_test.go @@ -4,8 +4,8 @@ import ( "os" "testing" - "git.fsdpf.net/go/db/v2/engine" - "git.fsdpf.net/go/db/v2/schema" + "git.fsdpf.net/go/db/engine" + "git.fsdpf.net/go/db/schema" "github.com/stretchr/testify/suite" _ "github.com/denisenkom/go-mssqldb" diff --git a/schema/schema.go b/schema/schema.go index 1be2309..037b36b 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -5,7 +5,7 @@ import ( "strings" "sync" - "git.fsdpf.net/go/db/v2" + "git.fsdpf.net/go/db" ) type Schema interface { @@ -27,6 +27,8 @@ type Schema interface { CompileDropColumn(bp *Blueprint) []string // 表重命名 CompileRename(bp *Blueprint) []string + // 修改表备注 + CompileModifyComment(bp *Blueprint) []string // 生成SQL GenerateSQL(sql string, args ...any) string } diff --git a/schema/util.go b/schema/util.go index ac4faa8..238dda6 100755 --- a/schema/util.go +++ b/schema/util.go @@ -1,26 +1,8 @@ package schema -import ( - "strings" - - "github.com/samber/lo" -) - func PrefixArray(prefix string, values []string) (items []string) { for _, value := range values { items = append(items, prefix+" "+value) } return items } - -func QuoteString(value any) string { - switch v := value.(type) { - case []string: - return strings.Join(lo.Map(v, func(item string, _ int) string { - return "'" + item + "'" - }), ", ") - case string: - return "'" + v + "'" - } - return "" -} diff --git a/select_dataset.go b/select_dataset.go index b2c8ec6..1523332 100644 --- a/select_dataset.go +++ b/select_dataset.go @@ -4,10 +4,10 @@ import ( "context" "fmt" - "git.fsdpf.net/go/db/v2/exec" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exec" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) // Dataset for creating and/or executing SELECT SQL statements. @@ -17,6 +17,7 @@ type SelectDataset struct { isPrepared prepared queryFactory exec.QueryFactory err error + hooks exec.Hooks } var ErrQueryFactoryNotFoundError = errors.New( @@ -94,6 +95,7 @@ func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset { isPrepared: sd.isPrepared, queryFactory: sd.queryFactory, err: sd.err, + hooks: sd.hooks, } } @@ -121,6 +123,7 @@ func (sd *SelectDataset) Update() *UpdateDataset { } } u.clauses = c + u.hooks = sd.hooks return u } @@ -130,13 +133,18 @@ func (sd *SelectDataset) Insert() *InsertDataset { i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory). Prepared(sd.isPrepared.Bool()) if sd.clauses.HasSources() { - i = i.Into(sd.GetClauses().From().Columns()[0]) + if from, ok := sd.GetClauses().From().Columns()[0].(exp.AliasedExpression); ok { + i = i.Into(from.Aliased()) + } else { + i = i.Into(from) + } } c := i.clauses for _, ce := range sd.clauses.CommonTables() { c = c.CommonTablesAppend(ce) } i.clauses = c + i.hooks = sd.hooks return i } @@ -164,6 +172,7 @@ func (sd *SelectDataset) Delete() *DeleteDataset { } } d.clauses = c + d.hooks = sd.hooks return d } @@ -353,6 +362,11 @@ func (sd *SelectDataset) joinTable(join exp.JoinExpression) *SelectDataset { return sd.copy(sd.clauses.JoinsAppend(join)) } +// Joins this Datasets table with another, prepend +func (sd *SelectDataset) JoinPrepend(table exp.Expression, condition exp.JoinCondition, joinType exp.JoinType) *SelectDataset { + return sd.copy(sd.clauses.JoinsPrepend(exp.NewConditionedJoinExpression(joinType, table, condition))) +} + // Adds a WHERE clause. See examples. func (sd *SelectDataset) Where(expressions ...exp.Expression) *SelectDataset { return sd.copy(sd.clauses.WhereAppend(expressions...)) @@ -387,6 +401,11 @@ func (sd *SelectDataset) withLock(strength exp.LockStrength, option exp.WaitOpti return sd.copy(sd.clauses.SetLock(exp.NewLock(strength, option, of...))) } +// Removes the Group BY clause. See examples. +func (sd *SelectDataset) ClearGroupBy() *SelectDataset { + return sd.copy(sd.clauses.ClearGroupBy()) +} + // Adds a GROUP BY clause. See examples. func (sd *SelectDataset) GroupBy(groupBy ...interface{}) *SelectDataset { return sd.copy(sd.clauses.SetGroupBy(exp.NewColumnListExpression(groupBy...))) @@ -551,8 +570,24 @@ func (sd *SelectDataset) ToSQL() (sql string, params []interface{}, err error) { // db.From("test").Select("col").Executor() // // See Dataset#ToUpdateSQL for arguments -func (sd *SelectDataset) Executor() exec.QueryExecutor { - return sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder()) +func (sd *SelectDataset) Executor() (executor exec.QueryExecutor) { + if sd.hooks != nil { + sd.SetError(sd.hooks.Before(sd)) + } + executor = sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder()) + if sd.hooks != nil { + executor.Hook(func(result interface{}) error { + return sd.hooks.After(sd, result) + }) + } + return executor +} + +func (sd *SelectDataset) WithHook(hooks exec.Hooks) *SelectDataset { + if sd.hooks == nil { + sd.hooks = hooks + } + return sd } // Appends this Dataset's SELECT statement to the SQLBuilder diff --git a/select_dataset_example_test.go b/select_dataset_example_test.go index 839c62d..bd1c986 100644 --- a/select_dataset_example_test.go +++ b/select_dataset_example_test.go @@ -8,8 +8,8 @@ import ( "regexp" "time" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" "github.com/lib/pq" ) diff --git a/select_dataset_test.go b/select_dataset_test.go index 8c7cc1f..19c1b60 100644 --- a/select_dataset_test.go +++ b/select_dataset_test.go @@ -3,11 +3,11 @@ package db_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/mocks" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/mocks" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" @@ -137,7 +137,7 @@ func (sds *selectDatasetSuite) TestDelete() { Limit(limit). Order(order...) ec := exp.NewDeleteClauses(). - SetFrom(dbv2.C("test")). + SetFrom(exp.NewColumnListExpression("test")). CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)). WhereAppend(ds.GetClauses().Where()). SetLimit(limit). diff --git a/sql_dialect.go b/sql_dialect.go index 5c6560b..2519dd8 100644 --- a/sql_dialect.go +++ b/sql_dialect.go @@ -4,9 +4,9 @@ import ( "strings" "sync" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" ) type ( diff --git a/sql_dialect_example_test.go b/sql_dialect_example_test.go index e105216..f7d210f 100644 --- a/sql_dialect_example_test.go +++ b/sql_dialect_example_test.go @@ -3,7 +3,7 @@ package db_test import ( "fmt" - dbv2 "git.fsdpf.net/go/db/v2" + dbv2 "git.fsdpf.net/go/db" ) func ExampleRegisterDialect() { diff --git a/sql_dialect_internal_test.go b/sql_dialect_internal_test.go index f985b4a..d1c8de4 100644 --- a/sql_dialect_internal_test.go +++ b/sql_dialect_internal_test.go @@ -3,9 +3,9 @@ package db import ( "testing" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen/mocks" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen/mocks" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/base_test.go b/sqlgen/base_test.go index 8f1b7bb..b39e55b 100644 --- a/sqlgen/base_test.go +++ b/sqlgen/base_test.go @@ -1,7 +1,7 @@ package sqlgen_test import ( - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/internal/sb" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/common_sql_generator.go b/sqlgen/common_sql_generator.go index 95c3e4e..faca045 100644 --- a/sqlgen/common_sql_generator.go +++ b/sqlgen/common_sql_generator.go @@ -1,9 +1,9 @@ package sqlgen import ( - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) var ErrNoUpdatedValuesProvided = errors.New("no update values provided") diff --git a/sqlgen/common_sql_generator_test.go b/sqlgen/common_sql_generator_test.go index 78ed1d9..c12ddf6 100644 --- a/sqlgen/common_sql_generator_test.go +++ b/sqlgen/common_sql_generator_test.go @@ -3,9 +3,9 @@ package sqlgen_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/delete_sql_generator.go b/sqlgen/delete_sql_generator.go index 81ab235..7b93f2d 100644 --- a/sqlgen/delete_sql_generator.go +++ b/sqlgen/delete_sql_generator.go @@ -1,9 +1,9 @@ package sqlgen import ( - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) type ( @@ -67,6 +67,14 @@ func (dsg *deleteSQLGenerator) Generate(b sb.SQLBuilder, clauses exp.DeleteClaus func (dsg *deleteSQLGenerator) DeleteBeginSQL(b sb.SQLBuilder, from exp.ColumnListExpression, multiTable bool) { b.Write(dsg.DialectOptions().DeleteClause) if multiTable && dsg.DialectOptions().SupportsDeleteTableHint { - dsg.SourcesSQL(b, from) + _from := exp.NewColumnListExpression() + for _, col := range from.Columns() { + if aliased, ok := col.(exp.AliasedExpression); ok { + _from = _from.Append(aliased.GetAs()) + } else { + _from = _from.Append(col) + } + } + dsg.SourcesSQL(b, _from) } } diff --git a/sqlgen/delete_sql_generator_test.go b/sqlgen/delete_sql_generator_test.go index a5caa69..59fd55e 100644 --- a/sqlgen/delete_sql_generator_test.go +++ b/sqlgen/delete_sql_generator_test.go @@ -3,10 +3,10 @@ package sqlgen_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) @@ -50,7 +50,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestDialect() { func (dsgs *deleteSQLGeneratorSuite) TestGenerate() { dc := exp.NewDeleteClauses(). - SetFrom(exp.NewIdentifierExpression("", "test", "")) + SetFrom(exp.NewColumnListExpression("test")) dsgs.assertCases( sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()), @@ -72,7 +72,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withUnsupportedFragment() { opts := sqlgen.DefaultDialectOptions() opts.DeleteSQLOrder = []sqlgen.SQLFragmentType{sqlgen.InsertBeingSQLFragment} dc := exp.NewDeleteClauses(). - SetFrom(exp.NewIdentifierExpression("", "test", "")) + SetFrom(exp.NewColumnListExpression("test")) dsgs.assertCases( sqlgen.NewDeleteSQLGenerator("test", opts), @@ -94,7 +94,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withErroredBuilder() { opts := sqlgen.DefaultDialectOptions() d := sqlgen.NewDeleteSQLGenerator("test", opts) - dc := exp.NewDeleteClauses().SetFrom(exp.NewIdentifierExpression("", "test", "")) + dc := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")) b := sb.NewSQLBuilder(false).SetError(errors.New("expected error")) d.Generate(b, dc) dsgs.assertErrorSQL(b, "db: expected error") @@ -111,7 +111,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withCommonTables() { tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil) - dc := exp.NewDeleteClauses().SetFrom(exp.NewIdentifierExpression("", "test_cte", "")) + dc := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test_cte")) dcCte1 := dc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test_cte", tse)) dcCte2 := dc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test_cte", tse)) @@ -150,7 +150,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withCommonTables() { func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withWhere() { dc := exp.NewDeleteClauses(). - SetFrom(exp.NewIdentifierExpression("", "test", "")). + SetFrom(exp.NewColumnListExpression("test")). WhereAppend(exp.NewLiteralExpression(`"a"=?`, 1)) dsgs.assertCases( sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()), @@ -166,7 +166,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withOrder() { opts.SupportsOrderByOnDelete = true dc := exp.NewDeleteClauses(). - SetFrom(exp.NewIdentifierExpression("", "test", "")). + SetFrom(exp.NewColumnListExpression("test")). SetOrder(exp.NewIdentifierExpression("", "", "c").Desc()) dsgs.assertCases( @@ -188,7 +188,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withLimit() { opts.SupportsLimitOnDelete = true dc := exp.NewDeleteClauses(). - SetFrom(exp.NewIdentifierExpression("", "test", "")). + SetFrom(exp.NewColumnListExpression("test")). SetLimit(1) dsgs.assertCases( @@ -210,7 +210,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withReturning() { opts.SupportsReturn = true dc := exp.NewDeleteClauses(). - SetFrom(exp.NewIdentifierExpression("", "test", "")). + SetFrom(exp.NewColumnListExpression("test")). SetReturning(exp.NewColumnListExpression("a", "b")) dsgs.assertCases( diff --git a/sqlgen/expression_sql_generator.go b/sqlgen/expression_sql_generator.go index a7f6a4f..b66df75 100644 --- a/sqlgen/expression_sql_generator.go +++ b/sqlgen/expression_sql_generator.go @@ -7,10 +7,10 @@ import ( "time" "unicode/utf8" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/internal/util" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/internal/util" ) type ( diff --git a/sqlgen/expression_sql_generator_test.go b/sqlgen/expression_sql_generator_test.go index c370e9b..70866c3 100644 --- a/sqlgen/expression_sql_generator_test.go +++ b/sqlgen/expression_sql_generator_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/insert_sql_generator.go b/sqlgen/insert_sql_generator.go index 9226147..6bdebb9 100644 --- a/sqlgen/insert_sql_generator.go +++ b/sqlgen/insert_sql_generator.go @@ -3,9 +3,9 @@ package sqlgen import ( "strings" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) type ( diff --git a/sqlgen/insert_sql_generator_test.go b/sqlgen/insert_sql_generator_test.go index f9430e7..9db0cea 100644 --- a/sqlgen/insert_sql_generator_test.go +++ b/sqlgen/insert_sql_generator_test.go @@ -3,9 +3,9 @@ package sqlgen_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/mocks/DeleteSQLGenerator.go b/sqlgen/mocks/DeleteSQLGenerator.go index 56275a9..43227ed 100644 --- a/sqlgen/mocks/DeleteSQLGenerator.go +++ b/sqlgen/mocks/DeleteSQLGenerator.go @@ -2,9 +2,9 @@ package mocks -import exp "git.fsdpf.net/go/db/v2/exp" +import exp "git.fsdpf.net/go/db/exp" import mock "github.com/stretchr/testify/mock" -import sb "git.fsdpf.net/go/db/v2/internal/sb" +import sb "git.fsdpf.net/go/db/internal/sb" // DeleteSQLGenerator is an autogenerated mock type for the DeleteSQLGenerator type type DeleteSQLGenerator struct { diff --git a/sqlgen/mocks/InsertSQLGenerator.go b/sqlgen/mocks/InsertSQLGenerator.go index 7fe4fc8..409d584 100644 --- a/sqlgen/mocks/InsertSQLGenerator.go +++ b/sqlgen/mocks/InsertSQLGenerator.go @@ -2,9 +2,9 @@ package mocks -import exp "git.fsdpf.net/go/db/v2/exp" +import exp "git.fsdpf.net/go/db/exp" import mock "github.com/stretchr/testify/mock" -import sb "git.fsdpf.net/go/db/v2/internal/sb" +import sb "git.fsdpf.net/go/db/internal/sb" // InsertSQLGenerator is an autogenerated mock type for the InsertSQLGenerator type type InsertSQLGenerator struct { diff --git a/sqlgen/mocks/SelectSQLGenerator.go b/sqlgen/mocks/SelectSQLGenerator.go index c04f1e8..7faa378 100644 --- a/sqlgen/mocks/SelectSQLGenerator.go +++ b/sqlgen/mocks/SelectSQLGenerator.go @@ -2,9 +2,9 @@ package mocks -import exp "git.fsdpf.net/go/db/v2/exp" +import exp "git.fsdpf.net/go/db/exp" import mock "github.com/stretchr/testify/mock" -import sb "git.fsdpf.net/go/db/v2/internal/sb" +import sb "git.fsdpf.net/go/db/internal/sb" // SelectSQLGenerator is an autogenerated mock type for the SelectSQLGenerator type type SelectSQLGenerator struct { diff --git a/sqlgen/mocks/TruncateSQLGenerator.go b/sqlgen/mocks/TruncateSQLGenerator.go index 9109876..7861a55 100644 --- a/sqlgen/mocks/TruncateSQLGenerator.go +++ b/sqlgen/mocks/TruncateSQLGenerator.go @@ -2,9 +2,9 @@ package mocks -import exp "git.fsdpf.net/go/db/v2/exp" +import exp "git.fsdpf.net/go/db/exp" import mock "github.com/stretchr/testify/mock" -import sb "git.fsdpf.net/go/db/v2/internal/sb" +import sb "git.fsdpf.net/go/db/internal/sb" // TruncateSQLGenerator is an autogenerated mock type for the TruncateSQLGenerator type type TruncateSQLGenerator struct { diff --git a/sqlgen/mocks/UpdateSQLGenerator.go b/sqlgen/mocks/UpdateSQLGenerator.go index 9d5de65..c3372c9 100644 --- a/sqlgen/mocks/UpdateSQLGenerator.go +++ b/sqlgen/mocks/UpdateSQLGenerator.go @@ -2,9 +2,9 @@ package mocks -import exp "git.fsdpf.net/go/db/v2/exp" +import exp "git.fsdpf.net/go/db/exp" import mock "github.com/stretchr/testify/mock" -import sb "git.fsdpf.net/go/db/v2/internal/sb" +import sb "git.fsdpf.net/go/db/internal/sb" // UpdateSQLGenerator is an autogenerated mock type for the UpdateSQLGenerator type type UpdateSQLGenerator struct { diff --git a/sqlgen/select_sql_generator.go b/sqlgen/select_sql_generator.go index dfae14c..4d26749 100644 --- a/sqlgen/select_sql_generator.go +++ b/sqlgen/select_sql_generator.go @@ -1,9 +1,9 @@ package sqlgen import ( - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) type ( diff --git a/sqlgen/select_sql_generator_test.go b/sqlgen/select_sql_generator_test.go index 70c1c3e..dbaaadc 100644 --- a/sqlgen/select_sql_generator_test.go +++ b/sqlgen/select_sql_generator_test.go @@ -3,11 +3,11 @@ package sqlgen_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/sql_dialect_options.go b/sqlgen/sql_dialect_options.go index 6f1071a..45d4204 100644 --- a/sqlgen/sql_dialect_options.go +++ b/sqlgen/sql_dialect_options.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "git.fsdpf.net/go/db/v2/exp" + "git.fsdpf.net/go/db/exp" ) type ( diff --git a/sqlgen/sql_dialect_options_test.go b/sqlgen/sql_dialect_options_test.go index 2c37f41..35f1b40 100644 --- a/sqlgen/sql_dialect_options_test.go +++ b/sqlgen/sql_dialect_options_test.go @@ -3,7 +3,7 @@ package sqlgen_test import ( "testing" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/truncate_sql_generator.go b/sqlgen/truncate_sql_generator.go index 9695fb2..35f41a6 100644 --- a/sqlgen/truncate_sql_generator.go +++ b/sqlgen/truncate_sql_generator.go @@ -3,9 +3,9 @@ package sqlgen import ( "strings" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) type ( diff --git a/sqlgen/truncate_sql_generator_test.go b/sqlgen/truncate_sql_generator_test.go index ae03ff3..7d8254c 100644 --- a/sqlgen/truncate_sql_generator_test.go +++ b/sqlgen/truncate_sql_generator_test.go @@ -3,10 +3,10 @@ package sqlgen_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) diff --git a/sqlgen/update_sql_generator.go b/sqlgen/update_sql_generator.go index 4c68398..2095ff9 100644 --- a/sqlgen/update_sql_generator.go +++ b/sqlgen/update_sql_generator.go @@ -1,9 +1,9 @@ package sqlgen import ( - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) type ( diff --git a/sqlgen/update_sql_generator_test.go b/sqlgen/update_sql_generator_test.go index a669671..5f76075 100644 --- a/sqlgen/update_sql_generator_test.go +++ b/sqlgen/update_sql_generator_test.go @@ -3,9 +3,9 @@ package sqlgen_test import ( "testing" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/sqlgen" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/sqlgen" "github.com/stretchr/testify/suite" ) diff --git a/truncate_dataset.go b/truncate_dataset.go index dbae2b0..cbe33aa 100644 --- a/truncate_dataset.go +++ b/truncate_dataset.go @@ -1,9 +1,9 @@ package db import ( - "git.fsdpf.net/go/db/v2/exec" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exec" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/sb" ) type TruncateDataset struct { diff --git a/truncate_dataset_test.go b/truncate_dataset_test.go index ee04d92..77b46c7 100644 --- a/truncate_dataset_test.go +++ b/truncate_dataset_test.go @@ -3,11 +3,11 @@ package db_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/mocks" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/mocks" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" diff --git a/update_dataset.go b/update_dataset.go index 805788e..c7636e5 100644 --- a/update_dataset.go +++ b/update_dataset.go @@ -1,10 +1,10 @@ package db import ( - "git.fsdpf.net/go/db/v2/exec" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" + "git.fsdpf.net/go/db/exec" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" ) type UpdateDataset struct { @@ -13,6 +13,7 @@ type UpdateDataset struct { isPrepared prepared queryFactory exec.QueryFactory err error + hooks exec.Hooks } var ErrUnsupportedUpdateTableType = errors.New("unsupported table type, a string or identifier expression is required") @@ -84,6 +85,7 @@ func (ud *UpdateDataset) copy(clauses exp.UpdateClauses) *UpdateDataset { isPrepared: ud.isPrepared, queryFactory: ud.queryFactory, err: ud.err, + hooks: ud.hooks, } } @@ -232,8 +234,24 @@ func (ud *UpdateDataset) ReturnsColumns() bool { // Generates the UPDATE sql, and returns an exec.QueryExecutor with the sql set to the UPDATE statement // // db.Update("test").Set(Record{"name":"Bob", update: time.Now()}).Executor() -func (ud *UpdateDataset) Executor() exec.QueryExecutor { - return ud.queryFactory.FromSQLBuilder(ud.updateSQLBuilder()) +func (ud *UpdateDataset) Executor() (executor exec.QueryExecutor) { + if ud.hooks != nil { + ud.SetError(ud.hooks.Before(ud)) + } + executor = ud.queryFactory.FromSQLBuilder(ud.updateSQLBuilder()) + if ud.hooks != nil { + executor.Hook(func(result interface{}) error { + return ud.hooks.After(ud, result) + }) + } + return executor +} + +func (ud *UpdateDataset) WithHook(hooks exec.Hooks) *UpdateDataset { + if ud.hooks == nil { + ud.hooks = hooks + } + return ud } func (ud *UpdateDataset) updateSQLBuilder() sb.SQLBuilder { diff --git a/update_dataset_example_test.go b/update_dataset_example_test.go index 8225b76..aa47cdf 100644 --- a/update_dataset_example_test.go +++ b/update_dataset_example_test.go @@ -5,9 +5,9 @@ import ( dbsql "database/sql" "fmt" - dbv2 "git.fsdpf.net/go/db/v2" - _ "git.fsdpf.net/go/db/v2/dialect/mysql" - "git.fsdpf.net/go/db/v2/exp" + dbv2 "git.fsdpf.net/go/db" + _ "git.fsdpf.net/go/db/dialect/mysql" + "git.fsdpf.net/go/db/exp" ) func ExampleUpdate_withStruct() { diff --git a/update_dataset_test.go b/update_dataset_test.go index 6063270..6add7ea 100644 --- a/update_dataset_test.go +++ b/update_dataset_test.go @@ -3,11 +3,11 @@ package db_test import ( "testing" - dbv2 "git.fsdpf.net/go/db/v2" - "git.fsdpf.net/go/db/v2/exp" - "git.fsdpf.net/go/db/v2/internal/errors" - "git.fsdpf.net/go/db/v2/internal/sb" - "git.fsdpf.net/go/db/v2/mocks" + dbv2 "git.fsdpf.net/go/db" + "git.fsdpf.net/go/db/exp" + "git.fsdpf.net/go/db/internal/errors" + "git.fsdpf.net/go/db/internal/sb" + "git.fsdpf.net/go/db/mocks" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite"