The first rule of writing fast programs: don't use dynamic memory
allocation!
Brings back some performance for the `SELECT 1` micro-benchmark,
although we're still not where we need to be.
This WIP driver uses the [purego](github.com/ebitengine/purego) library,
that supports cross platform `Dlopen`/`Dlsym` and not a whole lot else.
I really didn't want to use CGO, have very little experience with WASM
and I heard nothing but good things about this library. It's very easy
to use and stable especially when you consider the use case here of 3
functions.

NOTE: The WIP state that this PR is in right at this moment, is not able
to run these simple queries. This screengrab was taken from a couple
days ago when I wrote up a quick demo to load the library, call a simple
query and had it println! the result to make sure everything was working
properly.
I am opening this so kind of like the Java bindings, I can incrementally
work on this. I didn't want to submit a massive PR, try to keep them at
~1k lines max. The state of what's in this PR is highly subject and
likely to change.
I will update when they are at a working state where they can be tested
out and make sure they work across platforms.
Closes#776
I think it is mostly correct, not so sure how to handle `BLOB`. One
thing that caught my attention is that sqlite seems to have a
optimization for trivial cases, saving some bytecodes, for instance:

I'm looking that right now.
Closes#777
Take the logical OR of the values in register P1 and P2 and store the answer in register P3. If either P1 or P2 is nonzero (true) then the result is 1 (true) even if the other input is NULL. A NULL and false or two NULLs give a NULL output.
Take the logical AND of the values in registers P1 and P2 and write the result into register P3. If either P1 or P2 is 0 (false) then the result is 0 even if the other input is NULL. A NULL and true or two NULLs give a NULL output.
Change explanation is in the code comment for `Insn::Eq`:
```
/// Jump if either of the operands is null. Used for "jump when false" logic.
/// Eg. "SELECT * FROM users WHERE id = NULL" becomes:
/// <JUMP TO NEXT ROW IF id != NULL>
/// Without the jump_if_null flag it would not jump because the logical comparison "id != NULL" is never true.
/// This flag indicates that if either is null we should still jump.
jump_if_null: bool,
```
Closes#754
Excerpt from SQLite bytecode documentation for e.g. `Lt`:
> If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL bit is
clear then fall through if either operand is NULL.
I didn't want to start putting these flags into a bitmask so I just
added a separate boolean. Probably for sqlite `EXPLAIN` compatibility we
should, IF we want to be exactly compatible (which we aren't anyway atm)
Closes#755
Wire pragma wal_checkpoint to checkpoint infra
- add basic support for parsing and instruction emitting `pragma
wal_checkpoint;`
- checkpoint opcode for instruction
- checkpoint execution in `virtual machine`
- cli test
Part of #696.
Before
```
limbo> pragma wal_checkpoint;
× Parse error: Not a valid pragma name
```
After
```
Enter ".help" for usage hints.
limbo> pragma wal_checkpoint;
0|0|0
```
```
Closes#694
Relates to #127. This PR is still in draft and I have a few left things
to do (tests, improve implementation), just opening it so anyone can
track this work meanwhile.
Closes#664
This is a purely syntactic PR. It doesn't change behavior, just rewrites
some loops and removes unneeded parts, like lifetime annotations and
references. Mainly because the Clippy and IDE warnings get annoying.
Don't worry about the number of commits, I just separated based on type
of change.
Closes#732
Closes#728
Changes the API to one macro/annotation on the relevant function
```rust
#[scalar(name = "uuid4_str", alias = "gen_random_uuid")]
fn uuid4_str(_args: &[Value]) -> Value {
let uuid = uuid::Uuid::new_v4().to_string();
Value::from_text(uuid)
}
register_extension! {
scalars: { uuid4_str, uuid4 }
}
```
The only downside of this, is that for functions that use their
arguments, because this is not a trait, there is not really a way of
enforcing the function signature like there is with the other way.
Documentation has been added for this in the `scalar` macro, so
hopefully will not be an issue.
Also this PR cleans up the Aggregate API by changing the `args` and
`name` functions to constant associated types, as well as adds some
error handling and documentation.
```rust
impl AggFunc for Median {
type State = Vec<f64>;
const NAME: &'static str = "median";
const ARGS: i32 = 1;
fn step(state: &mut Self::State, args: &[Value]) {
if let Some(val) = args.first().and_then(Value::to_float) {
state.push(val);
}
}
//.. etc
```
Closes#735