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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

louwers
Copy link

@louwers louwers commented Aug 2, 2024

We were talking about adding support for the Session Extension of SQLite in #53752. This is not a run-time extension but rather a feature built in to the SQLite amalgamation. It can be enabled with a compile flag and is enabled by default on some platforms.

I have never contributed to Node.js before so I will probably need some help to bring this to a conclusion.


TODO:

  • Make sure sessions are deleted before the database they are attached to is closed
  • Consensus on API
  • Documentation
  • Allow custom conflict handler
  • Throw with specific (documented) exception in case of conflict when applying changeset since SQLite doesn't consider this to be an error I return false when applying the changeset is aborted due to a conflict
  • Allow generating a patchset as well
  • Allow specifying table name when creating session (to only track that table)
  • Implement Session.close()

Example usage:

const database1 = new DatabaseSync(':memory:');
const database2 = new DatabaseSync(':memory:');

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

const session = database1.createSession();

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

const changeset = session.changeset();
database2.applyChangeset(changeset);
// Now database2 contains the same data as database1 

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/gyp
  • @nodejs/security-wg

@louwers louwers marked this pull request as draft August 2, 2024 17:57
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Aug 2, 2024
@karimfromjordan
Copy link

Wouldn't this be solved if users could use their own SQLite build? That's already being considered according to the original issue.

@louwers
Copy link
Author

louwers commented Aug 2, 2024

@karimfromjordan Nope, because you need access to the C API to access this feature.

@anonrig
Copy link
Member

anonrig commented Aug 2, 2024

Can you add the appropriate documentation as well?

Copy link

codecov bot commented Aug 2, 2024

Codecov Report

Attention: Patch coverage is 91.05263% with 17 lines in your changes missing coverage. Please review.

Project coverage is 87.33%. Comparing base (7fea010) to head (fa4ac48).
Report is 2 commits behind head on main.

Files Patch % Lines
src/node_sqlite.cc 92.02% 2 Missing and 13 partials ⚠️
src/node_sqlite.h 0.00% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #54181    +/-   ##
========================================
  Coverage   87.32%   87.33%            
========================================
  Files         649      649            
  Lines      182618   182808   +190     
  Branches    35033    35083    +50     
========================================
+ Hits       159475   159653   +178     
- Misses      16407    16409     +2     
- Partials     6736     6746    +10     
Files Coverage Δ
src/node_sqlite.h 0.00% <0.00%> (ø)
src/node_sqlite.cc 86.27% <92.02%> (+2.27%) ⬆️

... and 22 files with indirect coverage changes

@louwers
Copy link
Author

louwers commented Aug 2, 2024

I need to make sure sessions are deleted before the database they are attached to is closed.

I think I need the DatabaseSync hold onto the Sessions it creates. Both are BaseObjects so I need to look into how to do that.

When a Session goes out of scope the corresponding DatabaseSync needs to no longer hold onto it. Conversely I also need to make sure that all operations on Session objects are no-ops after the corresponding database is closed.

@louwers louwers force-pushed the session-extension branch 2 times, most recently from 9fe44b8 to 36e1920 Compare August 3, 2024 14:01
@louwers louwers changed the title src, test: support for SQLite Session Extension src, test, doc: support for SQLite Session Extension Aug 3, 2024
@louwers
Copy link
Author

louwers commented Aug 3, 2024

@anonrig I have added documentation, most of the functionality and fixed the lint issues. Could you trigger another CI run?

I want to add support for generating a patchset and then this PR is ready for review.

I have some ideas for future improvements, e.g. more fine-grained conflict handling or exposing the utilities SQLite provides for inspecting and merging changesets, but they can be added in a backward-compatible manner.

@anonrig anonrig requested a review from cjihrig August 3, 2024 20:04
@louwers louwers marked this pull request as ready for review August 3, 2024 21:51
@louwers louwers force-pushed the session-extension branch 2 times, most recently from 8fc1cf0 to aaf0928 Compare August 3, 2024 22:17
@louwers louwers force-pushed the session-extension branch 2 times, most recently from 08b3562 to b544d2f Compare August 4, 2024 14:47
@RedYetiDev RedYetiDev added the sqlite Issues and PRs related to the SQLite subsystem. label Aug 4, 2024
@TheOneTheOnlyJJ
Copy link

