Skip to content

Commit

Permalink
sqlite: add support for SQLite Session Extension
Browse files Browse the repository at this point in the history
PR-URL: #54181
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
  • Loading branch information
louwers authored and ruyadorno committed Nov 27, 2024
1 parent 46311b0 commit 2c5bebc
Show file tree
Hide file tree
Showing 8 changed files with 842 additions and 46 deletions.
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'
],
'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 @@ -155,6 +155,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
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 @@ -317,8 +381,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 @@ -327,6 +422,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 @@ -891,6 +891,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
3 changes: 3 additions & 0 deletions src/env_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
V(fields_string, "fields") \
V(file_string, "file") \
V(filename_string, "filename") \
V(filter_string, "filter") \
V(fingerprint256_string, "fingerprint256") \
V(fingerprint512_string, "fingerprint512") \
V(fingerprint_string, "fingerprint") \
Expand Down Expand Up @@ -246,6 +247,7 @@
V(onchange_string, "onchange") \
V(onclienthello_string, "onclienthello") \
V(oncomplete_string, "oncomplete") \
V(onconflict_string, "onConflict") \
V(onconnection_string, "onconnection") \
V(ondone_string, "ondone") \
V(onerror_string, "onerror") \
Expand Down Expand Up @@ -413,6 +415,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

0 comments on commit 2c5bebc

Please sign in to comment.