Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sqlite: add support for SQLite Session Extension #54181

Merged
merged 31 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d3c9c6e
sqlite: add support for SQLite Session Extension
louwers Nov 2, 2024
210f947
doc: address PR comment @RedYetiDev
louwers Nov 2, 2024
bae7f67
sqlite: keep MemoryInfo related stuff together
louwers Nov 2, 2024
fd64575
sqlite: use snake_case for local variables
louwers Nov 2, 2024
ed2851f
sqlite: remove unneccesary use of node namespace
louwers Nov 2, 2024
bf9db72
sqlite: improve working filter parameter
louwers Nov 2, 2024
c4c8147
sqlite: add comment about using static functions
louwers Nov 2, 2024
351c34e
doc: improve wording default value onConflict
louwers Nov 2, 2024
a98e5b0
doc: capitalize Default
louwers Nov 2, 2024
6876522
doc: improve wording options documentation
louwers Nov 2, 2024
7362f4e
doc: improve options documentation
louwers Nov 2, 2024
fb91628
doc: improve wording table parameter
louwers Nov 2, 2024
f15f1b0
test: improve test name
louwers Nov 2, 2024
763da58
sqlite: move SQLITE_ENABLE_SESSION to node.gyp
louwers Nov 2, 2024
40837f2
test: split out sqlite session test and fix null __proto__
louwers Nov 2, 2024
b70d41c
sqlite: use FIXED_ONE_BYTE_STRING instead of NewFromUtf8
louwers Nov 2, 2024
56131e8
sqlite: don't use ToLocalChecked
louwers Nov 2, 2024
3202fbb
sqlite: avoid double check
louwers Nov 2, 2024
df83e4b
doc: add documentation for constants
louwers Nov 2, 2024
8addc58
doc: add explanation database name
louwers Nov 2, 2024
d31b038
test: remove unused imports test-sqlite.js
louwers Nov 2, 2024
4e62d6a
test: add some extra asserts for changeset and patchset
louwers Nov 2, 2024
7918a6c
test: create suite for conflict resolution
louwers Nov 2, 2024
f4b814d
test: fix deepStrictEqual helper
louwers Nov 2, 2024
14ba002
test: fix lint issues
louwers Nov 2, 2024
1d8da20
doc: fix lint issue
louwers Nov 2, 2024
80725c5
test: format import
louwers Nov 2, 2024
c3cf3f3
sqlite: run format
louwers Nov 16, 2024
6f08540
sqlite: add filter and onConflict to env_properties.h
louwers Nov 17, 2024
8776af6
sqlite: format
louwers Nov 17, 2024
092a28c
sqlite: simplify string conversion
louwers Nov 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions deps/sqlite/sqlite.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
'xcode_settings': {
'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden
},
'defines': [
'SQLITE_ENABLE_SESSION',
'SQLITE_ENABLE_PREUPDATE_HOOK'
louwers marked this conversation as resolved.
Show resolved Hide resolved
],
'include_dirs': ['.'],
'sources': [
'<@(sqlite_sources)',
Expand Down
101 changes: 101 additions & 0 deletions doc/api/sqlite.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,70 @@ added: v22.5.0
Compiles a SQL statement into a [prepared statement][]. This method is a wrapper
around [`sqlite3_prepare_v2()`][].

### `database.createSession([options])`

* `options` {Object} The configuration options for the session.
* `table` {string} A specific table to track changes for. By default, changes to all tables are tracked.
* `db` {string} Name of the database to track. This is useful when multiple databases have been added using [`ATTACH DATABASE`][]. **Default**: `'main'`.
* Returns: {Session} A session handle.

Creates and attaches a session to the database. This method is a wrapper around [`sqlite3session_create()`][] and [`sqlite3session_attach()`][].

### `database.applyChangeset(changeset[, options])`

* `changeset` {Uint8Array} A binary changeset or patchset.
* `options` {Object} The configuration options for how the changes will be applied.
* `filter` {Function} Skip changes that, when targeted table name is supplied to this function, return a truthy value.
By default, all changes are attempted.
* `onConflict` {number} Determines how conflicts are handled. **Default**: `SQLITE_CHANGESET_ABORT`.
* `SQLITE_CHANGESET_OMIT`: conflicting changes are omitted.
* `SQLITE_CHANGESET_REPLACE`: conflicting changes replace existing values.
* `SQLITE_CHANGESET_ABORT`: abort on conflict and roll back databsase.
* Returns: {boolean} Whether the changeset was applied succesfully without being aborted.

An exception is thrown if the database is not
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the same wording as elsewhere in the document. For consistency we should either keep it the same here or update it everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we just use plain Errors at this point, so I don't think it's worth it to write that everywhere.

open. This method is a wrapper around [`sqlite3changeset_apply()`][].

```js
const sourceDb = new DatabaseSync(':memory:');
const targetDb = new DatabaseSync(':memory:');

sourceDb.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, value TEXT)');
targetDb.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, value TEXT)');

const session = sourceDb.createSession();

const insert = sourceDb.prepare('INSERT INTO data (key, value) VALUES (?, ?)');
insert.run(1, 'hello');
insert.run(2, 'world');

const changeset = session.changeset();
targetDb.applyChangeset(changeset);
// Now that the changeset has been applied, targetDb contains the same data as sourceDb.
```

## Class: `Session`

### `session.changeset()`

* Returns: {Uint8Array} Binary changeset that can be applied to other databases.

Retrieves a changeset containing all changes since the changeset was created. Can be called multiple times.
An exception is thrown if the database or the session is not open. This method is a wrapper around [`sqlite3session_changeset()`][].

### `session.patchset()`

* Returns: {Uint8Array} Binary patchset that can be applied to other databases.

Similar to the method above, but generates a more compact patchset. See [Changesets and Patchsets][]
in the documentation of SQLite. An exception is thrown if the database or the session is not open. This method is a
wrapper around [`sqlite3session_patchset()`][].

### `session.close()`.

Closes the session. An exception is thrown if the database or the session is not open. This method is a
wrapper around [`sqlite3session_delete()`][].

## Class: `StatementSync`

<!-- YAML
Expand Down Expand Up @@ -326,8 +390,39 @@ exception.
| `TEXT` | {string} |
| `BLOB` | {Uint8Array} |

## SQLite constants

The following constants are exported by the `node:sqlite` module.

### SQLite Session constants

#### Conflict-resolution constants

The following constants are meant for use with [`database.applyChangeset()`](#databaseapplychangesetchangeset-options).

<table>
<tr>
<th>Constant</th>
<th>Description</th>
</tr>
<tr>
<td><code>SQLITE_CHANGESET_OMIT</code></td>
<td>Conflicting changes are omitted.</td>
</tr>
<tr>
<td><code>SQLITE_CHANGESET_REPLACE</code></td>
<td>Conflicting changes replace existing values.</td>
</tr>
<tr>
<td><code>SQLITE_CHANGESET_ABORT</code></td>
<td>Abort when a change encounters a conflict and roll back databsase.</td>
</tr>
</table>

[Changesets and Patchsets]: https://www.sqlite.org/sessionintro.html#changesets_and_patchsets
[SQL injection]: https://en.wikipedia.org/wiki/SQL_injection
[`--experimental-sqlite`]: cli.md#--experimental-sqlite
[`ATTACH DATABASE`]: https://www.sqlite.org/lang_attach.html
[`PRAGMA foreign_keys`]: https://www.sqlite.org/pragma.html#pragma_foreign_keys
[`sqlite3_changes64()`]: https://www.sqlite.org/c3ref/changes.html
[`sqlite3_close_v2()`]: https://www.sqlite.org/c3ref/close.html
Expand All @@ -336,6 +431,12 @@ exception.
[`sqlite3_last_insert_rowid()`]: https://www.sqlite.org/c3ref/last_insert_rowid.html
[`sqlite3_prepare_v2()`]: https://www.sqlite.org/c3ref/prepare.html
[`sqlite3_sql()`]: https://www.sqlite.org/c3ref/expanded_sql.html
[`sqlite3changeset_apply()`]: https://www.sqlite.org/session/sqlite3changeset_apply.html
[`sqlite3session_attach()`]: https://www.sqlite.org/session/sqlite3session_attach.html
[`sqlite3session_changeset()`]: https://www.sqlite.org/session/sqlite3session_changeset.html
[`sqlite3session_create()`]: https://www.sqlite.org/session/sqlite3session_create.html
[`sqlite3session_delete()`]: https://www.sqlite.org/session/sqlite3session_delete.html
[`sqlite3session_patchset()`]: https://www.sqlite.org/session/sqlite3session_patchset.html
[connection]: https://www.sqlite.org/c3ref/sqlite3.html
[data types]: https://www.sqlite.org/datatype3.html
[double-quoted string literals]: https://www.sqlite.org/quirks.html#dblquote
Expand Down
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@
# Warn when using deprecated V8 APIs.
'V8_DEPRECATION_WARNINGS=1',
'NODE_OPENSSL_SYSTEM_CERT_PATH="<(openssl_system_ca_path)"',
"SQLITE_ENABLE_SESSION"
],

# - "C4244: conversion from 'type1' to 'type2', possible loss of data"
Expand Down
1 change: 1 addition & 0 deletions src/env_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@
V(shutdown_wrap_template, v8::ObjectTemplate) \
V(socketaddress_constructor_template, v8::FunctionTemplate) \
V(sqlite_statement_sync_constructor_template, v8::FunctionTemplate) \
V(sqlite_session_constructor_template, v8::FunctionTemplate) \
V(streambaseentry_ctor_template, v8::FunctionTemplate) \
V(streambaseoutputstream_constructor_template, v8::ObjectTemplate) \
V(streamentry_ctor_template, v8::FunctionTemplate) \
Expand Down
Loading
Loading