This is excellent work that many projects will surely be based on. I am curious about the eventual wrapping of the entire Sessions C API (mixed & mashed into higher-level Node functions). Does this PR leave space for that? There are a few more Sessions API functions that would be of great use in Node.

return tmpl;
}

void Session::MemoryInfo(MemoryTracker* tracker) const {}
Copy link
Author

Choose a reason for hiding this comment

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

I am not sure how to implement these.

@louwers
Copy link
Author

louwers commented Aug 4, 2024

@TheOneTheOnlyJJ Thanks! I agree that exposing more functionality of the Session Extension in Node.js would be great. Here are some ideas:

  • Wrappers for sqlite3changeset_invert() (creates a changeset that undoes the changes in another changeset) and sqlite3changeset_concat() (combines changesets).
  • Allow closing a session. Right now a session is closed when it is garbage collected or when the corresponding database is closed. It is helpful to be able to manually close session in some cases.
  • Allowing the contents of a changeset to be read. You probably want to reuturn a Generator for this so you don't need to read the entire changeset it in memory at once.
  • Change the onConflict handler to take a function as well. You would be able to inspect the conflicting change and decide on the action (omit, replace, abort) for each conflicting change.
  • A wrapper for sqlite3session_diff().
  • Utilize streaming versions of the Session API (you would need another API for Node.js as well). These are helpful for reading or applying changesets that do not fit in memory.

Here is the full API for future reference: https://www.sqlite.org/session/funclist.html

Right now I first want to gather some feedback, but I'd be interested continue working on this, either in a follow-up PR or in this one. Since the SQLite integration is still under active development I think working incrementally is the preferred approach.

@TheOneTheOnlyJJ
Copy link

Good point, here's my view on them:

Yes, I was about to suggest these. There's also sqlite3session_isempty() and sqlite3session_memory_used() which are simpler (compared to the rest of the API) and could be wrapped more easily.

  • Allow closing a session. Right now a session is closed when it is garbage collected or when the corresponding database is closed. It is helpful to be able to manually close session in some cases.

This should be a high priority right now, as long-running Node processes (like server back-ends) that would use Sessions will have their memory slowly but surely filled up with no way of freeing it without closing the database connection. Applications could run for months or even longer without closing a database connection, so not being able to close Sessions and free their memory would deter developers from using them. The database should, of course, still keep a list of all active sessions and delete them when it is closed, just in case not all Sessions were manually closed. From my point of view, this function should be wrapped as soon as possible and be included in this PR. Not having it surely decreases the chances of getting this merged, and it would be a shame to lose out on this.

  • Allowing the contents of a changeset to be read. You probably want to reuturn a Generator for this so you don't need to read the entire changeset it in memory at once.
  • Change the onConflict handler to take a function as well. You would be able to inspect the conflicting change and decide on the action (omit, replace, abort) for each conflicting change.
  • A wrapper for sqlite3session_diff().

Very important functionalities, but again, they're not vital for achieving functional basic interaction with the extension. These 3 points could be bundled in a follow-up PR that handles the finer-grained data management side of the API. The changegroup could be exposed as an object with its API functions bound to it as methods.

  • Utilize streaming versions of the Session API (you would need another API for Node.js as well). These are helpful for reading or applying changesets that do not fit in memory.

I've noticed this in the documentation and it's definitely a part of the API that would be crucial in large apps with large databases. But again, for the current goal of incrementally proposing a stable API that exposes the API of the extension, this is not required right now. It would also require wrapping sqlite3session_config(), which allows configuring SQLITE_SESSION_CONFIG_STRMSIZE. This could be handled after the 3 points above get sorted out.

Also, before I wrap up...

I noticed that you proposed the createSession() method as a wrapper around both sqlite3session_create() and sqlite3session_attach(), combined. Looking at the API, I'm trying to figure out if there's any potential functionality or use case where exposing these functions separately in Node would make sense. The only potential use case I see is creating Sessions in one part of the application and then attaching them somewhere else. Maybe this would be useful if using a global Session that gets created at app start and attached later in the app life cycle? But, given that a Session cannot be unattached, this is likely not going to be useful in any way, so the separation of the functions is not needed. What's you input on this, do you see any potential use cases for the splitting of these 2 functions?

@louwers
Copy link
Author

louwers commented Aug 5, 2024

This should be a high priority right now

Agreed. I will still add this one as part of this PR.

What's you input on this, do you see any potential use cases for the splitting of these 2 functions?

