[feat] sqlite server facade
Signed-off-by: Anton Nesterov <anton@demiurg.io>
This commit is contained in:
parent
fa9d105e57
commit
80410d5585
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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]()
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
70
pkg/facade/SQLiteServer.go
Normal file
70
pkg/facade/SQLiteServer.go
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
29
pkg/facade/go.mod
Normal file
29
pkg/facade/go.mod
Normal file
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
module l12.xyz/dal/server
|
||||
module l12.xyz/dal/http
|
||||
|
||||
go 1.22.6
|
||||
|
8
pkg/handler/go.sum
Normal file
8
pkg/handler/go.sum
Normal file
|
@ -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=
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package handler
|
||||
|
||||
import (
|
||||
"io"
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue