[wip] #1 prototype for using iterator from nodejs addon

Signed-off-by: Anton Nesterov <anton@demiurg.io>
This commit is contained in:
Anton Nesterov 2024-08-26 21:10:35 +02:00
parent e4fdd426a8
commit 9150d3faf6
No known key found for this signature in database
GPG key ID: 59121E8AE2851FB5
8 changed files with 157 additions and 15 deletions

View file

@ -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<uint8_t> buf = args[0].As<Napi::Buffer<uint8_t>>();
GoSlice input = {reinterpret_cast<void *>(buf.Data()), long(buf.Length()), long(buf.Length())};
GoSlice result = HandleQuery(input);
return Napi::Buffer<char>::Copy(args.Env(), reinterpret_cast<char *>(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<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) {
exports["InitSQLite"] = Napi::Function::New(env, _InitSQLite);
exports["Handle"] = Napi::Function::New(env, Handle);
exports["RowIterator"] = Napi::Function::New(env, RowIterator);
return exports;
}

View file

@ -1,25 +1,64 @@
package main
// #include <stdlib.h>
// #include <stdio.h>
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() {}

View file

@ -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 <stdlib.h>
#include <stdio.h>
#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
}

View file

@ -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

View file

@ -1,5 +1,5 @@
import { test, expect } from "bun:test";
import DAL from "../Builder";
import {DAL} from "..";
const options = {
database: "test.sqlite",

View file

@ -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();

Binary file not shown.

View file

@ -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)