mirror of
https://github.com/hanzoai/better-sqlite3.git
synced 2026-09-24 16:57:15 +00:00
implemented LocalVector optimizations for newer version of v8
This commit is contained in:
+14
-8
@@ -18,13 +18,19 @@ module.exports = new Map([
|
||||
for (const str of pragma) await db.run(`PRAGMA ${str}`);
|
||||
return db;
|
||||
}],
|
||||
...!moduleExists('node:sqlite') ? [] : [
|
||||
['node:sqlite', async (filename, pragma) => {
|
||||
const db = new (require('node:sqlite').DatabaseSync)(filename);
|
||||
for (const str of pragma) db.exec(`PRAGMA ${str}`);
|
||||
return db;
|
||||
}]
|
||||
],
|
||||
]);
|
||||
|
||||
const moduleExists = (m) => { try { return require.resolve(m); } catch (e) {} };
|
||||
if (moduleExists('node:sqlite')) {
|
||||
module.exports.set('node:sqlite', async (filename, pragma) => {
|
||||
const db = new (require('node:sqlite').DatabaseSync)(filename, {open: true});
|
||||
for (const str of pragma) db.exec(`PRAGMA ${str}`);
|
||||
return db;
|
||||
});
|
||||
}
|
||||
function moduleExists(moduleName) {
|
||||
try {
|
||||
return !!(require.resolve(moduleName));
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,25 +3,20 @@ exports.readonly = false; // Inserting rows individually (`.run()`)
|
||||
|
||||
exports['better-sqlite3'] = (db, { table, columns }) => {
|
||||
const stmt = db.prepare(`INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`);
|
||||
const row = db.prepare(`SELECT * FROM ${table} LIMIT 1`).get();
|
||||
const row = db.prepare(`SELECT ${columns.join(', ')} FROM ${table} LIMIT 1`).get();
|
||||
return () => stmt.run(row);
|
||||
};
|
||||
|
||||
exports['node-sqlite3'] = async (db, { table, columns }) => {
|
||||
const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`;
|
||||
const row = Object.assign({}, ...Object.entries(await db.get(`SELECT * FROM ${table} LIMIT 1`))
|
||||
const row = Object.assign({}, ...Object.entries(await db.get(`SELECT ${columns.join(', ')} FROM ${table} LIMIT 1`))
|
||||
.filter(([k]) => columns.includes(k))
|
||||
.map(([k, v]) => ({ ['@' + k]: v })));
|
||||
return () => db.run(sql, row);
|
||||
};
|
||||
|
||||
exports['node:sqlite'] = (db, { table, columns }) => {
|
||||
const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`;
|
||||
const row = Object.assign({}, ...Object.entries(db.prepare(`SELECT * FROM ${table} LIMIT 1`).get())
|
||||
.filter(([k]) => columns.includes(k))
|
||||
.map(([k, v]) => ({ ['@' + k]: v })));
|
||||
return () => {
|
||||
const stmt = db.prepare(sql);
|
||||
return stmt.run(row);
|
||||
}
|
||||
};
|
||||
const stmt = db.prepare(`INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`);
|
||||
const row = db.prepare(`SELECT ${columns.join(', ')} FROM ${table} LIMIT 1`).get();
|
||||
return () => stmt.run(row);
|
||||
};
|
||||
|
||||
@@ -14,10 +14,7 @@ exports['node-sqlite3'] = async (db, { table, columns, count }) => {
|
||||
};
|
||||
|
||||
exports['node:sqlite'] = (db, { table, columns, count }) => {
|
||||
const sql = `SELECT ${columns.join(', ')} FROM ${table} WHERE rowid >= ? LIMIT 100`;
|
||||
const stmt = db.prepare(`SELECT ${columns.join(', ')} FROM ${table} WHERE rowid >= ? LIMIT 100`);
|
||||
let rowid = -100;
|
||||
return () => {
|
||||
const stmt = db.prepare(sql);
|
||||
return stmt.all((rowid += 100) % count + 1);
|
||||
}
|
||||
};
|
||||
return () => stmt.all((rowid += 100) % count + 1);
|
||||
};
|
||||
|
||||
@@ -23,17 +23,9 @@ exports['node-sqlite3'] = async (db, { table, columns, count }) => {
|
||||
};
|
||||
|
||||
exports['node:sqlite'] = (db, { table, columns, count }) => {
|
||||
const sql = `SELECT ${columns.join(', ')} FROM ${table} WHERE rowid >= ? LIMIT 100`;
|
||||
const stmt = db.prepare(`SELECT ${columns.join(', ')} FROM ${table} WHERE rowid >= ? LIMIT 100`);
|
||||
let rowid = -100;
|
||||
|
||||
if (!("iterate" in require("node:sqlite").StatementSync.prototype)) {
|
||||
// Error: StatementSync.iterate is not a function (added in Node v23.4.0+)
|
||||
return () => {};
|
||||
}
|
||||
|
||||
return () => {
|
||||
// Error: statement has been finalized
|
||||
// for (const row of db.prepare(sql).iterate((rowid += 100) % count + 1)) {}
|
||||
return () => {};
|
||||
for (const row of stmt.iterate((rowid += 100) % count + 1)) {}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,10 +14,7 @@ exports['node-sqlite3'] = async (db, { table, columns, count }) => {
|
||||
};
|
||||
|
||||
exports['node:sqlite'] = (db, { table, columns, count }) => {
|
||||
const sql = `SELECT ${columns.join(', ')} FROM ${table} WHERE rowid = ?`;
|
||||
const stmt = db.prepare(`SELECT ${columns.join(', ')} FROM ${table} WHERE rowid = ?`);
|
||||
let rowid = -1;
|
||||
return () => {
|
||||
const stmt = db.prepare(sql);
|
||||
return stmt.get(++rowid % count + 1);
|
||||
}
|
||||
};
|
||||
return () => stmt.get(++rowid % count + 1);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ exports.readonly = false; // Inserting 100 rows in a single transaction
|
||||
|
||||
exports['better-sqlite3'] = (db, { table, columns }) => {
|
||||
const stmt = db.prepare(`INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`);
|
||||
const row = db.prepare(`SELECT * FROM ${table} LIMIT 1`).get();
|
||||
const row = db.prepare(`SELECT ${columns.join(', ')} FROM ${table} LIMIT 1`).get();
|
||||
const trx = db.transaction((row) => {
|
||||
for (let i = 0; i < 100; ++i) stmt.run(row);
|
||||
});
|
||||
@@ -12,7 +12,7 @@ exports['better-sqlite3'] = (db, { table, columns }) => {
|
||||
|
||||
exports['node-sqlite3'] = async (db, { table, columns, driver, pragma }) => {
|
||||
const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`;
|
||||
const row = Object.assign({}, ...Object.entries(await db.get(`SELECT * FROM ${table} LIMIT 1`))
|
||||
const row = Object.assign({}, ...Object.entries(await db.get(`SELECT ${columns.join(', ')} FROM ${table} LIMIT 1`))
|
||||
.filter(([k]) => columns.includes(k))
|
||||
.map(([k, v]) => ({ ['@' + k]: v })));
|
||||
const open = require('../drivers').get(driver);
|
||||
@@ -40,14 +40,16 @@ exports['node-sqlite3'] = async (db, { table, columns, driver, pragma }) => {
|
||||
};
|
||||
|
||||
exports['node:sqlite'] = (db, { table, columns }) => {
|
||||
const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`;
|
||||
const row = Object.assign({}, ...Object.entries(db.prepare(`SELECT * FROM ${table} LIMIT 1`).get())
|
||||
.filter(([k]) => columns.includes(k))
|
||||
.map(([k, v]) => ({ ['@' + k]: v })));
|
||||
const stmt = db.prepare(`INSERT INTO ${table} (${columns.join(', ')}) VALUES (${columns.map(x => '@' + x).join(', ')})`);
|
||||
const row = db.prepare(`SELECT ${columns.join(', ')} FROM ${table} LIMIT 1`).get();
|
||||
return () => {
|
||||
const stmt = db.prepare(sql);
|
||||
db.exec(`BEGIN`);
|
||||
for (let i = 0; i < 100; ++i) stmt.run(row);
|
||||
db.exec(`COMMIT`);
|
||||
db.exec('BEGIN');
|
||||
try {
|
||||
for (let i = 0; i < 100; ++i) stmt.run(row);
|
||||
db.exec('COMMIT');
|
||||
} catch (err) {
|
||||
db.isTransaction && db.exec('ROLLBACK');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -23,6 +23,9 @@ class Backup;
|
||||
#include "util/bind-map.cpp"
|
||||
#include "util/data-converter.cpp"
|
||||
#include "util/data.cpp"
|
||||
#if defined(NODE_MODULE_VERSION) && NODE_MODULE_VERSION >= 127
|
||||
#include "util/row-builder.cpp"
|
||||
#endif
|
||||
|
||||
#include "objects/backup.hpp"
|
||||
#include "objects/statement.hpp"
|
||||
|
||||
@@ -180,11 +180,13 @@ NODE_METHOD(Statement::JS_get) {
|
||||
NODE_METHOD(Statement::JS_all) {
|
||||
STATEMENT_START(REQUIRE_STATEMENT_RETURNS_DATA, DOES_NOT_MUTATE);
|
||||
UseContext;
|
||||
v8::Local<v8::Array> result = v8::Array::New(isolate, 0);
|
||||
uint32_t row_count = 0;
|
||||
const bool safe_ints = stmt->safe_ints;
|
||||
const char mode = stmt->mode;
|
||||
|
||||
#if !defined(NODE_MODULE_VERSION) || NODE_MODULE_VERSION < 127
|
||||
bool js_error = false;
|
||||
uint32_t row_count = 0;
|
||||
v8::Local<v8::Array> result = v8::Array::New(isolate, 0);
|
||||
|
||||
while (sqlite3_step(handle) == SQLITE_ROW) {
|
||||
if (row_count == 0xffffffff) { ThrowRangeError("Array overflow (too many rows returned)"); js_error = true; break; }
|
||||
@@ -196,6 +198,31 @@ NODE_METHOD(Statement::JS_all) {
|
||||
}
|
||||
if (js_error) db->GetState()->was_js_error = true;
|
||||
STATEMENT_THROW();
|
||||
#else
|
||||
v8::LocalVector<v8::Value> rows(isolate);
|
||||
rows.reserve(8);
|
||||
|
||||
if (mode == Data::FLAT) {
|
||||
RowBuilder rowBuilder(isolate, handle, safe_ints);
|
||||
while (sqlite3_step(handle) == SQLITE_ROW) {
|
||||
rows.emplace_back(rowBuilder.GetRowJS());
|
||||
}
|
||||
} else {
|
||||
while (sqlite3_step(handle) == SQLITE_ROW) {
|
||||
rows.emplace_back(Data::GetRowJS(isolate, ctx, handle, safe_ints, mode));
|
||||
}
|
||||
}
|
||||
|
||||
if (sqlite3_reset(handle) == SQLITE_OK) {
|
||||
if (rows.size() > 0xffffffff) {
|
||||
ThrowRangeError("Array overflow (too many rows returned)");
|
||||
db->GetState()->was_js_error = true;
|
||||
} else {
|
||||
STATEMENT_RETURN(v8::Array::New(isolate, rows.data(), rows.size()));
|
||||
}
|
||||
}
|
||||
STATEMENT_THROW();
|
||||
#endif
|
||||
}
|
||||
|
||||
NODE_METHOD(Statement::JS_iterate) {
|
||||
@@ -268,8 +295,9 @@ NODE_METHOD(Statement::JS_columns) {
|
||||
REQUIRE_DATABASE_NOT_BUSY(stmt->db->GetState());
|
||||
Addon* addon = stmt->db->GetAddon();
|
||||
UseIsolate;
|
||||
UseContext;
|
||||
|
||||
#if !defined(NODE_MODULE_VERSION) || NODE_MODULE_VERSION < 127
|
||||
UseContext;
|
||||
int column_count = sqlite3_column_count(stmt->handle);
|
||||
v8::Local<v8::Array> columns = v8::Array::New(isolate);
|
||||
|
||||
@@ -302,6 +330,51 @@ NODE_METHOD(Statement::JS_columns) {
|
||||
}
|
||||
|
||||
info.GetReturnValue().Set(columns);
|
||||
#else
|
||||
v8::LocalVector<v8::Name> keys(isolate);
|
||||
keys.reserve(5);
|
||||
keys.emplace_back(addon->cs.name.Get(isolate).As<v8::Name>());
|
||||
keys.emplace_back(addon->cs.column.Get(isolate).As<v8::Name>());
|
||||
keys.emplace_back(addon->cs.table.Get(isolate).As<v8::Name>());
|
||||
keys.emplace_back(addon->cs.database.Get(isolate).As<v8::Name>());
|
||||
keys.emplace_back(addon->cs.type.Get(isolate).As<v8::Name>());
|
||||
|
||||
int column_count = sqlite3_column_count(stmt->handle);
|
||||
v8::LocalVector<v8::Value> columns(isolate);
|
||||
columns.reserve(column_count);
|
||||
|
||||
for (int i = 0; i < column_count; ++i) {
|
||||
v8::LocalVector<v8::Value> values(isolate);
|
||||
keys.reserve(5);
|
||||
values.emplace_back(
|
||||
InternalizedFromUtf8OrNull(isolate, sqlite3_column_name(stmt->handle, i), -1)
|
||||
);
|
||||
values.emplace_back(
|
||||
InternalizedFromUtf8OrNull(isolate, sqlite3_column_origin_name(stmt->handle, i), -1)
|
||||
);
|
||||
values.emplace_back(
|
||||
InternalizedFromUtf8OrNull(isolate, sqlite3_column_table_name(stmt->handle, i), -1)
|
||||
);
|
||||
values.emplace_back(
|
||||
InternalizedFromUtf8OrNull(isolate, sqlite3_column_database_name(stmt->handle, i), -1)
|
||||
);
|
||||
values.emplace_back(
|
||||
InternalizedFromUtf8OrNull(isolate, sqlite3_column_decltype(stmt->handle, i), -1)
|
||||
);
|
||||
columns.emplace_back(
|
||||
v8::Object::New(isolate,
|
||||
v8::Null(isolate),
|
||||
keys.data(),
|
||||
values.data(),
|
||||
keys.size()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
info.GetReturnValue().Set(
|
||||
v8::Array::New(isolate, columns.data(), columns.size())
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
NODE_GETTER(Statement::JS_busy) {
|
||||
|
||||
+60
-11
@@ -77,17 +77,6 @@ namespace Data {
|
||||
SQLITE_VALUE_TO_JS(value, isolate, safe_ints, value);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> GetFlatRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints) {
|
||||
v8::Local<v8::Object> row = v8::Object::New(isolate);
|
||||
int column_count = sqlite3_column_count(handle);
|
||||
for (int i = 0; i < column_count; ++i) {
|
||||
row->Set(ctx,
|
||||
InternalizedFromUtf8(isolate, sqlite3_column_name(handle, i), -1),
|
||||
Data::GetValueJS(isolate, handle, i, safe_ints)).FromJust();
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> GetExpandedRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints) {
|
||||
v8::Local<v8::Object> row = v8::Object::New(isolate);
|
||||
int column_count = sqlite3_column_count(handle);
|
||||
@@ -107,6 +96,20 @@ namespace Data {
|
||||
return row;
|
||||
}
|
||||
|
||||
#if !defined(NODE_MODULE_VERSION) || NODE_MODULE_VERSION < 127
|
||||
|
||||
v8::Local<v8::Value> GetFlatRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints) {
|
||||
v8::Local<v8::Object> row = v8::Object::New(isolate);
|
||||
int column_count = sqlite3_column_count(handle);
|
||||
for (int i = 0; i < column_count; ++i) {
|
||||
row->Set(ctx,
|
||||
InternalizedFromUtf8(isolate, sqlite3_column_name(handle, i), -1),
|
||||
Data::GetValueJS(isolate, handle, i, safe_ints)
|
||||
).FromJust();
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> GetRawRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints) {
|
||||
v8::Local<v8::Array> row = v8::Array::New(isolate);
|
||||
int column_count = sqlite3_column_count(handle);
|
||||
@@ -125,6 +128,52 @@ namespace Data {
|
||||
return v8::Local<v8::Value>();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
v8::Local<v8::Value> GetFlatRowJS(v8::Isolate* isolate, sqlite3_stmt* handle, bool safe_ints) {
|
||||
int column_count = sqlite3_column_count(handle);
|
||||
v8::LocalVector<v8::Name> keys(isolate);
|
||||
v8::LocalVector<v8::Value> values(isolate);
|
||||
keys.reserve(column_count);
|
||||
values.reserve(column_count);
|
||||
for (int i = 0; i < column_count; ++i) {
|
||||
keys.emplace_back(
|
||||
InternalizedFromUtf8(isolate, sqlite3_column_name(handle, i), -1).As<v8::Name>()
|
||||
);
|
||||
values.emplace_back(
|
||||
Data::GetValueJS(isolate, handle, i, safe_ints)
|
||||
);
|
||||
}
|
||||
return v8::Object::New(
|
||||
isolate,
|
||||
v8::Null(isolate),
|
||||
keys.data(),
|
||||
values.data(),
|
||||
column_count
|
||||
);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> GetRawRowJS(v8::Isolate* isolate, sqlite3_stmt* handle, bool safe_ints) {
|
||||
int column_count = sqlite3_column_count(handle);
|
||||
v8::LocalVector<v8::Value> row(isolate);
|
||||
row.reserve(column_count);
|
||||
for (int i = 0; i < column_count; ++i) {
|
||||
row.emplace_back(Data::GetValueJS(isolate, handle, i, safe_ints));
|
||||
}
|
||||
return v8::Array::New(isolate, row.data(), row.size());
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> GetRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints, char mode) {
|
||||
if (mode == FLAT) return GetFlatRowJS(isolate, handle, safe_ints);
|
||||
if (mode == PLUCK) return GetValueJS(isolate, handle, 0, safe_ints);
|
||||
if (mode == EXPAND) return GetExpandedRowJS(isolate, ctx, handle, safe_ints);
|
||||
if (mode == RAW) return GetRawRowJS(isolate, handle, safe_ints);
|
||||
assert(false);
|
||||
return v8::Local<v8::Value>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void GetArgumentsJS(v8::Isolate* isolate, v8::Local<v8::Value>* out, sqlite3_value** values, int argument_count, bool safe_ints) {
|
||||
assert(argument_count > 0);
|
||||
for (int i = 0; i < argument_count; ++i) {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
class RowBuilder {
|
||||
public:
|
||||
|
||||
explicit RowBuilder(
|
||||
v8::Isolate* isolate,
|
||||
sqlite3_stmt* handle,
|
||||
bool safe_ints
|
||||
) :
|
||||
isolate(isolate),
|
||||
handle(handle),
|
||||
column_count(-1),
|
||||
safe_ints(safe_ints),
|
||||
keys(isolate) {}
|
||||
|
||||
v8::Local<v8::Value> GetRowJS() {
|
||||
if (column_count < 0) {
|
||||
column_count = sqlite3_column_count(handle);
|
||||
keys.reserve(column_count);
|
||||
for (int i = 0; i < column_count; ++i) {
|
||||
keys.emplace_back(
|
||||
InternalizedFromUtf8(isolate, sqlite3_column_name(handle, i), -1)
|
||||
.As<v8::Name>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
v8::LocalVector<v8::Value> values(isolate);
|
||||
values.reserve(column_count);
|
||||
for (int i = 0; i < column_count; ++i) {
|
||||
values.emplace_back(
|
||||
Data::GetValueJS(isolate, handle, i, safe_ints)
|
||||
);
|
||||
}
|
||||
|
||||
return v8::Object::New(isolate,
|
||||
v8::Null(isolate),
|
||||
keys.data(),
|
||||
values.data(),
|
||||
column_count
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate;
|
||||
sqlite3_stmt* handle;
|
||||
int column_count;
|
||||
const bool safe_ints;
|
||||
v8::LocalVector<v8::Name> keys;
|
||||
};
|
||||
Reference in New Issue
Block a user