Repository: nodejs/node
Pull request: sqlite: support TypedArray and DataView in `StatementSync`

Fixes: https://github.com/nodejs/node/issues/56384

Changed files:
filename: doc/api/sqlite.md
patch: @@ -339,11 +339,15 @@ over hand-crafted SQL strings when handling user input.
 
 <!-- YAML
 added: v22.5.0
+changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/56385
+    description: Add support for `DataView` and typed array objects for `anonymousParameters`.
 -->
 
 * `namedParameters` {Object} An optional object used to bind named parameters.
   The keys of this object are used to configure the mapping.
-* `...anonymousParameters` {null|number|bigint|string|Buffer|Uint8Array} Zero or
+* `...anonymousParameters` {null|number|bigint|string|Buffer|TypedArray|DataView} Zero or
   more values to bind to anonymous parameters.
 * Returns: {Array} An array of objects. Each object corresponds to a row
   returned by executing the prepared statement. The keys and values of each
@@ -371,11 +375,15 @@ execution of this prepared statement. This property is a wrapper around
 
 <!-- YAML
 added: v22.5.0
+changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/56385
+    description: Add support for `DataView` and typed array objects for `anonymousParameters`.
 -->
 
 * `namedParameters` {Object} An optional object used to bind named parameters.
   The keys of this object are used to configure the mapping.
-* `...anonymousParameters` {null|number|bigint|string|Buffer|Uint8Array} Zero or
+* `...anonymousParameters` {null|number|bigint|string|Buffer|TypedArray|DataView} Zero or
   more values to bind to anonymous parameters.
 * Returns: {Object|undefined} An object corresponding to the first row returned
   by executing the prepared statement. The keys and values of the object
@@ -391,11 +399,15 @@ values in `namedParameters` and `anonymousParameters`.
 
 <!-- YAML
 added: v23.4.0
+changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/56385
+    description: Add support for `DataView` and typed array objects for `anonymousParameters`.
 -->
 
 * `namedParameters` {Object} An optional object used to bind named parameters.
   The keys of this object are used to configure the mapping.
-* `...anonymousParameters` {null|number|bigint|string|Buffer|Uint8Array} Zero or
+* `...anonymousParameters` {null|number|bigint|string|Buffer|TypedArray|DataView} Zero or
   more values to bind to anonymous parameters.
 * Returns: {Iterator} An iterable iterator of objects. Each object corresponds to a row
   returned by executing the prepared statement. The keys and values of each
@@ -410,11 +422,15 @@ the values in `namedParameters` and `anonymousParameters`.
 
 <!-- YAML
 added: v22.5.0
+changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/56385
+    description: Add support for `DataView` and typed array objects for `anonymousParameters`.
 -->
 
 * `namedParameters` {Object} An optional object used to bind named parameters.
   The keys of this object are used to configure the mapping.
-* `...anonymousParameters` {null|number|bigint|string|Buffer|Uint8Array} Zero or
+* `...anonymousParameters` {null|number|bigint|string|Buffer|TypedArray|DataView} Zero or
   more values to bind to anonymous parameters.
 * Returns: {Object}
   * `changes`: {number|bigint} The number of rows modified, inserted, or deleted

filename: src/node_sqlite.cc
patch: @@ -960,7 +960,7 @@ bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
   int anon_idx = 1;
   int anon_start = 0;
 
-  if (args[0]->IsObject() && !args[0]->IsUint8Array()) {
+  if (args[0]->IsObject() && !args[0]->IsArrayBufferView()) {
     Local<Object> obj = args[0].As<Object>();
     Local<Context> context = obj->GetIsolate()->GetCurrentContext();
     Local<Array> keys;
@@ -1065,7 +1065,7 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
         statement_, index, *val, val.length(), SQLITE_TRANSIENT);
   } else if (value->IsNull()) {
     r = sqlite3_bind_null(statement_, index);
-  } else if (value->IsUint8Array()) {
+  } else if (value->IsArrayBufferView()) {
     ArrayBufferViewContents<uint8_t> buf(value);
     r = sqlite3_bind_blob(
         statement_, index, buf.data(), buf.length(), SQLITE_TRANSIENT);

filename: test/parallel/test-sqlite-typed-array-and-data-view.js
patch: @@ -0,0 +1,61 @@
+'use strict';
+require('../common');
+const tmpdir = require('../common/tmpdir');
+const { join } = require('node:path');
+const { DatabaseSync } = require('node:sqlite');
+const { suite, test } = require('node:test');
+let cnt = 0;
+
+tmpdir.refresh();
+
+function nextDb() {
+  return join(tmpdir.path, `database-${cnt++}.db`);
+}
+
+const arrayBuffer = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]).buffer;
+const TypedArrays = [
+  ['Int8Array', Int8Array],
+  ['Uint8Array', Uint8Array],
+  ['Uint8ClampedArray', Uint8ClampedArray],
+  ['Int16Array', Int16Array],
+  ['Uint16Array', Uint16Array],
+  ['Int32Array', Int32Array],
+  ['Uint32Array', Uint32Array],
+  ['Float32Array', Float32Array],
+  ['Float64Array', Float64Array],
+  ['BigInt64Array', BigInt64Array],
+  ['BigUint64Array', BigUint64Array],
+  ['DataView', DataView],
+];
+
+suite('StatementSync with TypedArray/DataView', () => {
+  for (const [displayName, TypedArray] of TypedArrays) {
+    test(displayName, (t) => {
+      const db = new DatabaseSync(nextDb());
+      t.after(() => { db.close(); });
+      db.exec('CREATE TABLE test (data BLOB)');
+      // insert
+      {
+        const stmt = db.prepare('INSERT INTO test VALUES (?)');
+        stmt.run(new TypedArray(arrayBuffer));
+      }
+      // select all
+      {
+        const stmt = db.prepare('SELECT * FROM test');
+        const row = stmt.get();
+        t.assert.ok(row.data instanceof Uint8Array);
+        t.assert.strictEqual(row.data.length, 8);
+        t.assert.deepStrictEqual(row.data, new Uint8Array(arrayBuffer));
+      }
+      // query
+      {
+        const stmt = db.prepare('SELECT * FROM test WHERE data = ?');
+        const rows = stmt.all(new TypedArray(arrayBuffer));
+        t.assert.strictEqual(rows.length, 1);
+        t.assert.ok(rows[0].data instanceof Uint8Array);
+        t.assert.strictEqual(rows[0].data.length, 8);
+        t.assert.deepStrictEqual(rows[0].data, new Uint8Array(arrayBuffer));
+      }
+    });
+  }
+});