Compare commits

...

14 commits

Author SHA1 Message Date
Anton Nesterov c0c9b7c9b4
[feat] prep for cffi
Signed-off-by: Anton Nesterov <anton@demiurg.io>
2024-08-27 16:35:17 +02:00
Anton Nesterov ab5de2ae51
[fix] #1 keep C binding from default module exports (compat with Bun)
Signed-off-by: Anton Nesterov <anton@demiurg.io>
2024-08-26 23:13:56 +02:00
Anton Nesterov 48a26223c0
[fix] #1 js bindings
Signed-off-by: Anton Nesterov <anton@demiurg.io>
2024-08-26 22:51:43 +02:00
Anton Nesterov ad8614f30c
[feat] #1 nodejs addon bench
Signed-off-by: Anton Nesterov <anton@demiurg.io>
2024-08-26 22:42:56 +02:00
Anton Nesterov 18a0a1e9a1
[wip] #1 nodejs addon bench (memory)
Signed-off-by: Anton Nesterov <anton@demiurg.io>
2024-08-26 22:28:27 +02:00
Anton Nesterov 9150d3faf6
[wip] #1 prototype for using iterator from nodejs addon
Signed-off-by: Anton Nesterov <anton@demiurg.io>
2024-08-26 21:10:35 +02:00
Anton Nesterov e4fdd426a8
[chore] update packages 2024-08-23 07:11:55 +02:00
Anton Nesterov ae641b7ee8
[chore] pub packages 2024-08-23 07:07:43 +02:00
Anton Nesterov d4638d4c4b
[fix] use response controller to flush the buffer 2024-08-23 07:03:54 +02:00
Anton Nesterov cfafadff47
Merge branch 'main' of github.com:nesterow/dal 2024-08-22 20:04:32 +02:00
Anton Nesterov 34d3db3ab9
[fix] readme 2024-08-22 11:17:28 +02:00
Anton Nesterov 0d612173f9
[chore] fix readme 2024-08-22 09:30:37 +02:00
Anton Nesterov b816b0ecdb
[chore] fmt readme 2024-08-21 22:48:50 +02:00
Anton Nesterov 0669177a8b
[chore] fmt readme 2024-08-21 22:40:53 +02:00
24 changed files with 362 additions and 68 deletions

3
.gitignore vendored
View file

@ -3,6 +3,9 @@
*.a *.a
*.dylib *.dylib
build/ build/
database.sqlite
database.sqlite-shm
database.sqlite-wal
# Logs # Logs
logs logs

View file

