sqlite / sqlite-wasm

SQLite Wasm conveniently wrapped as an ES Module.
580 stars 47 forks source link

Add `undefined` and `boolean` to `BindingSpec` type #83

Closed louwers closed 2 months ago

louwers commented 2 months ago

Even though these types will never be retrieved from SQLite, using them in a bind is perfectly valid.

undefined will get translated to NULL, boolean to 1 (true) or 0 (false).

sgbeal commented 2 months ago

Achtung: whether undefined counts as null depends on the API. In some of them (am on a mobile device so cannot readily verify which ones), undefined is used as a no-op to simplify certain binding cases. e.g. oo1.Stmt.bind() does not treat undefined like null.

https://sqlite.org/wasm/doc/trunk/api-oo1.md

louwers commented 2 months ago

Oops. Let me put some more thought into this.

undefined is used as a no-op to simplify certain binding cases

Well technically you can still pass if even if it is a no-op.

louwers commented 2 months ago
  // this works
  db.prepare("INSERT INTO test VALUES (?, ?, ?)").bind([2, undefined, 1]).step()

  // also works
  db.prepare("INSERT INTO test VALUES (?, ?, ?)").bind(1, 1).bind(2, undefined).bind(3, 3).step();

  // also seems to work
  db.prepare("INSERT INTO test VALUES (?, 2, 3)").bind(undefined).step()

From the docs:

undefined as a standalone value is a no-op: passing undefined as a value to this function will not actually bind anything and this function will skip confirmation that binding is even legal. (Those semantics simplify certain client-side uses.) Conversely, a value of undefined as an array or object property when binding an array/object (see below) is treated the same as null.

louwers commented 2 months ago

@sgbeal I modified the pull request to not allow undefined as a stand-alone bind.

sgbeal commented 2 months ago
  // this works
  db.prepare("INSERT INTO test VALUES (?, ?, ?)").bind([2, undefined, 1]).step()

  // also works
  db.prepare("INSERT INTO test VALUES (?, ?, ?)").bind(1, 1).bind(2, undefined).bind(3, 3).step();

  // also seems to work
  db.prepare("INSERT INTO test VALUES (?, 2, 3)").bind(undefined).step()

Note that the default value for unbound parameters is null, so in the above cases it will appear as if undefined translates to null, but it's not really doing so. In these cases, it's just coincidence.

louwers commented 2 months ago

@sgbeal Yes I figured!

That is why I excluded undefined in the type for binds that don't use an array or object.

sgbeal commented 2 months ago

Historical note: the undefined==noop oddity was based on client-side experience with an earlier sqlite binding done for a different language (this particular binding being my 7th-ish). Though i currently cannot for the life of me find a concrete example where that was useful for me, it did have a genuine utility at the time it was conceived and implemented. If i can manage to remember specifically what that utility is i'll update the docs to explain it.

tomayac commented 2 months ago

So, @sgbeal, is this good to merge then? Thanks for the contribution already, @louwers!

sgbeal commented 2 months ago

i'm a poor judge of typescript but it looks fine to my layman's eyes.

tomayac commented 2 months ago

Released: https://github.com/sqlite/sqlite-wasm/releases/tag/3.46.1-build2