docs: Add CLAUDE.md with codebase guidance

Create comprehensive documentation for future Claude Code instances working in this repository, including:
- Development commands for testing, building, and code quality
- Core architecture overview of the SQL query builder system
- Directory structure and component explanations
- Testing patterns and conventions
- Key dependencies and their purposes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-27 15:47:28 +08:00
co-authored by Claude
parent 764eccfafd
commit 304d553b3c
113 changed files with 914 additions and 432 deletions
+10 -1
View File
@@ -5,7 +5,7 @@ import (
"database/sql" "database/sql"
"sync" "sync"
"git.fsdpf.net/go/db/v2/exec" "git.fsdpf.net/go/db/exec"
) )
type ( type (
@@ -432,6 +432,9 @@ type (
Tx SQLTx Tx SQLTx
qf exec.QueryFactory qf exec.QueryFactory
qfOnce sync.Once 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 // See Database#ExecContext
func (td *TxDatabase) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { 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...) td.Trace("EXEC", query, args...)
return td.Tx.ExecContext(ctx, 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 // See Database#QueryContext
func (td *TxDatabase) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { 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...) td.Trace("QUERY", query, args...)
return td.Tx.QueryContext(ctx, query, args...) return td.Tx.QueryContext(ctx, query, args...)
} }
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
) )
func ExampleDatabase_Begin() { func ExampleDatabase_Begin() {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"sync" "sync"
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -3,8 +3,8 @@ package db
import ( import (
"time" "time"
"git.fsdpf.net/go/db/v2/internal/util" "git.fsdpf.net/go/db/internal/util"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
) )
type DialectWrapper struct { type DialectWrapper struct {
+10 -10
View File
@@ -4,16 +4,16 @@ import (
"fmt" "fmt"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/v2/dialect/mysql" _ "git.fsdpf.net/go/db/dialect/mysql"
_ "git.fsdpf.net/go/db/v2/dialect/postgres" _ "git.fsdpf.net/go/db/dialect/postgres"
_ "git.fsdpf.net/go/db/v2/dialect/sqlite3" _ "git.fsdpf.net/go/db/dialect/sqlite3"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
) )
// Creating a mysql dataset. Be sure to import the mysql adapter. // Creating a mysql dataset. Be sure to import the mysql adapter.
func ExampleDialect_datasetMysql() { func ExampleDialect_datasetMysql() {
// import _ "git.fsdpf.net/go/db/v2/dialect/mysql" // import _ "git.fsdpf.net/go/db/dialect/mysql"
d := dbv2.Dialect("mysql") d := dbv2.Dialect("mysql")
ds := d.From("test").Where(dbv2.Ex{ 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. // Creating a mysql database. Be sure to import the mysql adapter.
func ExampleDialect_dbMysql() { func ExampleDialect_dbMysql() {
// import _ "git.fsdpf.net/go/db/v2/dialect/mysql" // import _ "git.fsdpf.net/go/db/dialect/mysql"
type item struct { type item struct {
ID int64 `db:"id"` ID int64 `db:"id"`
@@ -84,7 +84,7 @@ func ExampleDialect_dbMysql() {
// Creating a mysql dataset. Be sure to import the postgres adapter // Creating a mysql dataset. Be sure to import the postgres adapter
func ExampleDialect_datasetPostgres() { func ExampleDialect_datasetPostgres() {
// import _ "git.fsdpf.net/go/db/v2/dialect/postgres" // import _ "git.fsdpf.net/go/db/dialect/postgres"
d := dbv2.Dialect("postgres") d := dbv2.Dialect("postgres")
ds := d.From("test").Where(dbv2.Ex{ 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 // Creating a postgres dataset. Be sure to import the postgres adapter
func ExampleDialect_dbPostgres() { func ExampleDialect_dbPostgres() {
// import _ "git.fsdpf.net/go/db/v2/dialect/postgres" // import _ "git.fsdpf.net/go/db/dialect/postgres"
type item struct { type item struct {
ID int64 `db:"id"` ID int64 `db:"id"`
@@ -155,7 +155,7 @@ func ExampleDialect_dbPostgres() {
// Creating a mysql dataset. Be sure to import the sqlite3 adapter // Creating a mysql dataset. Be sure to import the sqlite3 adapter
func ExampleDialect_datasetSqlite3() { func ExampleDialect_datasetSqlite3() {
// import _ "git.fsdpf.net/go/db/v2/dialect/sqlite3" // import _ "git.fsdpf.net/go/db/dialect/sqlite3"
d := dbv2.Dialect("sqlite3") d := dbv2.Dialect("sqlite3")
ds := d.From("test").Where(dbv2.Ex{ 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 // Creating a sqlite3 database. Be sure to import the sqlite3 adapter
func ExampleDialect_dbSqlite3() { func ExampleDialect_dbSqlite3() {
// import _ "git.fsdpf.net/go/db/v2/dialect/sqlite3" // import _ "git.fsdpf.net/go/db/dialect/sqlite3"
type item struct { type item struct {
ID int64 `db:"id"` ID int64 `db:"id"`
Address string `db:"address"` Address string `db:"address"`
+1 -1
View File
@@ -3,7 +3,7 @@ package db_test
import ( import (
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+27 -14
View File
@@ -1,10 +1,10 @@
package db package db
import ( import (
"git.fsdpf.net/go/db/v2/exec" "git.fsdpf.net/go/db/exec"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
var ErrBadFromArgument = errors.New("unsupported DeleteDataset#From argument, a string or identifier expression is required") var ErrBadFromArgument = errors.New("unsupported DeleteDataset#From argument, a string or identifier expression is required")
@@ -15,6 +15,7 @@ type DeleteDataset struct {
isPrepared prepared isPrepared prepared
queryFactory exec.QueryFactory queryFactory exec.QueryFactory
err error err error
hooks exec.Hooks
} }
// used internally by database to create a database with a specific adapter // 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, isPrepared: dd.isPrepared,
queryFactory: dd.queryFactory, queryFactory: dd.queryFactory,
err: dd.err, 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 // 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 // LiteralExpression: (See Literal) Will use the literal SQL
func (dd *DeleteDataset) From(table interface{}) *DeleteDataset { func (dd *DeleteDataset) From(table interface{}) *DeleteDataset {
switch t := table.(type) { return dd.copy(dd.clauses.SetFrom(exp.NewColumnListExpression(table)))
case exp.IdentifierExpression:
return dd.copy(dd.clauses.SetFrom(t)) // switch t := table.(type) {
case string: // case exp.IdentifierExpression:
return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t))) // return dd.copy(dd.clauses.SetFrom(t))
default: // case string:
panic(ErrBadFromArgument) // return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t)))
} // default:
// panic(ErrBadFromArgument)
// }
} }
// Adds a WHERE clause. See examples. // Adds a WHERE clause. See examples.
@@ -232,8 +236,17 @@ func (dd *DeleteDataset) ReturnsColumns() bool {
// db.Delete("test").Exec() // db.Delete("test").Exec()
// //
// See Dataset#ToUpdateSQL for arguments // See Dataset#ToUpdateSQL for arguments
func (dd *DeleteDataset) Executor() exec.QueryExecutor { func (dd *DeleteDataset) Executor() (executor exec.QueryExecutor) {
return dd.queryFactory.FromSQLBuilder(dd.deleteSQLBuilder()) 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 { func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder {
+2 -2
View File
@@ -3,8 +3,8 @@ package db_test
import ( import (
"fmt" "fmt"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/v2/dialect/mysql" _ "git.fsdpf.net/go/db/dialect/mysql"
) )
func ExampleDelete() { func ExampleDelete() {
+45 -45
View File
@@ -3,11 +3,11 @@ package db_test
import ( import (
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/mocks" "git.fsdpf.net/go/db/mocks"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@@ -100,7 +100,7 @@ func (dds *deleteDatasetSuite) TestPrepared() {
func (dds *deleteDatasetSuite) TestGetClauses() { func (dds *deleteDatasetSuite) TestGetClauses() {
ds := dbv2.Delete("test") ds := dbv2.Delete("test")
ce := exp.NewDeleteClauses().SetFrom(dbv2.I("test")) ce := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test"))
dds.Equal(ce, ds.GetClauses()) dds.Equal(ce, ds.GetClauses())
} }
@@ -110,12 +110,12 @@ func (dds *deleteDatasetSuite) TestWith() {
dds.assertCases( dds.assertCases(
deleteTestCase{ deleteTestCase{
ds: bd.With("test-cte", from), 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)), CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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( dds.assertCases(
deleteTestCase{ deleteTestCase{
ds: bd.WithRecursive("test-cte", from), 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)), CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from)),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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( dds.assertCases(
deleteTestCase{ deleteTestCase{
ds: bd.From("items2"), ds: bd.From("items2"),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items2")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.From(dbv2.C("items2")), ds: bd.From(dbv2.C("items2")),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items2")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.From(dbv2.T("items2")), ds: bd.From(dbv2.T("items2")),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.T("items2")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.From("schema.table"), ds: bd.From("schema.table"),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.I("schema.table")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("schema.table")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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{ deleteTestCase{
ds: bd.Where(dbv2.Ex{"a": 1}), ds: bd.Where(dbv2.Ex{"a": 1}),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
WhereAppend(dbv2.Ex{"a": 1}), WhereAppend(dbv2.Ex{"a": 1}),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Where(dbv2.Ex{"a": 1}).Where(dbv2.C("b").Eq("c")), ds: bd.Where(dbv2.Ex{"a": 1}).Where(dbv2.C("b").Eq("c")),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
WhereAppend(dbv2.Ex{"a": 1}). WhereAppend(dbv2.Ex{"a": 1}).
WhereAppend(dbv2.C("b").Eq("c")), WhereAppend(dbv2.C("b").Eq("c")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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{ deleteTestCase{
ds: bd.ClearWhere(), ds: bd.ClearWhere(),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")), SetFrom(exp.NewColumnListExpression("items")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, ds: bd,
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
WhereAppend(dbv2.Ex{"a": 1}), WhereAppend(dbv2.Ex{"a": 1}),
}, },
) )
@@ -212,24 +212,24 @@ func (dds *deleteDatasetSuite) TestOrder() {
deleteTestCase{ deleteTestCase{
ds: bd.Order(dbv2.C("a").Asc()), ds: bd.Order(dbv2.C("a").Asc()),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()), SetOrder(dbv2.C("a").Asc()),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Order(dbv2.C("a").Asc()).Order(dbv2.C("b").Desc()), ds: bd.Order(dbv2.C("a").Asc()).Order(dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("b").Desc()), SetOrder(dbv2.C("b").Desc()),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Order(dbv2.C("a").Asc(), dbv2.C("b").Desc()), ds: bd.Order(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()), SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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{ deleteTestCase{
ds: bd.OrderAppend(dbv2.C("b").Desc()), ds: bd.OrderAppend(dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()), SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, ds: bd,
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()), SetOrder(dbv2.C("a").Asc()),
}, },
) )
@@ -258,13 +258,13 @@ func (dds *deleteDatasetSuite) TestOrderPrepend() {
deleteTestCase{ deleteTestCase{
ds: bd.OrderPrepend(dbv2.C("b").Desc()), ds: bd.OrderPrepend(dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("b").Desc(), dbv2.C("a").Asc()), SetOrder(dbv2.C("b").Desc(), dbv2.C("a").Asc()),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, ds: bd,
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()), SetOrder(dbv2.C("a").Asc()),
}, },
) )
@@ -275,12 +275,12 @@ func (dds *deleteDatasetSuite) TestClearOrder() {
dds.assertCases( dds.assertCases(
deleteTestCase{ deleteTestCase{
ds: bd.ClearOrder(), ds: bd.ClearOrder(),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, ds: bd,
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()), SetOrder(dbv2.C("a").Asc()),
}, },
) )
@@ -292,26 +292,26 @@ func (dds *deleteDatasetSuite) TestLimit() {
deleteTestCase{ deleteTestCase{
ds: bd.Limit(10), ds: bd.Limit(10),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")). SetFrom(exp.NewColumnListExpression("test")).
SetLimit(uint(10)), SetLimit(uint(10)),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Limit(0), ds: bd.Limit(0),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Limit(10).Limit(2), ds: bd.Limit(10).Limit(2),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")). SetFrom(exp.NewColumnListExpression("test")).
SetLimit(uint(2)), SetLimit(uint(2)),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Limit(10).Limit(0), ds: bd.Limit(10).Limit(0),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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{ deleteTestCase{
ds: bd.LimitAll(), ds: bd.LimitAll(),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")). SetFrom(exp.NewColumnListExpression("test")).
SetLimit(dbv2.L("ALL")), SetLimit(dbv2.L("ALL")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Limit(10).LimitAll(), ds: bd.Limit(10).LimitAll(),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")). SetFrom(exp.NewColumnListExpression("test")).
SetLimit(dbv2.L("ALL")), SetLimit(dbv2.L("ALL")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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( dds.assertCases(
deleteTestCase{ deleteTestCase{
ds: bd.ClearLimit(), ds: bd.ClearLimit(),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, 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{ deleteTestCase{
ds: bd.Returning("a"), ds: bd.Returning("a"),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression("a")), SetReturning(exp.NewColumnListExpression("a")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Returning(), ds: bd.Returning(),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression()), SetReturning(exp.NewColumnListExpression()),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Returning(nil), ds: bd.Returning(nil),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression()), SetReturning(exp.NewColumnListExpression()),
}, },
deleteTestCase{ deleteTestCase{
ds: bd.Returning("a").Returning("b"), ds: bd.Returning("a").Returning("b"),
clauses: exp.NewDeleteClauses(). clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression("b")), SetReturning(exp.NewColumnListExpression("b")),
}, },
deleteTestCase{ deleteTestCase{
ds: bd, ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")), clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
}, },
) )
} }
+6 -6
View File
@@ -1,8 +1,8 @@
package mysql package mysql
import ( import (
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
) )
func DialectOptions() *db.SQLDialectOptions { func DialectOptions() *db.SQLDialectOptions {
@@ -16,11 +16,11 @@ func DialectOptions() *db.SQLDialectOptions {
opts.SupportsConflictUpdateWhere = false opts.SupportsConflictUpdateWhere = false
opts.SupportsInsertIgnoreSyntax = true opts.SupportsInsertIgnoreSyntax = true
opts.SupportsConflictTarget = false opts.SupportsConflictTarget = false
opts.SupportsWithCTE = false opts.SupportsWithCTE = true
opts.SupportsWithCTERecursive = false opts.SupportsWithCTERecursive = true
opts.SupportsDistinctOn = false opts.SupportsDistinctOn = false
opts.SupportsWindowFunction = false opts.SupportsWindowFunction = true
opts.SupportsDeleteTableHint = true opts.SupportsDeleteTableHint = false
opts.UseFromClauseForMultipleUpdateTables = false opts.UseFromClauseForMultipleUpdateTables = false
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"regexp" "regexp"
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -9,8 +9,8 @@ import (
"testing" "testing"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/dialect/mysql" "git.fsdpf.net/go/db/dialect/mysql"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -1,6 +1,6 @@
package postgres package postgres
import "git.fsdpf.net/go/db/v2" import "git.fsdpf.net/go/db"
func DialectOptions() *db.SQLDialectOptions { func DialectOptions() *db.SQLDialectOptions {
do := db.DefaultDialectOptions() do := db.DefaultDialectOptions()
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
+2 -2
View File
@@ -3,8 +3,8 @@ package sqlite3
import ( import (
"time" "time"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
) )
func DialectOptions() *db.SQLDialectOptions { func DialectOptions() *db.SQLDialectOptions {
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"regexp" "regexp"
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+3 -3
View File
@@ -8,9 +8,9 @@ import (
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/dialect/mysql" "git.fsdpf.net/go/db/dialect/mysql"
"git.fsdpf.net/go/db/v2/dialect/sqlite3" "git.fsdpf.net/go/db/dialect/sqlite3"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+3 -3
View File
@@ -1,9 +1,9 @@
package sqlserver package sqlserver
import ( import (
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
) )
func DialectOptions() *db.SQLDialectOptions { func DialectOptions() *db.SQLDialectOptions {
+2 -2
View File
@@ -3,8 +3,8 @@ package sqlserver_test
import ( import (
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+3 -3
View File
@@ -7,10 +7,10 @@ import (
"testing" "testing"
"time" "time"
"git.fsdpf.net/go/db/v2/dialect/mysql" "git.fsdpf.net/go/db/dialect/mysql"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/v2/dialect/sqlserver" _ "git.fsdpf.net/go/db/dialect/sqlserver"
_ "github.com/denisenkom/go-mssqldb" _ "github.com/denisenkom/go-mssqldb"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+38 -9
View File
@@ -1,6 +1,7 @@
package engine package engine
import ( import (
"context"
"database/sql" "database/sql"
"fmt" "fmt"
@@ -8,15 +9,16 @@ import (
_ "github.com/denisenkom/go-mssqldb" _ "github.com/denisenkom/go-mssqldb"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/mattn/go-sqlite3"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
_ "git.fsdpf.net/go/db/v2/dialect/mysql" _ "git.fsdpf.net/go/db/dialect/mysql"
_ "git.fsdpf.net/go/db/v2/dialect/postgres" _ "git.fsdpf.net/go/db/dialect/postgres"
_ "git.fsdpf.net/go/db/v2/dialect/sqlite3" _ "git.fsdpf.net/go/db/dialect/sqlite3"
_ "git.fsdpf.net/go/db/v2/dialect/sqlserver" _ "git.fsdpf.net/go/db/dialect/sqlserver"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
) )
type Engine struct { type Engine struct {
@@ -50,12 +52,31 @@ func (e Engine) Connection(name string) *db.Database {
return db.New(cfg.Driver, _db) 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() dsn := cfg.ToDSN()
switch cfg.Driver { switch cfg.Driver {
case "mysql": case "mysql":
case "sqlite3": 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 "sqlserver":
case "postgres": case "postgres":
if url, err := pq.ParseURL(dsn); err == nil { if url, err := pq.ParseURL(dsn); err == nil {
@@ -80,10 +101,18 @@ func (e Engine) MakeConnection(cfg DBConfig) *sql.DB {
return db return db
} }
func Open(cfgs map[string]DBConfig) *Engine { func Open(cfgs map[string]DBConfig) Engine {
for n, cfg := range cfgs { for n, cfg := range cfgs {
_engine.configs[n] = cfg _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
} }
+19
View File
@@ -1,11 +1,19 @@
package engine package engine
import ( import (
"database/sql"
"fmt" "fmt"
"net/url" "net/url"
"time" "time"
"github.com/mattn/go-sqlite3"
) )
type MockDBConfig struct {
Driver string
Mock *sql.DB
}
// DBConfig 数据库配置结构体 // DBConfig 数据库配置结构体
type DBConfig struct { type DBConfig struct {
Driver string Driver string
@@ -48,6 +56,7 @@ type DBConfig struct {
Synchronous int Synchronous int
Cache string Cache string
BusyTimeout int BusyTimeout int
Raw func(*sqlite3.SQLiteConn) error
} }
SQLServer struct { 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 { func WithSQLiteJournal(journal string) Option {
return func(c *DBConfig) { return func(c *DBConfig) {
if c.Driver != "sqlite3" { if c.Driver != "sqlite3" {
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"git.fsdpf.net/go/db/v2/engine" "git.fsdpf.net/go/db/engine"
) )
// TestMySQLConfig 测试 MySQL 驱动的配置 // TestMySQLConfig 测试 MySQL 驱动的配置
+54 -6
View File
@@ -5,12 +5,13 @@ import (
gsql "database/sql" gsql "database/sql"
"reflect" "reflect"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util" "git.fsdpf.net/go/db/internal/util"
) )
type ( type (
QueryExecutor struct { QueryExecutor struct {
hook func(result interface{}) error
de DbExecutor de DbExecutor
err error err error
query string query string
@@ -19,6 +20,8 @@ type (
) )
var ( 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") 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") 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") 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()) 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 { if q.err != nil {
return nil, q.err 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) { func (q QueryExecutor) Query() (*gsql.Rows, error) {
return q.QueryContext(context.Background()) 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 { if q.err != nil {
return nil, q.err 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 // 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() 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. // Scanner will return a Scanner that can be used for manually scanning rows.
func (q QueryExecutor) Scanner() (Scanner, error) { func (q QueryExecutor) Scanner() (Scanner, error) {
return q.ScannerContext(context.Background()) return q.ScannerContext(context.Background())
@@ -251,3 +295,7 @@ func (q QueryExecutor) ScannerContext(ctx context.Context) (Scanner, error) {
} }
return NewScanner(rows), nil return NewScanner(rows), nil
} }
func (q *QueryExecutor) Hook(hook func(dataset interface{}) error) {
q.hook = hook
}
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
type ( type (
+7
View File
@@ -0,0 +1,7 @@
package exec
// Hooks 钩子实例
type Hooks interface {
Before(dataset interface{}) error
After(dataset interface{}, result interface{}) error
}
+132 -21
View File
@@ -2,11 +2,11 @@ package exec
import ( import (
"database/sql" "database/sql"
"encoding/json"
"reflect" "reflect"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/util"
"git.fsdpf.net/go/db/v2/internal/util"
) )
type ( type (
@@ -17,6 +17,8 @@ type (
ScanStructs(i interface{}) error ScanStructs(i interface{}) error
ScanVal(i interface{}) error ScanVal(i interface{}) error
ScanVals(i interface{}) error ScanVals(i interface{}) error
GetRecord() (map[string]any, error)
GetRecords() ([]map[string]any, error)
Close() error Close() error
Err() error Err() error
} }
@@ -49,6 +51,86 @@ func (s *scanner) Err() error {
return s.rows.Err() 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. // ScanStruct will scan the current row into i.
func (s *scanner) ScanStruct(i interface{}) error { func (s *scanner) ScanStruct(i interface{}) error {
// Setup columnMap and columns, but only once. // Setup columnMap and columns, but only once.
@@ -63,35 +145,37 @@ func (s *scanner) ScanStruct(i interface{}) error {
return err 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.columnMap = cm
s.columns = cols s.columns = cols
} }
scans := make([]interface{}, 0, len(s.columns)) scans, err := createColumnScans(s.columns, s.columnMap)
for _, col := range s.columns {
data, ok := s.columnMap[col]
if !ok { if err != nil {
return unableToFindFieldError(col) return err
}
// 处理 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 := s.rows.Scan(scans...); err != nil { if err := s.rows.Scan(scans...); err != nil {
return err return err
} }
record := exp.Record{} record := map[string]interface{}{}
for index, col := range s.columns { for index, col := range s.columns {
record[col] = scans[index] record[col] = scans[index]
} }
@@ -175,3 +259,30 @@ func checkScanValsTarget(i interface{}) (reflect.Value, error) {
} }
return val, nil 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
}
+25
View File
@@ -3,6 +3,7 @@ package exec
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/exp"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -67,3 +68,27 @@ func (s *scannerSuite) TestScanVals() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().ElementsMatch([]int{1, 2}, result) 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)
}
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+9 -2
View File
@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"git.fsdpf.net/go/db/v2/internal/util" "git.fsdpf.net/go/db/internal/util"
) )
type columnList struct { type columnList struct {
@@ -23,7 +23,8 @@ func NewColumnListExpression(vals ...interface{}) ColumnListExpression {
case Expression: case Expression:
cols = append(cols, t) cols = append(cols, t)
default: 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 { if valKind == reflect.Struct {
cm, err := util.GetColumnMap(val) cm, err := util.GetColumnMap(val)
@@ -39,6 +40,12 @@ func NewColumnListExpression(vals ...interface{}) ColumnListExpression {
} }
cols = append(cols, sc) 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 { } else {
panic(fmt.Sprintf("Cannot create expression from %+v", val)) panic(fmt.Sprintf("Cannot create expression from %+v", val))
} }
+5 -5
View File
@@ -8,8 +8,8 @@ type (
CommonTables() []CommonTableExpression CommonTables() []CommonTableExpression
CommonTablesAppend(cte CommonTableExpression) DeleteClauses CommonTablesAppend(cte CommonTableExpression) DeleteClauses
From() IdentifierExpression From() ColumnListExpression
SetFrom(table IdentifierExpression) DeleteClauses SetFrom(table ColumnListExpression) DeleteClauses
Where() ExpressionList Where() ExpressionList
ClearWhere() DeleteClauses ClearWhere() DeleteClauses
@@ -33,7 +33,7 @@ type (
} }
deleteClauses struct { deleteClauses struct {
commonTables []CommonTableExpression commonTables []CommonTableExpression
from IdentifierExpression from ColumnListExpression
where ExpressionList where ExpressionList
order ColumnListExpression order ColumnListExpression
limit interface{} limit interface{}
@@ -71,11 +71,11 @@ func (dc *deleteClauses) CommonTablesAppend(cte CommonTableExpression) DeleteCla
return ret return ret
} }
func (dc *deleteClauses) From() IdentifierExpression { func (dc *deleteClauses) From() ColumnListExpression {
return dc.from return dc.from
} }
func (dc *deleteClauses) SetFrom(table IdentifierExpression) DeleteClauses { func (dc *deleteClauses) SetFrom(table ColumnListExpression) DeleteClauses {
ret := dc.clone() ret := dc.clone()
ret.from = table ret.from = table
return ret return ret
+4 -4
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -17,7 +17,7 @@ func TestDeleteClausesSuite(t *testing.T) {
func (dcs *deleteClausesSuite) TestHasFrom() { func (dcs *deleteClausesSuite) TestHasFrom() {
c := exp.NewDeleteClauses() c := exp.NewDeleteClauses()
c2 := c.SetFrom(exp.NewIdentifierExpression("", "test", "")) c2 := c.SetFrom(exp.NewColumnListExpression("test"))
dcs.False(c.HasFrom()) dcs.False(c.HasFrom())
@@ -26,7 +26,7 @@ func (dcs *deleteClausesSuite) TestHasFrom() {
func (dcs *deleteClausesSuite) TestFrom() { func (dcs *deleteClausesSuite) TestFrom() {
c := exp.NewDeleteClauses() c := exp.NewDeleteClauses()
ti := exp.NewIdentifierExpression("", "a", "") ti := exp.NewColumnListExpression("a")
c2 := c.SetFrom(ti) c2 := c.SetFrom(ti)
dcs.Nil(c.From()) dcs.Nil(c.From())
@@ -36,7 +36,7 @@ func (dcs *deleteClausesSuite) TestFrom() {
func (dcs *deleteClausesSuite) TestSetFrom() { func (dcs *deleteClausesSuite) TestSetFrom() {
c := exp.NewDeleteClauses() c := exp.NewDeleteClauses()
ti := exp.NewIdentifierExpression("", "a", "") ti := exp.NewColumnListExpression("a")
c2 := c.SetFrom(ti) c2 := c.SetFrom(ti)
dcs.Nil(c.From()) dcs.Nil(c.From())
+1 -1
View File
@@ -3,7 +3,7 @@ package exp
import ( import (
"fmt" "fmt"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
// Behaviors // Behaviors
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"sort" "sort"
"strings" "strings"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
) )
type ( type (
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"reflect" "reflect"
"sort" "sort"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util" "git.fsdpf.net/go/db/internal/util"
) )
type ( type (
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"reflect" "reflect"
"sort" "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 // Alternative to writing map[string]interface{}. Can be used for Inserts, Updates or Deletes
+14
View File
@@ -22,6 +22,7 @@ type (
Joins() JoinExpressions Joins() JoinExpressions
JoinsAppend(jc JoinExpression) SelectClauses JoinsAppend(jc JoinExpression) SelectClauses
JoinsPrepend(jc JoinExpression) SelectClauses
Where() ExpressionList Where() ExpressionList
ClearWhere() SelectClauses ClearWhere() SelectClauses
@@ -41,6 +42,7 @@ type (
GroupBy() ColumnListExpression GroupBy() ColumnListExpression
SetGroupBy(cl ColumnListExpression) SelectClauses SetGroupBy(cl ColumnListExpression) SelectClauses
GroupByAppend(cl ColumnListExpression) SelectClauses GroupByAppend(cl ColumnListExpression) SelectClauses
ClearGroupBy() SelectClauses
Limit() interface{} Limit() interface{}
HasLimit() bool HasLimit() bool
@@ -197,6 +199,12 @@ func (c *selectClauses) JoinsAppend(jc JoinExpression) SelectClauses {
return ret 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 { func (c *selectClauses) Where() ExpressionList {
return c.where return c.where
} }
@@ -310,6 +318,12 @@ func (c *selectClauses) SetGroupBy(cl ColumnListExpression) SelectClauses {
return ret return ret
} }
func (c *selectClauses) ClearGroupBy() SelectClauses {
ret := c.clone()
ret.groupBy = nil
return ret
}
func (c *selectClauses) Limit() interface{} { func (c *selectClauses) Limit() interface{} {
return c.limit return c.limit
} }
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"reflect" "reflect"
"sort" "sort"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util" "git.fsdpf.net/go/db/internal/util"
) )
type ( type (
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -1,7 +1,7 @@
package db package db
import ( import (
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
) )
type ( type (
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"regexp" "regexp"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
) )
func ExampleAVG() { func ExampleAVG() {
+2 -2
View File
@@ -3,8 +3,8 @@ package db_test
import ( import (
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -1
View File
@@ -1,4 +1,4 @@
module git.fsdpf.net/go/db/v2 module git.fsdpf.net/go/db
go 1.19 go 1.19
@@ -18,6 +18,7 @@ require (
github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // 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/crypto v0.11.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
+2
View File
@@ -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.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 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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-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-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+83 -8
View File
@@ -2,11 +2,12 @@ package db
import ( import (
"fmt" "fmt"
"reflect"
"git.fsdpf.net/go/db/v2/exec" "git.fsdpf.net/go/db/exec"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
type InsertDataset struct { type InsertDataset struct {
@@ -15,6 +16,7 @@ type InsertDataset struct {
isPrepared prepared isPrepared prepared
queryFactory exec.QueryFactory queryFactory exec.QueryFactory
err error err error
hooks exec.Hooks
} }
var ErrUnsupportedIntoType = errors.New("unsupported table type, a string or identifier expression is required") 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, isPrepared: id.isPrepared,
queryFactory: id.queryFactory, queryFactory: id.queryFactory,
err: id.err, err: id.err,
hooks: id.hooks,
} }
} }
@@ -170,9 +173,65 @@ func (id *InsertDataset) ClearVals() *InsertDataset {
return id.copy(id.clauses.SetVals(nil)) 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 { 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. // 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 // 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() // db.Insert("test").Rows(Record{"name":"Bob"}).Executor().Exec()
func (id *InsertDataset) Executor() exec.QueryExecutor { func (id *InsertDataset) Executor() (executor exec.QueryExecutor) {
return id.queryFactory.FromSQLBuilder(id.insertSQLBuilder()) 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 { func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"fmt" "fmt"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/v2/dialect/postgres" _ "git.fsdpf.net/go/db/dialect/postgres"
) )
func ExampleInsert_dbv2Record() { func ExampleInsert_dbv2Record() {
+5 -5
View File
@@ -4,11 +4,11 @@ import (
"testing" "testing"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/mocks" "git.fsdpf.net/go/db/mocks"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
+9 -8
View File
@@ -5,7 +5,7 @@ import (
"sort" "sort"
"strings" "strings"
"git.fsdpf.net/go/db/v2/internal/tag" "git.fsdpf.net/go/db/internal/tag"
) )
type ( 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 // if PkgPath is empty then it is an exported field
columnName := getColumnName(&f, dbTag) columnName := getColumnName(&f, dbTag)
if !shouldIgnoreField(dbTag) { if !shouldIgnoreField(dbTag) {
if !implementsScanner(f.Type) { // 移除原来的关联结构,并 scans 字段出现 table.col
subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes) // if !implementsScanner(f.Type) {
if len(subCm) != 0 { // subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes)
subColMaps = append(subColMaps, subCm) // if len(subCm) != 0 {
continue // subColMaps = append(subColMaps, subCm)
} // continue
} // }
// }
ffTag := tag.New("ff", f.Tag) ffTag := tag.New("ff", f.Tag)
columnName = strings.Join(append(prefixes, columnName), ".") columnName = strings.Join(append(prefixes, columnName), ".")
cm[columnName] = newColumnData(&f, columnName, fieldIndex, ffTag) cm[columnName] = newColumnData(&f, columnName, fieldIndex, ffTag)
+39 -11
View File
@@ -2,11 +2,12 @@ package util
import ( import (
"database/sql" "database/sql"
"encoding/json"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
) )
const ( const (
@@ -56,6 +57,10 @@ func IsStruct(k reflect.Kind) bool {
return k == reflect.Struct return k == reflect.Struct
} }
func IsMap(k reflect.Kind) bool {
return k == reflect.Map
}
func IsInvalid(k reflect.Kind) bool { func IsInvalid(k reflect.Kind) bool {
return k == reflect.Invalid return k == reflect.Invalid
} }
@@ -117,6 +122,15 @@ func GetSliceElementType(val reflect.Value) reflect.Type {
return elemType 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 // AppendSliceElement will append val to slice. Handles slice of pointers and
// not pointers. Val needs to be a pointer. // not pointers. Val needs to be a pointer.
func AppendSliceElement(slice, val reflect.Value) { func AppendSliceElement(slice, val reflect.Value) {
@@ -167,16 +181,8 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re
return v return v
case 1: case 1:
f := v.FieldByIndex(fieldIndex) f := v.FieldByIndex(fieldIndex)
srcVal := reflect.ValueOf(src).Elem() if err := SafeSetVarValue(f, src); err != nil {
// 处理 converting NULL to string is unsupported panic(err)
// 前面将 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)
} }
default: default:
f := v.Field(fieldIndex[0]) f := v.Field(fieldIndex[0])
@@ -196,6 +202,28 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re
return v 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{} type rowData = map[string]interface{}
// AssignStructVals will assign the data from rd to i. // AssignStructVals will assign the data from rd to i.
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing" "testing"
"time" "time"
"git.fsdpf.net/go/db/v2/internal/util" "git.fsdpf.net/go/db/internal/util"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"testing" "testing"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -2,10 +2,10 @@
package mocks 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 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 // SQLDialect is an autogenerated mock type for the SQLDialect type
type SQLDialect struct { type SQLDialect struct {
+14 -31
View File
@@ -10,10 +10,11 @@ type Blueprint struct {
table string // the table the blueprint describes. table string // the table the blueprint describes.
columns []*ColumnDefinition // columns that should be added to the table columns []*ColumnDefinition // columns that should be added to the table
commands []*Command // commands []*Command //
temporary bool // Whether to make the table temporary. Temporary bool // Whether to make the table temporary.
charset string // The default character set that should be used for the table. Charset string // The default character set that should be used for the table.
collation string // The collation 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. Engine string // The engine that should be used for the table.
Comment string
} }
type Command struct { type Command struct {
@@ -30,7 +31,7 @@ type CommandOptions struct {
} }
func NewBlueprint(table string) *Blueprint { 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{}) 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 { func (this *Blueprint) Rename(to string) *Command {
return this.addCommand("rename", CommandOptions{To: to}) 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 { func (this *Blueprint) Drop() *Command {
return this.addCommand("drop", CommandOptions{}) return this.addCommand("drop", CommandOptions{})
@@ -307,6 +304,8 @@ func (this *Blueprint) ToSql(sc Schema) (statements []string) {
statements = append(statements, sc.CompileDropColumn(this)...) statements = append(statements, sc.CompileDropColumn(this)...)
case "rename": case "rename":
statements = append(statements, sc.CompileRename(this)...) 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 return this.commands
} }
func (this *Blueprint) IsTemporary() bool {
return this.temporary
}
func (this *Blueprint) GetTable() string { func (this *Blueprint) GetTable() string {
return this.table 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 { func (this *Command) Command() string {
return this.Type return this.Type
+8 -1
View File
@@ -1,7 +1,7 @@
package schema package schema
import ( import (
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
) )
type Builder struct { type Builder struct {
@@ -48,6 +48,13 @@ func (this Builder) Rename(from, to string) error {
return this.Build(bp) 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 { func (this Builder) Drop(table string) error {
bp := NewBlueprint(table) bp := NewBlueprint(table)
+20 -10
View File
@@ -3,11 +3,11 @@ package mysql
import ( import (
"strings" "strings"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/dialect/mysql" "git.fsdpf.net/go/db/dialect/mysql"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
) )
type Mysql struct { type Mysql struct {
@@ -16,6 +16,7 @@ type Mysql struct {
} }
var mysqlDefaultModifiers = []string{ var mysqlDefaultModifiers = []string{
"Unsigned", "Charset", "Collate", "VirtualAs", "StoredAs", "Unsigned", "Charset", "Collate", "VirtualAs", "StoredAs",
"Nullable", "Default", "Increment", "Comment", "After", "First", "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 { func (this Mysql) CompileCreate(bp *schema.Blueprint) []string {
temporary := db.L("CREATE") temporary := db.L("CREATE")
if bp.IsTemporary() { if bp.Temporary {
temporary = db.L("CREATE TEMPORARY") temporary = db.L("CREATE TEMPORARY")
} }
columns := strings.Join(this.getAddedColumns(bp), ",\n") columns := strings.Join(this.getAddedColumns(bp), ",\n")
sql := this.GenerateSQL("? TABLE ? (\n?\n)", temporary, db.T(bp.GetTable()), db.L(columns)) sql := this.GenerateSQL("? TABLE ? (\n?\n)", temporary, db.T(bp.GetTable()), db.L(columns))
charset := bp.GetCharset() charset := bp.Charset
if charset == "" { if charset == "" {
charset = "utf8mb4" charset = "utf8mb4"
} }
collation := bp.GetCollation() collation := bp.Collation
if collation == "" { if collation == "" {
collation = "utf8mb4_general_ci" collation = "utf8mb4_general_ci"
} }
sql += this.GenerateSQL(" default charset=? collate=?", charset, collation) sql += this.GenerateSQL(" default charset=? collate=?", charset, collation)
if engine := bp.GetEngine(); engine != "" { if engine := bp.Engine; engine != "" {
sql += this.GenerateSQL(" engine=?", engine) sql += this.GenerateSQL(" engine=?", engine)
} }
if comment := bp.Comment; comment != "" {
sql += this.GenerateSQL(" comment=?", comment)
}
return []string{sql} return []string{sql}
} }
@@ -121,13 +126,18 @@ func (this Mysql) CompileRename(bp *schema.Blueprint) []string {
if len(commands) == 0 { if len(commands) == 0 {
panic("new table undefined") panic("new table undefined")
} }
toName = bp.GetCommands()[0].To toName = commands[0].To
if toName == "" { if toName == "" {
panic("new table undefined") panic("new table undefined")
} }
return []string{this.GenerateSQL("RENAME TABLE ? TO ?", db.T(bp.GetTable()), db.T(toName))} 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 { func (this Mysql) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
for _, modifier := range mysqlDefaultModifiers { for _, modifier := range mysqlDefaultModifiers {
sql += this.GetColumnModifier(modifier, bp, column) sql += this.GetColumnModifier(modifier, bp, column)
+4 -4
View File
@@ -3,9 +3,9 @@ package mysql_test
import ( import (
"os" "os"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -40,7 +40,7 @@ func (t *mysqlBuilderTest) TestGetColumnListing() {
// 创建表 // 创建表
func (t *mysqlBuilderTest) TestCreateTable() { func (t *mysqlBuilderTest) TestCreateTable() {
if err := t.builder.Create("users", func(table *schema.Blueprint) { if err := t.builder.Create("users", func(table *schema.Blueprint) {
table.Charset("utf8mb4") table.Charset = "utf8mb4"
table.BigIncrements("id").Comment("ID") table.BigIncrements("id").Comment("ID")
table.Boolean("enabled").Default("1").Comment("是否有效") table.Boolean("enabled").Default("1").Comment("是否有效")
+6 -4
View File
@@ -4,9 +4,9 @@ import (
"os" "os"
"testing" "testing"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
@@ -71,7 +71,7 @@ func (t *mysqlTest) TestTableExists() {
func (t *mysqlTest) TestCompileCreate() { func (t *mysqlTest) TestCompileCreate() {
bp := schema.NewBlueprint("users") bp := schema.NewBlueprint("users")
bp.Create() bp.Create()
bp.Charset("utf8mb4") bp.Charset = "utf8mb4"
bp.BigIncrements("id").AutoIncrement().Comment("ID") bp.BigIncrements("id").AutoIncrement().Comment("ID")
bp.Boolean("enabled").Default("1").Comment("是否有效") bp.Boolean("enabled").Default("1").Comment("是否有效")
@@ -188,3 +188,5 @@ func (t *mysqlTest) TestCompileRename() {
t.T().Log(sql) t.T().Log(sql)
} }
// 修改表备注
+11 -6
View File
@@ -3,11 +3,11 @@ package postgres
import ( import (
"strings" "strings"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/dialect/postgres" "git.fsdpf.net/go/db/dialect/postgres"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
) )
type Postgres struct { type Postgres struct {
@@ -41,7 +41,7 @@ func (this Postgres) TableExists(table string) (bool, error) {
func (this Postgres) CompileCreate(bp *schema.Blueprint) []string { func (this Postgres) CompileCreate(bp *schema.Blueprint) []string {
temporary := db.L("CREATE") temporary := db.L("CREATE")
if bp.IsTemporary() { if bp.Temporary {
temporary = db.L("CREATE TEMPORARY") temporary = db.L("CREATE TEMPORARY")
} }
columns := strings.Join(this.getAddedColumns(bp), ",\n") 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))} 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 { func (this Postgres) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
for _, modifier := range pgDefaultModifiers { for _, modifier := range pgDefaultModifiers {
sql += this.GetColumnModifier(modifier, bp, column) sql += this.GetColumnModifier(modifier, bp, column)
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"os" "os"
"testing" "testing"
"git.fsdpf.net/go/db/v2/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
_ "github.com/lib/pq" _ "github.com/lib/pq"
+18 -6
View File
@@ -5,11 +5,11 @@ import (
"strings" "strings"
"time" "time"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/dialect/sqlite3" "git.fsdpf.net/go/db/dialect/sqlite3"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
) )
var ( var (
@@ -72,8 +72,15 @@ func (this Sqlite3) CompileCreate(bp *schema.Blueprint) (sqls []string) {
} }
columns = append(columns, sql) columns = append(columns, sql)
} }
prefix := "? TABLE ? (\n?\n)"
if comment := bp.Comment; comment != "" {
prefix = "? TABLE ? ( -- " + comment + "\n?\n)"
}
sqls = append(sqls, sqls = append(sqls,
this.GenerateSQL("? TABLE ? (\n?\n)", this.GenerateSQL(prefix,
temporary, temporary,
db.T(bp.GetTable()), db.T(bp.GetTable()),
db.L(strings.Join(columns, "\n")), 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))} 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 { func (this Sqlite3) GetColumnModifier(modifier string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
switch modifier { switch modifier {
case "Collate": case "Collate":
+4 -4
View File
@@ -4,9 +4,9 @@ import (
"os" "os"
"testing" "testing"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@@ -68,7 +68,7 @@ func (t *sqlite3Test) TestGetColumnListing() {
func (t *sqlite3Test) TestCompileCreate() { func (t *sqlite3Test) TestCompileCreate() {
bp := schema.NewBlueprint("users") bp := schema.NewBlueprint("users")
bp.Create() bp.Create()
bp.Charset("utf8mb4") bp.Charset = "utf8mb4"
bp.BigIncrements("id").AutoIncrement().Comment("ID") bp.BigIncrements("id").AutoIncrement().Comment("ID")
bp.Boolean("enabled").Default("1").Comment("是否有效") bp.Boolean("enabled").Default("1").Comment("是否有效")
+35 -6
View File
@@ -3,11 +3,11 @@ package sqlserver
import ( import (
"strings" "strings"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/dialect/sqlserver" "git.fsdpf.net/go/db/dialect/sqlserver"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
) )
type SQLServer struct { type SQLServer struct {
@@ -39,7 +39,7 @@ func (this SQLServer) TableExists(table string) (bool, error) {
func (this SQLServer) CompileCreate(bp *schema.Blueprint) []string { func (this SQLServer) CompileCreate(bp *schema.Blueprint) []string {
temporary := db.L("CREATE") temporary := db.L("CREATE")
if bp.IsTemporary() { if bp.Temporary {
temporary = db.L("CREATE") temporary = db.L("CREATE")
} }
columns := strings.Join(this.getAddedColumns(bp), ",\n") 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))} 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 { func (this SQLServer) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
for _, modifier := range sqlServerDefaultModifiers { for _, modifier := range sqlServerDefaultModifiers {
sql += this.GetColumnModifier(modifier, bp, column) sql += this.GetColumnModifier(modifier, bp, column)
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"os" "os"
"testing" "testing"
"git.fsdpf.net/go/db/v2/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/v2/schema" "git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
_ "github.com/denisenkom/go-mssqldb" _ "github.com/denisenkom/go-mssqldb"
+3 -1
View File
@@ -5,7 +5,7 @@ import (
"strings" "strings"
"sync" "sync"
"git.fsdpf.net/go/db/v2" "git.fsdpf.net/go/db"
) )
type Schema interface { type Schema interface {
@@ -27,6 +27,8 @@ type Schema interface {
CompileDropColumn(bp *Blueprint) []string CompileDropColumn(bp *Blueprint) []string
// 表重命名 // 表重命名
CompileRename(bp *Blueprint) []string CompileRename(bp *Blueprint) []string
// 修改表备注
CompileModifyComment(bp *Blueprint) []string
// 生成SQL // 生成SQL
GenerateSQL(sql string, args ...any) string GenerateSQL(sql string, args ...any) string
} }
-18
View File
@@ -1,26 +1,8 @@
package schema package schema
import (
"strings"
"github.com/samber/lo"
)
func PrefixArray(prefix string, values []string) (items []string) { func PrefixArray(prefix string, values []string) (items []string) {
for _, value := range values { for _, value := range values {
items = append(items, prefix+" "+value) items = append(items, prefix+" "+value)
} }
return items 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 ""
}
+42 -7
View File
@@ -4,10 +4,10 @@ import (
"context" "context"
"fmt" "fmt"
"git.fsdpf.net/go/db/v2/exec" "git.fsdpf.net/go/db/exec"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
// Dataset for creating and/or executing SELECT SQL statements. // Dataset for creating and/or executing SELECT SQL statements.
@@ -17,6 +17,7 @@ type SelectDataset struct {
isPrepared prepared isPrepared prepared
queryFactory exec.QueryFactory queryFactory exec.QueryFactory
err error err error
hooks exec.Hooks
} }
var ErrQueryFactoryNotFoundError = errors.New( var ErrQueryFactoryNotFoundError = errors.New(
@@ -94,6 +95,7 @@ func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset {
isPrepared: sd.isPrepared, isPrepared: sd.isPrepared,
queryFactory: sd.queryFactory, queryFactory: sd.queryFactory,
err: sd.err, err: sd.err,
hooks: sd.hooks,
} }
} }
@@ -121,6 +123,7 @@ func (sd *SelectDataset) Update() *UpdateDataset {
} }
} }
u.clauses = c u.clauses = c
u.hooks = sd.hooks
return u return u
} }
@@ -130,13 +133,18 @@ func (sd *SelectDataset) Insert() *InsertDataset {
i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory). i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory).
Prepared(sd.isPrepared.Bool()) Prepared(sd.isPrepared.Bool())
if sd.clauses.HasSources() { 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 c := i.clauses
for _, ce := range sd.clauses.CommonTables() { for _, ce := range sd.clauses.CommonTables() {
c = c.CommonTablesAppend(ce) c = c.CommonTablesAppend(ce)
} }
i.clauses = c i.clauses = c
i.hooks = sd.hooks
return i return i
} }
@@ -164,6 +172,7 @@ func (sd *SelectDataset) Delete() *DeleteDataset {
} }
} }
d.clauses = c d.clauses = c
d.hooks = sd.hooks
return d return d
} }
@@ -353,6 +362,11 @@ func (sd *SelectDataset) joinTable(join exp.JoinExpression) *SelectDataset {
return sd.copy(sd.clauses.JoinsAppend(join)) 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. // Adds a WHERE clause. See examples.
func (sd *SelectDataset) Where(expressions ...exp.Expression) *SelectDataset { func (sd *SelectDataset) Where(expressions ...exp.Expression) *SelectDataset {
return sd.copy(sd.clauses.WhereAppend(expressions...)) 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...))) 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. // Adds a GROUP BY clause. See examples.
func (sd *SelectDataset) GroupBy(groupBy ...interface{}) *SelectDataset { func (sd *SelectDataset) GroupBy(groupBy ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SetGroupBy(exp.NewColumnListExpression(groupBy...))) 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() // db.From("test").Select("col").Executor()
// //
// See Dataset#ToUpdateSQL for arguments // See Dataset#ToUpdateSQL for arguments
func (sd *SelectDataset) Executor() exec.QueryExecutor { func (sd *SelectDataset) Executor() (executor exec.QueryExecutor) {
return sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder()) 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 // Appends this Dataset's SELECT statement to the SQLBuilder
+2 -2
View File
@@ -8,8 +8,8 @@ import (
"regexp" "regexp"
"time" "time"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"github.com/lib/pq" "github.com/lib/pq"
) )
+6 -6
View File
@@ -3,11 +3,11 @@ package db_test
import ( import (
"testing" "testing"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/mocks" "git.fsdpf.net/go/db/mocks"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@@ -137,7 +137,7 @@ func (sds *selectDatasetSuite) TestDelete() {
Limit(limit). Limit(limit).
Order(order...) Order(order...)
ec := exp.NewDeleteClauses(). ec := exp.NewDeleteClauses().
SetFrom(dbv2.C("test")). SetFrom(exp.NewColumnListExpression("test")).
CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)). CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)).
WhereAppend(ds.GetClauses().Where()). WhereAppend(ds.GetClauses().Where()).
SetLimit(limit). SetLimit(limit).
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"strings" "strings"
"sync" "sync"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
) )
type ( type (
+1 -1
View File
@@ -3,7 +3,7 @@ package db_test
import ( import (
"fmt" "fmt"
dbv2 "git.fsdpf.net/go/db/v2" dbv2 "git.fsdpf.net/go/db"
) )
func ExampleRegisterDialect() { func ExampleRegisterDialect() {
+3 -3
View File
@@ -3,9 +3,9 @@ package db
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen/mocks" "git.fsdpf.net/go/db/sqlgen/mocks"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+1 -1
View File
@@ -1,7 +1,7 @@
package sqlgen_test package sqlgen_test
import ( import (
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+3 -3
View File
@@ -1,9 +1,9 @@
package sqlgen package sqlgen
import ( import (
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
var ErrNoUpdatedValuesProvided = errors.New("no update values provided") var ErrNoUpdatedValuesProvided = errors.New("no update values provided")
+3 -3
View File
@@ -3,9 +3,9 @@ package sqlgen_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+12 -4
View File
@@ -1,9 +1,9 @@
package sqlgen package sqlgen
import ( import (
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
type ( 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) { func (dsg *deleteSQLGenerator) DeleteBeginSQL(b sb.SQLBuilder, from exp.ColumnListExpression, multiTable bool) {
b.Write(dsg.DialectOptions().DeleteClause) b.Write(dsg.DialectOptions().DeleteClause)
if multiTable && dsg.DialectOptions().SupportsDeleteTableHint { 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)
} }
} }
+12 -12
View File
@@ -3,10 +3,10 @@ package sqlgen_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -50,7 +50,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestDialect() {
func (dsgs *deleteSQLGeneratorSuite) TestGenerate() { func (dsgs *deleteSQLGeneratorSuite) TestGenerate() {
dc := exp.NewDeleteClauses(). dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")) SetFrom(exp.NewColumnListExpression("test"))
dsgs.assertCases( dsgs.assertCases(
sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()), sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()),
@@ -72,7 +72,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withUnsupportedFragment() {
opts := sqlgen.DefaultDialectOptions() opts := sqlgen.DefaultDialectOptions()
opts.DeleteSQLOrder = []sqlgen.SQLFragmentType{sqlgen.InsertBeingSQLFragment} opts.DeleteSQLOrder = []sqlgen.SQLFragmentType{sqlgen.InsertBeingSQLFragment}
dc := exp.NewDeleteClauses(). dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")) SetFrom(exp.NewColumnListExpression("test"))
dsgs.assertCases( dsgs.assertCases(
sqlgen.NewDeleteSQLGenerator("test", opts), sqlgen.NewDeleteSQLGenerator("test", opts),
@@ -94,7 +94,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withErroredBuilder() {
opts := sqlgen.DefaultDialectOptions() opts := sqlgen.DefaultDialectOptions()
d := sqlgen.NewDeleteSQLGenerator("test", opts) 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")) b := sb.NewSQLBuilder(false).SetError(errors.New("expected error"))
d.Generate(b, dc) d.Generate(b, dc)
dsgs.assertErrorSQL(b, "db: expected error") dsgs.assertErrorSQL(b, "db: expected error")
@@ -111,7 +111,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withCommonTables() {
tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil) 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)) dcCte1 := dc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test_cte", tse))
dcCte2 := dc.CommonTablesAppend(exp.NewCommonTableExpression(true, "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() { func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withWhere() {
dc := exp.NewDeleteClauses(). dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")). SetFrom(exp.NewColumnListExpression("test")).
WhereAppend(exp.NewLiteralExpression(`"a"=?`, 1)) WhereAppend(exp.NewLiteralExpression(`"a"=?`, 1))
dsgs.assertCases( dsgs.assertCases(
sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()), sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()),
@@ -166,7 +166,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withOrder() {
opts.SupportsOrderByOnDelete = true opts.SupportsOrderByOnDelete = true
dc := exp.NewDeleteClauses(). dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")). SetFrom(exp.NewColumnListExpression("test")).
SetOrder(exp.NewIdentifierExpression("", "", "c").Desc()) SetOrder(exp.NewIdentifierExpression("", "", "c").Desc())
dsgs.assertCases( dsgs.assertCases(
@@ -188,7 +188,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withLimit() {
opts.SupportsLimitOnDelete = true opts.SupportsLimitOnDelete = true
dc := exp.NewDeleteClauses(). dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")). SetFrom(exp.NewColumnListExpression("test")).
SetLimit(1) SetLimit(1)
dsgs.assertCases( dsgs.assertCases(
@@ -210,7 +210,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withReturning() {
opts.SupportsReturn = true opts.SupportsReturn = true
dc := exp.NewDeleteClauses(). dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")). SetFrom(exp.NewColumnListExpression("test")).
SetReturning(exp.NewColumnListExpression("a", "b")) SetReturning(exp.NewColumnListExpression("a", "b"))
dsgs.assertCases( dsgs.assertCases(
+4 -4
View File
@@ -7,10 +7,10 @@ import (
"time" "time"
"unicode/utf8" "unicode/utf8"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/internal/util" "git.fsdpf.net/go/db/internal/util"
) )
type ( type (
+4 -4
View File
@@ -7,10 +7,10 @@ import (
"testing" "testing"
"time" "time"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+3 -3
View File
@@ -3,9 +3,9 @@ package sqlgen
import ( import (
"strings" "strings"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/errors" "git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
) )
type ( type (
+3 -3
View File
@@ -3,9 +3,9 @@ package sqlgen_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/db/v2/exp" "git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/v2/internal/sb" "git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen" "git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks 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 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 // DeleteSQLGenerator is an autogenerated mock type for the DeleteSQLGenerator type
type DeleteSQLGenerator struct { type DeleteSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks 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 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 // InsertSQLGenerator is an autogenerated mock type for the InsertSQLGenerator type
type InsertSQLGenerator struct { type InsertSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks 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 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 // SelectSQLGenerator is an autogenerated mock type for the SelectSQLGenerator type
type SelectSQLGenerator struct { type SelectSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks 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 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 // TruncateSQLGenerator is an autogenerated mock type for the TruncateSQLGenerator type
type TruncateSQLGenerator struct { type TruncateSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks 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 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 // UpdateSQLGenerator is an autogenerated mock type for the UpdateSQLGenerator type
type UpdateSQLGenerator struct { type UpdateSQLGenerator struct {

Some files were not shown because too many files have changed in this diff Show More