From 2b604f85f190caf9e21d3150310aea1650dd0585 Mon Sep 17 00:00:00 2001 From: Alexander Trakhimenok Date: Sat, 25 Jul 2026 20:20:21 +0100 Subject: [PATCH] feat: return the sealed dal.DB from NewDatabase dalgo v0.64.2 renames today's dal.DB to dal.Backend and seals dal.DB so only dal.NewDB can produce one, gaining the framework's BeforeSave validation and hook pipeline on every read-write transaction. Bump the dependency and change NewDatabase's single return statement to dal.NewDB(&database{...}), and update the package's var _ dal.DB assertion to dal.Backend (a duplicate of that assertion is dropped in the same edit). dalgo2sql validates nothing itself today, so the framework's pipeline is a pure addition rather than a change in behaviour, and every existing check keeps passing unmodified. Sealing dal.DB does ripple through the test suite, though: many whitebox tests type-assert NewDatabase's return value straight to the concrete *database so they can call adapter methods (Insert, Update, Delete, onlyReadWriteTx, ...) that are not part of dal.DB. That assertion now fails because the returned value is dal.NewDB's wrapper struct, not *database. Recover the concrete backend at each of those call sites with dal.BackendOf, exactly the escape hatch the framework added for this. One of these, TestTransaction/Upsert, needed the same fix a step earlier: it asserts a capability (Upsert) that is specific to this adapter and not part of any dal package interface, so the framework's validated transaction wrapper does not promote it either; unwrapping via dal.BackendOf before opening the transaction restores the adapter's own transaction type the assertion expects. Add TestConformance, wiring the new dalgotest.RunConformance suite against a real in-memory SQLite database via the same modernc.org/sqlite driver already used elsewhere in this package's tests, so it needs no external database or env-gate and genuinely proves the framework validates writes before this adapter is entered. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SkkrXdtf8mU2GRo2hHsHT1 Signed-off-by: Alexander Trakhimenok --- conformance_test.go | 37 +++++++++++++++++++++++++++++++++++++ coverage_additions_test.go | 28 ++++++++++++++-------------- database.go | 14 ++++++++------ database_test.go | 2 +- deleter_test.go | 14 +++++++------- end2end/go.mod | 6 +++--- end2end/go.sum | 14 ++++++-------- getter_map_coverage_test.go | 4 ++-- go.mod | 6 +++--- go.sum | 14 ++++++-------- inserter_options_test.go | 4 ++-- inserter_test.go | 8 ++++---- setter_test.go | 16 ++++++++-------- sql_map_get_test.go | 2 +- sql_map_test.go | 4 ++-- transaction_test.go | 10 ++++++++-- updater_test.go | 12 ++++++------ 17 files changed, 118 insertions(+), 77 deletions(-) create mode 100644 conformance_test.go diff --git a/conformance_test.go b/conformance_test.go new file mode 100644 index 0000000..e5c1211 --- /dev/null +++ b/conformance_test.go @@ -0,0 +1,37 @@ +package dalgo2sql + +import ( + "fmt" + "testing" + + "github.com/dal-go/dalgo/dal" + "github.com/dal-go/dalgo/dalgotest" +) + +// TestConformance runs the shared dalgotest suite against a real (in-memory) +// SQLite database, via the same modernc.org/sqlite driver already used by +// sql_map_get_test.go — no external database or env-gate is needed. +// +// dalgo2sql itself validates nothing (there is no Validate call anywhere in +// this adapter). Every check here passes because dal.NewDB's write pipeline +// runs BeforeSave validation and hooks before this adapter's code is ever +// entered — see database.go's NewDatabase. +func TestConformance(t *testing.T) { + createSQL := fmt.Sprintf(`CREATE TABLE %s ( + ID TEXT PRIMARY KEY, + Name TEXT + )`, dalgotest.DefaultCollection) + + opts := DbOptions{ + Recordsets: map[string]*Recordset{ + dalgotest.DefaultCollection: NewRecordset( + dalgotest.DefaultCollection, Table, []dal.FieldRef{dal.Field("ID")}, + ), + }, + } + + dalgotest.RunConformance(t, func(t *testing.T) (dal.DB, func()) { + sqlDB := openTestSQLiteDB(t, createSQL) + return NewDatabase(sqlDB, newSchema(), opts), nil + }) +} diff --git a/coverage_additions_test.go b/coverage_additions_test.go index 1d12498..bf412a7 100644 --- a/coverage_additions_test.go +++ b/coverage_additions_test.go @@ -750,7 +750,7 @@ func TestUpdater_Errors(t *testing.T) { t.Fatal(err) } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) // no recordsets and no top-level PrimaryKey -> PrimaryKeyFieldNames returns nil key := record.NewKeyWithID("users", "u1") // update without primary key -> error @@ -766,11 +766,11 @@ func TestUpdater_Errors(t *testing.T) { t.Fatal(err) } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("a"), dal.Field("b")}), }, - }).(*database) + })).(*database) key := record.NewKeyWithID("users", "u1") err = db.Update(ctx, key, nil) if !errors.Is(err, dal.ErrNotImplementedYet) { @@ -784,11 +784,11 @@ func TestUpdater_Errors(t *testing.T) { t.Fatal(err) } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) mock.ExpectExec("UPDATE users SET").WillReturnError(errors.New("exec fail")) err = db.Update(ctx, record.NewKeyWithID("users", "u1"), nil) if err == nil { @@ -802,11 +802,11 @@ func TestUpdater_Errors(t *testing.T) { t.Fatal(err) } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) mock.ExpectExec("UPDATE users SET").WillReturnError(errors.New("nope")) err = db.UpdateMulti(ctx, []*record.Key{record.NewKeyWithID("users", "u1")}, nil) if err == nil { @@ -826,11 +826,11 @@ func TestInserter_Errors(t *testing.T) { t.Fatal(err) } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) mock.ExpectExec("INSERT INTO users").WillReturnError(errors.New("insert fail")) rec := record.NewRecordWithData(record.NewKeyWithID("users", "u1"), &user{Name: "J"}) err = db.Insert(ctx, rec) @@ -873,11 +873,11 @@ func TestDeleter_MultiInSingleTable_CustomPK(t *testing.T) { } defer closeDatabase(t, sqlDB) ctx := context.Background() - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("uid")}), }, - }).(*database) + })).(*database) keys := []*record.Key{ record.NewKeyWithID("users", "u1"), record.NewKeyWithID("users", "u2"), @@ -897,7 +897,7 @@ func TestDeleter_MultiInSingleTable_ExecError(t *testing.T) { } defer closeDatabase(t, sqlDB) ctx := context.Background() - db := NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) keys := []*record.Key{ record.NewKeyWithID("users", "u1"), record.NewKeyWithID("users", "u2"), @@ -919,11 +919,11 @@ func TestSetter_SetMulti_Error(t *testing.T) { } defer closeDatabase(t, sqlDB) ctx := context.Background() - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) mock.ExpectBegin() mock.ExpectQuery("SELECT ID FROM users WHERE ID = ?"). WithArgs("u1"). diff --git a/database.go b/database.go index 34c4f3e..4b495ef 100644 --- a/database.go +++ b/database.go @@ -9,7 +9,7 @@ import ( "github.com/dal-go/dalgo/recordset" ) -var _ dal.DB = (*database)(nil) +var _ dal.Backend = (*database)(nil) type database struct { dal.ConcurrencyAvailable // SupportsConcurrentConnections() = true (standard SQL pool) @@ -103,9 +103,11 @@ func (dtb *database) ExecuteQueryToRecordsReader(ctx context.Context, query dal. return getRecordsReader(ctx, query, dtb.db.QueryContext) } -var _ dal.DB = (*database)(nil) - -// NewDatabase creates a new instance of DALgo adapter to SQL database +// NewDatabase creates a new instance of DALgo adapter to SQL database. +// +// The returned dal.DB is sealed by dal.NewDB: every read-write transaction it +// starts hands the worker a transaction whose writes run the framework's +// BeforeSave validation and hooks before reaching this adapter's code. func NewDatabase(db *sql.DB, schema dal.Schema, options DbOptions) dal.DB { if db == nil { panic("db is a required parameter, got nil") @@ -113,7 +115,7 @@ func NewDatabase(db *sql.DB, schema dal.Schema, options DbOptions) dal.DB { if schema == nil { panic("schema is a required parameter, got nil") } - return &database{ + return dal.NewDB(&database{ recordsReaderProvider: recordsReaderProvider{ executeQuery: db.QueryContext, }, @@ -121,5 +123,5 @@ func NewDatabase(db *sql.DB, schema dal.Schema, options DbOptions) dal.DB { db: db, schema: schema, options: options, - } + }) } diff --git a/database_test.go b/database_test.go index 5ace1ba..5e46a1f 100644 --- a/database_test.go +++ b/database_test.go @@ -99,7 +99,7 @@ func TestOptions_PrimaryKeyFieldNames(t *testing.T) { func newDatabase(t *testing.T) (sqlDB *sql.DB, mock sqlmock.Sqlmock, db *database, closer func(), err error) { sqlDB, mock, err = sqlmock.New() - db = NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db = dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) closer = func() { closeDatabase(t, sqlDB) } diff --git a/deleter_test.go b/deleter_test.go index e39279f..909b97f 100644 --- a/deleter_test.go +++ b/deleter_test.go @@ -20,7 +20,7 @@ func TestDeleter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) key := record.NewKeyWithID("users", "u1") mock.ExpectExec("DELETE FROM users WHERE ID = ?"). @@ -41,11 +41,11 @@ func TestDeleter(t *testing.T) { defer closeDatabase(t, sqlDB) rs := NewRecordset("users", Table, []dal.FieldRef{dal.Field("uid")}) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": rs, }, - }).(*database) + })).(*database) key := record.NewKeyWithID("users", "u1") mock.ExpectExec("DELETE FROM users WHERE uid = ?"). @@ -65,7 +65,7 @@ func TestDeleter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) keys := []*record.Key{ record.NewKeyWithID("users", "u1"), record.NewKeyWithID("users", "u2"), @@ -90,7 +90,7 @@ func TestDeleter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) keys := []*record.Key{ record.NewKeyWithID("users", "u1"), record.NewKeyWithID("posts", "p1"), @@ -112,7 +112,7 @@ func TestDeleter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) key := record.NewKeyWithID("users", "u1") mock.ExpectExec("DELETE FROM users WHERE ID = ?"). @@ -132,7 +132,7 @@ func TestDeleter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{}).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{})).(*database) keys := []*record.Key{ record.NewKeyWithID("users", "u1"), record.NewKeyWithID("users", "u2"), diff --git a/end2end/go.mod b/end2end/go.mod index 83455d1..52a3def 100644 --- a/end2end/go.mod +++ b/end2end/go.mod @@ -5,7 +5,7 @@ go 1.25.0 toolchain go1.26.5 require ( - github.com/dal-go/dalgo v0.63.2 + github.com/dal-go/dalgo v0.64.2 github.com/dal-go/dalgo2sql v0.9.6 // No version as we alway replace it with local version github.com/mattn/go-sqlite3 v1.14.48 ) @@ -13,9 +13,9 @@ require ( replace github.com/dal-go/dalgo2sql => ./../ require ( - github.com/RoaringBitmap/roaring/v2 v2.22.0 // indirect + github.com/RoaringBitmap/roaring/v2 v2.24.0 // indirect github.com/bits-and-blooms/bitset v1.24.6 // indirect - github.com/dal-go/record v0.1.0 // indirect + github.com/dal-go/record v0.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/georgysavva/scany/v2 v2.1.4 // indirect github.com/jackc/pgx/v5 v5.7.6 // indirect diff --git a/end2end/go.sum b/end2end/go.sum index 5c82b46..022e028 100644 --- a/end2end/go.sum +++ b/end2end/go.sum @@ -1,17 +1,15 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/RoaringBitmap/roaring/v2 v2.22.0 h1:aGqjvTSkJSTP7W6q518EHiK9RRRb5gJbCaaciCFr/Lg= -github.com/RoaringBitmap/roaring/v2 v2.22.0/go.mod h1:SfT3of9nYh3vis1dIbCj4Yw6KQGujTN+f345nrN/0JA= +github.com/RoaringBitmap/roaring/v2 v2.24.0 h1:zQkkBZtG3WRP4j+P3A5DO221SvL1Br88TJkhyqEQRZo= +github.com/RoaringBitmap/roaring/v2 v2.24.0/go.mod h1:SfT3of9nYh3vis1dIbCj4Yw6KQGujTN+f345nrN/0JA= github.com/bits-and-blooms/bitset v1.24.6 h1:qcrftZUVBIwfs+m+nhoCBAPT+ZPZZjti8SbHbDQQkZ4= github.com/bits-and-blooms/bitset v1.24.6/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= github.com/cockroachdb/cockroach-go/v2 v2.2.0/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI= -github.com/dal-go/dalgo v0.63.1 h1:GEJAGlNH5xGLdFasSIRrYdqGfd0+4A9DGQ843qJQbyA= -github.com/dal-go/dalgo v0.63.1/go.mod h1:LtD5XVzb1kAdXaRcWVNy4F2ROC4fqR4jqD3q/GD4fJQ= -github.com/dal-go/dalgo v0.63.2 h1:L3hDte5QaorZngNK619V1zKach4qfeb9jOQQUsn4vrc= -github.com/dal-go/dalgo v0.63.2/go.mod h1:LtD5XVzb1kAdXaRcWVNy4F2ROC4fqR4jqD3q/GD4fJQ= -github.com/dal-go/record v0.1.0 h1:hA4143oZwIgtBH/1BRTUEZMCpDfXQr3ONlVXX8USCNg= -github.com/dal-go/record v0.1.0/go.mod h1:quwsVJTT0f6y3Mhx+yHpTobY7luX1M6kyO6fdJ/AFYE= +github.com/dal-go/dalgo v0.64.2 h1:uWCRISCMTpuwjq+VKlymq3kBtdr0c09gcsr75zmeqrw= +github.com/dal-go/dalgo v0.64.2/go.mod h1:PZGzE0AqnaJgPEDTSo7ayfKZaoglhFQ9FxHecF3aQAc= +github.com/dal-go/record v0.1.1 h1:N2WVDBnm2tOb83h5DJqFyINB5kH0vLjKlYFAgCJWv2g= +github.com/dal-go/record v0.1.1/go.mod h1:quwsVJTT0f6y3Mhx+yHpTobY7luX1M6kyO6fdJ/AFYE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= diff --git a/getter_map_coverage_test.go b/getter_map_coverage_test.go index 5482504..5bd7ca0 100644 --- a/getter_map_coverage_test.go +++ b/getter_map_coverage_test.go @@ -46,7 +46,7 @@ func TestMapDataGetIntoNilMapPointer(t *testing.T) { name TEXT NOT NULL )` sqlDB := openTestSQLiteDB(t, createSQL) - db := NewDatabase(sqlDB, newSchema(), opts).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), opts)).(*database) rec := record.NewRecordWithData( record.NewKeyWithID("widgets", "w1"), @@ -85,7 +85,7 @@ func TestMapDataGetBlobAsString(t *testing.T) { payload BLOB NOT NULL )` sqlDB := openTestSQLiteDB(t, createSQL) - db := NewDatabase(sqlDB, newSchema(), opts).(*database) + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), opts)).(*database) // Insert a genuine BLOB value directly so the driver returns []byte on read. if _, err := sqlDB.Exec(`INSERT INTO blobs (id, payload) VALUES (?, ?)`, "b1", []byte("hello-blob")); err != nil { diff --git a/go.mod b/go.mod index 56a7750..5a8748c 100644 --- a/go.mod +++ b/go.mod @@ -8,14 +8,14 @@ toolchain go1.26.5 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 - github.com/dal-go/dalgo v0.63.2 - github.com/dal-go/record v0.1.0 + github.com/dal-go/dalgo v0.64.2 + github.com/dal-go/record v0.1.1 github.com/georgysavva/scany/v2 v2.1.4 modernc.org/sqlite v1.54.0 ) require ( - github.com/RoaringBitmap/roaring/v2 v2.22.0 // indirect + github.com/RoaringBitmap/roaring/v2 v2.24.0 // indirect github.com/bits-and-blooms/bitset v1.24.6 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 3a5fe06..41dd4c7 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,15 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/RoaringBitmap/roaring/v2 v2.22.0 h1:aGqjvTSkJSTP7W6q518EHiK9RRRb5gJbCaaciCFr/Lg= -github.com/RoaringBitmap/roaring/v2 v2.22.0/go.mod h1:SfT3of9nYh3vis1dIbCj4Yw6KQGujTN+f345nrN/0JA= +github.com/RoaringBitmap/roaring/v2 v2.24.0 h1:zQkkBZtG3WRP4j+P3A5DO221SvL1Br88TJkhyqEQRZo= +github.com/RoaringBitmap/roaring/v2 v2.24.0/go.mod h1:SfT3of9nYh3vis1dIbCj4Yw6KQGujTN+f345nrN/0JA= github.com/bits-and-blooms/bitset v1.24.6 h1:qcrftZUVBIwfs+m+nhoCBAPT+ZPZZjti8SbHbDQQkZ4= github.com/bits-and-blooms/bitset v1.24.6/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= github.com/cockroachdb/cockroach-go/v2 v2.2.0/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI= -github.com/dal-go/dalgo v0.63.1 h1:GEJAGlNH5xGLdFasSIRrYdqGfd0+4A9DGQ843qJQbyA= -github.com/dal-go/dalgo v0.63.1/go.mod h1:LtD5XVzb1kAdXaRcWVNy4F2ROC4fqR4jqD3q/GD4fJQ= -github.com/dal-go/dalgo v0.63.2 h1:L3hDte5QaorZngNK619V1zKach4qfeb9jOQQUsn4vrc= -github.com/dal-go/dalgo v0.63.2/go.mod h1:LtD5XVzb1kAdXaRcWVNy4F2ROC4fqR4jqD3q/GD4fJQ= -github.com/dal-go/record v0.1.0 h1:hA4143oZwIgtBH/1BRTUEZMCpDfXQr3ONlVXX8USCNg= -github.com/dal-go/record v0.1.0/go.mod h1:quwsVJTT0f6y3Mhx+yHpTobY7luX1M6kyO6fdJ/AFYE= +github.com/dal-go/dalgo v0.64.2 h1:uWCRISCMTpuwjq+VKlymq3kBtdr0c09gcsr75zmeqrw= +github.com/dal-go/dalgo v0.64.2/go.mod h1:PZGzE0AqnaJgPEDTSo7ayfKZaoglhFQ9FxHecF3aQAc= +github.com/dal-go/record v0.1.1 h1:N2WVDBnm2tOb83h5DJqFyINB5kH0vLjKlYFAgCJWv2g= +github.com/dal-go/record v0.1.1/go.mod h1:quwsVJTT0f6y3Mhx+yHpTobY7luX1M6kyO6fdJ/AFYE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= diff --git a/inserter_options_test.go b/inserter_options_test.go index d894e17..feadfee 100644 --- a/inserter_options_test.go +++ b/inserter_options_test.go @@ -17,11 +17,11 @@ func newUsersDatabaseWithMock(t *testing.T) (*database, sqlmock.Sqlmock, func()) if err != nil { t.Fatal(err) } - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) return db, mock, func() { closeDatabase(t, sqlDB) } } diff --git a/inserter_test.go b/inserter_test.go index 2b35e83..16f2d3b 100644 --- a/inserter_test.go +++ b/inserter_test.go @@ -23,11 +23,11 @@ func TestInserter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) u := user{Name: "u1"} record := dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), &u) @@ -49,11 +49,11 @@ func TestInserter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) records := []dalrecord.Record{ dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), &user{Name: "u1"}), diff --git a/setter_test.go b/setter_test.go index eb33f86..ea4f057 100644 --- a/setter_test.go +++ b/setter_test.go @@ -20,11 +20,11 @@ func TestSetter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) u := user{Name: "u1"} record := dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), &u) @@ -45,11 +45,11 @@ func TestSetter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) u := user{Name: "u1"} record := dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), &u) @@ -70,11 +70,11 @@ func TestSetter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) records := []dalrecord.Record{ dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), &user{Name: "u1"}), @@ -98,11 +98,11 @@ func TestSetter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) u := user{Name: "u1"} record := dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), &u) diff --git a/sql_map_get_test.go b/sql_map_get_test.go index 6e342d4..ba4c5d3 100644 --- a/sql_map_get_test.go +++ b/sql_map_get_test.go @@ -47,7 +47,7 @@ func TestMapDataGetRoundTrip(t *testing.T) { newDB := func(t *testing.T) *database { t.Helper() sqlDB := openTestSQLiteDB(t, createSQL) - return NewDatabase(sqlDB, newSchema(), opts).(*database) + return dal.BackendOf(NewDatabase(sqlDB, newSchema(), opts)).(*database) } t.Run("Insert_then_Get_map", func(t *testing.T) { diff --git a/sql_map_test.go b/sql_map_test.go index cef2cc1..9e20dfe 100644 --- a/sql_map_test.go +++ b/sql_map_test.go @@ -120,11 +120,11 @@ func TestInserter_MapData(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) data := map[string]any{"col_b": 42, "col_a": "x"} record := dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), data) diff --git a/transaction_test.go b/transaction_test.go index 4ca3f43..d64187c 100644 --- a/transaction_test.go +++ b/transaction_test.go @@ -171,11 +171,17 @@ func TestTransaction(t *testing.T) { t.Run("Upsert", func(t *testing.T) { sqlDB, mock, _ := sqlmock.New() defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + // Upsert is an adapter-specific convenience method, not part of any + // dal package interface, so the framework's validated transaction + // wrapper (dal.validatedTx) does not promote it. dal.BackendOf + // recovers the adapter's own Backend so RunReadwriteTransaction + // hands the worker the concrete *transaction the type assertion + // below expects, exactly as it did before dal.DB was sealed. + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }) + })) mock.ExpectBegin() mock.ExpectQuery("SELECT ID FROM users WHERE ID = ?").WillReturnRows(sqlmock.NewRows([]string{"ID"})) // not exists mock.ExpectExec("INSERT INTO users").WillReturnResult(sqlmock.NewResult(1, 1)) diff --git a/updater_test.go b/updater_test.go index e8ee839..f317bb0 100644 --- a/updater_test.go +++ b/updater_test.go @@ -20,11 +20,11 @@ func TestUpdater(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) key := dalrecord.NewKeyWithID("users", "id1") updates := []update.Update{ @@ -48,11 +48,11 @@ func TestUpdater(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) keys := []*dalrecord.Key{ dalrecord.NewKeyWithID("users", "id1"), @@ -82,11 +82,11 @@ func TestUpserter(t *testing.T) { } defer closeDatabase(t, sqlDB) - db := NewDatabase(sqlDB, newSchema(), DbOptions{ + db := dal.BackendOf(NewDatabase(sqlDB, newSchema(), DbOptions{ Recordsets: map[string]*Recordset{ "users": NewRecordset("users", Table, []dal.FieldRef{dal.Field("ID")}), }, - }).(*database) + })).(*database) u := user{Name: "u1"} record := dalrecord.NewRecordWithData(dalrecord.NewKeyWithID("users", "id1"), &u)