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:
+10
-1
@@ -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...)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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")),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.fsdpf.net/go/db/v2/engine"
|
||||
"git.fsdpf.net/go/db/engine"
|
||||
)
|
||||
|
||||
// TestMySQLConfig 测试 MySQL 驱动的配置
|
||||
|
||||
+54
-6
@@ -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
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"git.fsdpf.net/go/db/v2/internal/sb"
|
||||
"git.fsdpf.net/go/db/internal/sb"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package exec
|
||||
|
||||
// Hooks 钩子实例
|
||||
type Hooks interface {
|
||||
Before(dataset interface{}) error
|
||||
After(dataset interface{}, result interface{}) error
|
||||
}
|
||||
+132
-21
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 (
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
@@ -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 (
|
||||
|
||||
@@ -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
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -1,7 +1,7 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/db/v2/exp"
|
||||
"git.fsdpf.net/go/db/exp"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -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
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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("是否有效")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
// 修改表备注
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,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("是否有效")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
@@ -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 (
|
||||
|
||||
@@ -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,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
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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,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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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,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,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,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,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,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,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,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
Reference in New Issue
Block a user