I don't see any benefits. Furthermore, I like the philosophy of better-sqlite3:

better-sqlite3's public API must be as simple as possible. Rather than calling 3 functions in a specific order, it's simpler for users to call a single function. Rather than providing many similar functions for doing similar things (e.g., "convenience functions"), there should just be one function that is already convenient by design. Sane defaults should be applied when possible. A function's minimal call signature should be as small as possible, with progressively complex customization available when needed. Function names should only be as long as necessary to convey their purpose. For any new feature, it should be easy to showcase code examples that is are so simple that they are self-explanatory.

In this vein I also oped to default to aborting on conflict, instead of requiring an explicit conflict handler to be passed like in the API of SQLite.

@TheOneTheOnlyJJ
Copy link

Agreed. I will still add this one as part of this PR.

Yes, this would be ideal. For further updates other than this, I believe it's best to wait for the final form of the node:sqlite API itself.

I don't see any benefits. Furthermore, I like the philosophy of better-sqlite3:

I was checking if any additional functionality could be squeezed out by separating the functions. Given that there is none, your decision is a sensible one and I support it.

In this vein I also oped to default to aborting on conflict, instead of requiring an explicit conflict handler to be passed like in the API of SQLite.

This is also the best sane default option, as every developer should customize this behavior according to their use case.

I'm not experienced in the internals of Node, so my contribution here can only amount to discussing the API design and smaller implementation details.

I guess the next step now (after implementing the Session close wrapper) is to wait for a maintainer to review this?

@cjihrig cjihrig added the request-ci Add this label to start a Jenkins CI on a PR. label Aug 23, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 23, 2024
@nodejs-github-bot
Copy link
Collaborator

@louwers
Copy link
Author

louwers commented Aug 24, 2024

@cjihrig There was a merge conflict on CI for some reason. I merged main back in again, could you trigger another run? 🙂

@cjihrig cjihrig added the request-ci Add this label to start a Jenkins CI on a PR. label Aug 24, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 24, 2024
@nodejs-github-bot
Copy link
Collaborator

@louwers
Copy link
Author

louwers commented Aug 24, 2024

Not sure why CI is reporting a merge conflict:

17:44:52 HEAD is now at d5316e8426 Merge remote-tracking branch 'origin/main' into session-extension
17:44:52 + git status
17:44:52 HEAD detached at d5316e8426
17:44:52 Untracked files:
17:44:52   (use "git add <file>..." to include in what will be committed)
17:44:52 	env.properties
17:44:52 
17:44:52 nothing added to commit but untracked files present (use "git add" to track)
17:44:52 + git rev-parse HEAD
17:44:52 d5316e8426b89634a76a9926cfa887488b9d4523
17:44:52 + git rev-parse origin/main
17:44:52 e272cfbf05adc8d8687aad9ede5660ab99b360c6
17:44:52 + [ -n origin/main ]
17:44:52 + git rev-parse origin/main
17:44:52 + REBASE_ONTO=e272cfbf05adc8d8687aad9ede5660ab99b360c6
17:44:52 + git rebase --committer-date-is-author-date e272cfbf05adc8d8687aad9ede5660ab99b360c6
17:44:53 Rebasing (1/33)
17:44:53 Auto-merging src/node_sqlite.cc
17:44:53 Auto-merging src/node_sqlite.h
17:44:53 CONFLICT (content): Merge conflict in src/node_sqlite.h
17:44:53 Auto-merging test/parallel/test-sqlite.js
17:44:53 error: could not apply bc951f733b... src, test: support for SQLite Session Extension

I pinged the build team.

Edit: turns out merge commits are not supported.

Edit 2: I managed to fix it with the help of the build team 🎉

@louwers louwers force-pushed the session-extension branch 4 times, most recently from e704b1a to 62ca2a4 Compare August 24, 2024 19:59
@louwers louwers changed the title sqlite: support for SQLite Session Extension sqlite: add support for SQLite Session Extension Aug 24, 2024
louwers and others added 3 commits August 25, 2024 14:15
These objects are dictionaries, and a query can return columns with
special names like `__proto__` (which would be ignored without this
change).

Also construct the object by passing vectors of properties for better
performance and improve error handling by using `MaybeLocal`.

PR-URL: nodejs#54350
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@louwers
Copy link
Author

louwers commented Aug 25, 2024

@cjihrig CI was passing, but I just had to rebase again.

