From c3542196758fed71ac3a975995b58247fca736ca Mon Sep 17 00:00:00 2001
From: Henrik Ingo
Date: Mon, 20 Jan 2025 04:49:52 +0200
Subject: [PATCH 01/14] =?UTF-8?q?Add=20Nyrki=C3=B6=20change=20point=20dete?=
=?UTF-8?q?ction=20to=20'cargo=20bench'=20workflow?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This adds a separate push-only.yml workflow. For now pull request
API wasn't integrated yet, so shouldn't run on PRs.
disable cargo color
---
.github/workflows/push_only.yml | 42 +++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 .github/workflows/push_only.yml
diff --git a/.github/workflows/push_only.yml b/.github/workflows/push_only.yml
new file mode 100644
index 000000000..1c10bbb62
--- /dev/null
+++ b/.github/workflows/push_only.yml
@@ -0,0 +1,42 @@
+name: Benchmarks+Nyrkiö
+
+# Pull request support isn't integrated to the github-action-benchmark so run only post-merge
+on:
+ push:
+ branches: [ "main", "master", "notmain", "add-nyrkio" ]
+
+env:
+ CARGO_TERM_COLOR: never
+
+jobs:
+ bench:
+ runs-on: ubuntu-latest
+ environment: test
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ # cache: 'npm'
+ # - name: Install dependencies
+ # run: npm install && npm run build
+
+ - name: Bench
+ run: cargo bench 2>&1 | tee output.txt
+
+ - name: Analyze benchmark result with Nyrkiö
+ uses: nyrkio/github-action-benchmark@HEAD
+ with:
+ name: turso
+ tool: criterion
+ output-file-path: output.txt
+ fail-on-alert: true
+ # Nyrkiö configuration
+ nyrkio-enable: true
+ # Get yours from https://nyrkio.com/docs/getting-started
+ nyrkio-token: ${{ secrets.NYRKIO_JWT_TOKEN }}
+
+ # Old way...
+ # Explicitly set this to null. We don't want threshold based alerts today.
+ external-data-json-path: null
+ gh-repository: null
From 667a3f594e4faea8d20e1d006fb2d79680f8a863 Mon Sep 17 00:00:00 2001
From: pedrocarlo
Date: Wed, 22 Jan 2025 15:02:25 -0300
Subject: [PATCH 02/14] change foreign discord link
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5b4b7bd30..85fb2f1ea 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@
-
+
---
From fcd893284b6a61f55766d4cd779a5c38c1f34819 Mon Sep 17 00:00:00 2001
From: sonhmai <>
Date: Thu, 23 Jan 2025 09:51:46 +0700
Subject: [PATCH 03/14] chore: fix typos
---
core/storage/page_cache.rs | 2 +-
core/storage/wal.rs | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/core/storage/page_cache.rs b/core/storage/page_cache.rs
index e21433af1..27ea8244e 100644
--- a/core/storage/page_cache.rs
+++ b/core/storage/page_cache.rs
@@ -6,7 +6,7 @@ use super::pager::PageRef;
// In limbo, page cache is shared by default, meaning that multiple frames from WAL can reside in
// the cache, meaning, we need a way to differentiate between pages cached in different
-// connections. For this we include the max_frame that will read a connection from so that if two
+// connections. For this we include the max_frame that a connection will read from so that if two
// connections have different max_frames, they might or not have different frame read from WAL.
//
// WAL was introduced after Shared cache in SQLite, so this is why these two features don't work
diff --git a/core/storage/wal.rs b/core/storage/wal.rs
index 5d40d5a1d..0de3b7590 100644
--- a/core/storage/wal.rs
+++ b/core/storage/wal.rs
@@ -187,7 +187,7 @@ pub enum CheckpointStatus {
// min_frame and max_frame is the range of frames that can be safely transferred from WAL to db
// file.
// current_page is a helper to iterate through all the pages that might have a frame in the safe
-// range. This is inneficient for now.
+// range. This is inefficient for now.
struct OngoingCheckpoint {
page: PageRef,
state: CheckpointState,
@@ -228,13 +228,13 @@ pub struct WalFileShared {
max_frame: u64,
nbackfills: u64,
// Frame cache maps a Page to all the frames it has stored in WAL in ascending order.
- // This is do to easily find the frame it must checkpoint each connection if a checkpoint is
+ // This is to easily find the frame it must checkpoint each connection if a checkpoint is
// necessary.
// One difference between SQLite and limbo is that we will never support multi process, meaning
// we don't need WAL's index file. So we can do stuff like this without shared memory.
- // TODO: this will need refactoring because this is incredible memory inneficient.
+ // TODO: this will need refactoring because this is incredible memory inefficient.
frame_cache: HashMap>,
- // Another memory inneficient array made to just keep track of pages that are in frame_cache.
+ // Another memory inefficient array made to just keep track of pages that are in frame_cache.
pages_in_frames: Vec,
last_checksum: (u32, u32), // Check of last frame in WAL, this is a cumulative checksum over all frames in the WAL
file: Rc,
From 545990f80633e77f9f9451b540b92a2d7455a206 Mon Sep 17 00:00:00 2001
From: PThorpe92
Date: Wed, 22 Jan 2025 10:17:59 -0500
Subject: [PATCH 04/14] Support returning column names from prepared statement
---
core/lib.rs | 9 +++++++-
core/translate/emitter.rs | 12 +++++++++--
core/vdbe/builder.rs | 3 +++
core/vdbe/mod.rs | 1 +
tests/integration/common.rs | 42 +++++++++++++++++++++++++++++++++++++
5 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/core/lib.rs b/core/lib.rs
index f093762fb..d75510f7c 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -295,7 +295,6 @@ impl Connection {
pub(crate) fn run_cmd(self: &Rc, cmd: Cmd) -> Result> {
let db = self.db.clone();
let syms: &SymbolTable = &db.syms.borrow();
-
match cmd {
Cmd::Stmt(stmt) => {
let program = Rc::new(translate::translate(
@@ -466,6 +465,10 @@ impl Statement {
Ok(Rows::new(stmt))
}
+ pub fn columns(&self) -> &[String] {
+ &self.program.columns
+ }
+
pub fn parameters(&self) -> ¶meters::Parameters {
&self.program.parameters
}
@@ -513,6 +516,10 @@ impl Rows {
pub fn next_row(&mut self) -> Result> {
self.stmt.step()
}
+
+ pub fn columns(&self) -> &[String] {
+ self.stmt.columns()
+ }
}
pub(crate) struct SymbolTable {
diff --git a/core/translate/emitter.rs b/core/translate/emitter.rs
index 13daa85ed..939a287f0 100644
--- a/core/translate/emitter.rs
+++ b/core/translate/emitter.rs
@@ -175,7 +175,11 @@ fn emit_program_for_select(
// Finalize program
epilogue(program, init_label, start_offset)?;
-
+ program.columns = plan
+ .result_columns
+ .iter()
+ .map(|rc| rc.name.clone())
+ .collect::>();
Ok(())
}
@@ -286,7 +290,11 @@ fn emit_program_for_delete(
// Finalize program
epilogue(program, init_label, start_offset)?;
-
+ program.columns = plan
+ .result_columns
+ .iter()
+ .map(|rc| rc.name.clone())
+ .collect::>();
Ok(())
}
diff --git a/core/vdbe/builder.rs b/core/vdbe/builder.rs
index 08b35d9e3..0af4d1182 100644
--- a/core/vdbe/builder.rs
+++ b/core/vdbe/builder.rs
@@ -30,6 +30,7 @@ pub struct ProgramBuilder {
// map of instruction index to manual comment (used in EXPLAIN)
comments: HashMap,
pub parameters: Parameters,
+ pub columns: Vec,
}
#[derive(Debug, Clone)]
@@ -60,6 +61,7 @@ impl ProgramBuilder {
seekrowid_emitted_bitmask: 0,
comments: HashMap::new(),
parameters: Parameters::new(),
+ columns: Vec::new(),
}
}
@@ -352,6 +354,7 @@ impl ProgramBuilder {
parameters: self.parameters,
n_change: Cell::new(0),
change_cnt_on,
+ columns: self.columns,
}
}
}
diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs
index 98b328b71..27b39fcab 100644
--- a/core/vdbe/mod.rs
+++ b/core/vdbe/mod.rs
@@ -284,6 +284,7 @@ pub struct Program {
pub auto_commit: bool,
pub n_change: Cell,
pub change_cnt_on: bool,
+ pub columns: Vec,
}
impl Program {
diff --git a/tests/integration/common.rs b/tests/integration/common.rs
index 86f4b7b3f..07c840b23 100644
--- a/tests/integration/common.rs
+++ b/tests/integration/common.rs
@@ -67,3 +67,45 @@ pub(crate) fn compare_string(a: &String, b: &String) {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::TempDatabase;
+
+ #[test]
+ fn test_statement_columns() -> anyhow::Result<()> {
+ let _ = env_logger::try_init();
+ let tmp_db =
+ TempDatabase::new("create table test (foo integer, bar integer, baz integer);");
+ let conn = tmp_db.connect_limbo();
+
+ let stmt = conn.prepare("select * from test;")?;
+
+ let columns = stmt.columns();
+ assert_eq!(columns.len(), 3);
+ assert_eq!(&columns[0], "foo");
+ assert_eq!(&columns[1], "bar");
+ assert_eq!(&columns[2], "baz");
+
+ let stmt = conn.prepare("select foo, bar from test;")?;
+
+ let columns = stmt.columns();
+ assert_eq!(columns.len(), 2);
+ assert_eq!(&columns[0], "foo");
+ assert_eq!(&columns[1], "bar");
+
+ let stmt = conn.prepare("delete from test;")?;
+ let columns = stmt.columns();
+ assert_eq!(columns.len(), 0);
+
+ let stmt = conn.prepare("insert into test (foo, bar, baz) values (1, 2, 3);")?;
+ let columns = stmt.columns();
+ assert_eq!(columns.len(), 0);
+
+ let stmt = conn.prepare("delete from test where foo = 1")?;
+ let columns = stmt.columns();
+ assert_eq!(columns.len(), 0);
+
+ Ok(())
+ }
+}
From f7a8d1b4289d6d6afbf8200d40070dce5e02c720 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?=
Date: Tue, 21 Jan 2025 19:00:25 +0900
Subject: [PATCH 05/14] Change
`Java_org_github_tursodatabase_core_LimboStatement_step` to run in loop to
handle `StepResult::IO`
---
bindings/java/rs_src/limbo_statement.rs | 34 +++++++++++--------------
1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/bindings/java/rs_src/limbo_statement.rs b/bindings/java/rs_src/limbo_statement.rs
index cdd8a5c75..54b1fbb27 100644
--- a/bindings/java/rs_src/limbo_statement.rs
+++ b/bindings/java/rs_src/limbo_statement.rs
@@ -55,25 +55,21 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboStatement_step<'l
}
};
- match stmt.stmt.step() {
- Ok(StepResult::Row(row)) => match row_to_obj_array(&mut env, &row) {
- Ok(row) => to_limbo_step_result(&mut env, STEP_RESULT_ID_ROW, Some(row)),
- Err(e) => {
- set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
- to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None)
- }
- },
- Ok(StepResult::IO) => match env.new_object_array(0, "java/lang/Object", JObject::null()) {
- Ok(row) => to_limbo_step_result(&mut env, STEP_RESULT_ID_IO, Some(row.into())),
- Err(e) => {
- set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
- to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None)
- }
- },
- Ok(StepResult::Done) => to_limbo_step_result(&mut env, STEP_RESULT_ID_DONE, None),
- Ok(StepResult::Interrupt) => to_limbo_step_result(&mut env, STEP_RESULT_ID_INTERRUPT, None),
- Ok(StepResult::Busy) => to_limbo_step_result(&mut env, STEP_RESULT_ID_BUSY, None),
- _ => to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None),
+ loop {
+ match stmt.stmt.step() {
+ Ok(StepResult::Row(row)) => match row_to_obj_array(&mut env, &row) {
+ Ok(row) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_ROW, Some(row)),
+ Err(e) => {
+ set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
+ return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None)
+ }
+ },
+ Ok(StepResult::IO) => {},
+ Ok(StepResult::Done) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_DONE, None),
+ Ok(StepResult::Interrupt) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_INTERRUPT, None),
+ Ok(StepResult::Busy) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_BUSY, None),
+ _ => return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None),
+ }
}
}
From 82e9fe02190c2dfbda7208a6eaa496640b9863e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?=
Date: Tue, 21 Jan 2025 19:00:38 +0900
Subject: [PATCH 06/14] Handle invalid step results
---
.../org/github/tursodatabase/core/LimboResultSet.java | 5 +++++
.../org/github/tursodatabase/core/LimboStepResult.java | 9 +++++++++
.../github/tursodatabase/jdbc4/JDBC4ResultSetTest.java | 3 ---
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java
index 19d730727..882d2b78b 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java
@@ -64,6 +64,11 @@ public class LimboResultSet {
row++;
}
+ if (lastStepResult.isInInvalidState()) {
+ open = false;
+ throw new SQLException("step() returned invalid result: " + lastStepResult);
+ }
+
pastLastRow = lastStepResult.isDone();
if (pastLastRow) {
open = false;
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java
index 7870cbeab..27a8dfc05 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java
@@ -13,6 +13,7 @@ public class LimboStepResult {
private static final int STEP_RESULT_ID_IO = 20;
private static final int STEP_RESULT_ID_DONE = 30;
private static final int STEP_RESULT_ID_INTERRUPT = 40;
+ // Indicates that the database file could not be written because of concurrent activity by some other connection
private static final int STEP_RESULT_ID_BUSY = 50;
private static final int STEP_RESULT_ID_ERROR = 60;
@@ -41,6 +42,14 @@ public class LimboStepResult {
return stepResultId == STEP_RESULT_ID_DONE;
}
+ public boolean isInInvalidState() {
+ // current implementation doesn't allow STEP_RESULT_ID_IO to be returned
+ return stepResultId == STEP_RESULT_ID_IO ||
+ stepResultId == STEP_RESULT_ID_INTERRUPT ||
+ stepResultId == STEP_RESULT_ID_BUSY ||
+ stepResultId == STEP_RESULT_ID_ERROR;
+ }
+
@Override
public String toString() {
return "LimboStepResult{" +
diff --git a/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ResultSetTest.java b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ResultSetTest.java
index e717232a8..88a499b9d 100644
--- a/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ResultSetTest.java
+++ b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ResultSetTest.java
@@ -9,7 +9,6 @@ import java.util.Properties;
import org.github.tursodatabase.TestUtils;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class JDBC4ResultSetTest {
@@ -27,7 +26,6 @@ class JDBC4ResultSetTest {
}
@Test
- @Disabled("https://github.com/tursodatabase/limbo/pull/743#issuecomment-2600746904")
void invoking_next_before_the_last_row_should_return_true() throws Exception {
stmt.executeUpdate("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
stmt.executeUpdate("INSERT INTO users VALUES (1, 'sinwoo');");
@@ -41,7 +39,6 @@ class JDBC4ResultSetTest {
}
@Test
- @Disabled("https://github.com/tursodatabase/limbo/pull/743#issuecomment-2600746904")
void invoking_next_after_the_last_row_should_return_false() throws Exception {
stmt.executeUpdate("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
stmt.executeUpdate("INSERT INTO users VALUES (1, 'sinwoo');");
From d05ffce613b94995bf678e86ccf95c151346269a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?=
Date: Tue, 21 Jan 2025 19:08:35 +0900
Subject: [PATCH 07/14] Apply fmt
---
bindings/java/rs_src/limbo_statement.rs | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/bindings/java/rs_src/limbo_statement.rs b/bindings/java/rs_src/limbo_statement.rs
index 54b1fbb27..92080f314 100644
--- a/bindings/java/rs_src/limbo_statement.rs
+++ b/bindings/java/rs_src/limbo_statement.rs
@@ -7,6 +7,7 @@ use jni::JNIEnv;
use limbo_core::{Statement, StepResult};
pub const STEP_RESULT_ID_ROW: i32 = 10;
+#[allow(dead_code)]
pub const STEP_RESULT_ID_IO: i32 = 20;
pub const STEP_RESULT_ID_DONE: i32 = 30;
pub const STEP_RESULT_ID_INTERRUPT: i32 = 40;
@@ -61,13 +62,19 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboStatement_step<'l
Ok(row) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_ROW, Some(row)),
Err(e) => {
set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
- return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None)
+ return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None);
}
},
- Ok(StepResult::IO) => {},
- Ok(StepResult::Done) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_DONE, None),
- Ok(StepResult::Interrupt) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_INTERRUPT, None),
- Ok(StepResult::Busy) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_BUSY, None),
+ Ok(StepResult::IO) => {}
+ Ok(StepResult::Done) => {
+ return to_limbo_step_result(&mut env, STEP_RESULT_ID_DONE, None)
+ }
+ Ok(StepResult::Interrupt) => {
+ return to_limbo_step_result(&mut env, STEP_RESULT_ID_INTERRUPT, None)
+ }
+ Ok(StepResult::Busy) => {
+ return to_limbo_step_result(&mut env, STEP_RESULT_ID_BUSY, None)
+ }
_ => return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None),
}
}
From 36dff168b3f2641a230bc46055d027bb6b071a78 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?=
Date: Wed, 22 Jan 2025 08:50:47 +0900
Subject: [PATCH 08/14] Execute io.run_once when receiving StepResult::IO
---
bindings/java/rs_src/limbo_connection.rs | 2 +-
bindings/java/rs_src/limbo_statement.rs | 10 +++++++---
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/bindings/java/rs_src/limbo_connection.rs b/bindings/java/rs_src/limbo_connection.rs
index 1399d8b42..5441acee5 100644
--- a/bindings/java/rs_src/limbo_connection.rs
+++ b/bindings/java/rs_src/limbo_connection.rs
@@ -69,7 +69,7 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboConnection_prepar
};
match connection.conn.prepare(sql) {
- Ok(stmt) => LimboStatement::new(stmt).to_ptr(),
+ Ok(stmt) => LimboStatement::new(stmt, connection.clone()).to_ptr(),
Err(e) => {
set_err_msg_and_throw_exception(
&mut env,
diff --git a/bindings/java/rs_src/limbo_statement.rs b/bindings/java/rs_src/limbo_statement.rs
index 92080f314..29ecb7cd1 100644
--- a/bindings/java/rs_src/limbo_statement.rs
+++ b/bindings/java/rs_src/limbo_statement.rs
@@ -1,5 +1,6 @@
use crate::errors::Result;
use crate::errors::{LimboError, LIMBO_ETC};
+use crate::limbo_connection::LimboConnection;
use crate::utils::set_err_msg_and_throw_exception;
use jni::objects::{JObject, JValue};
use jni::sys::jlong;
@@ -16,11 +17,12 @@ pub const STEP_RESULT_ID_ERROR: i32 = 60;
pub struct LimboStatement {
pub(crate) stmt: Statement,
+ pub(crate) connection: LimboConnection,
}
impl LimboStatement {
- pub fn new(stmt: Statement) -> Self {
- LimboStatement { stmt }
+ pub fn new(stmt: Statement, connection: LimboConnection) -> Self {
+ LimboStatement { stmt, connection }
}
pub fn to_ptr(self) -> jlong {
@@ -65,7 +67,9 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboStatement_step<'l
return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None);
}
},
- Ok(StepResult::IO) => {}
+ Ok(StepResult::IO) => {
+ stmt.connection.io.run_once().unwrap();
+ }
Ok(StepResult::Done) => {
return to_limbo_step_result(&mut env, STEP_RESULT_ID_DONE, None)
}
From 0481e692172be53f3f549df72851ff71c7f635d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?=
Date: Fri, 24 Jan 2025 14:07:52 +0900
Subject: [PATCH 09/14] Handle Err case from `connection.io`
---
bindings/java/rs_src/limbo_statement.rs | 35 ++++++++++++++-----------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/bindings/java/rs_src/limbo_statement.rs b/bindings/java/rs_src/limbo_statement.rs
index 29ecb7cd1..4277b599e 100644
--- a/bindings/java/rs_src/limbo_statement.rs
+++ b/bindings/java/rs_src/limbo_statement.rs
@@ -59,27 +59,32 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboStatement_step<'l
};
loop {
- match stmt.stmt.step() {
- Ok(StepResult::Row(row)) => match row_to_obj_array(&mut env, &row) {
- Ok(row) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_ROW, Some(row)),
- Err(e) => {
+ match stmt
+ .stmt
+ .step()
+ .map_err(|_e| to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None))
+ .unwrap()
+ {
+ StepResult::Row(row) => {
+ return match row_to_obj_array(&mut env, &row) {
+ Ok(row) => to_limbo_step_result(&mut env, STEP_RESULT_ID_ROW, Some(row)),
+ Err(e) => {
+ set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
+ to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None)
+ }
+ }
+ }
+ StepResult::IO => {
+ if let Err(e) = stmt.connection.io.run_once() {
set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None);
}
- },
- Ok(StepResult::IO) => {
- stmt.connection.io.run_once().unwrap();
}
- Ok(StepResult::Done) => {
- return to_limbo_step_result(&mut env, STEP_RESULT_ID_DONE, None)
- }
- Ok(StepResult::Interrupt) => {
+ StepResult::Done => return to_limbo_step_result(&mut env, STEP_RESULT_ID_DONE, None),
+ StepResult::Interrupt => {
return to_limbo_step_result(&mut env, STEP_RESULT_ID_INTERRUPT, None)
}
- Ok(StepResult::Busy) => {
- return to_limbo_step_result(&mut env, STEP_RESULT_ID_BUSY, None)
- }
- _ => return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None),
+ StepResult::Busy => return to_limbo_step_result(&mut env, STEP_RESULT_ID_BUSY, None),
}
}
}
From 53586b9d009aba63fac6d08535dd811dd8cedf40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?=
Date: Fri, 24 Jan 2025 15:06:10 +0900
Subject: [PATCH 10/14] Break the loop when step() returns Err
---
bindings/java/rs_src/limbo_statement.rs | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/bindings/java/rs_src/limbo_statement.rs b/bindings/java/rs_src/limbo_statement.rs
index 4277b599e..7de4b2c19 100644
--- a/bindings/java/rs_src/limbo_statement.rs
+++ b/bindings/java/rs_src/limbo_statement.rs
@@ -53,18 +53,17 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboStatement_step<'l
Ok(stmt) => stmt,
Err(e) => {
set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
-
- return JObject::null();
+ return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None);
}
};
loop {
- match stmt
- .stmt
- .step()
- .map_err(|_e| to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None))
- .unwrap()
- {
+ let step_result = match stmt.stmt.step() {
+ Ok(result) => result,
+ Err(_) => return to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None),
+ };
+
+ match step_result {
StepResult::Row(row) => {
return match row_to_obj_array(&mut env, &row) {
Ok(row) => to_limbo_step_result(&mut env, STEP_RESULT_ID_ROW, Some(row)),
From f10b41c5b57585c49dbd019c7181bd207c2fc4e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?=
Date: Fri, 24 Jan 2025 15:43:44 +0900
Subject: [PATCH 11/14] Pass io to statement from db
---
bindings/java/rs_src/limbo_connection.rs | 7 ++-
bindings/java/rs_src/limbo_db.rs | 45 +++----------------
.../github/tursodatabase/core/LimboDB.java | 28 +++++-------
3 files changed, 22 insertions(+), 58 deletions(-)
diff --git a/bindings/java/rs_src/limbo_connection.rs b/bindings/java/rs_src/limbo_connection.rs
index 5441acee5..dd54e9087 100644
--- a/bindings/java/rs_src/limbo_connection.rs
+++ b/bindings/java/rs_src/limbo_connection.rs
@@ -9,16 +9,19 @@ use jni::sys::jlong;
use jni::JNIEnv;
use limbo_core::Connection;
use std::rc::Rc;
+use std::sync::Arc;
#[derive(Clone)]
#[allow(dead_code)]
pub struct LimboConnection {
+ // Because java's LimboConnection is 1:1 mapped to limbo connection, we can use Rc
pub(crate) conn: Rc,
- pub(crate) io: Rc,
+ // Because io is shared across multiple `LimboConnection`s, wrap it with Arc
+ pub(crate) io: Arc,
}
impl LimboConnection {
- pub fn new(conn: Rc, io: Rc) -> Self {
+ pub fn new(conn: Rc, io: Arc) -> Self {
LimboConnection { conn, io }
}
diff --git a/bindings/java/rs_src/limbo_db.rs b/bindings/java/rs_src/limbo_db.rs
index 09d8afa75..16cb3d66b 100644
--- a/bindings/java/rs_src/limbo_db.rs
+++ b/bindings/java/rs_src/limbo_db.rs
@@ -5,16 +5,16 @@ use jni::objects::{JByteArray, JObject};
use jni::sys::{jint, jlong};
use jni::JNIEnv;
use limbo_core::Database;
-use std::rc::Rc;
use std::sync::Arc;
struct LimboDB {
db: Arc,
+ io: Arc,
}
impl LimboDB {
- pub fn new(db: Arc) -> Self {
- LimboDB { db }
+ pub fn new(db: Arc, io: Arc) -> Self {
+ LimboDB { db, io }
}
pub fn to_ptr(self) -> jlong {
@@ -76,14 +76,13 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB_openUtf8<'loca
}
};
- LimboDB::new(db).to_ptr()
+ LimboDB::new(db, io).to_ptr()
}
#[no_mangle]
pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB_connect0<'local>(
mut env: JNIEnv<'local>,
obj: JObject<'local>,
- file_path_byte_arr: JByteArray<'local>,
db_pointer: jlong,
) -> jlong {
let db = match to_limbo_db(db_pointer) {
@@ -94,41 +93,7 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB_connect0<'loca
}
};
- let path = match env
- .convert_byte_array(file_path_byte_arr)
- .map_err(|e| e.to_string())
- {
- Ok(bytes) => match String::from_utf8(bytes) {
- Ok(s) => s,
- Err(e) => {
- set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
- return 0;
- }
- },
- Err(e) => {
- set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
- return 0;
- }
- };
-
- let io: Rc = match path.as_str() {
- ":memory:" => match limbo_core::MemoryIO::new() {
- Ok(io) => Rc::new(io),
- Err(e) => {
- set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
- return 0;
- }
- },
- _ => match limbo_core::PlatformIO::new() {
- Ok(io) => Rc::new(io),
- Err(e) => {
- set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string());
- return 0;
- }
- },
- };
- let conn = LimboConnection::new(db.db.connect(), io);
-
+ let conn = LimboConnection::new(db.db.connect(), db.io.clone());
conn.to_ptr()
}
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java
index 89d13b8cf..ad6ee68a0 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java
@@ -1,5 +1,9 @@
package org.github.tursodatabase.core;
+import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArray;
+
+import java.sql.SQLException;
+import java.util.concurrent.locks.ReentrantLock;
import org.github.tursodatabase.LimboErrorCode;
import org.github.tursodatabase.annotations.NativeInvocation;
@@ -8,12 +12,6 @@ import org.github.tursodatabase.utils.LimboExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.util.concurrent.locks.ReentrantLock;
-
-import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArray;
-
/**
* This class provides a thin JNI layer over the SQLite3 C API.
*/
@@ -39,7 +37,7 @@ public final class LimboDB extends AbstractDB {
* Loads the SQLite interface backend.
*/
public static void load() {
- if (isLoaded) return;
+ if (isLoaded) {return;}
try {
System.loadLibrary("_limbo_java");
@@ -49,7 +47,7 @@ public final class LimboDB extends AbstractDB {
}
/**
- * @param url e.g. "jdbc:sqlite:fileName
+ * @param url e.g. "jdbc:sqlite:fileName
* @param filePath e.g. path to file
*/
public static LimboDB create(String url, String filePath) throws SQLException {
@@ -86,7 +84,9 @@ public final class LimboDB extends AbstractDB {
byte[] filePathBytes = stringToUtf8ByteArray(filePath);
if (filePathBytes == null) {
- throw LimboExceptionUtils.buildLimboException(LimboErrorCode.LIMBO_ETC.code, "File path cannot be converted to byteArray. File name: " + filePath);
+ throw LimboExceptionUtils.buildLimboException(
+ LimboErrorCode.LIMBO_ETC.code,
+ "File path cannot be converted to byteArray. File name: " + filePath);
}
dbPointer = openUtf8(filePathBytes, openFlags);
@@ -95,14 +95,10 @@ public final class LimboDB extends AbstractDB {
@Override
public long connect() throws SQLException {
- byte[] filePathBytes = stringToUtf8ByteArray(filePath);
- if (filePathBytes == null) {
- throw LimboExceptionUtils.buildLimboException(LimboErrorCode.LIMBO_ETC.code, "File path cannot be converted to byteArray. File name: " + filePath);
- }
- return connect0(filePathBytes, dbPointer);
+ return connect0(dbPointer);
}
- private native long connect0(byte[] path, long databasePtr) throws SQLException;
+ private native long connect0(long databasePtr) throws SQLException;
@VisibleForTesting
native void throwJavaException(int errorCode) throws SQLException;
@@ -110,7 +106,7 @@ public final class LimboDB extends AbstractDB {
/**
* Throws formatted SQLException with error code and message.
*
- * @param errorCode Error code.
+ * @param errorCode Error code.
* @param errorMessageBytes Error message.
*/
@NativeInvocation(invokedFrom = "limbo_db.rs")
From aff454b5f6318b874b0f5366b6b2e0387d50828b Mon Sep 17 00:00:00 2001
From: Diego Reis
Date: Sat, 25 Jan 2025 02:12:50 -0300
Subject: [PATCH 12/14] Implement And bytecode
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.
---
COMPAT.md | 2 +-
core/translate/expr.rs | 7 +++++
core/vdbe/explain.rs | 9 +++++++
core/vdbe/insn.rs | 58 ++++++++++++++++++++++++++++++++++++++++++
core/vdbe/mod.rs | 9 +++++--
5 files changed, 82 insertions(+), 3 deletions(-)
diff --git a/COMPAT.md b/COMPAT.md
index 17ce24568..b476bc3f0 100644
--- a/COMPAT.md
+++ b/COMPAT.md
@@ -400,7 +400,7 @@ Modifiers:
| AggFinal | Yes |
| AggStep | Yes |
| AggStep | Yes |
-| And | No |
+| And | Yes |
| AutoCommit | No |
| BitAnd | Yes |
| BitNot | Yes |
diff --git a/core/translate/expr.rs b/core/translate/expr.rs
index 476b78d77..d522ddec2 100644
--- a/core/translate/expr.rs
+++ b/core/translate/expr.rs
@@ -605,6 +605,13 @@ pub fn translate_expr(
dest: target_register,
});
}
+ ast::Operator::And => {
+ program.emit_insn(Insn::And {
+ lhs: e1_reg,
+ rhs: e2_reg,
+ dest: target_register,
+ });
+ }
ast::Operator::BitwiseAnd => {
program.emit_insn(Insn::BitAnd {
lhs: e1_reg,
diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs
index 80f419ed7..40d6f25e9 100644
--- a/core/vdbe/explain.rs
+++ b/core/vdbe/explain.rs
@@ -1120,6 +1120,15 @@ pub fn insn_to_str(
0,
format!("r[{}]=r[{}] + r[{}]", dest, lhs, rhs),
),
+ Insn::And { lhs, rhs, dest } => (
+ "And",
+ *rhs as i32,
+ *lhs as i32,
+ *dest as i32,
+ OwnedValue::build_text(Rc::new("".to_string())),
+ 0,
+ format!("r[{}]=(r[{}] && r[{}])", dest, lhs, rhs),
+ ),
};
format!(
"{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}",
diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs
index f3f5e36a7..4e393116c 100644
--- a/core/vdbe/insn.rs
+++ b/core/vdbe/insn.rs
@@ -551,6 +551,12 @@ pub enum Insn {
rhs: usize,
dest: usize,
},
+ /// Take the logical AND of the values in registers P1 and P2 and write the result into register P3.
+ And {
+ lhs: usize,
+ rhs: usize,
+ dest: usize,
+ },
}
fn cast_text_to_numerical(value: &str) -> OwnedValue {
@@ -955,3 +961,55 @@ pub fn exec_concat(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue {
(OwnedValue::Record(_), _) | (_, OwnedValue::Record(_)) => unreachable!(),
}
}
+
+pub fn exec_and(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
+ if let OwnedValue::Agg(agg) = lhs {
+ lhs = agg.final_value();
+ }
+ if let OwnedValue::Agg(agg) = rhs {
+ rhs = agg.final_value();
+ }
+
+ match (lhs, rhs) {
+ (_, OwnedValue::Integer(0))
+ | (OwnedValue::Integer(0), _)
+ | (_, OwnedValue::Float(0.0))
+ | (OwnedValue::Float(0.0), _) => OwnedValue::Integer(0),
+ (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null,
+ _ => OwnedValue::Integer(1),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::types::OwnedValue;
+
+ use super::exec_and;
+
+ #[test]
+ fn test_exec_and() {
+ let inputs = vec![
+ (OwnedValue::Integer(0), OwnedValue::Null),
+ (OwnedValue::Null, OwnedValue::Integer(1)),
+ (OwnedValue::Null, OwnedValue::Null),
+ (OwnedValue::Float(0.0), OwnedValue::Null),
+ (OwnedValue::Integer(1), OwnedValue::Float(2.2)),
+ ];
+ let outpus = vec![
+ OwnedValue::Integer(0),
+ OwnedValue::Null,
+ OwnedValue::Null,
+ OwnedValue::Integer(0),
+ OwnedValue::Integer(1),
+ ];
+
+ assert_eq!(
+ inputs.len(),
+ outpus.len(),
+ "Inputs and Outputs should have same size"
+ );
+ for (i, (lhs, rhs)) in inputs.iter().enumerate() {
+ assert_eq!(exec_and(lhs, rhs), outpus[i]);
+ }
+ }
+}
diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs
index 98b328b71..24eb7657c 100644
--- a/core/vdbe/mod.rs
+++ b/core/vdbe/mod.rs
@@ -46,8 +46,8 @@ use crate::{
use crate::{resolve_ext_path, Connection, Result, Rows, TransactionState, DATABASE_VERSION};
use datetime::{exec_date, exec_datetime_full, exec_julianday, exec_time, exec_unixepoch};
use insn::{
- exec_add, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_concat, exec_divide,
- exec_multiply, exec_remainder, exec_shift_left, exec_shift_right, exec_subtract,
+ exec_add, exec_and, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_concat,
+ exec_divide, exec_multiply, exec_remainder, exec_shift_left, exec_shift_right, exec_subtract,
};
use likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape};
use rand::distributions::{Distribution, Uniform};
@@ -2346,6 +2346,11 @@ impl Program {
exec_concat(&state.registers[*lhs], &state.registers[*rhs]);
state.pc += 1;
}
+ Insn::And { lhs, rhs, dest } => {
+ state.registers[*dest] =
+ exec_and(&state.registers[*lhs], &state.registers[*rhs]);
+ state.pc += 1;
+ }
}
}
}
From e7d95399e347dbe115cd43f346fcd6f2c02c200e Mon Sep 17 00:00:00 2001
From: Diego Reis
Date: Sat, 25 Jan 2025 02:54:14 -0300
Subject: [PATCH 13/14] Add Or bytecode
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.
---
COMPAT.md | 2 +-
core/translate/expr.rs | 7 ++++
core/vdbe/explain.rs | 9 +++++
core/vdbe/insn.rs | 92 +++++++++++++++++++++++++++++++++++++++++-
core/vdbe/mod.rs | 8 +++-
5 files changed, 115 insertions(+), 3 deletions(-)
diff --git a/COMPAT.md b/COMPAT.md
index b476bc3f0..f7bbb963a 100644
--- a/COMPAT.md
+++ b/COMPAT.md
@@ -493,7 +493,7 @@ Modifiers:
| OpenWrite | No |
| OpenWriteAsync | Yes |
| OpenWriteAwait | Yes |
-| Or | No |
+| Or | Yes |
| Pagecount | No |
| Param | No |
| ParseSchema | No |
diff --git a/core/translate/expr.rs b/core/translate/expr.rs
index d522ddec2..abec8b9a8 100644
--- a/core/translate/expr.rs
+++ b/core/translate/expr.rs
@@ -612,6 +612,13 @@ pub fn translate_expr(
dest: target_register,
});
}
+ ast::Operator::Or => {
+ program.emit_insn(Insn::Or {
+ lhs: e1_reg,
+ rhs: e2_reg,
+ dest: target_register,
+ });
+ }
ast::Operator::BitwiseAnd => {
program.emit_insn(Insn::BitAnd {
lhs: e1_reg,
diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs
index 40d6f25e9..89967608a 100644
--- a/core/vdbe/explain.rs
+++ b/core/vdbe/explain.rs
@@ -1129,6 +1129,15 @@ pub fn insn_to_str(
0,
format!("r[{}]=(r[{}] && r[{}])", dest, lhs, rhs),
),
+ Insn::Or { lhs, rhs, dest } => (
+ "Or",
+ *rhs as i32,
+ *lhs as i32,
+ *dest as i32,
+ OwnedValue::build_text(Rc::new("".to_string())),
+ 0,
+ format!("r[{}]=(r[{}] || r[{}])", dest, lhs, rhs),
+ ),
};
format!(
"{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}",
diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs
index 4e393116c..3fdd02a14 100644
--- a/core/vdbe/insn.rs
+++ b/core/vdbe/insn.rs
@@ -557,6 +557,12 @@ pub enum Insn {
rhs: usize,
dest: usize,
},
+ /// Take the logical OR of the values in register P1 and P2 and store the answer in register P3.
+ Or {
+ lhs: usize,
+ rhs: usize,
+ dest: usize,
+ },
}
fn cast_text_to_numerical(value: &str) -> OwnedValue {
@@ -980,9 +986,43 @@ pub fn exec_and(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
}
}
+pub fn exec_or(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
+ if let OwnedValue::Agg(agg) = lhs {
+ lhs = agg.final_value();
+ }
+ if let OwnedValue::Agg(agg) = rhs {
+ rhs = agg.final_value();
+ }
+
+ match (lhs, rhs) {
+ (OwnedValue::Null, OwnedValue::Null)
+ | (OwnedValue::Null, OwnedValue::Float(0.0))
+ | (OwnedValue::Float(0.0), OwnedValue::Null)
+ | (OwnedValue::Null, OwnedValue::Integer(0))
+ | (OwnedValue::Integer(0), OwnedValue::Null) => OwnedValue::Null,
+ (OwnedValue::Float(0.0), OwnedValue::Integer(0))
+ | (OwnedValue::Integer(0), OwnedValue::Float(0.0))
+ | (OwnedValue::Float(0.0), OwnedValue::Float(0.0))
+ | (OwnedValue::Integer(0), OwnedValue::Integer(0)) => OwnedValue::Integer(0),
+ (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_or(
+ &cast_text_to_numerical(&lhs.value),
+ &cast_text_to_numerical(&rhs.value),
+ ),
+ (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => {
+ exec_or(&cast_text_to_numerical(&text.value), other)
+ }
+ _ => OwnedValue::Integer(1),
+ }
+}
+
#[cfg(test)]
mod tests {
- use crate::types::OwnedValue;
+ use std::rc::Rc;
+
+ use crate::{
+ types::{LimboText, OwnedValue},
+ vdbe::insn::exec_or,
+ };
use super::exec_and;
@@ -1012,4 +1052,54 @@ mod tests {
assert_eq!(exec_and(lhs, rhs), outpus[i]);
}
}
+
+ #[test]
+ fn test_exec_or() {
+ let inputs = vec![
+ (OwnedValue::Integer(0), OwnedValue::Null),
+ (OwnedValue::Null, OwnedValue::Integer(1)),
+ (OwnedValue::Null, OwnedValue::Null),
+ (OwnedValue::Float(0.0), OwnedValue::Null),
+ (OwnedValue::Integer(1), OwnedValue::Float(2.2)),
+ (OwnedValue::Float(0.0), OwnedValue::Integer(0)),
+ (
+ OwnedValue::Integer(0),
+ OwnedValue::Text(LimboText::new(Rc::new("string".to_string()))),
+ ),
+ (
+ OwnedValue::Integer(0),
+ OwnedValue::Text(LimboText::new(Rc::new("1".to_string()))),
+ ),
+ (
+ OwnedValue::Integer(0),
+ OwnedValue::Text(LimboText::new(Rc::new("".to_string()))),
+ ),
+ ];
+ let outpus = vec![
+ OwnedValue::Null,
+ OwnedValue::Integer(1),
+ OwnedValue::Null,
+ OwnedValue::Null,
+ OwnedValue::Integer(1),
+ OwnedValue::Integer(0),
+ OwnedValue::Integer(0),
+ OwnedValue::Integer(1),
+ OwnedValue::Integer(0),
+ ];
+
+ assert_eq!(
+ inputs.len(),
+ outpus.len(),
+ "Inputs and Outputs should have same size"
+ );
+ for (i, (lhs, rhs)) in inputs.iter().enumerate() {
+ assert_eq!(
+ exec_or(lhs, rhs),
+ outpus[i],
+ "Wrong OR for lhs: {}, rhs: {}",
+ lhs,
+ rhs
+ );
+ }
+ }
}
diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs
index 24eb7657c..2c4dd89fb 100644
--- a/core/vdbe/mod.rs
+++ b/core/vdbe/mod.rs
@@ -47,7 +47,8 @@ use crate::{resolve_ext_path, Connection, Result, Rows, TransactionState, DATABA
use datetime::{exec_date, exec_datetime_full, exec_julianday, exec_time, exec_unixepoch};
use insn::{
exec_add, exec_and, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_concat,
- exec_divide, exec_multiply, exec_remainder, exec_shift_left, exec_shift_right, exec_subtract,
+ exec_divide, exec_multiply, exec_or, exec_remainder, exec_shift_left, exec_shift_right,
+ exec_subtract,
};
use likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape};
use rand::distributions::{Distribution, Uniform};
@@ -2351,6 +2352,11 @@ impl Program {
exec_and(&state.registers[*lhs], &state.registers[*rhs]);
state.pc += 1;
}
+ Insn::Or { lhs, rhs, dest } => {
+ state.registers[*dest] =
+ exec_or(&state.registers[*lhs], &state.registers[*rhs]);
+ state.pc += 1;
+ }
}
}
}
From 7902d5f2005dff0440e5f5a21de50adce7b96bd9 Mon Sep 17 00:00:00 2001
From: Diego Reis
Date: Sat, 25 Jan 2025 03:08:51 -0300
Subject: [PATCH 14/14] Fix Text handling of And bytecode
---
core/vdbe/insn.rs | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs
index 3fdd02a14..f17a1a354 100644
--- a/core/vdbe/insn.rs
+++ b/core/vdbe/insn.rs
@@ -982,6 +982,13 @@ pub fn exec_and(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue {
| (_, OwnedValue::Float(0.0))
| (OwnedValue::Float(0.0), _) => OwnedValue::Integer(0),
(OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null,
+ (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_and(
+ &cast_text_to_numerical(&lhs.value),
+ &cast_text_to_numerical(&rhs.value),
+ ),
+ (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => {
+ exec_and(&cast_text_to_numerical(&text.value), other)
+ }
_ => OwnedValue::Integer(1),
}
}
@@ -1034,13 +1041,28 @@ mod tests {
(OwnedValue::Null, OwnedValue::Null),
(OwnedValue::Float(0.0), OwnedValue::Null),
(OwnedValue::Integer(1), OwnedValue::Float(2.2)),
+ (
+ OwnedValue::Integer(0),
+ OwnedValue::Text(LimboText::new(Rc::new("string".to_string()))),
+ ),
+ (
+ OwnedValue::Integer(0),
+ OwnedValue::Text(LimboText::new(Rc::new("1".to_string()))),
+ ),
+ (
+ OwnedValue::Integer(1),
+ OwnedValue::Text(LimboText::new(Rc::new("1".to_string()))),
+ ),
];
- let outpus = vec![
+ let outpus = [
OwnedValue::Integer(0),
OwnedValue::Null,
OwnedValue::Null,
OwnedValue::Integer(0),
OwnedValue::Integer(1),
+ OwnedValue::Integer(0),
+ OwnedValue::Integer(0),
+ OwnedValue::Integer(1),
];
assert_eq!(
@@ -1049,7 +1071,13 @@ mod tests {
"Inputs and Outputs should have same size"
);
for (i, (lhs, rhs)) in inputs.iter().enumerate() {
- assert_eq!(exec_and(lhs, rhs), outpus[i]);
+ assert_eq!(
+ exec_and(lhs, rhs),
+ outpus[i],
+ "Wrong AND for lhs: {}, rhs: {}",
+ lhs,
+ rhs
+ );
}
}
@@ -1075,7 +1103,7 @@ mod tests {
OwnedValue::Text(LimboText::new(Rc::new("".to_string()))),
),
];
- let outpus = vec![
+ let outpus = [
OwnedValue::Null,
OwnedValue::Integer(1),
OwnedValue::Null,