From 9150d3faf6f1e99bd18c2135c68f368878737bad Mon Sep 17 00:00:00 2001 From: Anton Nesterov Date: Mon, 26 Aug 2024 21:10:35 +0200 Subject: [PATCH] [wip] #1 prototype for using iterator from nodejs addon Signed-off-by: Anton Nesterov --- binding/dal.cc | 25 +++++++++++-- binding/dal.go | 49 ++++++++++++++++++++++--- binding/dal.h | 10 ++++- binding/go.mod | 2 +- dal/__test__/builder.test.ts | 2 +- dal/__test__/sqlite.node.cjs | 15 ++++++-- dal/__test__/srv/test.sqlite | Bin 8192 -> 0 bytes pkg/facade/SQLIteShared.go | 69 +++++++++++++++++++++++++++++++++++ 8 files changed, 157 insertions(+), 15 deletions(-) delete mode 100644 dal/__test__/srv/test.sqlite diff --git a/binding/dal.cc b/binding/dal.cc index d1788fa..9e1580a 100644 --- a/binding/dal.cc +++ b/binding/dal.cc @@ -7,17 +7,34 @@ static void _InitSQLite(const Napi::CallbackInfo& args) { 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 buf = args[0].As>(); GoSlice input = {reinterpret_cast(buf.Data()), long(buf.Length()), long(buf.Length())}; - GoSlice result = HandleQuery(input); - return Napi::Buffer::Copy(args.Env(), reinterpret_cast(result.data), result.len); + int iter = CreateRowIterator(input); + auto next_row = [=](const Napi::CallbackInfo& a){ + void* next = NextRow(iter); + if (next == nullptr) { + FreeIter(iter); + return Napi::Buffer::New(env, 0); + } + Napi::Buffer val = Napi::Buffer::Copy(env, reinterpret_cast(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) { exports["InitSQLite"] = Napi::Function::New(env, _InitSQLite); - exports["Handle"] = Napi::Function::New(env, Handle); + exports["RowIterator"] = Napi::Function::New(env, RowIterator); return exports; } diff --git a/binding/dal.go b/binding/dal.go index cbaa8e2..1a00223 100644 --- a/binding/dal.go +++ b/binding/dal.go @@ -1,25 +1,64 @@ package main +// #include +// #include import "C" import ( + "fmt" "strings" + "unsafe" "github.com/nesterow/dal/pkg/facade" _ "github.com/mattn/go-sqlite3" ) +var iterators = make(map[int]*facade.RowsIter) +var itersize = make(map[int]C.int) + //export InitSQLite func InitSQLite(pragmas string) { pragmasArray := strings.Split(pragmas, ";") facade.InitSQLite(pragmasArray) } -//export HandleQuery -func HandleQuery(input []byte) []byte { - var out []byte - facade.HandleQuery(&input, &out) - return out +//export CreateRowIterator +func CreateRowIterator(input []byte) C.int { + var it = &facade.RowsIter{} + it.Exec(input) + 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() {} diff --git a/binding/dal.h b/binding/dal.h index 9da9f6b..419fed7 100644 --- a/binding/dal.h +++ b/binding/dal.h @@ -19,6 +19,11 @@ typedef struct { const char *p; ptrdiff_t n; } _GoString_; /* Start of preamble from import "C" comments. */ +#line 3 "dal.go" + #include + #include + +#line 1 "cgo-generated-wrapper" /* End of preamble from import "C" comments. */ @@ -75,7 +80,10 @@ extern "C" { #endif 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 } diff --git a/binding/go.mod b/binding/go.mod index 893bc96..017122d 100644 --- a/binding/go.mod +++ b/binding/go.mod @@ -25,7 +25,7 @@ require ( 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/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/utils v0.0.0-20240820192515-7a408c994181 // indirect github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect diff --git a/dal/__test__/builder.test.ts b/dal/__test__/builder.test.ts index 59b60f8..7e47622 100644 --- a/dal/__test__/builder.test.ts +++ b/dal/__test__/builder.test.ts @@ -1,5 +1,5 @@ import { test, expect } from "bun:test"; -import DAL from "../Builder"; +import {DAL} from ".."; const options = { database: "test.sqlite", diff --git a/dal/__test__/sqlite.node.cjs b/dal/__test__/sqlite.node.cjs index bd84643..f913023 100644 --- a/dal/__test__/sqlite.node.cjs +++ b/dal/__test__/sqlite.node.cjs @@ -1,6 +1,15 @@ const fs = require("fs"); const dal = require("../../build/Release/dal.node"); -dal.InitSQLite(Buffer.from([])); +//dal.InitSQLite(Buffer.from([])); const buf = fs.readFileSync("./pkg/__test__/proto_test.msgpack"); -data = dal.Handle(buf); -console.log(data); +//console.log(dal.Handle(buf)); +const iter = dal.RowIterator(buf); +console.log(iter); +for (let i = 0; i < 10; i++) { + const b = iter.next(); + if (b.length === 0) { + break; + } + console.log(b.toString()); +} +iter.free(); \ No newline at end of file diff --git a/dal/__test__/srv/test.sqlite b/dal/__test__/srv/test.sqlite deleted file mode 100644 index 8467090a263d61dc6f4d65842e2b6ac870c50599..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeI$F-rq66bJB^Yl^+0M3BSvjS5GXegW?!Nb%HqMrg+zy+Xlya2G_!j((JWA!lcI zcauWbb{77RJo0}b({D?j9*dQ$dD*mU>v&A3Bm`YC6H(uHqAt2-Aux-NBQG@C3h zG=~$uSS@&RZ}g2Wd0I~HvU1M1dYns~RGne6QF4Y&QU-hHDeLz6~ z0uX=z1Rwwb2tWV=5P$##An;EF22#?Qs%%>gdLb>F#>SDPM7gGO@3GiL|6B0!2kM|M A#{d8T diff --git a/pkg/facade/SQLIteShared.go b/pkg/facade/SQLIteShared.go index 9c5a3d7..7fc36c3 100644 --- a/pkg/facade/SQLIteShared.go +++ b/pkg/facade/SQLIteShared.go @@ -1,6 +1,7 @@ package facade import ( + "database/sql" "reflect" "github.com/nesterow/dal/pkg/adapter" @@ -25,6 +26,73 @@ func InitSQLite(pragmas []string) { } } +type RowsIter struct { + Result []byte + 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() + data := make([]interface{}, len(columns)) + 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 { InitSQLite([]string{}) req := proto.Request{} @@ -83,6 +151,7 @@ func HandleQuery(input *[]byte, output *[]byte) int { *output, _ = res.MarshalMsg(nil) return 0 } + defer rows.Close() columns, _ := rows.Columns() types, _ := rows.ColumnTypes() cols, _ := proto.MarshalRow(columns)