Would love to get some feedback on this PR. 🙏

I can understand if you want to hold off with this until the core functionality has matured.

@cjihrig
Copy link
Contributor

cjihrig commented Aug 25, 2024

Would love to get some feedback on this PR.

I'll give the code a look. I think we need a @nodejs/sqlite team that can be pinged on PRs like this. I don't want to become a blocker (I don't currently have a lot of free time for Node, and the time I do have leading up to v23 will be largely spent on the test runner). I think it would also be good for getting more opinions on what to include in the API.

@louwers
Copy link
Author

louwers commented Aug 25, 2024

Pinging some people that reviewed SQLite PRs in the past. @fhinkel @jasnell @benjamingr @anonrig @H4ad @aduh95 @atlowChemi @RedYetiDev @tniessen @targos

Feedback on the API, this functionality or the code would be very welcome.

Copy link
Contributor

@cjihrig cjihrig left a comment

Choose a reason for hiding this comment

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

Left some comments, but this is really good work, especially for a first time contribution.

deps/sqlite/sqlite.gyp Show resolved Hide resolved
test/parallel/test-sqlite.js Show resolved Hide resolved
Comment on lines +174 to +176
* `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 (default).
Copy link
Contributor

Choose a reason for hiding this comment

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

These constants need their own documentation. You can see something like https://nodejs.org/api/fs.html#fs-constants as an example.

doc/api/sqlite.md Outdated Show resolved Hide resolved
* `options` {Object} An optional object used to configure the session.
* `table` {string} When provided, only changes to this table are tracked by the created session.
By default, changes to all tables are tracked.
* `db` {string} Name of the database to track. Default: `'main'`.
Copy link
Contributor

Choose a reason for hiding this comment

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

It may be worth linking to something describing what 'main' is. I can imagine users that aren't overly familiar with SQLite not knowing what this is.

@@ -239,6 +256,175 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
}

void DatabaseSync::CreateSession(const FunctionCallbackInfo<Value>& args) {
std::string table;
std::string dbName = "main";
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use snake_case for local variables.

Environment* env = Environment::GetCurrent(args);
if (args.Length() > 0) {
if (!args[0]->IsObject()) {
node::THROW_ERR_INVALID_ARG_TYPE(
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a PR open to remove unnecessary uses of the node namespace in this file. Can you remove them in this PR as well.

Local<Object> options = args[0].As<Object>();

Local<String> table_key =
String::NewFromUtf8(env->isolate(), "table", NewStringType::kNormal)
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to use NewFromUtf8(). Search this file for FIXED_ONE_BYTE_STRING() for a slightly simpler way of doing this.

insert2.run(2, 'world');
const select1 = 'SELECT * FROM data1 ORDER BY key';
const select2 = 'SELECT * FROM data2 ORDER BY key';
t.assert.strictEqual(database2.applyChangeset(session.changeset()), true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add tests that validates the return types of session.changeset() and session.patchset().

Comment on lines +340 to +341
conflictCallback = nullptr;
filterCallback = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to use static functions here?

Copy link
Author

Choose a reason for hiding this comment

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

I could use an anonymous namespace instead, but SQLite needs function pointers.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it's fine since we can't have two calls running at the same time. We can revisit in the future if it becomes an issue.

Copy link
Member

@RedYetiDev RedYetiDev left a comment

Choose a reason for hiding this comment

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

I'm not an SQL expert, but these are my opinions on the docs

@@ -153,6 +153,74 @@ 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} An optional object used to configure the session.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* `options` {Object} An optional object used to configure the session.
* `options` {Object} The configuration options for the session.

Comment on lines +159 to +160
* `table` {string} When provided, only changes to this table are tracked by the created session.
By default, changes to all tables are tracked.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* `table` {string} When provided, only changes to this table are tracked by the created session.
By default, changes to all tables are tracked.
* `table` {string} A specific table to track changes for. By default, changes to all tables are tracked.

* `options` {Object} An optional object used to configure the session.
* `table` {string} When provided, only changes to this table are tracked by the created session.
By default, changes to all tables are tracked.
* `db` {string} Name of the database to track. Default: `'main'`.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* `db` {string} Name of the database to track. Default: `'main'`.
* `db` {string} Name of the database to track. **Default**: `'main'`.

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

* `changeset` {Uint8Array} A binary changeset or patchset.
* `options` {Object} An optional object to configure how changes are applied.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* `options` {Object} An optional object to configure how changes are applied.
* `options` {Object} The configuration options for how the changes will be applied.

Comment on lines +170 to +172
* `filter` {Function} Optional function. Skips changes when a truthy value is returned from it.
Takes the name of the table that a change targets as first argument. When this option is not
provided all changes are attempted.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* `filter` {Function} Optional function. Skips changes when a truthy value is returned from it.
Takes the name of the table that a change targets as first argument. When this option is not
provided all changes are attempted.
* `filter` {Function} Skip changes that, when targeted table name is supplied to this function, return a truthy value. By default, all changes are attempted.

Comment on lines +173 to +176
* `onConflict` {number} Determines how conflicts are handled. When provided, must be one of the values below:
* `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 (default).
Copy link
Member

Choose a reason for hiding this comment

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

I don't think the "When provided..." should be included. Just, "it must be one...".

Secondly, what is the default?

* `SQLITE_CHANGESET_ABORT`: abort on conflict and roll back databsase (default).
* 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?

Comment on lines +182 to +183
Example usage is demonstrated below.

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Example usage is demonstrated below.


const changeset = session.changeset();
database2.applyChangeset(changeset);
// Now database2 contains the same data as database1
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Now database2 contains the same data as database1
// Now that the changeset has been applied, database2 contains the same data as database1.

Secondly, maybe rename these variables to source and target? for clarity?

Comment on lines +108 to +114
void MemoryInfo(MemoryTracker* tracker) const override;
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static BaseObjectPtr<Session> Create(Environment* env,
BaseObjectWeakPtr<DatabaseSync> database,
sqlite3_session* session);

Copy link
Member

Choose a reason for hiding this comment

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

Just minor nit... let's keep the MemoryInfo related stuff together.

Suggested change
void MemoryInfo(MemoryTracker* tracker) const override;
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static BaseObjectPtr<Session> Create(Environment* env,
BaseObjectWeakPtr<DatabaseSync> database,
sqlite3_session* session);
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static BaseObjectPtr<Session> Create(Environment* env,
BaseObjectWeakPtr<DatabaseSync> database,
sqlite3_session* session);
void MemoryInfo(MemoryTracker* tracker) const override;

for (auto* session : sessions_) {
sqlite3session_delete(session);
}
sessions_.clear();
Copy link
Member

Choose a reason for hiding this comment

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

If the sqlite3_session* can be wrapped into an std::unique_ptr with a custom deleter then we don't need to iterate over them and delete before clearing. Would be a bit safer.

Comment on lines +277 to +278
Local<Value> table_value =
options->Get(env->context(), table_key).ToLocalChecked();
Copy link
Member

Choose a reason for hiding this comment

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

Nit: it is generally preferable to avoid use of ToLocalChecked() if possible. Something like

Suggested change
Local<Value> table_value =
options->Get(env->context(), table_key).ToLocalChecked();
Local<Value> table_value;
if (!options->Get(env->context(), table_key).ToLocal(&table_value)) {
return;
}

Comment on lines +364 to +366
String::NewFromUtf8(
env->isolate(), "onConflict", NewStringType::kNormal)
.ToLocalChecked();
Copy link
Member

Choose a reason for hiding this comment

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

Likely better to add this to the set of strings available via Environment... e.g. env->onconflict_string()

Comment on lines +367 to +369
if (options->HasOwnProperty(env->context(), conflictKey).FromJust()) {
Local<Value> conflictValue =
options->Get(env->context(), conflictKey).ToLocalChecked();
Copy link
Member

Choose a reason for hiding this comment

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

Nit: why not just unconditionally try to get the value and ignore it if the value returned is undefined? Would save the extra double property lookup here.


Local<String> filterKey =
String::NewFromUtf8(env->isolate(), "filter", NewStringType::kNormal)
.ToLocalChecked();
Copy link
Member

Choose a reason for hiding this comment

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

Nit: move to Environment... e.g. env->filter_string()

@TheOneTheOnlyJJ
Copy link

  • Utilize streaming versions of the Session API (you would need another API for Node.js as well). These are helpful for reading or applying changesets that do not fit in memory.

Just for reference in the future, this part of the API could get heavy inspiration from #54213, once it gets finalised, approved & merged.

Co-authored-by: Colin Ihrig <cjihrig@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. sqlite Issues and PRs related to the SQLite subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants