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"
"sync"
"git.fsdpf.net/go/db/v2/exec"
"git.fsdpf.net/go/db/exec"
)
type (
@@ -432,6 +432,9 @@ type (
Tx SQLTx
qf exec.QueryFactory
qfOnce sync.Once
// [fix] commands out of sync. Did you run multiple statements at once?
mu sync.Mutex
}
)
@@ -496,6 +499,9 @@ func (td *TxDatabase) Exec(query string, args ...interface{}) (sql.Result, error
// See Database#ExecContext
func (td *TxDatabase) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
td.mu.Lock()
defer td.mu.Unlock()
td.Trace("EXEC", query, args...)
return td.Tx.ExecContext(ctx, query, args...)
}
@@ -518,6 +524,9 @@ func (td *TxDatabase) Query(query string, args ...interface{}) (*sql.Rows, error
// See Database#QueryContext
func (td *TxDatabase) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
td.mu.Lock()
defer td.mu.Unlock()
td.Trace("QUERY", query, args...)
return td.Tx.QueryContext(ctx, query, args...)
}
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"fmt"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
dbv2 "git.fsdpf.net/go/db"
)
func ExampleDatabase_Begin() {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"sync"
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/internal/errors"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/internal/errors"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite"
)
+2 -2
View File
@@ -3,8 +3,8 @@ package db
import (
"time"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db/internal/util"
"git.fsdpf.net/go/db/sqlgen"
)
type DialectWrapper struct {
+10 -10
View File
@@ -4,16 +4,16 @@ import (
"fmt"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
_ "git.fsdpf.net/go/db/v2/dialect/mysql"
_ "git.fsdpf.net/go/db/v2/dialect/postgres"
_ "git.fsdpf.net/go/db/v2/dialect/sqlite3"
dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/dialect/mysql"
_ "git.fsdpf.net/go/db/dialect/postgres"
_ "git.fsdpf.net/go/db/dialect/sqlite3"
"github.com/DATA-DOG/go-sqlmock"
)
// Creating a mysql dataset. Be sure to import the mysql adapter.
func ExampleDialect_datasetMysql() {
// import _ "git.fsdpf.net/go/db/v2/dialect/mysql"
// import _ "git.fsdpf.net/go/db/dialect/mysql"
d := dbv2.Dialect("mysql")
ds := d.From("test").Where(dbv2.Ex{
@@ -34,7 +34,7 @@ func ExampleDialect_datasetMysql() {
// Creating a mysql database. Be sure to import the mysql adapter.
func ExampleDialect_dbMysql() {
// import _ "git.fsdpf.net/go/db/v2/dialect/mysql"
// import _ "git.fsdpf.net/go/db/dialect/mysql"
type item struct {
ID int64 `db:"id"`
@@ -84,7 +84,7 @@ func ExampleDialect_dbMysql() {
// Creating a mysql dataset. Be sure to import the postgres adapter
func ExampleDialect_datasetPostgres() {
// import _ "git.fsdpf.net/go/db/v2/dialect/postgres"
// import _ "git.fsdpf.net/go/db/dialect/postgres"
d := dbv2.Dialect("postgres")
ds := d.From("test").Where(dbv2.Ex{
@@ -105,7 +105,7 @@ func ExampleDialect_datasetPostgres() {
// Creating a postgres dataset. Be sure to import the postgres adapter
func ExampleDialect_dbPostgres() {
// import _ "git.fsdpf.net/go/db/v2/dialect/postgres"
// import _ "git.fsdpf.net/go/db/dialect/postgres"
type item struct {
ID int64 `db:"id"`
@@ -155,7 +155,7 @@ func ExampleDialect_dbPostgres() {
// Creating a mysql dataset. Be sure to import the sqlite3 adapter
func ExampleDialect_datasetSqlite3() {
// import _ "git.fsdpf.net/go/db/v2/dialect/sqlite3"
// import _ "git.fsdpf.net/go/db/dialect/sqlite3"
d := dbv2.Dialect("sqlite3")
ds := d.From("test").Where(dbv2.Ex{
@@ -176,7 +176,7 @@ func ExampleDialect_datasetSqlite3() {
// Creating a sqlite3 database. Be sure to import the sqlite3 adapter
func ExampleDialect_dbSqlite3() {
// import _ "git.fsdpf.net/go/db/v2/dialect/sqlite3"
// import _ "git.fsdpf.net/go/db/dialect/sqlite3"
type item struct {
ID int64 `db:"id"`
Address string `db:"address"`
+1 -1
View File
@@ -3,7 +3,7 @@ package db_test
import (
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
dbv2 "git.fsdpf.net/go/db"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite"
)
+27 -14
View File
@@ -1,10 +1,10 @@
package db
import (
"git.fsdpf.net/go/db/v2/exec"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/exec"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
)
var ErrBadFromArgument = errors.New("unsupported DeleteDataset#From argument, a string or identifier expression is required")
@@ -15,6 +15,7 @@ type DeleteDataset struct {
isPrepared prepared
queryFactory exec.QueryFactory
err error
hooks exec.Hooks
}
// used internally by database to create a database with a specific adapter
@@ -87,6 +88,7 @@ func (dd *DeleteDataset) copy(clauses exp.DeleteClauses) *DeleteDataset {
isPrepared: dd.isPrepared,
queryFactory: dd.queryFactory,
err: dd.err,
hooks: dd.hooks,
}
}
@@ -119,14 +121,16 @@ func (dd *DeleteDataset) WithRecursive(name string, subquery exp.Expression) *De
// Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased
// LiteralExpression: (See Literal) Will use the literal SQL
func (dd *DeleteDataset) From(table interface{}) *DeleteDataset {
switch t := table.(type) {
case exp.IdentifierExpression:
return dd.copy(dd.clauses.SetFrom(t))
case string:
return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t)))
default:
panic(ErrBadFromArgument)
}
return dd.copy(dd.clauses.SetFrom(exp.NewColumnListExpression(table)))
// switch t := table.(type) {
// case exp.IdentifierExpression:
// return dd.copy(dd.clauses.SetFrom(t))
// case string:
// return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t)))
// default:
// panic(ErrBadFromArgument)
// }
}
// Adds a WHERE clause. See examples.
@@ -232,8 +236,17 @@ func (dd *DeleteDataset) ReturnsColumns() bool {
// db.Delete("test").Exec()
//
// See Dataset#ToUpdateSQL for arguments
func (dd *DeleteDataset) Executor() exec.QueryExecutor {
return dd.queryFactory.FromSQLBuilder(dd.deleteSQLBuilder())
func (dd *DeleteDataset) Executor() (executor exec.QueryExecutor) {
if dd.hooks != nil {
dd.SetError(dd.hooks.Before(dd))
}
executor = dd.queryFactory.FromSQLBuilder(dd.deleteSQLBuilder())
if dd.hooks != nil {
executor.Hook(func(result interface{}) error {
return dd.hooks.After(dd, result)
})
}
return executor
}
func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder {
+2 -2
View File
@@ -3,8 +3,8 @@ package db_test
import (
"fmt"
dbv2 "git.fsdpf.net/go/db/v2"
_ "git.fsdpf.net/go/db/v2/dialect/mysql"
dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/dialect/mysql"
)
func ExampleDelete() {
+45 -45
View File
@@ -3,11 +3,11 @@ package db_test
import (
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/mocks"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/mocks"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
@@ -100,7 +100,7 @@ func (dds *deleteDatasetSuite) TestPrepared() {
func (dds *deleteDatasetSuite) TestGetClauses() {
ds := dbv2.Delete("test")
ce := exp.NewDeleteClauses().SetFrom(dbv2.I("test"))
ce := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test"))
dds.Equal(ce, ds.GetClauses())
}
@@ -110,12 +110,12 @@ func (dds *deleteDatasetSuite) TestWith() {
dds.assertCases(
deleteTestCase{
ds: bd.With("test-cte", from),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")).
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")).
CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
},
)
}
@@ -126,12 +126,12 @@ func (dds *deleteDatasetSuite) TestWithRecursive() {
dds.assertCases(
deleteTestCase{
ds: bd.WithRecursive("test-cte", from),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")).
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")).
CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from)),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
},
)
}
@@ -141,23 +141,23 @@ func (dds *deleteDatasetSuite) TestFrom_withIdentifier() {
dds.assertCases(
deleteTestCase{
ds: bd.From("items2"),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items2")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
},
deleteTestCase{
ds: bd.From(dbv2.C("items2")),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items2")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
},
deleteTestCase{
ds: bd.From(dbv2.T("items2")),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.T("items2")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
},
deleteTestCase{
ds: bd.From("schema.table"),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.I("schema.table")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("schema.table")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
},
)
@@ -172,19 +172,19 @@ func (dds *deleteDatasetSuite) TestWhere() {
deleteTestCase{
ds: bd.Where(dbv2.Ex{"a": 1}),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
WhereAppend(dbv2.Ex{"a": 1}),
},
deleteTestCase{
ds: bd.Where(dbv2.Ex{"a": 1}).Where(dbv2.C("b").Eq("c")),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
WhereAppend(dbv2.Ex{"a": 1}).
WhereAppend(dbv2.C("b").Eq("c")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
},
)
}
@@ -195,12 +195,12 @@ func (dds *deleteDatasetSuite) TestClearWhere() {
deleteTestCase{
ds: bd.ClearWhere(),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")),
SetFrom(exp.NewColumnListExpression("items")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
WhereAppend(dbv2.Ex{"a": 1}),
},
)
@@ -212,24 +212,24 @@ func (dds *deleteDatasetSuite) TestOrder() {
deleteTestCase{
ds: bd.Order(dbv2.C("a").Asc()),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()),
},
deleteTestCase{
ds: bd.Order(dbv2.C("a").Asc()).Order(dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("b").Desc()),
},
deleteTestCase{
ds: bd.Order(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
},
)
}
@@ -240,13 +240,13 @@ func (dds *deleteDatasetSuite) TestOrderAppend() {
deleteTestCase{
ds: bd.OrderAppend(dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()),
},
)
@@ -258,13 +258,13 @@ func (dds *deleteDatasetSuite) TestOrderPrepend() {
deleteTestCase{
ds: bd.OrderPrepend(dbv2.C("b").Desc()),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("b").Desc(), dbv2.C("a").Asc()),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()),
},
)
@@ -275,12 +275,12 @@ func (dds *deleteDatasetSuite) TestClearOrder() {
dds.assertCases(
deleteTestCase{
ds: bd.ClearOrder(),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetOrder(dbv2.C("a").Asc()),
},
)
@@ -292,26 +292,26 @@ func (dds *deleteDatasetSuite) TestLimit() {
deleteTestCase{
ds: bd.Limit(10),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")).
SetFrom(exp.NewColumnListExpression("test")).
SetLimit(uint(10)),
},
deleteTestCase{
ds: bd.Limit(0),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
},
deleteTestCase{
ds: bd.Limit(10).Limit(2),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")).
SetFrom(exp.NewColumnListExpression("test")).
SetLimit(uint(2)),
},
deleteTestCase{
ds: bd.Limit(10).Limit(0),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
},
)
}
@@ -322,18 +322,18 @@ func (dds *deleteDatasetSuite) TestLimitAll() {
deleteTestCase{
ds: bd.LimitAll(),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")).
SetFrom(exp.NewColumnListExpression("test")).
SetLimit(dbv2.L("ALL")),
},
deleteTestCase{
ds: bd.Limit(10).LimitAll(),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("test")).
SetFrom(exp.NewColumnListExpression("test")).
SetLimit(dbv2.L("ALL")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
},
)
}
@@ -343,11 +343,11 @@ func (dds *deleteDatasetSuite) TestClearLimit() {
dds.assertCases(
deleteTestCase{
ds: bd.ClearLimit(),
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("test")).SetLimit(uint(10)),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")).SetLimit(uint(10)),
},
)
}
@@ -358,30 +358,30 @@ func (dds *deleteDatasetSuite) TestReturning() {
deleteTestCase{
ds: bd.Returning("a"),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression("a")),
},
deleteTestCase{
ds: bd.Returning(),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression()),
},
deleteTestCase{
ds: bd.Returning(nil),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression()),
},
deleteTestCase{
ds: bd.Returning("a").Returning("b"),
clauses: exp.NewDeleteClauses().
SetFrom(dbv2.C("items")).
SetFrom(exp.NewColumnListExpression("items")).
SetReturning(exp.NewColumnListExpression("b")),
},
deleteTestCase{
ds: bd,
clauses: exp.NewDeleteClauses().SetFrom(dbv2.C("items")),
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
},
)
}
+6 -6
View File
@@ -1,8 +1,8 @@
package mysql
import (
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
)
func DialectOptions() *db.SQLDialectOptions {
@@ -16,11 +16,11 @@ func DialectOptions() *db.SQLDialectOptions {
opts.SupportsConflictUpdateWhere = false
opts.SupportsInsertIgnoreSyntax = true
opts.SupportsConflictTarget = false
opts.SupportsWithCTE = false
opts.SupportsWithCTERecursive = false
opts.SupportsWithCTE = true
opts.SupportsWithCTERecursive = true
opts.SupportsDistinctOn = false
opts.SupportsWindowFunction = false
opts.SupportsDeleteTableHint = true
opts.SupportsWindowFunction = true
opts.SupportsDeleteTableHint = false
opts.UseFromClauseForMultipleUpdateTables = false
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"regexp"
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+2 -2
View File
@@ -9,8 +9,8 @@ import (
"testing"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/dialect/mysql"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/dialect/mysql"
_ "github.com/go-sql-driver/mysql"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -1,6 +1,6 @@
package postgres
import "git.fsdpf.net/go/db/v2"
import "git.fsdpf.net/go/db"
func DialectOptions() *db.SQLDialectOptions {
do := db.DefaultDialectOptions()
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"testing"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
dbv2 "git.fsdpf.net/go/db"
"github.com/lib/pq"
"github.com/stretchr/testify/suite"
+2 -2
View File
@@ -3,8 +3,8 @@ package sqlite3
import (
"time"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
)
func DialectOptions() *db.SQLDialectOptions {
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"regexp"
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+3 -3
View File
@@ -8,9 +8,9 @@ import (
_ "github.com/mattn/go-sqlite3"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/dialect/mysql"
"git.fsdpf.net/go/db/v2/dialect/sqlite3"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/dialect/mysql"
"git.fsdpf.net/go/db/dialect/sqlite3"
"github.com/stretchr/testify/suite"
)
+3 -3
View File
@@ -1,9 +1,9 @@
package sqlserver
import (
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/sqlgen"
)
func DialectOptions() *db.SQLDialectOptions {
+2 -2
View File
@@ -3,8 +3,8 @@ package sqlserver_test
import (
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+3 -3
View File
@@ -7,10 +7,10 @@ import (
"testing"
"time"
"git.fsdpf.net/go/db/v2/dialect/mysql"
"git.fsdpf.net/go/db/dialect/mysql"
dbv2 "git.fsdpf.net/go/db/v2"
_ "git.fsdpf.net/go/db/v2/dialect/sqlserver"
dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/dialect/sqlserver"
_ "github.com/denisenkom/go-mssqldb"
"github.com/stretchr/testify/suite"
)
+38 -9
View File
@@ -1,6 +1,7 @@
package engine
import (
"context"
"database/sql"
"fmt"
@@ -8,15 +9,16 @@ import (
_ "github.com/denisenkom/go-mssqldb"
_ "github.com/go-sql-driver/mysql"
"github.com/mattn/go-sqlite3"
_ "github.com/mattn/go-sqlite3"
_ "git.fsdpf.net/go/db/v2/dialect/mysql"
_ "git.fsdpf.net/go/db/v2/dialect/postgres"
_ "git.fsdpf.net/go/db/v2/dialect/sqlite3"
_ "git.fsdpf.net/go/db/v2/dialect/sqlserver"
_ "git.fsdpf.net/go/db/dialect/mysql"
_ "git.fsdpf.net/go/db/dialect/postgres"
_ "git.fsdpf.net/go/db/dialect/sqlite3"
_ "git.fsdpf.net/go/db/dialect/sqlserver"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/internal/errors"
)
type Engine struct {
@@ -50,12 +52,31 @@ func (e Engine) Connection(name string) *db.Database {
return db.New(cfg.Driver, _db)
}
func (e Engine) MakeConnection(cfg DBConfig) *sql.DB {
func (e Engine) MakeConnection(cfg DBConfig) (db *sql.DB) {
dsn := cfg.ToDSN()
switch cfg.Driver {
case "mysql":
case "sqlite3":
defer func() {
if r := recover(); r != nil {
panic(r)
}
if cfg.SQLite.Raw == nil {
return
}
conn, err := db.Conn(context.Background())
if err != nil {
panic(err)
}
err = conn.Raw(func(driverConn any) error {
sqliteConn := driverConn.(*sqlite3.SQLiteConn)
return cfg.SQLite.Raw(sqliteConn)
})
if err != nil {
panic(err)
}
}()
case "sqlserver":
case "postgres":
if url, err := pq.ParseURL(dsn); err == nil {
@@ -80,10 +101,18 @@ func (e Engine) MakeConnection(cfg DBConfig) *sql.DB {
return db
}
func Open(cfgs map[string]DBConfig) *Engine {
func Open(cfgs map[string]DBConfig) Engine {
for n, cfg := range cfgs {
_engine.configs[n] = cfg
}
return _engine
return *_engine
}
func Mock(cfgs map[string]MockDBConfig) Engine {
for k, cfg := range cfgs {
_engine.dbs[k] = cfg.Mock
_engine.configs[k] = DBConfig{Driver: cfg.Driver}
}
return *_engine
}
+19
View File
@@ -1,11 +1,19 @@
package engine
import (
"database/sql"
"fmt"
"net/url"
"time"
"github.com/mattn/go-sqlite3"
)
type MockDBConfig struct {
Driver string
Mock *sql.DB
}
// DBConfig 数据库配置结构体
type DBConfig struct {
Driver string
@@ -48,6 +56,7 @@ type DBConfig struct {
Synchronous int
Cache string
BusyTimeout int
Raw func(*sqlite3.SQLiteConn) error
}
SQLServer struct {
@@ -379,6 +388,16 @@ func WithSQLiteFile(file string) Option {
}
}
// SQLite 注册函数
func WithSQLiteRaw(raw func(*sqlite3.SQLiteConn) error) Option {
return func(c *DBConfig) {
if c.Driver != "sqlite3" {
panic("WithSQLiteRegFn is only valid for sqlite3 driver")
}
c.SQLite.Raw = raw
}
}
func WithSQLiteJournal(journal string) Option {
return func(c *DBConfig) {
if c.Driver != "sqlite3" {
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"testing"
"time"
"git.fsdpf.net/go/db/v2/engine"
"git.fsdpf.net/go/db/engine"
)
// TestMySQLConfig 测试 MySQL 驱动的配置
+54 -6
View File
@@ -5,12 +5,13 @@ import (
gsql "database/sql"
"reflect"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/util"
)
type (
QueryExecutor struct {
hook func(result interface{}) error
de DbExecutor
err error
query string
@@ -19,6 +20,8 @@ type (
)
var (
errUnsupportedScanMapType = errors.New("type must be a pointer to a map when scanning into a map")
errUnsupportedScanMapsType = errors.New("type must be a pointer to a slice when scanning into a map")
errUnsupportedScanStructType = errors.New("type must be a pointer to a struct when scanning into a struct")
errUnsupportedScanStructsType = errors.New("type must be a pointer to a slice when scanning into structs")
errUnsupportedScanValsType = errors.New("type must be a pointer to a slice when scanning into vals")
@@ -38,22 +41,34 @@ func (q QueryExecutor) Exec() (gsql.Result, error) {
return q.ExecContext(context.Background())
}
func (q QueryExecutor) ExecContext(ctx context.Context) (gsql.Result, error) {
func (q QueryExecutor) ExecContext(ctx context.Context) (result gsql.Result, err error) {
if q.err != nil {
return nil, q.err
}
return q.de.ExecContext(ctx, q.query, q.args...)
result, err = q.de.ExecContext(ctx, q.query, q.args...)
defer func() {
if err == nil && q.hook != nil {
err = q.hook(result)
}
}()
return result, err
}
func (q QueryExecutor) Query() (*gsql.Rows, error) {
return q.QueryContext(context.Background())
}
func (q QueryExecutor) QueryContext(ctx context.Context) (*gsql.Rows, error) {
func (q QueryExecutor) QueryContext(ctx context.Context) (result *gsql.Rows, err error) {
if q.err != nil {
return nil, q.err
}
return q.de.QueryContext(ctx, q.query, q.args...)
result, err = q.de.QueryContext(ctx, q.query, q.args...)
defer func() {
if err == nil && q.hook != nil {
err = q.hook(result)
}
}()
return result, err
}
// This will execute the SQL and append results to the slice
@@ -238,6 +253,35 @@ func (q QueryExecutor) ScanValContext(ctx context.Context, i interface{}) (bool,
return false, scanner.Err()
}
func (q QueryExecutor) GetRecord() (map[string]any, error) {
return q.GetRecordContext(context.Background())
}
func (q QueryExecutor) GetRecordContext(ctx context.Context) (map[string]any, error) {
scanner, err := q.ScannerContext(ctx)
if err != nil {
return nil, err
}
defer func() { _ = scanner.Close() }()
if scanner.Next() {
return scanner.GetRecord()
}
return nil, scanner.Err()
}
func (q QueryExecutor) GetRecords() ([]map[string]any, error) {
return q.GetRecordsContext(context.Background())
}
func (q QueryExecutor) GetRecordsContext(ctx context.Context) ([]map[string]any, error) {
scanner, err := q.ScannerContext(ctx)
if err != nil {
return nil, err
}
defer func() { _ = scanner.Close() }()
return scanner.GetRecords()
}
// Scanner will return a Scanner that can be used for manually scanning rows.
func (q QueryExecutor) Scanner() (Scanner, error) {
return q.ScannerContext(context.Background())
@@ -251,3 +295,7 @@ func (q QueryExecutor) ScannerContext(ctx context.Context) (Scanner, error) {
}
return NewScanner(rows), nil
}
func (q *QueryExecutor) Hook(hook func(dataset interface{}) error) {
q.hook = hook
}
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/internal/sb"
)
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 (
"database/sql"
"encoding/json"
"reflect"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/util"
)
type (
@@ -17,6 +17,8 @@ type (
ScanStructs(i interface{}) error
ScanVal(i interface{}) error
ScanVals(i interface{}) error
GetRecord() (map[string]any, error)
GetRecords() ([]map[string]any, error)
Close() error
Err() error
}
@@ -49,6 +51,86 @@ func (s *scanner) Err() error {
return s.rows.Err()
}
func (s *scanner) GetRecords() ([]map[string]any, error) {
records := []map[string]any{}
for s.Next() {
row, err := s.GetRecord()
if err != nil {
return records, err
}
records = append(records, row)
}
return records, s.Err()
}
func (s *scanner) GetRecord() (record map[string]any, err error) {
// Setup columns, but only once.
if s.columns == nil || s.columnMap == nil {
colsType, err := s.rows.ColumnTypes()
if err != nil {
return nil, err
}
s.columns = make([]string, len(colsType))
s.columnMap = util.ColumnMap{}
for i := range colsType {
s.columns[i] = colsType[i].Name()
typ := colsType[i].ScanType()
// 强制日期时间为字符串
if typ == reflect.TypeOf(sql.NullTime{}) {
typ = reflect.TypeOf(sql.Null[[]uint8]{})
}
s.columnMap[colsType[i].Name()] = util.ColumnData{
ColumnName: colsType[i].Name(),
GoType: typ,
}
}
}
scans := make([]interface{}, len(s.columns))
for i, col := range s.columns {
scans[i] = reflect.New(s.columnMap[col].GoType).Interface()
}
if err := s.rows.Scan(scans...); err != nil {
return nil, err
}
record = make(map[string]interface{}, len(s.columns))
for i, col := range s.columns {
var vv any
switch v := scans[i].(type) {
case *sql.Null[[]uint8]: // 强制日期时间为字符串
if vv, err = v.Value(); err == nil && vv != nil {
vv = string(vv.([]uint8))
}
case *sql.RawBytes:
if len(*v) > 1 {
if rune((*v)[0]) == rune('[') || rune((*v)[0]) == rune('{') {
err = json.Unmarshal(*v, &vv)
} else {
vv = string(*v)
}
} else {
vv = string(*v)
}
default:
vv = reflect.Indirect(reflect.ValueOf(v)).Interface()
}
if err != nil {
return
}
record[col] = vv
}
return record, s.Err()
}
// ScanStruct will scan the current row into i.
func (s *scanner) ScanStruct(i interface{}) error {
// Setup columnMap and columns, but only once.
@@ -63,35 +145,37 @@ func (s *scanner) ScanStruct(i interface{}) error {
return err
}
// 补全未知字段类型
if len(cols) != len(cm) {
colTypes, err := s.rows.ColumnTypes()
if err != nil {
return err
}
for _, t := range colTypes {
if _, ok := cm[t.Name()]; !ok {
cm[t.Name()] = util.ColumnData{
ColumnName: t.Name(),
GoType: t.ScanType(),
}
}
}
}
s.columnMap = cm
s.columns = cols
}
scans := make([]interface{}, 0, len(s.columns))
for _, col := range s.columns {
data, ok := s.columnMap[col]
scans, err := createColumnScans(s.columns, s.columnMap)
if !ok {
return unableToFindFieldError(col)
}
// 处理 converting NULL to string is unsupported
// 前面将 string 和 int 类型转为了 *string 和 *int
switch data.GoType.Kind() {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Float32, reflect.Float64,
reflect.Bool:
scans = append(scans, reflect.New(reflect.PointerTo(data.GoType)).Interface())
default:
scans = append(scans, reflect.New(data.GoType).Interface())
}
if err != nil {
return err
}
if err := s.rows.Scan(scans...); err != nil {
return err
}
record := exp.Record{}
record := map[string]interface{}{}
for index, col := range s.columns {
record[col] = scans[index]
}
@@ -175,3 +259,30 @@ func checkScanValsTarget(i interface{}) (reflect.Value, error) {
}
return val, nil
}
func createColumnScans(cols []string, cm util.ColumnMap) (scans []interface{}, err error) {
scans = make([]interface{}, 0, len(cols))
for _, col := range cols {
data, ok := cm[col]
if !ok {
return scans, unableToFindFieldError(col)
}
// 处理 converting NULL to string is unsupported
// 前面将 string 和 int 类型转为了 *string 和 *int
switch data.GoType.Kind() {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Float32, reflect.Float64,
reflect.Bool:
scans = append(scans, reflect.New(reflect.PointerTo(data.GoType)).Interface())
case reflect.Map, reflect.Slice, reflect.Struct:
scans = append(scans, reflect.New(reflect.PointerTo(reflect.TypeOf(json.RawMessage{}))).Interface())
default:
scans = append(scans, reflect.New(data.GoType).Interface())
}
}
return scans, nil
}
+25
View File
@@ -3,6 +3,7 @@ package exec
import (
"testing"
"git.fsdpf.net/go/db/exp"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite"
)
@@ -67,3 +68,27 @@ func (s *scannerSuite) TestScanVals() {
s.Require().NoError(err)
s.Require().ElementsMatch([]int{1, 2}, result)
}
func (s *scannerSuite) TestGetRecords() {
db, mock, err := sqlmock.New()
s.Require().NoError(err)
mock.ExpectQuery("SELECT \\* FROM `items`").
WithArgs().
WillReturnRows(
sqlmock.NewRows([]string{"address", "name"}).
AddRow("111 Test Addr", "Test1").
AddRow("111 Test Addr", "Test1"),
)
rows, err := db.Query("SELECT \\* FROM `items`")
s.Require().NoError(err)
result, err := NewScanner(rows).GetRecords()
s.Require().NoError(err)
s.Equal([]exp.Record{
{"address": "111 Test Addr", "name": "Test1"},
{"address": "111 Test Addr", "name": "Test1"},
}, result)
}
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+9 -2
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"reflect"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/util"
)
type columnList struct {
@@ -23,7 +23,8 @@ func NewColumnListExpression(vals ...interface{}) ColumnListExpression {
case Expression:
cols = append(cols, t)
default:
_, valKind := util.GetTypeInfo(val, reflect.Indirect(reflect.ValueOf(val)))
refVal := reflect.Indirect(reflect.ValueOf(val))
_, valKind := util.GetTypeInfo(val, refVal)
if valKind == reflect.Struct {
cm, err := util.GetColumnMap(val)
@@ -39,6 +40,12 @@ func NewColumnListExpression(vals ...interface{}) ColumnListExpression {
}
cols = append(cols, sc)
}
} else if refVal.Kind() == reflect.Slice {
items := make([]any, refVal.Len())
for i := 0; i < refVal.Len(); i++ {
items[i] = refVal.Index(i).Interface()
}
return NewColumnListExpression(items...)
} else {
panic(fmt.Sprintf("Cannot create expression from %+v", val))
}
+5 -5
View File
@@ -8,8 +8,8 @@ type (
CommonTables() []CommonTableExpression
CommonTablesAppend(cte CommonTableExpression) DeleteClauses
From() IdentifierExpression
SetFrom(table IdentifierExpression) DeleteClauses
From() ColumnListExpression
SetFrom(table ColumnListExpression) DeleteClauses
Where() ExpressionList
ClearWhere() DeleteClauses
@@ -33,7 +33,7 @@ type (
}
deleteClauses struct {
commonTables []CommonTableExpression
from IdentifierExpression
from ColumnListExpression
where ExpressionList
order ColumnListExpression
limit interface{}
@@ -71,11 +71,11 @@ func (dc *deleteClauses) CommonTablesAppend(cte CommonTableExpression) DeleteCla
return ret
}
func (dc *deleteClauses) From() IdentifierExpression {
func (dc *deleteClauses) From() ColumnListExpression {
return dc.from
}
func (dc *deleteClauses) SetFrom(table IdentifierExpression) DeleteClauses {
func (dc *deleteClauses) SetFrom(table ColumnListExpression) DeleteClauses {
ret := dc.clone()
ret.from = table
return ret
+4 -4
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
@@ -17,7 +17,7 @@ func TestDeleteClausesSuite(t *testing.T) {
func (dcs *deleteClausesSuite) TestHasFrom() {
c := exp.NewDeleteClauses()
c2 := c.SetFrom(exp.NewIdentifierExpression("", "test", ""))
c2 := c.SetFrom(exp.NewColumnListExpression("test"))
dcs.False(c.HasFrom())
@@ -26,7 +26,7 @@ func (dcs *deleteClausesSuite) TestHasFrom() {
func (dcs *deleteClausesSuite) TestFrom() {
c := exp.NewDeleteClauses()
ti := exp.NewIdentifierExpression("", "a", "")
ti := exp.NewColumnListExpression("a")
c2 := c.SetFrom(ti)
dcs.Nil(c.From())
@@ -36,7 +36,7 @@ func (dcs *deleteClausesSuite) TestFrom() {
func (dcs *deleteClausesSuite) TestSetFrom() {
c := exp.NewDeleteClauses()
ti := exp.NewIdentifierExpression("", "a", "")
ti := exp.NewColumnListExpression("a")
c2 := c.SetFrom(ti)
dcs.Nil(c.From())
+1 -1
View File
@@ -3,7 +3,7 @@ package exp
import (
"fmt"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/internal/sb"
)
// Behaviors
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"sort"
"strings"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/internal/errors"
)
type (
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"reflect"
"sort"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/util"
)
type (
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"reflect"
"sort"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/util"
)
// Alternative to writing map[string]interface{}. Can be used for Inserts, Updates or Deletes
+14
View File
@@ -22,6 +22,7 @@ type (
Joins() JoinExpressions
JoinsAppend(jc JoinExpression) SelectClauses
JoinsPrepend(jc JoinExpression) SelectClauses
Where() ExpressionList
ClearWhere() SelectClauses
@@ -41,6 +42,7 @@ type (
GroupBy() ColumnListExpression
SetGroupBy(cl ColumnListExpression) SelectClauses
GroupByAppend(cl ColumnListExpression) SelectClauses
ClearGroupBy() SelectClauses
Limit() interface{}
HasLimit() bool
@@ -197,6 +199,12 @@ func (c *selectClauses) JoinsAppend(jc JoinExpression) SelectClauses {
return ret
}
func (c *selectClauses) JoinsPrepend(jc JoinExpression) SelectClauses {
ret := c.clone()
ret.joins = append(JoinExpressions{jc}, ret.joins...)
return ret
}
func (c *selectClauses) Where() ExpressionList {
return c.where
}
@@ -310,6 +318,12 @@ func (c *selectClauses) SetGroupBy(cl ColumnListExpression) SelectClauses {
return ret
}
func (c *selectClauses) ClearGroupBy() SelectClauses {
ret := c.clone()
ret.groupBy = nil
return ret
}
func (c *selectClauses) Limit() interface{} {
return c.limit
}
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"reflect"
"sort"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/util"
)
type (
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package exp_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package db
import (
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/exp"
)
type (
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"fmt"
"regexp"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
)
func ExampleAVG() {
+2 -2
View File
@@ -3,8 +3,8 @@ package db_test
import (
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"github.com/stretchr/testify/suite"
)
+2 -1
View File
@@ -1,4 +1,4 @@
module git.fsdpf.net/go/db/v2
module git.fsdpf.net/go/db
go 1.19
@@ -18,6 +18,7 @@ require (
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+83 -8
View File
@@ -2,11 +2,12 @@ package db
import (
"fmt"
"reflect"
"git.fsdpf.net/go/db/v2/exec"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/exec"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
)
type InsertDataset struct {
@@ -15,6 +16,7 @@ type InsertDataset struct {
isPrepared prepared
queryFactory exec.QueryFactory
err error
hooks exec.Hooks
}
var ErrUnsupportedIntoType = errors.New("unsupported table type, a string or identifier expression is required")
@@ -88,6 +90,7 @@ func (id *InsertDataset) copy(clauses exp.InsertClauses) *InsertDataset {
isPrepared: id.isPrepared,
queryFactory: id.queryFactory,
err: id.err,
hooks: id.hooks,
}
}
@@ -170,9 +173,65 @@ func (id *InsertDataset) ClearVals() *InsertDataset {
return id.copy(id.clauses.SetVals(nil))
}
// Insert rows. Rows can be a map, db.Record or struct. See examples.
// Insert rows. Rows can be a map, db.Record, struct, slice or array. See examples.
func (id *InsertDataset) Rows(rows ...interface{}) *InsertDataset {
return id.copy(id.clauses.SetRows(rows))
// If a single argument is a slice or array, expand it as rows
if len(rows) == 1 {
val := reflect.ValueOf(rows[0])
kind := val.Kind()
if kind == reflect.Slice || kind == reflect.Array {
expanded := make([]interface{}, val.Len())
for i := 0; i < val.Len(); i++ {
expanded[i] = val.Index(i).Interface()
}
// 递归调用 Rows 展开切片或数组
return id.Rows(expanded...)
}
}
converted := make([]interface{}, len(rows))
for i, row := range rows {
switch v := row.(type) {
case map[string]interface{}:
converted[i] = v
case Record:
converted[i] = map[string]interface{}(v)
default:
val := reflect.ValueOf(row)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() == reflect.Slice || val.Kind() == reflect.Array {
panic("Rows: nested slice/array not supported, pass as top-level argument")
}
if val.Kind() != reflect.Struct {
panic("Rows: unsupported row type, must be map, Record, struct, slice or array")
}
result := make(map[string]interface{})
typ := val.Type()
for j := 0; j < val.NumField(); j++ {
field := typ.Field(j)
// Skip unexported fields
if field.PkgPath != "" {
continue
}
// Use db tag if present, otherwise use field name
key := field.Name
if dbTag, ok := field.Tag.Lookup("db"); ok && dbTag != "" {
if dbTag == "-" {
continue // Skip fields with db tag "-"
}
key = dbTag
}
result[key] = val.Field(j).Interface()
}
converted[i] = result
}
}
return id.copy(id.clauses.SetRows(converted))
}
// Clears the rows for this insert dataset. See examples.
@@ -259,8 +318,24 @@ func (id *InsertDataset) ReturnsColumns() bool {
// Generates the INSERT sql, and returns an QueryExecutor struct with the sql set to the INSERT statement
//
// db.Insert("test").Rows(Record{"name":"Bob"}).Executor().Exec()
func (id *InsertDataset) Executor() exec.QueryExecutor {
return id.queryFactory.FromSQLBuilder(id.insertSQLBuilder())
func (id *InsertDataset) Executor() (executor exec.QueryExecutor) {
if id.hooks != nil {
id.SetError(id.hooks.Before(id))
}
executor = id.queryFactory.FromSQLBuilder(id.insertSQLBuilder())
if id.hooks != nil {
executor.Hook(func(result interface{}) error {
return id.hooks.After(id, result)
})
}
return executor
}
func (id *InsertDataset) WithHook(hooks exec.Hooks) *InsertDataset {
if id.hooks == nil {
id.hooks = hooks
}
return id
}
func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"fmt"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
_ "git.fsdpf.net/go/db/v2/dialect/postgres"
dbv2 "git.fsdpf.net/go/db"
_ "git.fsdpf.net/go/db/dialect/postgres"
)
func ExampleInsert_dbv2Record() {
+5 -5
View File
@@ -4,11 +4,11 @@ import (
"testing"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/mocks"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/mocks"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
+9 -8
View File
@@ -5,7 +5,7 @@ import (
"sort"
"strings"
"git.fsdpf.net/go/db/v2/internal/tag"
"git.fsdpf.net/go/db/internal/tag"
)
type (
@@ -37,13 +37,14 @@ func newColumnMap(t reflect.Type, fieldIndex []int, prefixes []string) ColumnMap
// if PkgPath is empty then it is an exported field
columnName := getColumnName(&f, dbTag)
if !shouldIgnoreField(dbTag) {
if !implementsScanner(f.Type) {
subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes)
if len(subCm) != 0 {
subColMaps = append(subColMaps, subCm)
continue
}
}
// 移除原来的关联结构,并 scans 字段出现 table.col
// if !implementsScanner(f.Type) {
// subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes)
// if len(subCm) != 0 {
// subColMaps = append(subColMaps, subCm)
// continue
// }
// }
ffTag := tag.New("ff", f.Tag)
columnName = strings.Join(append(prefixes, columnName), ".")
cm[columnName] = newColumnData(&f, columnName, fieldIndex, ffTag)
+39 -11
View File
@@ -2,11 +2,12 @@ package util
import (
"database/sql"
"encoding/json"
"reflect"
"strings"
"sync"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/internal/errors"
)
const (
@@ -56,6 +57,10 @@ func IsStruct(k reflect.Kind) bool {
return k == reflect.Struct
}
func IsMap(k reflect.Kind) bool {
return k == reflect.Map
}
func IsInvalid(k reflect.Kind) bool {
return k == reflect.Invalid
}
@@ -117,6 +122,15 @@ func GetSliceElementType(val reflect.Value) reflect.Type {
return elemType
}
func GetMapElementType(val reflect.Value) reflect.Type {
elemType := val.Type().Elem()
if elemType.Kind() == reflect.Ptr {
elemType = elemType.Elem()
}
return elemType
}
// AppendSliceElement will append val to slice. Handles slice of pointers and
// not pointers. Val needs to be a pointer.
func AppendSliceElement(slice, val reflect.Value) {
@@ -167,16 +181,8 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re
return v
case 1:
f := v.FieldByIndex(fieldIndex)
srcVal := reflect.ValueOf(src).Elem()
// 处理 converting NULL to string is unsupported
// 前面将 string 和 int 类型转为了 *string 和 *int
// 这里做还原
if srcVal.IsNil() {
f.Set(reflect.Zero(f.Type()))
} else if f.Type().ConvertibleTo(srcVal.Type().Elem()) {
f.Set(srcVal.Elem())
} else {
f.Set(srcVal)
if err := SafeSetVarValue(f, src); err != nil {
panic(err)
}
default:
f := v.Field(fieldIndex[0])
@@ -196,6 +202,28 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re
return v
}
func SafeSetVarValue(v reflect.Value, src interface{}) error {
f := reflect.Indirect(v)
srcVal := reflect.ValueOf(src).Elem()
// 处理 converting NULL to string is unsupported
// 前面将 string 和 int 类型转为了 *string 和 *int
// 这里做还原
if srcVal.IsNil() {
f.Set(reflect.Zero(f.Type()))
} else if f.Type().ConvertibleTo(srcVal.Type().Elem()) {
f.Set(srcVal.Elem())
} else if f.Type().ConvertibleTo(srcVal.Type()) {
f.Set(srcVal)
} else if u, ok := srcVal.Interface().(*json.RawMessage); ok && len(*u) >= 2 {
if err := json.Unmarshal(*u, f.Addr().Interface()); err != nil {
return err
}
}
return nil
}
type rowData = map[string]interface{}
// AssignStructVals will assign the data from rd to i.
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/util"
"github.com/stretchr/testify/suite"
)
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"testing"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/suite"
)
+2 -2
View File
@@ -2,10 +2,10 @@
package mocks
import exp "git.fsdpf.net/go/db/v2/exp"
import exp "git.fsdpf.net/go/db/exp"
import mock "github.com/stretchr/testify/mock"
import sb "git.fsdpf.net/go/db/v2/internal/sb"
import sb "git.fsdpf.net/go/db/internal/sb"
// SQLDialect is an autogenerated mock type for the SQLDialect type
type SQLDialect struct {
+14 -31
View File
@@ -10,10 +10,11 @@ type Blueprint struct {
table string // the table the blueprint describes.
columns []*ColumnDefinition // columns that should be added to the table
commands []*Command //
temporary bool // Whether to make the table temporary.
charset string // The default character set that should be used for the table.
collation string // The collation that should be used for the table.
engine string // The engine that should be used for the table.
Temporary bool // Whether to make the table temporary.
Charset string // The default character set that should be used for the table.
Collation string // The collation that should be used for the table.
Engine string // The engine that should be used for the table.
Comment string
}
type Command struct {
@@ -30,7 +31,7 @@ type CommandOptions struct {
}
func NewBlueprint(table string) *Blueprint {
return &Blueprint{table: table, charset: "utf8mb4", collation: "utf8mb4_general_ci"}
return &Blueprint{table: table}
}
// 字符串
@@ -263,21 +264,17 @@ func (this *Blueprint) Create() *Command {
return this.addCommand("create", CommandOptions{})
}
// 设置临时表标记
func (this *Blueprint) Temporary() {
this.temporary = true
}
// 设置表字符集
func (this *Blueprint) Charset(charset string) {
this.charset = charset
}
// 修改表名
func (this *Blueprint) Rename(to string) *Command {
return this.addCommand("rename", CommandOptions{To: to})
}
// 修改表备注
func (this *Blueprint) ModifyComment(comment string) *Command {
this.Comment = comment
return this.addCommand("modifyComment", CommandOptions{})
}
// 删除表
func (this *Blueprint) Drop() *Command {
return this.addCommand("drop", CommandOptions{})
@@ -307,6 +304,8 @@ func (this *Blueprint) ToSql(sc Schema) (statements []string) {
statements = append(statements, sc.CompileDropColumn(this)...)
case "rename":
statements = append(statements, sc.CompileRename(this)...)
case "modifyComment":
statements = append(statements, sc.CompileModifyComment(this)...)
}
}
@@ -420,26 +419,10 @@ func (this *Blueprint) GetCommands() []*Command {
return this.commands
}
func (this *Blueprint) IsTemporary() bool {
return this.temporary
}
func (this *Blueprint) GetTable() string {
return this.table
}
func (this *Blueprint) GetCharset() string {
return this.charset
}
func (this *Blueprint) GetEngine() string {
return this.engine
}
func (this *Blueprint) GetCollation() string {
return this.collation
}
// 命令类型
func (this *Command) Command() string {
return this.Type
+8 -1
View File
@@ -1,7 +1,7 @@
package schema
import (
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db"
)
type Builder struct {
@@ -48,6 +48,13 @@ func (this Builder) Rename(from, to string) error {
return this.Build(bp)
}
// 修改表备注
func (this Builder) ModifyComment(table, comment string) error {
bp := NewBlueprint(table)
bp.ModifyComment(comment)
return this.Build(bp)
}
// 删除表
func (this Builder) Drop(table string) error {
bp := NewBlueprint(table)
+20 -10
View File
@@ -3,11 +3,11 @@ package mysql
import (
"strings"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/dialect/mysql"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/dialect/mysql"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/sqlgen"
)
type Mysql struct {
@@ -16,6 +16,7 @@ type Mysql struct {
}
var mysqlDefaultModifiers = []string{
"Unsigned", "Charset", "Collate", "VirtualAs", "StoredAs",
"Nullable", "Default", "Increment", "Comment", "After", "First",
}
@@ -47,29 +48,33 @@ func (this Mysql) TableExists(table string) (bool, error) {
// 创建表
func (this Mysql) CompileCreate(bp *schema.Blueprint) []string {
temporary := db.L("CREATE")
if bp.IsTemporary() {
if bp.Temporary {
temporary = db.L("CREATE TEMPORARY")
}
columns := strings.Join(this.getAddedColumns(bp), ",\n")
sql := this.GenerateSQL("? TABLE ? (\n?\n)", temporary, db.T(bp.GetTable()), db.L(columns))
charset := bp.GetCharset()
charset := bp.Charset
if charset == "" {
charset = "utf8mb4"
}
collation := bp.GetCollation()
collation := bp.Collation
if collation == "" {
collation = "utf8mb4_general_ci"
}
sql += this.GenerateSQL(" default charset=? collate=?", charset, collation)
if engine := bp.GetEngine(); engine != "" {
if engine := bp.Engine; engine != "" {
sql += this.GenerateSQL(" engine=?", engine)
}
if comment := bp.Comment; comment != "" {
sql += this.GenerateSQL(" comment=?", comment)
}
return []string{sql}
}
@@ -121,13 +126,18 @@ func (this Mysql) CompileRename(bp *schema.Blueprint) []string {
if len(commands) == 0 {
panic("new table undefined")
}
toName = bp.GetCommands()[0].To
toName = commands[0].To
if toName == "" {
panic("new table undefined")
}
return []string{this.GenerateSQL("RENAME TABLE ? TO ?", db.T(bp.GetTable()), db.T(toName))}
}
// 修改表备注
func (this Mysql) CompileModifyComment(bp *schema.Blueprint) []string {
return []string{this.GenerateSQL("ALTER TABLE ? COMMENT = ?", db.T(bp.GetTable()), db.V(bp.Comment))}
}
func (this Mysql) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
for _, modifier := range mysqlDefaultModifiers {
sql += this.GetColumnModifier(modifier, bp, column)
+4 -4
View File
@@ -3,9 +3,9 @@ package mysql_test
import (
"os"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/engine"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite"
)
@@ -40,7 +40,7 @@ func (t *mysqlBuilderTest) TestGetColumnListing() {
// 创建表
func (t *mysqlBuilderTest) TestCreateTable() {
if err := t.builder.Create("users", func(table *schema.Blueprint) {
table.Charset("utf8mb4")
table.Charset = "utf8mb4"
table.BigIncrements("id").Comment("ID")
table.Boolean("enabled").Default("1").Comment("是否有效")
+6 -4
View File
@@ -4,9 +4,9 @@ import (
"os"
"testing"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/engine"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite"
_ "github.com/go-sql-driver/mysql"
@@ -71,7 +71,7 @@ func (t *mysqlTest) TestTableExists() {
func (t *mysqlTest) TestCompileCreate() {
bp := schema.NewBlueprint("users")
bp.Create()
bp.Charset("utf8mb4")
bp.Charset = "utf8mb4"
bp.BigIncrements("id").AutoIncrement().Comment("ID")
bp.Boolean("enabled").Default("1").Comment("是否有效")
@@ -188,3 +188,5 @@ func (t *mysqlTest) TestCompileRename() {
t.T().Log(sql)
}
// 修改表备注
+11 -6
View File
@@ -3,11 +3,11 @@ package postgres
import (
"strings"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/dialect/postgres"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/dialect/postgres"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/sqlgen"
)
type Postgres struct {
@@ -41,7 +41,7 @@ func (this Postgres) TableExists(table string) (bool, error) {
func (this Postgres) CompileCreate(bp *schema.Blueprint) []string {
temporary := db.L("CREATE")
if bp.IsTemporary() {
if bp.Temporary {
temporary = db.L("CREATE TEMPORARY")
}
columns := strings.Join(this.getAddedColumns(bp), ",\n")
@@ -96,6 +96,11 @@ func (this Postgres) CompileRename(bp *schema.Blueprint) []string {
return []string{this.GenerateSQL("ALTER TABLE ? RENAME TO ?", db.T(bp.GetTable()), db.T(toName))}
}
// 修改表备注
func (this Postgres) CompileModifyComment(bp *schema.Blueprint) []string {
return []string{this.GenerateSQL("COMMENT ON TABLE ? IS ?", db.T(bp.GetTable()), db.V(bp.Comment))}
}
func (this Postgres) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
for _, modifier := range pgDefaultModifiers {
sql += this.GetColumnModifier(modifier, bp, column)
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"os"
"testing"
"git.fsdpf.net/go/db/v2/engine"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite"
_ "github.com/lib/pq"
+18 -6
View File
@@ -5,11 +5,11 @@ import (
"strings"
"time"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/dialect/sqlite3"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/dialect/sqlite3"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/sqlgen"
)
var (
@@ -72,8 +72,15 @@ func (this Sqlite3) CompileCreate(bp *schema.Blueprint) (sqls []string) {
}
columns = append(columns, sql)
}
prefix := "? TABLE ? (\n?\n)"
if comment := bp.Comment; comment != "" {
prefix = "? TABLE ? ( -- " + comment + "\n?\n)"
}
sqls = append(sqls,
this.GenerateSQL("? TABLE ? (\n?\n)",
this.GenerateSQL(prefix,
temporary,
db.T(bp.GetTable()),
db.L(strings.Join(columns, "\n")),
@@ -152,6 +159,11 @@ func (this Sqlite3) CompileRename(bp *schema.Blueprint) []string {
return []string{this.GenerateSQL("RENAME TABLE ? TO ?", db.T(bp.GetTable()), db.T(toName))}
}
// 修改表备注
func (this Sqlite3) CompileModifyComment(bp *schema.Blueprint) []string {
return []string{}
}
func (this Sqlite3) GetColumnModifier(modifier string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
switch modifier {
case "Collate":
+4 -4
View File
@@ -4,9 +4,9 @@ import (
"os"
"testing"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/engine"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite"
_ "github.com/mattn/go-sqlite3"
@@ -68,7 +68,7 @@ func (t *sqlite3Test) TestGetColumnListing() {
func (t *sqlite3Test) TestCompileCreate() {
bp := schema.NewBlueprint("users")
bp.Create()
bp.Charset("utf8mb4")
bp.Charset = "utf8mb4"
bp.BigIncrements("id").AutoIncrement().Comment("ID")
bp.Boolean("enabled").Default("1").Comment("是否有效")
+35 -6
View File
@@ -3,11 +3,11 @@ package sqlserver
import (
"strings"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/dialect/sqlserver"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/dialect/sqlserver"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/schema"
"git.fsdpf.net/go/db/sqlgen"
)
type SQLServer struct {
@@ -39,7 +39,7 @@ func (this SQLServer) TableExists(table string) (bool, error) {
func (this SQLServer) CompileCreate(bp *schema.Blueprint) []string {
temporary := db.L("CREATE")
if bp.IsTemporary() {
if bp.Temporary {
temporary = db.L("CREATE")
}
columns := strings.Join(this.getAddedColumns(bp), ",\n")
@@ -94,6 +94,35 @@ func (this SQLServer) CompileRename(bp *schema.Blueprint) []string {
return []string{this.GenerateSQL("EXEC sp_rename ?, ?", db.T(bp.GetTable()), db.T(toName))}
}
// 修改表备注
func (this SQLServer) CompileModifyComment(bp *schema.Blueprint) []string {
return []string{
this.GenerateSQL(`
IF EXISTS (
SELECT 1 FROM sys.extended_properties
WHERE major_id = OBJECT_ID(?)
AND name = 'MS_Description'
AND minor_id = 0
)
BEGIN
EXEC sp_updateextendedproperty
@name = N'MS_Description',
@value = ?,
@level0type = N'SCHEMA', @level0name = 'dbo',
@level1type = N'TABLE', @level1name = ?
END
ELSE
BEGIN
EXEC sp_addextendedproperty
@name = N'MS_Description',
@value = ?,
@level0type = N'SCHEMA', @level0name = 'dbo',
@level1type = N'TABLE', @level1name = ?
END
`, db.T(bp.GetTable()), db.V(bp.Comment), db.T(bp.GetTable()), db.V(bp.Comment), db.T(bp.GetTable())),
}
}
func (this SQLServer) addModifiers(sql string, bp *schema.Blueprint, column *schema.ColumnDefinition) string {
for _, modifier := range sqlServerDefaultModifiers {
sql += this.GetColumnModifier(modifier, bp, column)
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"os"
"testing"
"git.fsdpf.net/go/db/v2/engine"
"git.fsdpf.net/go/db/v2/schema"
"git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/db/schema"
"github.com/stretchr/testify/suite"
_ "github.com/denisenkom/go-mssqldb"
+3 -1
View File
@@ -5,7 +5,7 @@ import (
"strings"
"sync"
"git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db"
)
type Schema interface {
@@ -27,6 +27,8 @@ type Schema interface {
CompileDropColumn(bp *Blueprint) []string
// 表重命名
CompileRename(bp *Blueprint) []string
// 修改表备注
CompileModifyComment(bp *Blueprint) []string
// 生成SQL
GenerateSQL(sql string, args ...any) string
}
-18
View File
@@ -1,26 +1,8 @@
package schema
import (
"strings"
"github.com/samber/lo"
)
func PrefixArray(prefix string, values []string) (items []string) {
for _, value := range values {
items = append(items, prefix+" "+value)
}
return items
}
func QuoteString(value any) string {
switch v := value.(type) {
case []string:
return strings.Join(lo.Map(v, func(item string, _ int) string {
return "'" + item + "'"
}), ", ")
case string:
return "'" + v + "'"
}
return ""
}
+42 -7
View File
@@ -4,10 +4,10 @@ import (
"context"
"fmt"
"git.fsdpf.net/go/db/v2/exec"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/exec"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
)
// Dataset for creating and/or executing SELECT SQL statements.
@@ -17,6 +17,7 @@ type SelectDataset struct {
isPrepared prepared
queryFactory exec.QueryFactory
err error
hooks exec.Hooks
}
var ErrQueryFactoryNotFoundError = errors.New(
@@ -94,6 +95,7 @@ func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset {
isPrepared: sd.isPrepared,
queryFactory: sd.queryFactory,
err: sd.err,
hooks: sd.hooks,
}
}
@@ -121,6 +123,7 @@ func (sd *SelectDataset) Update() *UpdateDataset {
}
}
u.clauses = c
u.hooks = sd.hooks
return u
}
@@ -130,13 +133,18 @@ func (sd *SelectDataset) Insert() *InsertDataset {
i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory).
Prepared(sd.isPrepared.Bool())
if sd.clauses.HasSources() {
i = i.Into(sd.GetClauses().From().Columns()[0])
if from, ok := sd.GetClauses().From().Columns()[0].(exp.AliasedExpression); ok {
i = i.Into(from.Aliased())
} else {
i = i.Into(from)
}
}
c := i.clauses
for _, ce := range sd.clauses.CommonTables() {
c = c.CommonTablesAppend(ce)
}
i.clauses = c
i.hooks = sd.hooks
return i
}
@@ -164,6 +172,7 @@ func (sd *SelectDataset) Delete() *DeleteDataset {
}
}
d.clauses = c
d.hooks = sd.hooks
return d
}
@@ -353,6 +362,11 @@ func (sd *SelectDataset) joinTable(join exp.JoinExpression) *SelectDataset {
return sd.copy(sd.clauses.JoinsAppend(join))
}
// Joins this Datasets table with another, prepend
func (sd *SelectDataset) JoinPrepend(table exp.Expression, condition exp.JoinCondition, joinType exp.JoinType) *SelectDataset {
return sd.copy(sd.clauses.JoinsPrepend(exp.NewConditionedJoinExpression(joinType, table, condition)))
}
// Adds a WHERE clause. See examples.
func (sd *SelectDataset) Where(expressions ...exp.Expression) *SelectDataset {
return sd.copy(sd.clauses.WhereAppend(expressions...))
@@ -387,6 +401,11 @@ func (sd *SelectDataset) withLock(strength exp.LockStrength, option exp.WaitOpti
return sd.copy(sd.clauses.SetLock(exp.NewLock(strength, option, of...)))
}
// Removes the Group BY clause. See examples.
func (sd *SelectDataset) ClearGroupBy() *SelectDataset {
return sd.copy(sd.clauses.ClearGroupBy())
}
// Adds a GROUP BY clause. See examples.
func (sd *SelectDataset) GroupBy(groupBy ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SetGroupBy(exp.NewColumnListExpression(groupBy...)))
@@ -551,8 +570,24 @@ func (sd *SelectDataset) ToSQL() (sql string, params []interface{}, err error) {
// db.From("test").Select("col").Executor()
//
// See Dataset#ToUpdateSQL for arguments
func (sd *SelectDataset) Executor() exec.QueryExecutor {
return sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder())
func (sd *SelectDataset) Executor() (executor exec.QueryExecutor) {
if sd.hooks != nil {
sd.SetError(sd.hooks.Before(sd))
}
executor = sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder())
if sd.hooks != nil {
executor.Hook(func(result interface{}) error {
return sd.hooks.After(sd, result)
})
}
return executor
}
func (sd *SelectDataset) WithHook(hooks exec.Hooks) *SelectDataset {
if sd.hooks == nil {
sd.hooks = hooks
}
return sd
}
// Appends this Dataset's SELECT statement to the SQLBuilder
+2 -2
View File
@@ -8,8 +8,8 @@ import (
"regexp"
"time"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"github.com/lib/pq"
)
+6 -6
View File
@@ -3,11 +3,11 @@ package db_test
import (
"testing"
dbv2 "git.fsdpf.net/go/db/v2"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/mocks"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/mocks"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
@@ -137,7 +137,7 @@ func (sds *selectDatasetSuite) TestDelete() {
Limit(limit).
Order(order...)
ec := exp.NewDeleteClauses().
SetFrom(dbv2.C("test")).
SetFrom(exp.NewColumnListExpression("test")).
CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)).
WhereAppend(ds.GetClauses().Where()).
SetLimit(limit).
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"strings"
"sync"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen"
)
type (
+1 -1
View File
@@ -3,7 +3,7 @@ package db_test
import (
"fmt"
dbv2 "git.fsdpf.net/go/db/v2"
dbv2 "git.fsdpf.net/go/db"
)
func ExampleRegisterDialect() {
+3 -3
View File
@@ -3,9 +3,9 @@ package db
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen/mocks"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen/mocks"
"github.com/stretchr/testify/suite"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package sqlgen_test
import (
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/internal/sb"
"github.com/stretchr/testify/suite"
)
+3 -3
View File
@@ -1,9 +1,9 @@
package sqlgen
import (
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
)
var ErrNoUpdatedValuesProvided = errors.New("no update values provided")
+3 -3
View File
@@ -3,9 +3,9 @@ package sqlgen_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite"
)
+12 -4
View File
@@ -1,9 +1,9 @@
package sqlgen
import (
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
)
type (
@@ -67,6 +67,14 @@ func (dsg *deleteSQLGenerator) Generate(b sb.SQLBuilder, clauses exp.DeleteClaus
func (dsg *deleteSQLGenerator) DeleteBeginSQL(b sb.SQLBuilder, from exp.ColumnListExpression, multiTable bool) {
b.Write(dsg.DialectOptions().DeleteClause)
if multiTable && dsg.DialectOptions().SupportsDeleteTableHint {
dsg.SourcesSQL(b, from)
_from := exp.NewColumnListExpression()
for _, col := range from.Columns() {
if aliased, ok := col.(exp.AliasedExpression); ok {
_from = _from.Append(aliased.GetAs())
} else {
_from = _from.Append(col)
}
}
dsg.SourcesSQL(b, _from)
}
}
+12 -12
View File
@@ -3,10 +3,10 @@ package sqlgen_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite"
)
@@ -50,7 +50,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestDialect() {
func (dsgs *deleteSQLGeneratorSuite) TestGenerate() {
dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", ""))
SetFrom(exp.NewColumnListExpression("test"))
dsgs.assertCases(
sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()),
@@ -72,7 +72,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withUnsupportedFragment() {
opts := sqlgen.DefaultDialectOptions()
opts.DeleteSQLOrder = []sqlgen.SQLFragmentType{sqlgen.InsertBeingSQLFragment}
dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", ""))
SetFrom(exp.NewColumnListExpression("test"))
dsgs.assertCases(
sqlgen.NewDeleteSQLGenerator("test", opts),
@@ -94,7 +94,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withErroredBuilder() {
opts := sqlgen.DefaultDialectOptions()
d := sqlgen.NewDeleteSQLGenerator("test", opts)
dc := exp.NewDeleteClauses().SetFrom(exp.NewIdentifierExpression("", "test", ""))
dc := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test"))
b := sb.NewSQLBuilder(false).SetError(errors.New("expected error"))
d.Generate(b, dc)
dsgs.assertErrorSQL(b, "db: expected error")
@@ -111,7 +111,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withCommonTables() {
tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil)
dc := exp.NewDeleteClauses().SetFrom(exp.NewIdentifierExpression("", "test_cte", ""))
dc := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test_cte"))
dcCte1 := dc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test_cte", tse))
dcCte2 := dc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test_cte", tse))
@@ -150,7 +150,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withCommonTables() {
func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withWhere() {
dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")).
SetFrom(exp.NewColumnListExpression("test")).
WhereAppend(exp.NewLiteralExpression(`"a"=?`, 1))
dsgs.assertCases(
sqlgen.NewDeleteSQLGenerator("test", sqlgen.DefaultDialectOptions()),
@@ -166,7 +166,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withOrder() {
opts.SupportsOrderByOnDelete = true
dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")).
SetFrom(exp.NewColumnListExpression("test")).
SetOrder(exp.NewIdentifierExpression("", "", "c").Desc())
dsgs.assertCases(
@@ -188,7 +188,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withLimit() {
opts.SupportsLimitOnDelete = true
dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")).
SetFrom(exp.NewColumnListExpression("test")).
SetLimit(1)
dsgs.assertCases(
@@ -210,7 +210,7 @@ func (dsgs *deleteSQLGeneratorSuite) TestGenerate_withReturning() {
opts.SupportsReturn = true
dc := exp.NewDeleteClauses().
SetFrom(exp.NewIdentifierExpression("", "test", "")).
SetFrom(exp.NewColumnListExpression("test")).
SetReturning(exp.NewColumnListExpression("a", "b"))
dsgs.assertCases(
+4 -4
View File
@@ -7,10 +7,10 @@ import (
"time"
"unicode/utf8"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/internal/util"
)
type (
+4 -4
View File
@@ -7,10 +7,10 @@ import (
"testing"
"time"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite"
)
+3 -3
View File
@@ -3,9 +3,9 @@ package sqlgen
import (
"strings"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/sb"
)
type (
+3 -3
View File
@@ -3,9 +3,9 @@ package sqlgen_test
import (
"testing"
"git.fsdpf.net/go/db/v2/exp"
"git.fsdpf.net/go/db/v2/internal/sb"
"git.fsdpf.net/go/db/v2/sqlgen"
"git.fsdpf.net/go/db/exp"
"git.fsdpf.net/go/db/internal/sb"
"git.fsdpf.net/go/db/sqlgen"
"github.com/stretchr/testify/suite"
)
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks
import exp "git.fsdpf.net/go/db/v2/exp"
import exp "git.fsdpf.net/go/db/exp"
import mock "github.com/stretchr/testify/mock"
import sb "git.fsdpf.net/go/db/v2/internal/sb"
import sb "git.fsdpf.net/go/db/internal/sb"
// DeleteSQLGenerator is an autogenerated mock type for the DeleteSQLGenerator type
type DeleteSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks
import exp "git.fsdpf.net/go/db/v2/exp"
import exp "git.fsdpf.net/go/db/exp"
import mock "github.com/stretchr/testify/mock"
import sb "git.fsdpf.net/go/db/v2/internal/sb"
import sb "git.fsdpf.net/go/db/internal/sb"
// InsertSQLGenerator is an autogenerated mock type for the InsertSQLGenerator type
type InsertSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks
import exp "git.fsdpf.net/go/db/v2/exp"
import exp "git.fsdpf.net/go/db/exp"
import mock "github.com/stretchr/testify/mock"
import sb "git.fsdpf.net/go/db/v2/internal/sb"
import sb "git.fsdpf.net/go/db/internal/sb"
// SelectSQLGenerator is an autogenerated mock type for the SelectSQLGenerator type
type SelectSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks
import exp "git.fsdpf.net/go/db/v2/exp"
import exp "git.fsdpf.net/go/db/exp"
import mock "github.com/stretchr/testify/mock"
import sb "git.fsdpf.net/go/db/v2/internal/sb"
import sb "git.fsdpf.net/go/db/internal/sb"
// TruncateSQLGenerator is an autogenerated mock type for the TruncateSQLGenerator type
type TruncateSQLGenerator struct {
+2 -2
View File
@@ -2,9 +2,9 @@
package mocks
import exp "git.fsdpf.net/go/db/v2/exp"
import exp "git.fsdpf.net/go/db/exp"
import mock "github.com/stretchr/testify/mock"
import sb "git.fsdpf.net/go/db/v2/internal/sb"
import sb "git.fsdpf.net/go/db/internal/sb"
// UpdateSQLGenerator is an autogenerated mock type for the UpdateSQLGenerator type
type UpdateSQLGenerator struct {

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