@ -1,19 +1,22 @@
# DAL # DAL
**Data Access Layer** **Data Access Layer**
DAL is a proxy layer for SQL databases with a MongoDB inspired query interface. DAL is a proxy layer for SQL databases with a MongoDB inspired query interface.
It can be used as a Go or NodeJS package (requires compiler) to create your own proxy and apply custom middlewares. It can be used as a Go or NodeJS package (requires compiler).
It is modular and allows to create your own proxy and apply custom middlewares.
_Notes:_ _Notes:_
- This project is still in early alpha. You need to build it yourself and use at your own risk. - This project is still in **early alpha**. You need to build it yourself and use at your own risk.
- At the time only SQLite is implemented, however, other drivers might work. - At the time only SQLite is implemented, however, other drivers might work.
_Use cases:_ _Use cases:_
- For IOT networks when MySQL/PG are too heavy. - For IOT networks when MySQL/PG are too heavy.
- If you need a layer between your application and the database (i.e. for caching). - If you need a layer between your application and the database (i.e. for caching).
- If you want a MongoDB-like query interface for your SQL. - If you want a MongoDB-like query interface for your SQL db.
- When you need a SQLite proxy (useful to share datasets with services)
## Usage ## Usage
@ -102,16 +105,26 @@ const db = new DAL({
url: "http://localhost:8111", url: "http://localhost:8111",
}); });
// SELECT * FROM test t WHERE name GLOB '*son' AND age >= 18
const rows = db const rows = db
.In("test t") .In("test t")
.Find({ .Find({
name: { $glob: "*son" }, name: { $glob: "*son" },
age: { $gte: 18 }, age: { $gte: 18 },
}) // SELECT * FROM test t WHERE name GLOB '*son' AND age >= 18 })
.As(UserDTO) // Map every row to DTO .As(UserDTO) // Map every row to DTO
.Rows(); // Get iterator .Rows();
for await (const row of rows) { for await (const row of rows) {
console.log(row); // Jason, Jackson console.log(row); // Jason, Jackson
} }
``` ```
## Internals
The client uses a light builder and messagepack over http. It is relatively easy to implement a client in any language see [the docs](./doc/)
## License
While in alpha stage the project is free for research purposes.
Later it will be released under MIT-like license with AI/dataset exclusion terms.

View file

@ -7,17 +7,34 @@ static void _InitSQLite(const Napi::CallbackInfo& args) {
InitSQLite(charstr); InitSQLite(charstr);
} }
static Napi::Value Handle(const Napi::CallbackInfo& args) { static Napi::Object RowIterator(const Napi::CallbackInfo& args) {
Napi::Env env = args.Env();
Napi::Object it = Napi::Object::New( env );
Napi::Buffer<uint8_t> buf = args[0].As<Napi::Buffer<uint8_t>>(); Napi::Buffer<uint8_t> buf = args[0].As<Napi::Buffer<uint8_t>>();
GoSlice input = {reinterpret_cast<void *>(buf.Data()), long(buf.Length()), long(buf.Length())}; GoSlice input = {reinterpret_cast<void *>(buf.Data()), long(buf.Length()), long(buf.Length())};
GoSlice result = HandleQuery(input); int iter = CreateRowIterator(input);
return Napi::Buffer<char>::Copy(args.Env(), reinterpret_cast<char *>(result.data), result.len); auto next_row = [=](const Napi::CallbackInfo& a){
void* next = NextRow(iter);
if (next == nullptr) {
FreeIter(iter);
return Napi::Buffer<uint8_t>::New(env, 0);
}
Napi::Buffer<uint8_t> val = Napi::Buffer<uint8_t>::Copy(env, reinterpret_cast<uint8_t *>(next), GetLen(iter));
free(next);
return val;
};
auto free_iter = [=](const Napi::CallbackInfo& a){
FreeIter(iter);
};
it.Set("next", Napi::Function::New(env, next_row));
it.Set("free", Napi::Function::New(env, free_iter));
return it;
} }
static Napi::Object Init(Napi::Env env, Napi::Object exports) { static Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports["InitSQLite"] = Napi::Function::New(env, _InitSQLite); exports["InitSQLite"] = Napi::Function::New(env, _InitSQLite);
exports["Handle"] = Napi::Function::New(env, Handle); exports["RowIterator"] = Napi::Function::New(env, RowIterator);
return exports; return exports;
} }

View file

