@dolthub/doltlite 0.10.6 → 0.11.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -173,7 +173,7 @@ bun test # run the test suite
173
173
 
174
174
  ## Versioning
175
175
 
176
- The package version tracks the DoltLite version. `@dolthub/doltlite@0.10.0` ships the DoltLite 0.10.0 amalgamation.
176
+ The package version tracks the DoltLite version. `@dolthub/doltlite@0.11.8` ships the DoltLite 0.11.8 amalgamation.
177
177
 
178
178
  ## License
179
179
 
package/binding.gyp CHANGED
@@ -6,9 +6,6 @@
6
6
  "amalgamation/doltlite_orig.c",
7
7
  "amalgamation/doltlite.c",
8
8
  "amalgamation/blake3_sse2_impl.c",
9
- "amalgamation/blake3_sse41_impl.c",
10
- "amalgamation/blake3_avx2_impl.c",
11
- "amalgamation/blake3_avx512_impl.c",
12
9
  "amalgamation/blake3_neon_impl.c",
13
10
  "src/addon.cpp",
14
11
  "src/bun_compat.cpp",
@@ -26,7 +23,10 @@
26
23
  "SQLITE_ENABLE_JSON1",
27
24
  "SQLITE_ENABLE_RTREE",
28
25
  "SQLITE_ENABLE_COLUMN_METADATA",
29
- "DOLTLITE_PROLLY=1"
26
+ "DOLTLITE_PROLLY=1",
27
+ "BLAKE3_NO_SSE41=1",
28
+ "BLAKE3_NO_AVX2=1",
29
+ "BLAKE3_NO_AVX512=1"
30
30
  ],
31
31
  "cflags": ["-std=c11", "-fvisibility=hidden"],
32
32
  "cflags_cc": ["-std=c++17", "-fvisibility=hidden"],
