diff --git a/dal/__test__/srv/go.mod b/dal/__test__/srv/go.mod index c7c559a..2881dd3 100644 --- a/dal/__test__/srv/go.mod +++ b/dal/__test__/srv/go.mod @@ -12,13 +12,11 @@ replace l12.xyz/dal/adapter v0.0.0 => ../../../pkg/adapter replace l12.xyz/dal/utils v0.0.0 => ../../../pkg/utils - - replace l12.xyz/dal/proto v0.0.0 => ../../../pkg/proto -require l12.xyz/dal/server v0.0.0 +require l12.xyz/dal/handler v0.0.0 -replace l12.xyz/dal/server v0.0.0 => ../../../pkg/server +replace l12.xyz/dal/handler v0.0.0 => ../../../pkg/handler require ( github.com/mattn/go-sqlite3 v1.14.22 @@ -27,6 +25,6 @@ require ( github.com/tinylib/msgp v1.2.0 // indirect l12.xyz/dal/builder v0.0.0 // indirect l12.xyz/dal/filters v0.0.0 // indirect - l12.xyz/dal/utils v0.0.0 // indirect l12.xyz/dal/proto v0.0.0 // indirect + l12.xyz/dal/utils v0.0.0 // indirect ) diff --git a/dal/__test__/srv/main.go b/dal/__test__/srv/main.go index 6657134..8a9b670 100644 --- a/dal/__test__/srv/main.go +++ b/dal/__test__/srv/main.go @@ -9,7 +9,7 @@ import ( _ "github.com/mattn/go-sqlite3" "l12.xyz/dal/adapter" - "l12.xyz/dal/server" + "l12.xyz/dal/handler" ) func mock(adapter adapter.DBAdapter) { @@ -34,7 +34,7 @@ func main() { Type: "sqlite3", } mock(db) - queryHandler := server.QueryHandler(db) + queryHandler := handler.QueryHandler(db) mux := http.NewServeMux() mux.Handle("/", queryHandler) fmt.Println("Server running on port 8111") diff --git a/doc/dal-internals.md b/doc/dal-internals.md index 8c3f1b4..b21457e 100644 --- a/doc/dal-internals.md +++ b/doc/dal-internals.md @@ -59,11 +59,11 @@ Locations: Parsing the row stream (pseudo code): -```python +``` header = [0x81, 0xa1, 0x72] -input: byte[] = ... -buffer: byte[] = [] -output: byte[][] = [] +input: byte[] +buffer: byte[] +output: byte[][] while i < input.length: if input[i] != 0x81: buffer << input[i] @@ -140,7 +140,7 @@ Locations: ``` ### Builder Methods -In|Find|Select|Fields|Join|Group|Sort|Limit|Offset|Delete|Insert|Set|Update|OnConflict|DoUpdate|DoNothing +Raw|In|Find|Select|Fields|Join|Group|Sort|Limit|Offset|Delete|Insert|Set|Update|OnConflict|DoUpdate|DoNothing [TS Docs]() [Golang Docs]() diff --git a/pkg/adapter/DBAdapter.go b/pkg/adapter/DBAdapter.go index 02e5525..2d84552 100644 --- a/pkg/adapter/DBAdapter.go +++ b/pkg/adapter/DBAdapter.go @@ -8,12 +8,13 @@ import ( /* DBAdapter -Automatically creates connections for each database URL. -Executes queries on the specified database. -Closes connections older than ConnectionLiveTime +- Automatically creates connections for each database URL. +- Executes queries on the specified database. +- Closes connections older than ConnectionLiveTime */ type DBAdapter struct { Type string + DbInit []string MaxAttempts int ConnectionLiveTime int dbs *DBMap @@ -25,6 +26,10 @@ type DBMap struct { ConnectionTime map[string]int64 } +func (a *DBAdapter) AfterOpen(sql string) { + a.DbInit = append(a.DbInit, sql) +} + func (a *DBAdapter) Open(url string) (*sql.DB, error) { defer a.CleanUp() @@ -69,6 +74,9 @@ func (a *DBAdapter) Open(url string) (*sql.DB, error) { } attempts[url] = 0 + for _, sql := range a.DbInit { + connections[url].Exec(sql) + } return connections[url], nil } @@ -113,6 +121,10 @@ func (a *DBAdapter) Query(req Query) (*sql.Rows, error) { if req.Transaction { tx, _ := db.Begin() rows, err := tx.Query(req.Expression, req.Data...) + if err != nil { + tx.Rollback() + return nil, err + } tx.Commit() return rows, err } @@ -131,6 +143,10 @@ func (a *DBAdapter) Exec(req Query) (sql.Result, error) { if req.Transaction { tx, _ := db.Begin() result, err := tx.Exec(req.Expression, req.Data...) + if err != nil { + tx.Rollback() + return nil, err + } tx.Commit() return result, err } diff --git a/pkg/facade/SQLiteServer.go b/pkg/facade/SQLiteServer.go new file mode 100644 index 0000000..7c646c1 --- /dev/null +++ b/pkg/facade/SQLiteServer.go @@ -0,0 +1,70 @@ +package facade + +import ( + "net/http" + "os" + "strings" + + "l12.xyz/dal/adapter" + "l12.xyz/dal/handler" +) + +type SQLiteServer struct { + Cwd string + Port string +} + +/* +Init initializes the SQLiteServer struct with the required environment variables. +- `SQLITE_DIRECTORY` is the directory where the SQLite database is stored. +- `SQLITE_PORT` is the port on which the server listens. +*/ +func (s *SQLiteServer) Init() { + s.Cwd = os.Getenv("SQLITE_DIRECTORY") + if s.Cwd == "" { + panic("env variable `SQLITE_DIRECTORY` is not set") + } + os.MkdirAll(s.Cwd, os.ModePerm) + os.Chdir(s.Cwd) + s.Cwd = os.Getenv("SQLITE_PORT") + if s.Port == "" { + s.Port = "8118" + } +} + +/* +GetAdapter returns a DBAdapter struct with the SQLite3 dialect registered. +- The `SQLITE_PRAGMAS` environment variable is expected to be a semicolon separated list of PRAGMA statements. +*/ +func (s *SQLiteServer) GetAdapter() adapter.DBAdapter { + adapter.RegisterDialect("sqlite3", adapter.CommonDialect{}) + db := adapter.DBAdapter{ + Type: "sqlite3", + } + db.AfterOpen("PRAGMA journal_mode=WAL") + for _, pragma := range strings.Split(os.Getenv("SQLITE_PRAGMAS"), ";") { + if pragma == "" { + continue + } + db.AfterOpen(pragma) + } + return db +} + +/* +GetHanfler returns a http.Handler configured for the SQLiteServer. +*/ +func (s *SQLiteServer) GetHandler() http.Handler { + return handler.QueryHandler(s.GetAdapter()) +} + +/* +Serve starts the basic server on the configured port. +Use `GetHandler` to get a handler for a custom server. +*/ +func (s *SQLiteServer) Serve() { + err := http.ListenAndServe(":"+s.Port, s.GetHandler()) + if err != nil { + panic(err) + } +} diff --git a/pkg/facade/go.mod b/pkg/facade/go.mod new file mode 100644 index 0000000..e33992d --- /dev/null +++ b/pkg/facade/go.mod @@ -0,0 +1,29 @@ +module l12.xyz/dal/facade + +go 1.22.6 + +replace l12.xyz/dal/filters v0.0.0 => ../filters + +replace l12.xyz/dal/builder v0.0.0 => ../builder + +require l12.xyz/dal/adapter v0.0.0 + +replace l12.xyz/dal/adapter v0.0.0 => ../adapter + +replace l12.xyz/dal/utils v0.0.0 => ../utils + +require ( + github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/tinylib/msgp v1.2.0 // indirect + l12.xyz/dal/builder v0.0.0 // indirect + l12.xyz/dal/filters v0.0.0 // indirect + l12.xyz/dal/proto v0.0.0 // indirect + l12.xyz/dal/utils v0.0.0 // indirect +) + +replace l12.xyz/dal/proto v0.0.0 => ../proto + +require l12.xyz/dal/handler v0.0.0 + +replace l12.xyz/dal/handler v0.0.0 => ../handler diff --git a/pkg/server/go.sum b/pkg/facade/go.sum similarity index 100% rename from pkg/server/go.sum rename to pkg/facade/go.sum diff --git a/pkg/server/go.mod b/pkg/handler/go.mod similarity index 96% rename from pkg/server/go.mod rename to pkg/handler/go.mod index 0e700df..093baab 100644 --- a/pkg/server/go.mod +++ b/pkg/handler/go.mod @@ -1,4 +1,4 @@ -module l12.xyz/dal/server +module l12.xyz/dal/http go 1.22.6 diff --git a/pkg/handler/go.sum b/pkg/handler/go.sum new file mode 100644 index 0000000..5cf32ca --- /dev/null +++ b/pkg/handler/go.sum @@ -0,0 +1,8 @@ +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4= +github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/tinylib/msgp v1.2.0 h1:0uKB/662twsVBpYUPbokj4sTSKhWFKB7LopO2kWK8lY= +github.com/tinylib/msgp v1.2.0/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= diff --git a/pkg/server/query_handler.go b/pkg/handler/query_handler.go similarity index 99% rename from pkg/server/query_handler.go rename to pkg/handler/query_handler.go index 11b2c4d..e477570 100644 --- a/pkg/server/query_handler.go +++ b/pkg/handler/query_handler.go @@ -1,4 +1,4 @@ -package server +package handler import ( "io" diff --git a/pkg/server/query_handler_test.go b/pkg/handler/query_handler_test.go similarity index 99% rename from pkg/server/query_handler_test.go rename to pkg/handler/query_handler_test.go index e4241be..3a2faaf 100644 --- a/pkg/server/query_handler_test.go +++ b/pkg/handler/query_handler_test.go @@ -1,4 +1,4 @@ -package server +package handler import ( "bytes" diff --git a/pkg/proto/request.go b/pkg/proto/request.go index 4203fa9..15bbfae 100644 --- a/pkg/proto/request.go +++ b/pkg/proto/request.go @@ -49,6 +49,14 @@ func (q *Request) Parse(dialect adapter.Dialect) (adapter.Query, error) { if cmd.Method == "Insert" || cmd.Method == "Set" || cmd.Method == "Delete" { exec = true } + // check if raw is an exec query + if cmd.Method == "Raw" { + qo, ok := cmd.Args[0].(map[string]interface{}) + if ok { + sq := qo["s"].(string) + exec = !strings.HasPrefix(sq, "SELECT") + } + } args := make([]reflect.Value, len(cmd.Args)) for i, arg := range cmd.Args { args[i] = reflect.ValueOf(arg)