-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
next steps: extended schema.go and created duckdb.go
- Loading branch information
Ludwig Lehnert
committed
Sep 23, 2024
1 parent
6a7ac24
commit 34f8393
Showing
9 changed files
with
629 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package ldb | ||
|
||
type DatabaseAdapter interface { | ||
Close() error | ||
Begin() (DatabaseTransaction, error) | ||
} | ||
|
||
type DatabaseTransaction interface { | ||
// perform commit; implementation may be omitted for NoSQL datbases | ||
Commit() error | ||
// perform rollback; implementation may be omitted for NoSQL databases | ||
Rollback() error | ||
|
||
SaveCollection(collection Collection) error | ||
DropCollection(collection Collection) error | ||
|
||
SaveView(view View) error | ||
DropView(view View) error | ||
|
||
// checks if the migration with the given name has already been performed | ||
MigrationExists(migrationName string) (bool, error) | ||
// saves the given migration name to the migration history | ||
FinishMigration(migrationName string) error | ||
|
||
// GetCollection(name string, fields map[string]FieldType) ([]any, error) | ||
// GetRecord(collection string, fields map[string]FieldType, id string) (any, error) | ||
// CreateRecord(collection string, fields map[string]FieldType, data map[string]any) (string, error) | ||
// UpdateRecord(collection string, fields map[string]FieldType, id string, data map[string]any) error | ||
// DeleteRecord(collection string, fields map[string]FieldType, id string) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package ldb | ||
|
||
import ( | ||
"database/sql" | ||
"fmt" | ||
"strings" | ||
|
||
_ "github.com/marcboeker/go-duckdb" | ||
"github.com/samber/lo" | ||
) | ||
|
||
var _ DatabaseAdapter = DuckDBAdapter{} | ||
var _ DatabaseTransaction = DuckDBTransaction{} | ||
|
||
type DuckDBAdapter struct { | ||
db *sql.DB | ||
} | ||
|
||
func OpenDuckDBAdapter(databaseFilePath string) (*DuckDBAdapter, error) { | ||
db, err := sql.Open("duckdb", databaseFilePath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &DuckDBAdapter{db}, nil | ||
} | ||
|
||
func (s DuckDBAdapter) Close() error { | ||
return s.db.Close() | ||
} | ||
|
||
func (s DuckDBAdapter) Begin() (DatabaseTransaction, error) { | ||
tx, err := s.db.Begin() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return DatabaseTransaction(DuckDBTransaction{tx}), nil | ||
} | ||
|
||
type DuckDBTransaction struct { | ||
tx *sql.Tx | ||
} | ||
|
||
// Commit implements DatabaseTransaction. | ||
func (s DuckDBTransaction) Commit() error { | ||
return s.tx.Commit() | ||
} | ||
|
||
// Rollback implements DatabaseTransaction. | ||
func (s DuckDBTransaction) Rollback() error { | ||
return s.tx.Rollback() | ||
} | ||
|
||
// SaveCollection implements DatabaseTransaction. | ||
func (s DuckDBTransaction) SaveCollection(collection Collection) error { | ||
// create collection if not exists | ||
if collection.original == nil { | ||
columns := []string{} | ||
for _, field := range collection.Schema.Fields { | ||
columns = append(columns, columnSQL(field.Name, field.Schema.Type)) | ||
} | ||
|
||
sql := fmt.Sprintf("CREATE TABLE %s (%s)", collection.Name, strings.Join(columns, ", ")) | ||
|
||
_, err := s.tx.Exec(sql) | ||
return err | ||
} | ||
|
||
// rename collection if neccessary | ||
if collection.original.Name != collection.Name { | ||
sql := fmt.Sprintf("ALTER TABLE %s RENAME TO %s", collection.original.Name, collection.Name) | ||
_, err := s.tx.Exec(sql) | ||
if err != nil { | ||
|
||
return err | ||
} | ||
} | ||
|
||
createFields := lo.Filter(collection.Schema.Fields, func(field *Field, i int) bool { | ||
return field.original == nil | ||
}) | ||
|
||
renameFields := lo.Filter(collection.Schema.Fields, func(field *Field, i int) bool { | ||
return field.original.original.Name != field.Name | ||
}) | ||
|
||
removeFields := []*Field{} | ||
if collection.original != nil { | ||
removeFields = lo.Filter(collection.original.Schema.Fields, func(origField *Field, i int) bool { | ||
_, found := lo.Find(collection.Schema.Fields, func(field *Field) bool { | ||
return field.original != nil && field.original.Name == origField.Name | ||
}) | ||
|
||
return !found | ||
}) | ||
} | ||
|
||
for _, field := range removeFields { | ||
sql := fmt.Sprintf("ALTER TABLE %s DROP COLUMN %s", collection.Name, field.Name) | ||
if _, err := s.tx.Exec(sql); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
for _, field := range renameFields { | ||
sql := fmt.Sprintf("ALTER TABLE %s RENAME COLUMN %s TO %s", collection.Name, field.original.Name, field.Name) | ||
if _, err := s.tx.Exec(sql); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
for _, field := range createFields { | ||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", collection.Name, columnSQL(field.Name, field.Schema.Type)) | ||
if _, err := s.tx.Exec(sql); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// DropCollection implements DatabaseTransaction. | ||
func (s DuckDBTransaction) DropCollection(collection Collection) error { | ||
panic("unimplemented") | ||
} | ||
|
||
// SaveView implements DatabaseTransaction. | ||
func (s DuckDBTransaction) SaveView(view View) error { | ||
panic("unimplemented") | ||
} | ||
|
||
// DropView implements DatabaseTransaction. | ||
func (s DuckDBTransaction) DropView(view View) error { | ||
panic("unimplemented") | ||
} | ||
|
||
// MigrationExists implements DatabaseTransaction. | ||
func (s DuckDBTransaction) MigrationExists(migrationName string) (bool, error) { | ||
panic("unimplemented") | ||
} | ||
|
||
// FinishMigration implements DatabaseTransaction. | ||
func (s DuckDBTransaction) FinishMigration(migrationName string) error { | ||
panic("unimplemented") | ||
} | ||
|
||
func withNullConstraint(sql string, nullable bool) string { | ||
if nullable { | ||
return sql + " NULL" | ||
} | ||
|
||
return sql + " NOT NULL" | ||
} | ||
|
||
func columnSQL(column string, fieldType FieldType) string { | ||
switch ft := fieldType.(type) { | ||
case FieldTypeBool: | ||
return withNullConstraint(column+" BOOL", ft.Nullable) | ||
|
||
case FieldTypeDateTime: | ||
return withNullConstraint(column+" TIMESTAMP", ft.Nullable) | ||
|
||
case FieldTypeEnum: | ||
return withNullConstraint(column+" TEXT", ft.Nullable) | ||
|
||
case FieldTypeFloat: | ||
return withNullConstraint(column+" REAL", ft.Nullable) | ||
|
||
case FieldTypeId: | ||
sql := withNullConstraint(column+" TEXT", ft.Nullable || ft.PrimaryKey) | ||
|
||
if ft.PrimaryKey { | ||
sql += " PRIMARY KEY" | ||
} | ||
|
||
return sql | ||
|
||
case FieldTypeInt: | ||
return withNullConstraint(column+" BIGINT", ft.Nullable) | ||
|
||
case FieldTypeSingleRelation: | ||
sql := withNullConstraint(column+" TEXT", ft.Nullable) | ||
sql += " REFERENCES " + ft.Collection + "(id)" | ||
|
||
if ft.CascadeDelete { | ||
sql += " ON DELETE CASCADE" | ||
} | ||
|
||
return sql | ||
|
||
case FieldTypeText: | ||
return withNullConstraint(column+" TEXT", ft.Nullable) | ||
|
||
default: | ||
panic("SQLiteAdapter: unexpected fieldType") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,26 @@ | ||
module lehnert.dev/ldb | ||
|
||
go 1.22.7 | ||
|
||
require ( | ||
github.com/marcboeker/go-duckdb v1.8.0 | ||
github.com/samber/lo v1.47.0 | ||
) | ||
|
||
require ( | ||
github.com/apache/arrow/go/v17 v17.0.0 // indirect | ||
github.com/goccy/go-json v0.10.3 // indirect | ||
github.com/google/flatbuffers v24.3.25+incompatible // indirect | ||
github.com/klauspost/compress v1.17.9 // indirect | ||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect | ||
github.com/mitchellh/mapstructure v1.5.0 // indirect | ||
github.com/pierrec/lz4/v4 v4.1.21 // indirect | ||
github.com/zeebo/xxh3 v1.0.2 // indirect | ||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect | ||
golang.org/x/mod v0.18.0 // indirect | ||
golang.org/x/sync v0.7.0 // indirect | ||
golang.org/x/sys v0.21.0 // indirect | ||
golang.org/x/text v0.16.0 // indirect | ||
golang.org/x/tools v0.22.0 // indirect | ||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
github.com/apache/arrow/go/v17 v17.0.0 h1:RRR2bdqKcdbss9Gxy2NS/hK8i4LDMh23L6BbkN5+F54= | ||
github.com/apache/arrow/go/v17 v17.0.0/go.mod h1:jR7QHkODl15PfYyjM2nU+yTLScZ/qfj7OSUZmJ8putc= | ||
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/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= | ||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= | ||
github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= | ||
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= | ||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= | ||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= | ||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= | ||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | ||
github.com/marcboeker/go-duckdb v1.8.0 h1:iOWv1wTL0JIMqpyns6hCf5XJJI4fY6lmJNk+itx5RRo= | ||
github.com/marcboeker/go-duckdb v1.8.0/go.mod h1:2oV8BZv88S16TKGKM+Lwd0g7DX84x0jMxjTInThC8Is= | ||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | ||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= | ||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= | ||
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= | ||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= | ||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= | ||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= | ||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= | ||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= | ||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= | ||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= | ||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= | ||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= | ||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= | ||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= | ||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= | ||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= | ||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= | ||
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= | ||
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package ldb_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"lehnert.dev/ldb" | ||
) | ||
|
||
func TestSQLite(t *testing.T) { | ||
adapter, err := ldb.OpenDuckDBAdapter("/tmp/test.db") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
tx, err := adapter.Begin() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if err := tx.SaveCollection(ldb.Collection{ | ||
Name: "test0", | ||
Schema: &ldb.CollectionSchema{ | ||
Fields: []*ldb.Field{ | ||
{ | ||
Name: "id", | ||
Schema: &ldb.FieldSchema{ | ||
Type: ldb.FieldTypeId{ | ||
PrimaryKey: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if err := tx.SaveCollection(ldb.Collection{ | ||
Name: "test1", | ||
Schema: &ldb.CollectionSchema{ | ||
Fields: []*ldb.Field{ | ||
{Name: "bool", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeBool{}}}, | ||
{Name: "datetime", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeDateTime{}}}, | ||
{Name: "enum", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeEnum{EnumValues: []string{"a", "b", "c"}}}}, | ||
{Name: "float", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeFloat{}}}, | ||
{Name: "id", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeId{PrimaryKey: true}}}, | ||
{Name: "int", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeInt{}}}, | ||
{Name: "singleRelation", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeSingleRelation{Collection: "test0"}}}, | ||
{Name: "text", Schema: &ldb.FieldSchema{Type: ldb.FieldTypeText{}}}, | ||
}, | ||
}, | ||
}); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if err := tx.Commit(); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if err := adapter.Close(); err != nil { | ||
t.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package ldb |
Oops, something went wrong.