[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/utils v0.0.0 => ../../../pkg/utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
replace l12.xyz/dal/proto v0.0.0 => ../../../pkg/proto
|
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 (
|
require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
|
@ -27,6 +25,6 @@ require (
|
||||||
github.com/tinylib/msgp v1.2.0 // indirect
|
github.com/tinylib/msgp v1.2.0 // indirect
|
||||||
l12.xyz/dal/builder v0.0.0 // indirect
|
l12.xyz/dal/builder v0.0.0 // indirect
|
||||||
l12.xyz/dal/filters 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/proto v0.0.0 // indirect
|
||||||
|
l12.xyz/dal/utils v0.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"l12.xyz/dal/adapter"
|
"l12.xyz/dal/adapter"
|
||||||
"l12.xyz/dal/server"
|
"l12.xyz/dal/handler"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mock(adapter adapter.DBAdapter) {
|
func mock(adapter adapter.DBAdapter) {
|
||||||
|
@ -34,7 +34,7 @@ func main() {
|
||||||
Type: "sqlite3",
|
Type: "sqlite3",
|
||||||
}
|
}
|
||||||
mock(db)
|
mock(db)
|
||||||
queryHandler := server.QueryHandler(db)
|
queryHandler := handler.QueryHandler(db)
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.Handle("/", queryHandler)
|
mux.Handle("/", queryHandler)
|
||||||
fmt.Println("Server running on port 8111")
|
fmt.Println("Server running on port 8111")
|
||||||
|
|
|
@ -59,11 +59,11 @@ Locations:
|
||||||
|
|
||||||
Parsing the row stream (pseudo code):
|
Parsing the row stream (pseudo code):
|
||||||
|
|
||||||
```python
|
```
|
||||||
header = [0x81, 0xa1, 0x72]
|
header = [0x81, 0xa1, 0x72]
|
||||||
input: byte[] = ...
|
input: byte[]
|
||||||
buffer: byte[] = []
|
buffer: byte[]
|
||||||
output: byte[][] = []
|
output: byte[][]
|
||||||
while i < input.length:
|
while i < input.length:
|
||||||
if input[i] != 0x81:
|
if input[i] != 0x81:
|
||||||
buffer << input[i]
|
buffer << input[i]
|
||||||
|
@ -140,7 +140,7 @@ Locations:
|
||||||
```
|
```
|
||||||
|
|
||||||
### Builder Methods
|
### 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]()
|
[TS Docs]()
|
||||||
[Golang Docs]()
|
[Golang Docs]()
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,13 @@ import (
|
||||||
|
|
||||||
/*
|
/*
|
||||||
DBAdapter
|
DBAdapter
|
||||||
Automatically creates connections for each database URL.
|
- Automatically creates connections for each database URL.
|
||||||
Executes queries on the specified database.
|
- Executes queries on the specified database.
|
||||||
Closes connections older than ConnectionLiveTime
|
- Closes connections older than ConnectionLiveTime
|
||||||
*/
|
*/
|
||||||
type DBAdapter struct {
|
type DBAdapter struct {
|
||||||
Type string
|
Type string
|
||||||
|
DbInit []string
|
||||||
MaxAttempts int
|
MaxAttempts int
|
||||||
ConnectionLiveTime int
|
ConnectionLiveTime int
|
||||||
dbs *DBMap
|
dbs *DBMap
|
||||||
|
@ -25,6 +26,10 @@ type DBMap struct {
|
||||||
ConnectionTime map[string]int64
|
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) {
|
func (a *DBAdapter) Open(url string) (*sql.DB, error) {
|
||||||
defer a.CleanUp()
|
defer a.CleanUp()
|
||||||
|
|
||||||
|
@ -69,6 +74,9 @@ func (a *DBAdapter) Open(url string) (*sql.DB, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
attempts[url] = 0
|
attempts[url] = 0
|
||||||
|
for _, sql := range a.DbInit {
|
||||||
|
connections[url].Exec(sql)
|
||||||
|
}
|
||||||
return connections[url], nil
|
return connections[url], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +121,10 @@ func (a *DBAdapter) Query(req Query) (*sql.Rows, error) {
|
||||||
if req.Transaction {
|
if req.Transaction {
|
||||||
tx, _ := db.Begin()
|
tx, _ := db.Begin()
|
||||||
rows, err := tx.Query(req.Expression, req.Data...)
|
rows, err := tx.Query(req.Expression, req.Data...)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
return rows, err
|
return rows, err
|
||||||
}
|
}
|
||||||
|
@ -131,6 +143,10 @@ func (a *DBAdapter) Exec(req Query) (sql.Result, error) {
|
||||||
if req.Transaction {
|
if req.Transaction {
|
||||||
tx, _ := db.Begin()
|
tx, _ := db.Begin()
|
||||||
result, err := tx.Exec(req.Expression, req.Data...)
|
result, err := tx.Exec(req.Expression, req.Data...)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
return result, err
|
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
|
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 (
|
import (
|
||||||
"io"
|
"io"
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"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" {
|
if cmd.Method == "Insert" || cmd.Method == "Set" || cmd.Method == "Delete" {
|
||||||
exec = true
|
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))
|
args := make([]reflect.Value, len(cmd.Args))
|
||||||
for i, arg := range cmd.Args {
|
for i, arg := range cmd.Args {
|
||||||
args[i] = reflect.ValueOf(arg)
|
args[i] = reflect.ValueOf(arg)
|
||||||
|
|
Loading…
Reference in a new issue