package/index.d.ts CHANGED
@@ -85,13 +85,21 @@ export class StatementSync {
85
85
  /** Execute the statement and return change metadata. */
86
86
  run(...params: unknown[]): RunResult
87
87
  /** Return the first result row, or undefined. */
88
- get(...params: unknown[]): Record<string, unknown> | undefined
88
+ get(...params: unknown[]): Record<string, unknown> | unknown[] | undefined
89
89
  /** Return all result rows. */
90
- all(...params: unknown[]): Record<string, unknown>[]
90
+ all(...params: unknown[]): Array<Record<string, unknown> | unknown[]>
91
91
  /** Return an iterator over result rows. */
92
- iterate(...params: unknown[]): IterableIterator<Record<string, unknown>>
92
+ iterate(...params: unknown[]): IterableIterator<Record<string, unknown> | unknown[]>
93
93
  /** Return column metadata for the statement. */
94
94
  columns(): ColumnInfo[]
95
+ /** Return result rows as arrays instead of objects. */
96
+ setReturnArrays(enabled: boolean): void
97
+ /** Return integer columns as BigInt values. */
98
+ setReadBigInts(enabled: boolean): void
99
+ /** Allow bare object keys for named parameters. */
100
+ setAllowBareNamedParameters(enabled: boolean): void
101
+ /** Ignore unknown named parameter keys. */
102
+ setAllowUnknownNamedParameters(enabled: boolean): void
95
103
  /** The SQL source text of the statement. */
96
104
  readonly sourceSQL: string
97
105
  /** The SQL text with bound parameters expanded. */
package/index.js CHANGED
@@ -10,15 +10,21 @@ function loadAddon() {
10
10
  const arch = process.arch
11
11
  const prebuilt = path.join(__dirname, "prebuilds", `${platform}-${arch}`, "doltlite.node")
12
12
  const compiled = path.join(__dirname, "build", "Release", "doltlite.node")
13
+ const compiledObjTarget = path.join(__dirname, "build", "Release", "obj.target", "doltlite.node")
13
14
 
14
- for (const candidate of [prebuilt, compiled]) {
15
+ const errors = []
16
+ for (const candidate of [prebuilt, compiled, compiledObjTarget]) {
17
+ if (!fs.existsSync(candidate)) continue
15
18
  try {
16
19
  return require(candidate)
17
- } catch {}
20
+ } catch (err) {
21
+ errors.push(`${candidate}: ${err && err.message ? err.message : err}`)
22
+ }
18
23
  }
19
24
 
20
25
  throw new Error(
21
26
  `@dolthub/doltlite: no native binary found for ${platform}-${arch}.\n` +
27
+ (errors.length ? `Tried:\n${errors.join("\n")}\n` : "") +
22
28
  `Run \`npm install\` to build from source, or file an issue at ` +
23
29
  `https://github.com/dolthub/doltlite-node/issues`
24
30
  )
package/package.json CHANGED
@@ -1,21 +1,27 @@
1
1
  {
2
2
  "name": "@dolthub/doltlite",
3
- "version": "0.10.6",
3
+ "version": "0.11.8",
4
4
  "description": "Node.js bindings for DoltLite — a SQLite fork with Git-style version control",
5
5
  "license": "Apache-2.0",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "type": "commonjs",
9
+ "publishConfig": {
10
+ "access": "public",
11
+ "registry": "https://registry.npmjs.org/"
12
+ },
9
13
  "files": [
10
14
  "index.js",
11
15
  "index.d.ts",
12
16
  "src/",
13
17
  "binding.gyp",
18
+ "scripts/install.js",
14
19
  "scripts/download.js",
20
+ "scripts/build-amalgamation.js",
15
21
  "prebuilds/"
16
22
  ],
17
23
  "scripts": {
18
- "install": "node scripts/download.js && node-gyp rebuild",
24
+ "install": "node scripts/install.js",
19
25
  "build": "node-gyp rebuild",
20
26
  "build:debug": "node-gyp rebuild --debug",
21
27
  "test": "bun test test/",
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ // Prepares the DoltLite release amalgamation for the native Node addon.
3
+
4
+ "use strict"
5
+
6
+ const fs = require("fs")
7
+ const path = require("path")
8
+ const pkg = require("../package.json")
9
+
10
+ const outDir = path.join(__dirname, "../amalgamation")
11
+ const srcRoot = path.join(outDir, ".source")
12
+ const srcDir = path.join(srcRoot, "src")
13
+ const blake3Dir = path.join(srcRoot, "ext", "blake3")
14
+
15
+ const inC = path.join(srcRoot, "sqlite3.c")
16
+ const inH = path.join(srcRoot, "sqlite3.h")
17
+ const outC = path.join(outDir, "doltlite.c")
18
+ const outH = path.join(outDir, "doltlite.h")
19
+ const outOrig = path.join(outDir, "doltlite_orig.c")
20
+ const versionMarker = path.join(outDir, ".doltlite-source-version")
21
+
22
+ if (!fs.existsSync(inC)) {
23
+ console.error(`build-amalgamation: ${inC} not found - run download.js first`)
24
+ process.exit(1)
25
+ }
26
+ if (!fs.existsSync(inH)) {
27
+ console.error(`build-amalgamation: ${inH} not found - run download.js first`)
28
+ process.exit(1)
29
+ }
30
+
31
+ console.log("Preparing released doltlite amalgamation...")
32
+ fs.mkdirSync(outDir, { recursive: true })
33
+ let amalgamation = fs.readFileSync(inC, "utf8")
34
+ amalgamation += `
35
+
36
+ #if defined(DOLTLITE_PROLLY) && defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
37
+ SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){
38
+ (void)pPager;
39
+ return 0;
40
+ }
41
+ #endif
42
+ `
43
+ fs.writeFileSync(outC, amalgamation)
44
+ fs.copyFileSync(inH, outH)
45
+ fs.writeFileSync(versionMarker, `${pkg.version}\n`)
46
+
47
+ // Kept for binding.gyp compatibility. DoltLite release amalgamations are now
48
+ // self-contained and include the prefixed original SQLite implementation.
49
+ fs.writeFileSync(outOrig, [
50
+ "/* Generated by build-amalgamation.js - intentionally empty.",
51
+ "** The released doltlite.c amalgamation is self-contained. */",
52
+ "",
53
+ ].join("\n"))
54
+
55
+ // Copy headers included by addon sources and by the BLAKE3 SIMD wrappers.
56
+ for (const hdrDir of [srcRoot, srcDir, blake3Dir]) {
57
+ if (!fs.existsSync(hdrDir)) continue
58
+ for (const f of fs.readdirSync(hdrDir)) {
59
+ if (f.endsWith(".h")) {
60
+ fs.copyFileSync(path.join(hdrDir, f), path.join(outDir, f))
61
+ }
62
+ }
63
+ }
64
+
65
+ const simdWrappers = [
66
+ ["blake3_sse2_impl.c", "blake3_sse2.c", "defined(__x86_64__)||defined(_M_X64)||defined(__i386__)"],
67
+ ["blake3_neon_impl.c", "blake3_neon.c", "defined(__aarch64__)||defined(__arm__)"],
68
+ ]
69
+
70
+ for (const [wrapName, srcName, archGuard] of simdWrappers) {
71
+ const srcPath = path.join(blake3Dir, srcName)
72
+ if (!fs.existsSync(srcPath)) continue
73
+ const relSrc = path.relative(outDir, srcPath).replace(/\\/g, "/")
74
+ fs.writeFileSync(path.join(outDir, wrapName), [
75
+ "/* Generated by build-amalgamation.js - DO NOT EDIT */",
76
+ `#if ${archGuard}`,
77
+ `#include "${relSrc}"`,
78
+ "#endif",
79
+ "",
80
+ ].join("\n"))
81
+ console.log(`SIMD wrapper ${wrapName} written`)
82
+ }
83
+
84
+ console.log(`Amalgamation written to ${outC}`)
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
- // Downloads the doltlite autoconf source tarball from GitHub releases and
3
- // builds a complete amalgamation from it, and bundles the matching doltlite
2
+ // Downloads the doltlite autoconf source tarball from GitHub releases,
3
+ // prepares its released amalgamation for node-gyp, and bundles the matching doltlite
4
4
  // CLI binary for the current platform into prebuilds/${platform}-${arch}/
5
5
  // so that binPath() can return an absolute path.
6
6
  //
7
- // The autoconf tarball contains both sqlite3.c (the base SQLite amalgamation
8
- // with doltlite's storage patches) and the prolly tree + dolt SQL function
9
- // source files under src/. We stitch them into a single doltlite.c that,
10
- // when compiled with -DDOLTLITE_PROLLY=1, has full version-control support.
7
+ // The autoconf tarball contains a self-contained sqlite3.c with DoltLite's
8
+ // prolly tree and SQL function sources already included. We copy it to
9
+ // amalgamation/doltlite.c so the addon builds the exact released source.
11
10
 
12
11
  "use strict"
13
12
 
@@ -22,7 +21,22 @@ const version = pkg.version
22
21
  const amalgDir = path.join(__dirname, "../amalgamation")
23
22
  const outC = path.join(amalgDir, "doltlite.c")
24
23
  const outH = path.join(amalgDir, "doltlite.h")
25
- const srcRoot = path.join(__dirname, "../doltlite-src")
24
+ const versionMarker = path.join(amalgDir, ".doltlite-source-version")
25
+ const srcRoot = path.join(amalgDir, ".source")
26
+
27
+ function normalizeVersion(v) {
28
+ return String(v || "").trim().replace(/^v/, "")
29
+ }
30
+
31
+ function sourceReady() {
32
+ if (!fs.existsSync(outC) || !fs.existsSync(outH) || !fs.existsSync(versionMarker)) return false
33
+ try {
34
+ const marker = fs.readFileSync(versionMarker, "utf8")
35
+ return normalizeVersion(marker) === normalizeVersion(version)
36
+ } catch (_) {
37
+ return false
38
+ }
39
+ }
26
40
 
27
41
  // Upstream dolthub/doltlite ships the CLI for these platform-arch pairs;
28
42
  // place the binary next to the .node addon so binPath() can return it.
@@ -51,7 +65,7 @@ function download(url, dest, cb) {
51
65
  }
52
66
 
53
67
  function fetchSource(cb) {
54
- if (fs.existsSync(outC) && fs.existsSync(outH)) return cb()
68
+ if (sourceReady()) return cb()
55
69
 
56
70
  const tarballUrl = `https://github.com/dolthub/doltlite/releases/download/v${version}/doltlite-autoconf-${version}.tar.gz`
57
71
  const tarballPath = path.join(__dirname, `../doltlite-autoconf-${version}.tar.gz`)
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // Uses a packaged native prebuild when available, otherwise prepares source
3
+ // and builds the addon locally.
4
+
5
+ "use strict"
6
+
7
+ const fs = require("fs")
8
+ const path = require("path")
9
+ const { spawnSync } = require("child_process")
10
+
11
+ const root = path.join(__dirname, "..")
12
+ const prebuilt = path.join(root, "prebuilds", `${process.platform}-${process.arch}`, "doltlite.node")
13
+
14
+ function run(command, args) {
15
+ const result = spawnSync(command, args, { cwd: root, stdio: "inherit", shell: process.platform === "win32" })
16
+ if (result.error) {
17
+ console.error(result.error.message)
18
+ process.exit(1)
19
+ }
20
+ if (result.status !== 0) process.exit(result.status || 1)
21
+ }
22
+
23
+ if (fs.existsSync(prebuilt)) {
24
+ console.log(`@dolthub/doltlite: using prebuilt addon ${path.relative(root, prebuilt)}`)
25
+ process.exit(0)
26
+ }
27
+
28
+ run(process.execPath, [path.join(__dirname, "download.js")])
29
+ run("node-gyp", ["rebuild"])
@@ -10,7 +10,7 @@ extern "C" napi_value _doltlite_init(napi_env env, napi_value exports);
10
10
  // this weak definition. MSVC doesn't support weak functions; skip it there
11
11
  // since Windows builds target Node 22+ which supplies the symbol via NAPI_MODULE.
12
12
  #if !defined(_MSC_VER)
13
- extern "C" __attribute__((weak)) napi_value napi_register_module_v1(napi_env env, napi_value exports) {
13
+ extern "C" __attribute__((weak, visibility("default"))) napi_value napi_register_module_v1(napi_env env, napi_value exports) {
14
14
  return _doltlite_init(env, exports);
15
15
  }
16
16
  #endif
package/src/database.cpp CHANGED
@@ -4,6 +4,19 @@
4
4
  #include <vector>
5
5
  #include <string>
6
6
 
7
+ static std::string SqliteOpenPath(const std::string& path) {
8
+ #ifdef _WIN32
9
+ if (path != ":memory:" && path.rfind("file:", 0) != 0) {
10
+ std::string normalized = path;
11
+ for (char& c : normalized) {
12
+ if (c == '\\') c = '/';
13
+ }
14
+ return normalized;
15
+ }
16
+ #endif
17
+ return path;
18
+ }
19
+
7
20
  // ── Constructor ──────────────────────────────────────────────────────────────
8
21
 
9
22
  Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Database>(info) {
@@ -32,13 +45,15 @@ Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Database>(
32
45
  : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
33
46
  flags |= SQLITE_OPEN_URI;
34
47
 
35
- int rc = sqlite3_open_v2(path.c_str(), &db_, flags, nullptr);
48
+ std::string openPath = SqliteOpenPath(path);
49
+ int rc = sqlite3_open_v2(openPath.c_str(), &db_, flags, nullptr);
36
50
  if (rc != SQLITE_OK) {
37
51
  std::string msg = db_ ? sqlite3_errmsg(db_) : "Failed to open database";
38
52
  if (db_) { sqlite3_close(db_); db_ = nullptr; }
39
53
  Napi::Error::New(env, msg).ThrowAsJavaScriptException();
40
54
  return;
41
55
  }
56
+ if (path != ":memory:") path_ = path;
42
57
 
43
58
  sqlite3_busy_timeout(db_, 5000);
44
59
  sqlite3_exec(db_, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr);
@@ -65,7 +80,8 @@ Napi::Value Database::Open(const Napi::CallbackInfo& info) {
65
80
  return env.Undefined();
66
81
  }
67
82
  std::string path = info[0].As<Napi::String>().Utf8Value();
68
- int rc = sqlite3_open_v2(path.c_str(), &db_,
83
+ std::string openPath = SqliteOpenPath(path);
84
+ int rc = sqlite3_open_v2(openPath.c_str(), &db_,
69
85
  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI,
70
86
  nullptr);
71
87
  if (rc != SQLITE_OK) {
@@ -73,11 +89,13 @@ Napi::Value Database::Open(const Napi::CallbackInfo& info) {
73
89
  if (db_) { sqlite3_close(db_); db_ = nullptr; }
74
90
  Napi::Error::New(env, msg).ThrowAsJavaScriptException();
75
91
  }
92
+ if (rc == SQLITE_OK && path != ":memory:") path_ = path;
76
93
  return env.Undefined();
77
94
  }
78
95
 
79
96
  Napi::Value Database::Close(const Napi::CallbackInfo& info) {
80
97
  if (db_) { sqlite3_close_v2(db_); db_ = nullptr; }
98
+ path_.clear();
81
99
  return info.Env().Undefined();
82
100
  }
83
101
 
@@ -124,6 +142,7 @@ Napi::Value Database::IsTransactionGetter(const Napi::CallbackInfo& info) {
124
142
  Napi::Value Database::Location(const Napi::CallbackInfo& info) {
125
143
  Napi::Env env = info.Env();
126
144
  if (!db_) return env.Null();
145
+ if (!path_.empty()) return Napi::String::New(env, path_);
127
146
  const char* loc = sqlite3_db_filename(db_, "main");
128
147
  if (!loc || loc[0] == '\0' || strcmp(loc, ":memory:") == 0) return env.Null();
129
148
  return Napi::String::New(env, loc);
package/src/database.h CHANGED
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
  #include <napi.h>
3
3
  #include "doltlite.h"
4
+ #include <string>
4
5
 
5
6
  class Database : public Napi::ObjectWrap<Database> {
6
7
  public:
@@ -13,6 +14,7 @@ public:
13
14
 
14
15
  private:
15
16
  sqlite3* db_ = nullptr;
17
+ std::string path_;
16
18
 
17
19
  // node:sqlite-compatible methods
18
20
  Napi::Value Exec(const Napi::CallbackInfo& info);
package/src/statement.cpp CHANGED
@@ -39,7 +39,8 @@ Statement::~Statement() {
39
39
  Napi::Value Statement::Run(const Napi::CallbackInfo& info) {
40
40
  Napi::Env env = info.Env();
41
41
  if (!stmt_) { Napi::Error::New(env, "Statement is finalised").ThrowAsJavaScriptException(); return env.Undefined(); }
42
- if (!BindArgs(env, stmt_, info)) return env.Undefined();
42
+ if (!BindArgs(env, stmt_, info, 0, allowBareNamedParameters_,
43
+ allowUnknownNamedParameters_)) return env.Undefined();
43
44
 
44
45
  int rc = sqlite3_step(stmt_);
45
46
  sqlite3_reset(stmt_);
@@ -60,11 +61,15 @@ Napi::Value Statement::Run(const Napi::CallbackInfo& info) {
60
61
  Napi::Value Statement::Get(const Napi::CallbackInfo& info) {
61
62
  Napi::Env env = info.Env();
62
63
  if (!stmt_) { Napi::Error::New(env, "Statement is finalised").ThrowAsJavaScriptException(); return env.Undefined(); }
63
- if (!BindArgs(env, stmt_, info)) return env.Undefined();
64
+ if (!BindArgs(env, stmt_, info, 0, allowBareNamedParameters_,
65
+ allowUnknownNamedParameters_)) return env.Undefined();
64
66
 
65
67
  int rc = sqlite3_step(stmt_);
66
68
  Napi::Value result = env.Undefined();
67
- if (rc == SQLITE_ROW) result = RowToObject(env, stmt_);
69
+ if (rc == SQLITE_ROW) {
70
+ result = returnArrays_ ? (Napi::Value)RowToArray(env, stmt_, readBigInts_)
71
+ : (Napi::Value)RowToObject(env, stmt_, readBigInts_);
72
+ }
68
73
  else if (rc != SQLITE_DONE) ThrowSQLiteError(env, db_->Handle(), "get");
69
74
  sqlite3_reset(stmt_);
70
75
  return result;
@@ -75,18 +80,42 @@ Napi::Value Statement::Get(const Napi::CallbackInfo& info) {
75
80
  Napi::Value Statement::All(const Napi::CallbackInfo& info) {
76
81
  Napi::Env env = info.Env();
77
82
  if (!stmt_) { Napi::Error::New(env, "Statement is finalised").ThrowAsJavaScriptException(); return env.Undefined(); }
78
- if (!BindArgs(env, stmt_, info)) return env.Undefined();
83
+ if (!BindArgs(env, stmt_, info, 0, allowBareNamedParameters_,
84
+ allowUnknownNamedParameters_)) return env.Undefined();
79
85
 
80
86
  auto result = Napi::Array::New(env);
81
87
  uint32_t idx = 0;
82
88
  int rc;
83
89
  while ((rc = sqlite3_step(stmt_)) == SQLITE_ROW)
84
- result.Set(idx++, RowToObject(env, stmt_));
90
+ result.Set(idx++, returnArrays_ ? (Napi::Value)RowToArray(env, stmt_, readBigInts_)
91
+ : (Napi::Value)RowToObject(env, stmt_, readBigInts_));
85
92
  sqlite3_reset(stmt_);
86
93
  if (rc != SQLITE_DONE) ThrowSQLiteError(env, db_->Handle(), "all");
87
94
  return result;
88
95
  }
89
96
 
97
+ // ── node:sqlite compatibility flags ──────────────────────────────────────────
98
+
99
+ Napi::Value Statement::SetReturnArrays(const Napi::CallbackInfo& info) {
100
+ returnArrays_ = info.Length() > 0 && info[0].ToBoolean().Value();
101
+ return info.Env().Undefined();
102
+ }
103
+
104
+ Napi::Value Statement::SetReadBigInts(const Napi::CallbackInfo& info) {
105
+ readBigInts_ = info.Length() > 0 && info[0].ToBoolean().Value();
106
+ return info.Env().Undefined();
107
+ }
108
+
109
+ Napi::Value Statement::SetAllowBareNamedParameters(const Napi::CallbackInfo& info) {
110
+ allowBareNamedParameters_ = info.Length() > 0 && info[0].ToBoolean().Value();
111
+ return info.Env().Undefined();
112
+ }
113
+
114
+ Napi::Value Statement::SetAllowUnknownNamedParameters(const Napi::CallbackInfo& info) {
115
+ allowUnknownNamedParameters_ = info.Length() > 0 && info[0].ToBoolean().Value();
116
+ return info.Env().Undefined();
117
+ }
118
+
90
119
  // ── iterate() → IterableIterator ─────────────────────────────────────────────
91
120
  // Returns a plain JS iterator object: { next() { return {value, done} } }
92
121
  // A full Symbol.iterator implementation requires a persistent Napi::Reference
@@ -174,11 +203,15 @@ Napi::Value Statement::ExpandedSQLGetter(const Napi::CallbackInfo& info) {
174
203
 
175
204
  Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) {
176
205
  Napi::Function ctor = DefineClass(env, "StatementSync", {
177
- InstanceMethod("run", &Statement::Run),
178
- InstanceMethod("get", &Statement::Get),
179
- InstanceMethod("all", &Statement::All),
180
- InstanceMethod("iterate", &Statement::Iterate),
181
- InstanceMethod("columns", &Statement::Columns),
206
+ InstanceMethod("run", &Statement::Run),
207
+ InstanceMethod("get", &Statement::Get),
208
+ InstanceMethod("all", &Statement::All),
209
+ InstanceMethod("iterate", &Statement::Iterate),
210
+ InstanceMethod("columns", &Statement::Columns),
211
+ InstanceMethod("setReturnArrays", &Statement::SetReturnArrays),
212
+ InstanceMethod("setReadBigInts", &Statement::SetReadBigInts),
213
+ InstanceMethod("setAllowBareNamedParameters", &Statement::SetAllowBareNamedParameters),
214
+ InstanceMethod("setAllowUnknownNamedParameters", &Statement::SetAllowUnknownNamedParameters),
182
215
  InstanceAccessor("sourceSQL", &Statement::SourceSQLGetter, nullptr),
183
216
  InstanceAccessor("expandedSQL", &Statement::ExpandedSQLGetter, nullptr),
184
217
  });
package/src/statement.h CHANGED
@@ -16,12 +16,20 @@ private:
16
16
  sqlite3_stmt* stmt_ = nullptr;
17
17
  Database* db_ = nullptr;
18
18
  std::string source_;
19
+ bool returnArrays_ = false;
20
+ bool readBigInts_ = false;
21
+ bool allowBareNamedParameters_ = true;
22
+ bool allowUnknownNamedParameters_ = false;
19
23
 
20
24
  Napi::Value Run(const Napi::CallbackInfo& info);
21
25
  Napi::Value Get(const Napi::CallbackInfo& info);
22
26
  Napi::Value All(const Napi::CallbackInfo& info);
23
27
  Napi::Value Iterate(const Napi::CallbackInfo& info);
24
28
  Napi::Value Columns(const Napi::CallbackInfo& info);
29
+ Napi::Value SetReturnArrays(const Napi::CallbackInfo& info);
30
+ Napi::Value SetReadBigInts(const Napi::CallbackInfo& info);
31
+ Napi::Value SetAllowBareNamedParameters(const Napi::CallbackInfo& info);
32
+ Napi::Value SetAllowUnknownNamedParameters(const Napi::CallbackInfo& info);
25
33
  Napi::Value SourceSQLGetter(const Napi::CallbackInfo& info);
26
34
  Napi::Value ExpandedSQLGetter(const Napi::CallbackInfo& info);
27
35
 
package/src/util.h CHANGED
@@ -4,10 +4,14 @@
4
4
  #include <string>
5
5
 
6
6
  // Converts a SQLite column value to a Napi::Value.
7
- inline Napi::Value ColumnToNapi(Napi::Env env, sqlite3_stmt* stmt, int col) {
7
+ inline Napi::Value ColumnToNapi(Napi::Env env, sqlite3_stmt* stmt, int col,
8
+ bool readBigInts = false) {
8
9
  switch (sqlite3_column_type(stmt, col)) {
9
- case SQLITE_INTEGER:
10
- return Napi::Number::New(env, (double)sqlite3_column_int64(stmt, col));
10
+ case SQLITE_INTEGER: {
11
+ sqlite3_int64 i = sqlite3_column_int64(stmt, col);
12
+ if (readBigInts) return Napi::BigInt::New(env, (int64_t)i);
13
+ return Napi::Number::New(env, (double)i);
14
+ }
11
15
  case SQLITE_FLOAT:
12
16
  return Napi::Number::New(env, sqlite3_column_double(stmt, col));
13
17
  case SQLITE_TEXT: {
@@ -28,16 +32,28 @@ inline Napi::Value ColumnToNapi(Napi::Env env, sqlite3_stmt* stmt, int col) {
28
32
  }
29
33
 
30
34
  // Builds a JS object from the current row of a prepared statement.
31
- inline Napi::Object RowToObject(Napi::Env env, sqlite3_stmt* stmt) {
35
+ inline Napi::Object RowToObject(Napi::Env env, sqlite3_stmt* stmt,
36
+ bool readBigInts = false) {
32
37
  auto obj = Napi::Object::New(env);
33
38
  int n = sqlite3_column_count(stmt);
34
39
  for (int i = 0; i < n; i++) {
35
40
  const char* name = sqlite3_column_name(stmt, i);
36
- obj.Set(name, ColumnToNapi(env, stmt, i));
41
+ obj.Set(name, ColumnToNapi(env, stmt, i, readBigInts));
37
42
  }
38
43
  return obj;
39
44
  }
40
45
 
46
+ // Builds a JS array from the current row of a prepared statement.
47
+ inline Napi::Array RowToArray(Napi::Env env, sqlite3_stmt* stmt,
48
+ bool readBigInts = false) {
49
+ int n = sqlite3_column_count(stmt);
50
+ auto arr = Napi::Array::New(env, n);
51
+ for (int i = 0; i < n; i++) {
52
+ arr.Set((uint32_t)i, ColumnToNapi(env, stmt, i, readBigInts));
53
+ }
54
+ return arr;
55
+ }
56
+
41
57
  // Binds a JS value to a prepared statement parameter (1-based index).
42
58
  inline bool BindNapi(Napi::Env env, sqlite3_stmt* stmt, int idx, Napi::Value val) {
43
59
  if (val.IsNull() || val.IsUndefined()) {
@@ -74,9 +90,9 @@ inline bool BindNapi(Napi::Env env, sqlite3_stmt* stmt, int idx, Napi::Value val
74
90
  // named: bind({$name: val, ...}) or bare names {name: val} if allowBare=true
75
91
  inline bool BindArgs(Napi::Env env, sqlite3_stmt* stmt,
76
92
  const Napi::CallbackInfo& info, size_t startIdx = 0,
77
- bool allowBare = true) {
93
+ bool allowBare = true, bool allowUnknown = false) {
78
94
  sqlite3_clear_bindings(stmt);
79
- if (info.Length() <= (int)startIdx) return true;
95
+ if (info.Length() <= startIdx) return true;
80
96
 
81
97
  // Named parameters via a plain object
82
98
  if (info.Length() == startIdx + 1 && info[startIdx].IsObject()
@@ -85,15 +101,22 @@ inline bool BindArgs(Napi::Env env, sqlite3_stmt* stmt,
85
101
  auto keys = obj.GetPropertyNames();
86
102
  for (uint32_t i = 0; i < keys.Length(); i++) {
87
103
  std::string key = keys.Get(i).As<Napi::String>().Utf8Value();
88
- // Try $name, :name, @name prefixes, then bare if allowBare
89
104
  int idx = 0;
90
- for (const char* prefix : {"", "$", ":", "@"}) {
91
- std::string k = std::string(prefix) + key;
92
- idx = sqlite3_bind_parameter_index(stmt, k.c_str());
93
- if (idx > 0) break;
105
+ if (!key.empty() && (key[0] == '$' || key[0] == ':' || key[0] == '@')) {
106
+ idx = sqlite3_bind_parameter_index(stmt, key.c_str());
107
+ } else if (allowBare) {
108
+ for (const char* prefix : {"$", ":", "@", ""}) {
109
+ std::string k = std::string(prefix) + key;
110
+ idx = sqlite3_bind_parameter_index(stmt, k.c_str());
111
+ if (idx > 0) break;
112
+ }
113
+ }
114
+ if (idx == 0) {
115
+ if (allowUnknown) continue;
116
+ Napi::Error::New(env, "Unknown named parameter '" + key + "'")
117
+ .ThrowAsJavaScriptException();
118
+ return false;
94
119
  }
95
- if (idx == 0 && !allowBare) continue;
96
- if (idx == 0) continue;
97
120
  if (!BindNapi(env, stmt, idx, obj.Get(keys.Get(i)))) return false;
98
121
  }
99
122
  return true;
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file