@ -1,25 +1,64 @@
package main package main
// #include <stdlib.h>
// #include <stdio.h>
import "C" import "C"
import ( import (
"fmt"
"strings" "strings"
"unsafe"
"github.com/nesterow/dal/pkg/facade" "github.com/nesterow/dal/pkg/facade"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
var iterators = make(map[int]*facade.RowsIter)
var itersize = make(map[int]C.int)
//export InitSQLite //export InitSQLite
func InitSQLite(pragmas string) { func InitSQLite(pragmas string) {
pragmasArray := strings.Split(pragmas, ";") pragmasArray := strings.Split(pragmas, ";")
facade.InitSQLite(pragmasArray) facade.InitSQLite(pragmasArray)
} }
//export HandleQuery //export CreateRowIterator
func HandleQuery(input []byte) []byte { func CreateRowIterator(input []byte) C.int {
var out []byte var it = &facade.RowsIter{}
facade.HandleQuery(&input, &out) it.Exec(input)
return out ptr := C.int(len(iterators))
iterators[len(iterators)] = it
defer fmt.Println(ptr)
return ptr
}
//export NextRow
func NextRow(itid C.int) unsafe.Pointer {
it := iterators[int(itid)]
if it.Result != nil {
itersize[int(itid)] = C.int(len(it.Result))
return C.CBytes(it.Result)
}
data := it.Next()
if data == nil {
return nil
}
itersize[int(itid)] = C.int(len(data))
res := C.CBytes(data)
return res
}
//export GetLen
func GetLen(idx C.int) C.int {
return itersize[int(idx)]
}
//export FreeIter
func FreeIter(itid C.int) {
it := iterators[int(itid)]
it.Close()
delete(iterators, int(itid))
delete(itersize, int(itid))
} }
func main() {} func main() {}

View file

@ -19,6 +19,11 @@ typedef struct { const char *p; ptrdiff_t n; } _GoString_;
/* Start of preamble from import "C" comments. */ /* Start of preamble from import "C" comments. */
#line 3 "dal.go"
#include <stdlib.h>
#include <stdio.h>
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */ /* End of preamble from import "C" comments. */
@ -75,7 +80,10 @@ extern "C" {
#endif #endif
extern void InitSQLite(GoString pragmas); extern void InitSQLite(GoString pragmas);
extern GoSlice HandleQuery(GoSlice input); extern int CreateRowIterator(GoSlice input);
extern void* NextRow(int itid);
extern int GetLen(int idx);
extern void FreeIter(int itid);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -25,7 +25,7 @@ require (
github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/handler v0.0.0-20240823050743-ae641b7ee894 // indirect
github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 // indirect github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 // indirect
github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 // indirect
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect

5
binding/makefile Normal file
View file

@ -0,0 +1,5 @@
build-lib:
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -o ../lib/libdal-arm64.dylib -buildmode=c-shared ./dal.go
for arch in amd64 arm64; do\
CC=x86_64-unknown-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=$${arch} go build -o ../lib/libdal-$${arch}.so -buildmode=c-shared ./dal.go;\
done

View file

@ -1,7 +1,11 @@
import { createRequire } from "node:module"; import { createRequire } from "node:module";
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
type RowIterator = {
next: () => Buffer;
free: () => void;
};
type SQLite = { type SQLite = {
InitSQLite: (pragmas: Buffer) => void; InitSQLite: (pragmas: Buffer) => void;
Handle: (input: Buffer) => Buffer; RowIterator: (input: Buffer) => RowIterator;
}; };
export default require("../build/Release/dal.node") as SQLite; export default require("../build/Release/dal.node") as SQLite;

View file

@ -0,0 +1,88 @@
const fs = require("fs");
const dal = require("../../build/Release/dal.node");
const Mb = (num) => Math.round(num / 1024 / 1024);
class Stats {
constructor() {
this.calls = 0;
this.avg_rss = 0;
this.avg_heapTotal = 0;
this.avg_heapUsed = 0;
this.avg_external = 0;
}
add(mem) {
this.calls++;
this.avg_rss += mem.rss;
this.avg_heapTotal += mem.heapTotal;
this.avg_heapUsed += mem.heapUsed;
this.avg_external += mem.external;
}
avg() {
const n = this.calls;
this.avg_rss /= n;
this.avg_heapTotal /= n;
this.avg_heapUsed /= n;
this.avg_external /= n;
}
print() {
console.log(`
AVERAGE:
rss: ${Mb(this.avg_rss)} Mb
external: ${Mb(this.avg_external)} Mb
buffers: ${Mb(this.avg_heapUsed)} Mb
total: ${Mb(this.avg_heapTotal)} Mb`);
}
}
const stats = new Stats();
let prevMem = process.memoryUsage();
stats.add(prevMem);
function MEM(when = "") {
const mem = process.memoryUsage();
stats.add(mem);
console.log(`
${when}
rss: ${Mb(mem.rss)} Mb [delta> ${mem.rss - prevMem.rss}]
external: ${Mb(mem.external)} Mb [delta> ${mem.external - prevMem.external}]
buffers: ${Mb(mem.heapUsed)} Mb [delta> ${mem.heapUsed - prevMem.heapUsed}]
total: ${Mb(mem.heapTotal)} Mb [delta> ${mem.heapTotal - prevMem.heapTotal}]`);
}
console.time("Time to end");
MEM("START");
const buf = fs.readFileSync("./pkg/__test__/proto_test.msgpack");
const Iterator = dal.RowIterator(buf);
MEM("AFTER INIT");
let dataTransferedBytes = 0;
for (let i = 0; i < 100000000; i++) {
const b = Iterator.next();
if (b.length === 0) {
break;
}
dataTransferedBytes += b.length;
if (i % 1000000 === 0) {
MEM(`ITERATION ${i}`);
}
}
MEM("AFTER ITERATION");
Iterator.free();
MEM("AFTER CLEANUP");
console.log("\nData transfered: ", Mb(dataTransferedBytes), "Mb");
console.timeEnd("Time to end");
setTimeout(() => {
MEM("AFTER SOME TIME");
stats.avg();
stats.print();
}, 30000);

View file

@ -1,5 +1,5 @@
import { test, expect } from "bun:test"; import { test, expect } from "bun:test";
import DAL from "../Builder"; import { DAL } from "..";
const options = { const options = {
database: "test.sqlite", database: "test.sqlite",
@ -22,7 +22,7 @@ test("Rows iter, no format", async () => {
}) })
.Rows<any[]>(); .Rows<any[]>();
for await (const row of rows) { for await (const row of rows) {
console.log(row); //console.log(row);
expect(row.length).toBe(3); expect(row.length).toBe(3);
} }
expect(true).toBe(true); expect(true).toBe(true);
@ -32,7 +32,7 @@ test("Rows iter, format", async () => {
const dal = new DAL(options); const dal = new DAL(options);
const rows = dal.In("test t").Find({}).As(DTO).Rows(); const rows = dal.In("test t").Find({}).As(DTO).Rows();
for await (const row of rows) { for await (const row of rows) {
console.log(row); //console.log(row);
//expect(row.id).toBe(1); //expect(row.id).toBe(1);
} }
expect(true).toBe(true); expect(true).toBe(true);

View file

@ -1,6 +0,0 @@
const fs = require("fs");
const dal = require("../../build/Release/dal.node");
dal.InitSQLite(Buffer.from([]));
const buf = fs.readFileSync("./pkg/__test__/proto_test.msgpack");
data = dal.Handle(buf);
console.log(data);

View file

@ -2,6 +2,8 @@ module srv
go 1.22.6 go 1.22.6
replace github.com/nesterow/dal/pkg/handler => ../../../pkg/handler
require ( require (
github.com/mattn/go-sqlite3 v1.14.22 github.com/mattn/go-sqlite3 v1.14.22
github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181
@ -9,10 +11,10 @@ require (
) )
require ( require (
github.com/nesterow/dal/pkg/builder v0.0.0-20240820191021-7dcd046e6ca0 // indirect github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/filters v0.0.0-20240820191021-7dcd046e6ca0 // indirect github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/proto v0.0.0-20240820191021-7dcd046e6ca0 // indirect github.com/nesterow/dal/pkg/proto v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/utils v0.0.0-20240820191021-7dcd046e6ca0 // indirect github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 // indirect
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/tinylib/msgp v1.2.0 // indirect github.com/tinylib/msgp v1.2.0 // indirect

View file

@ -2,16 +2,14 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 h1:wwWMAG/Uu/layF1wu6dJiUej0zisaVQRfyTNqjJpxac= github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 h1:wwWMAG/Uu/layF1wu6dJiUej0zisaVQRfyTNqjJpxac=
github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181/go.mod h1:TCkoNn14Hgl+iVENr73GHaDVEuAg8KizH0oXaV2qMKs= github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181/go.mod h1:TCkoNn14Hgl+iVENr73GHaDVEuAg8KizH0oXaV2qMKs=
github.com/nesterow/dal/pkg/builder v0.0.0-20240820191021-7dcd046e6ca0 h1:S37CEWRRLdeLA3V8J4XEa8CmmO4Z+auifYf1GfNRo64= github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 h1:Ex2XjUB7o4IRSWcgh8qgac8JQ2gbn6M+jUfom4zZccY=
github.com/nesterow/dal/pkg/builder v0.0.0-20240820191021-7dcd046e6ca0/go.mod h1:EmozdaZEPYE2+4qpK1cw0eIS8yx9LcrVRKstvq2dgCg= github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181/go.mod h1:mVt1QOFEeGLdi+ZbMLzVw3LVtFB2tYWxZRouhkOThJU=
github.com/nesterow/dal/pkg/filters v0.0.0-20240820191021-7dcd046e6ca0 h1:2CDPDKwR+PSGCI6dMbSqI5Zf8Ugg9nY2CMN87BDZ1Xg= github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 h1:Vi6v1T68/oayGfoX8k/FpgvfMsCUqxMsowTHKXY70VE=
github.com/nesterow/dal/pkg/filters v0.0.0-20240820191021-7dcd046e6ca0/go.mod h1:1wKJbE/13P756EjkLU8xzbdhR+dJYfn6Vlw4J2jFJaA= github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181/go.mod h1:6aUr+2+D+184FTSXgziW6r1bvcKTY+Ie3TW4iET0g6I=
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181 h1:dAzXf3pEYEKTF+WHJxmfy81XLPJ7BcFy3MEYYsLeyQE= github.com/nesterow/dal/pkg/proto v0.0.0-20240820192515-7a408c994181 h1:HcGM9fagbbGa0r/6TRrg+lzRbW79sOr6yTczYkO1miw=
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181/go.mod h1:yxMmRLpnj8PmBylCcVr0PJwpy2uLwNjsMFk36kGNWOE= github.com/nesterow/dal/pkg/proto v0.0.0-20240820192515-7a408c994181/go.mod h1:MUS8KEmlNqjUuhNyK4TOkUwjT9893jmNm++ukVz2hzU=
github.com/nesterow/dal/pkg/proto v0.0.0-20240820191021-7dcd046e6ca0 h1:p2VAOo7aMmFehU17S7aZ3tiwIMEjQUuzLHTNO4jQHWA= github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 h1:b/D2uq18D6CzAh+NUyD1cM7s/q/aQZEpXy57iKyzg8k=
github.com/nesterow/dal/pkg/proto v0.0.0-20240820191021-7dcd046e6ca0/go.mod h1:okcJheB01HZrsNryT2Hun1DjYI7Hj/+/oD6+4PVW3TU= github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181/go.mod h1:0XWmrxZfyoEAZoCOxP3quEOP7ZNK2Lhvtpeux8C74mw=
github.com/nesterow/dal/pkg/utils v0.0.0-20240820191021-7dcd046e6ca0 h1:CYDcRDYrXXD8fQdkXbbPMJvvLnBy4nI36BrRMQEsWUo=
github.com/nesterow/dal/pkg/utils v0.0.0-20240820191021-7dcd046e6ca0/go.mod h1:0XWmrxZfyoEAZoCOxP3quEOP7ZNK2Lhvtpeux8C74mw=
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4= 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/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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

41
dal/bun.ffi.ts Normal file
View file

@ -0,0 +1,41 @@
import { dlopen, FFIType, suffix, ptr } from "bun:ffi";
import { join } from "node:path";
import fs from "node:fs";
// TODO: build a shared library compatible with cffi
const libname = `libdal-arm64.${suffix}`;
const libpath = join(__dirname, '..', 'lib', libname);
const {
symbols: {
InitSQLite,
CreateRowIterator,
NextRow,
GetLen,
FreeIter,
},
} = dlopen(
libpath,
{
InitSQLite: {
args: [ FFIType.cstring ],
returns: FFIType.void,
},
CreateRowIterator: {
args: [ FFIType.cstring ],
returns: FFIType.i32,
},
NextRow: {
args: [FFIType.i32],
returns: FFIType.cstring,
},
GetLen: {
args: [FFIType.i32],
returns: FFIType.i32,
},
FreeIter : {
args: [FFIType.i32],
returns: FFIType.void,
},
},
);

View file

@ -1,2 +1 @@
export { default as DAL } from "./Builder"; export { default as DAL } from "./Builder";
export { default as DALite } from "./CBuilder";

View file

@ -18,20 +18,26 @@ export default class CBuilder<
super({ database: opts.database, url: "" }); super({ database: opts.database, url: "" });
} }
/** /**
* Not really an iterator, since addonn allocates memory for all rows * TODO: handle responses
* but returns an iterator
*/ */
async *Rows<T = InstanceType<I>>(): AsyncGenerator<T> { async *Rows<T = InstanceType<I>>(): AsyncGenerator<T> {
this.formatRequest(); this.formatRequest();
const req = Buffer.from(encodeRequest(this.request)); const req = Buffer.from(encodeRequest(this.request));
const response = Binding.Handle(req); const iter = Binding.RowIterator(req);
const rows = decodeRows(response); for (;;) {
for (const row of rows) { const response = iter.next();
if (this.headerRow === null) { const rows = decodeRows(response);
this.headerRow = row.r; if (rows.length === 0) {
continue; iter.free();
break;
}
for (const row of rows) {
if (this.headerRow === null) {
this.headerRow = row.r;
continue;
}
yield this.formatRow(row.r);
} }
yield this.formatRow(row.r);
} }
} }
async Query<T = InstanceType<I>>(): Promise<T[]> { async Query<T = InstanceType<I>>(): Promise<T[]> {
@ -45,7 +51,8 @@ export default class CBuilder<
async Exec(): Promise<ExecResult> { async Exec(): Promise<ExecResult> {
this.formatRequest(); this.formatRequest();
const req = Buffer.from(encodeRequest(this.request)); const req = Buffer.from(encodeRequest(this.request));
const response = Binding.Handle(req); const iter = Binding.RowIterator(req);
const response = iter.next();
return decodeResponse(response); return decodeResponse(response);
} }
} }

4
go.mod
View file

@ -2,7 +2,7 @@ module github.com/nesterow/dal
go 1.22.6 go 1.22.6
require github.com/nesterow/dal/pkg/facade v0.0.0-20240821202841-36d7d315e298 require github.com/nesterow/dal/pkg/facade v0.0.0-20240823050743-ae641b7ee894
require github.com/mattn/go-sqlite3 v1.14.22 require github.com/mattn/go-sqlite3 v1.14.22
@ -10,7 +10,7 @@ require (
github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 // indirect
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/handler v0.0.0-20240823050743-ae641b7ee894 // indirect
github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 // indirect github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 // indirect
github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 // indirect github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 // indirect
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect

8
go.sum
View file

@ -4,12 +4,12 @@ github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 h1:wwWMAG
github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181/go.mod h1:TCkoNn14Hgl+iVENr73GHaDVEuAg8KizH0oXaV2qMKs= github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181/go.mod h1:TCkoNn14Hgl+iVENr73GHaDVEuAg8KizH0oXaV2qMKs=
github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 h1:Ex2XjUB7o4IRSWcgh8qgac8JQ2gbn6M+jUfom4zZccY= github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 h1:Ex2XjUB7o4IRSWcgh8qgac8JQ2gbn6M+jUfom4zZccY=
github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181/go.mod h1:mVt1QOFEeGLdi+ZbMLzVw3LVtFB2tYWxZRouhkOThJU= github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181/go.mod h1:mVt1QOFEeGLdi+ZbMLzVw3LVtFB2tYWxZRouhkOThJU=
github.com/nesterow/dal/pkg/facade v0.0.0-20240821202841-36d7d315e298 h1:Mb2gg8lR3yn4LGeOyobCSZll4wPAg5UqfmSUT2/dTD0= github.com/nesterow/dal/pkg/facade v0.0.0-20240823050743-ae641b7ee894 h1:MkHuizDCwVhyvVa/9sSBFsX7MiWZQAeOODS50GTe9nU=
github.com/nesterow/dal/pkg/facade v0.0.0-20240821202841-36d7d315e298/go.mod h1:/C5t/NOUVLCzEZJgpNPlEt0jqmEllVNUpngevE9aUBk= github.com/nesterow/dal/pkg/facade v0.0.0-20240823050743-ae641b7ee894/go.mod h1:MKaLSkZocQXy/wFqBzl+Bu7lb4xSCeItbTC7vjOPE0Q=
github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 h1:Vi6v1T68/oayGfoX8k/FpgvfMsCUqxMsowTHKXY70VE= github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 h1:Vi6v1T68/oayGfoX8k/FpgvfMsCUqxMsowTHKXY70VE=
github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181/go.mod h1:6aUr+2+D+184FTSXgziW6r1bvcKTY+Ie3TW4iET0g6I= github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181/go.mod h1:6aUr+2+D+184FTSXgziW6r1bvcKTY+Ie3TW4iET0g6I=
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181 h1:dAzXf3pEYEKTF+WHJxmfy81XLPJ7BcFy3MEYYsLeyQE= github.com/nesterow/dal/pkg/handler v0.0.0-20240823050743-ae641b7ee894 h1:nPlCoVDXzpFGX4cmInLOKoAB7cTpcnjGDevCahF1Db8=
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181/go.mod h1:yxMmRLpnj8PmBylCcVr0PJwpy2uLwNjsMFk36kGNWOE= github.com/nesterow/dal/pkg/handler v0.0.0-20240823050743-ae641b7ee894/go.mod h1:aUueTAR2yb5ZCcXqgm4euJTTlScTCqTyT187oNQN5zY=
github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 h1:bUnae1O6MEYWEZepd2ir3iSkydfX9/8DLRoH0U+EWHM= github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 h1:bUnae1O6MEYWEZepd2ir3iSkydfX9/8DLRoH0U+EWHM=
github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42/go.mod h1:YGGcF6rUmoknTFR3csZyLjJhpuFBw9S+DQa61P8B8kY= github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42/go.mod h1:YGGcF6rUmoknTFR3csZyLjJhpuFBw9S+DQa61P8B8kY=
github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 h1:b/D2uq18D6CzAh+NUyD1cM7s/q/aQZEpXy57iKyzg8k= github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 h1:b/D2uq18D6CzAh+NUyD1cM7s/q/aQZEpXy57iKyzg8k=

View file

@ -23,6 +23,7 @@
"test:client": "bun test:*", "test:client": "bun test:*",
"test:dal": "bun test dal/__test__", "test:dal": "bun test dal/__test__",
"test:serve": "cd dal/__test__/srv && go run main.go", "test:serve": "cd dal/__test__/srv && go run main.go",
"bench:node": "node ./dal/__test__/bench.node.cjs",
"fmt": "prettier --write .", "fmt": "prettier --write .",
"build": "tsc", "build": "tsc",
"prepublish": "tsc", "prepublish": "tsc",

View file

@ -1,6 +1,7 @@
package facade package facade
import ( import (
"database/sql"
"reflect" "reflect"
"github.com/nesterow/dal/pkg/adapter" "github.com/nesterow/dal/pkg/adapter"
@ -25,6 +26,80 @@ func InitSQLite(pragmas []string) {
} }
} }
type RowsIter struct {
Result []byte
Columns []string
rows *sql.Rows
}
func (r *RowsIter) Exec(input []byte) {
InitSQLite([]string{})
req := proto.Request{}
_, e := req.UnmarshalMsg(input)
query, err := req.Parse(adapter.GetDialect(db.Type))
if err != nil || e != nil {
res := proto.Response{
Msg: "failed to unmarshal request",
}
r.Result, _ = res.MarshalMsg(nil)
return
}
if query.Exec {
result, err := db.Exec(query)
if err != nil {
res := proto.Response{
Msg: err.Error(),
}
r.Result, _ = res.MarshalMsg(nil)
return
}
ra, _ := result.RowsAffected()
la, _ := result.LastInsertId()
res := proto.Response{
Id: 0,
RowsAffected: ra,
LastInsertId: la,
}
r.Result, _ = res.MarshalMsg(nil)
return
}
rows, err := db.Query(query)
if err != nil {
res := proto.Response{
Msg: err.Error(),
}
r.Result, _ = res.MarshalMsg(nil)
return
}
r.rows = rows
}
func (r *RowsIter) Close() {
if r.rows == nil {
return
}
r.rows.Close()
}
func (r *RowsIter) Next() []byte {
columns, _ := r.rows.Columns()
types, _ := r.rows.ColumnTypes()
if r.Columns == nil {
r.Columns = columns
cols, _ := proto.MarshalRow(columns)
return cols
}
data := make([]interface{}, len(columns))
r.rows.Next()
for i := range data {
typ := reflect.New(types[i].ScanType()).Interface()
data[i] = &typ
}
r.rows.Scan(data...)
cols, _ := proto.MarshalRow(data)
return cols
}
func HandleQuery(input *[]byte, output *[]byte) int { func HandleQuery(input *[]byte, output *[]byte) int {
InitSQLite([]string{}) InitSQLite([]string{})
req := proto.Request{} req := proto.Request{}
@ -83,6 +158,7 @@ func HandleQuery(input *[]byte, output *[]byte) int {
*output, _ = res.MarshalMsg(nil) *output, _ = res.MarshalMsg(nil)
return 0 return 0
} }
defer rows.Close()
columns, _ := rows.Columns() columns, _ := rows.Columns()
types, _ := rows.ColumnTypes() types, _ := rows.ColumnTypes()
cols, _ := proto.MarshalRow(columns) cols, _ := proto.MarshalRow(columns)

View file

@ -4,7 +4,7 @@ go 1.22.6
require ( require (
github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181 github.com/nesterow/dal/pkg/adapter v0.0.0-20240820192515-7a408c994181
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181 github.com/nesterow/dal/pkg/handler v0.0.0-20240823050743-ae641b7ee894
github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42
) )

View file

@ -6,8 +6,8 @@ github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181 h1:Ex2XjU
github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181/go.mod h1:mVt1QOFEeGLdi+ZbMLzVw3LVtFB2tYWxZRouhkOThJU= github.com/nesterow/dal/pkg/builder v0.0.0-20240820192515-7a408c994181/go.mod h1:mVt1QOFEeGLdi+ZbMLzVw3LVtFB2tYWxZRouhkOThJU=
github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 h1:Vi6v1T68/oayGfoX8k/FpgvfMsCUqxMsowTHKXY70VE= github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181 h1:Vi6v1T68/oayGfoX8k/FpgvfMsCUqxMsowTHKXY70VE=
github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181/go.mod h1:6aUr+2+D+184FTSXgziW6r1bvcKTY+Ie3TW4iET0g6I= github.com/nesterow/dal/pkg/filters v0.0.0-20240820192515-7a408c994181/go.mod h1:6aUr+2+D+184FTSXgziW6r1bvcKTY+Ie3TW4iET0g6I=
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181 h1:dAzXf3pEYEKTF+WHJxmfy81XLPJ7BcFy3MEYYsLeyQE= github.com/nesterow/dal/pkg/handler v0.0.0-20240823050743-ae641b7ee894 h1:nPlCoVDXzpFGX4cmInLOKoAB7cTpcnjGDevCahF1Db8=
github.com/nesterow/dal/pkg/handler v0.0.0-20240820192515-7a408c994181/go.mod h1:yxMmRLpnj8PmBylCcVr0PJwpy2uLwNjsMFk36kGNWOE= github.com/nesterow/dal/pkg/handler v0.0.0-20240823050743-ae641b7ee894/go.mod h1:aUueTAR2yb5ZCcXqgm4euJTTlScTCqTyT187oNQN5zY=
github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 h1:bUnae1O6MEYWEZepd2ir3iSkydfX9/8DLRoH0U+EWHM= github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42 h1:bUnae1O6MEYWEZepd2ir3iSkydfX9/8DLRoH0U+EWHM=
github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42/go.mod h1:YGGcF6rUmoknTFR3csZyLjJhpuFBw9S+DQa61P8B8kY= github.com/nesterow/dal/pkg/proto v0.0.0-20240820230328-f8e28d2a2e42/go.mod h1:YGGcF6rUmoknTFR3csZyLjJhpuFBw9S+DQa61P8B8kY=
github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 h1:b/D2uq18D6CzAh+NUyD1cM7s/q/aQZEpXy57iKyzg8k= github.com/nesterow/dal/pkg/utils v0.0.0-20240820192515-7a408c994181 h1:b/D2uq18D6CzAh+NUyD1cM7s/q/aQZEpXy57iKyzg8k=

View file

@ -63,19 +63,17 @@ func QueryHandler(db adapter.DBAdapter) http.Handler {
} }
defer rows.Close() defer rows.Close()
w.Header().Set("Connection", "Keep-Alive")
w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "application/x-msgpack") w.Header().Set("Content-Type", "application/x-msgpack")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "expected http.ResponseWriter to be an http.Flusher", http.StatusInternalServerError)
return
}
columns, _ := rows.Columns() columns, _ := rows.Columns()
types, _ := rows.ColumnTypes() types, _ := rows.ColumnTypes()
cols, _ := proto.MarshalRow(columns) cols, _ := proto.MarshalRow(columns)
w.Write(cols) w.Write(cols)
flusher.Flush()
rc := http.NewResponseController(w)
rc.Flush()
for rows.Next() { for rows.Next() {
data := make([]interface{}, len(columns)) data := make([]interface{}, len(columns))
@ -86,7 +84,8 @@ func QueryHandler(db adapter.DBAdapter) http.Handler {
rows.Scan(data...) rows.Scan(data...)
cols, _ := proto.MarshalRow(data) cols, _ := proto.MarshalRow(data)
w.Write(cols) w.Write(cols)
flusher.Flush() rc.Flush()
} }
}) })
} }

View file

@ -1,5 +1,5 @@
{ {
"files": ["dal/index.ts", "dal/Binding.ts"], "files": ["dal/index.ts", "dal/Binding.ts", "dal/node.napi.ts"],
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",