diff --git a/bindings/dart/rust/src/api/connect.rs b/bindings/dart/rust/src/api/connect.rs index ceb3fa87f..d29e2a867 100644 --- a/bindings/dart/rust/src/api/connect.rs +++ b/bindings/dart/rust/src/api/connect.rs @@ -23,10 +23,10 @@ pub struct ConnectArgs { pub async fn connect(args: ConnectArgs) -> RustConnection { let database = if args.url == ":memory:" { let io: Arc = Arc::new(turso_core::MemoryIO::new()); - turso_core::Database::open_file(io, args.url.as_str(), false, false) + turso_core::Database::open_file(io, args.url.as_str(), false, true) } else { let io: Arc = Arc::new(turso_core::PlatformIO::new().unwrap()); - turso_core::Database::open_file(io, args.url.as_str(), false, false) + turso_core::Database::open_file(io, args.url.as_str(), false, true) } .unwrap(); let connection = database.connect().unwrap(); diff --git a/bindings/go/limbo_test.go b/bindings/go/limbo_test.go index 61713d999..8fe36ae17 100644 --- a/bindings/go/limbo_test.go +++ b/bindings/go/limbo_test.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "math" + "slices" "testing" _ "github.com/tursodatabase/turso" @@ -683,6 +684,53 @@ func TestParameterOrdering(t *testing.T) { } } +func TestIndex(t *testing.T) { + newConn, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Error opening new connection: %v", err) + } + sql := "CREATE TABLE users (name TEXT PRIMARY KEY, email TEXT)" + _, err = newConn.Exec(sql) + if err != nil { + t.Fatalf("Error creating table: %v", err) + } + sql = "CREATE INDEX email_idx ON users(email)" + _, err = newConn.Exec(sql) + if err != nil { + t.Fatalf("Error creating index: %v", err) + } + + // Test inserting with parameters in a different order than + // the table definition. + sql = "INSERT INTO users VALUES ('alice', 'a@b.c'), ('bob', 'b@d.e')" + _, err = newConn.Exec(sql) + if err != nil { + t.Fatalf("Error inserting data: %v", err) + } + + for filter, row := range map[string][]string{ + "a@b.c": []string{"alice", "a@b.c"}, + "b@d.e": []string{"bob", "b@d.e"}, + } { + query := "SELECT * FROM users WHERE email = ?" + rows, err := newConn.Query(query, filter) + if err != nil { + t.Fatalf("Error executing query: %v", err) + } + for rows.Next() { + var name, email string + err := rows.Scan(&name, &email) + t.Log("name,email:", name, email) + if err != nil { + t.Fatal("Error scanning row: ", err) + } + if !slices.Equal([]string{name, email}, row) { + t.Fatal("Unexpected result", row, []string{name, email}) + } + } + } +} + func slicesAreEq(a, b []byte) bool { if len(a) != len(b) { fmt.Printf("LENGTHS NOT EQUAL: %d != %d\n", len(a), len(b)) diff --git a/bindings/go/rs_src/lib.rs b/bindings/go/rs_src/lib.rs index 8bb96022a..475ad062f 100644 --- a/bindings/go/rs_src/lib.rs +++ b/bindings/go/rs_src/lib.rs @@ -20,7 +20,7 @@ pub unsafe extern "C" fn db_open(path: *const c_char) -> *mut c_void { } let path = unsafe { std::ffi::CStr::from_ptr(path) }; let path = path.to_str().unwrap(); - let Ok((io, conn)) = Connection::from_uri(path, false, false, false) else { + let Ok((io, conn)) = Connection::from_uri(path, true, false, false) else { panic!("Failed to open connection with path: {path}"); }; LimboConn::new(conn, io).to_ptr() diff --git a/bindings/java/rs_src/turso_db.rs b/bindings/java/rs_src/turso_db.rs index fc5a506fe..1c533474c 100644 --- a/bindings/java/rs_src/turso_db.rs +++ b/bindings/java/rs_src/turso_db.rs @@ -68,7 +68,7 @@ pub extern "system" fn Java_tech_turso_core_TursoDB_openUtf8<'local>( } }; - let db = match Database::open_file(io.clone(), &path, false, false) { + let db = match Database::open_file(io.clone(), &path, false, true) { Ok(db) => db, Err(e) => { set_err_msg_and_throw_exception(&mut env, obj, TURSO_ETC, e.to_string()); diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index d1c138bf4..0d50c6a8f 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -66,7 +66,7 @@ impl Database { let db_file = Arc::new(DatabaseFile::new(file)); let db = - turso_core::Database::open(io.clone(), &path, db_file, false, false).map_err(|e| { + turso_core::Database::open(io.clone(), &path, db_file, false, true).map_err(|e| { Error::new( Status::GenericFailure, format!("Failed to open database: {e}"), diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 5e4b51ca4..2010d5d34 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -315,10 +315,9 @@ impl Drop for Connection { } #[allow(clippy::arc_with_non_send_sync)] -#[pyfunction(signature = (path, experimental_indexes=None))] -pub fn connect(path: &str, experimental_indexes: Option) -> Result { - let experimental_indexes = experimental_indexes.unwrap_or(true); - match turso_core::Connection::from_uri(path, experimental_indexes, false, false) { +#[pyfunction(signature = (path))] +pub fn connect(path: &str) -> Result { + match turso_core::Connection::from_uri(path, true, false, false) { Ok((io, conn)) => Ok(Connection { conn, _io: io }), Err(e) => Err(PyErr::new::(format!( "Failed to create connection: {e:?}" diff --git a/bindings/python/tests/test_database.py b/bindings/python/tests/test_database.py index 78c6987d0..e52e21a26 100644 --- a/bindings/python/tests/test_database.py +++ b/bindings/python/tests/test_database.py @@ -89,6 +89,24 @@ def test_in_memory_fetchone_select_all_users(provider): assert alice assert alice == (1, "alice") +@pytest.mark.parametrize("provider", ["sqlite3", "turso"]) +def test_in_memory_index(provider): + conn = connect(provider, ":memory:") + cursor = conn.cursor() + cursor.execute("CREATE TABLE users (name TEXT PRIMARY KEY, email TEXT)") + cursor.execute("CREATE INDEX email_idx ON users(email)") + cursor.execute("INSERT INTO users VALUES ('alice', 'a@b.c'), ('bob', 'b@d.e')") + + cursor.execute("SELECT * FROM users WHERE email = 'a@b.c'") + alice = cursor.fetchall() + + cursor.execute("SELECT * FROM users WHERE email = 'b@d.e'") + bob = cursor.fetchall() + + conn.close() + assert alice == [("alice", "a@b.c")] + assert bob == [("bob", "b@d.e")] + @pytest.mark.parametrize("provider", ["sqlite3", "turso"]) def test_fetchone_select_all_users(provider): diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index cd26abad6..8dc335d9c 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -108,27 +108,19 @@ impl Builder { io, self.path.as_str(), self.enable_mvcc, - indexes_enabled(), + true, )?; Ok(Database { inner: db }) } path => { let io: Arc = Arc::new(turso_core::PlatformIO::new()?); - let db = - turso_core::Database::open_file(io, path, self.enable_mvcc, indexes_enabled())?; + let db = turso_core::Database::open_file(io, path, self.enable_mvcc, true)?; Ok(Database { inner: db }) } } } } -fn indexes_enabled() -> bool { - #[cfg(feature = "experimental_indexes")] - return true; - #[cfg(not(feature = "experimental_indexes"))] - return false; -} - /// A database. /// /// The `Database` object points to a database and allows you to connect to it diff --git a/bindings/rust/tests/integration_tests.rs b/bindings/rust/tests/integration_tests.rs index 332e86665..da7cb039b 100644 --- a/bindings/rust/tests/integration_tests.rs +++ b/bindings/rust/tests/integration_tests.rs @@ -297,3 +297,40 @@ async fn test_row_get_conversion_error() { let result: Result = row.get(0); assert!(matches!(result, Err(Error::ConversionFailure(_)))); } + +#[tokio::test] +async fn test_index() { + let db = Builder::new_local(":memory:").build().await.unwrap(); + let conn = db.connect().unwrap(); + + conn.execute("CREATE TABLE users (name TEXT PRIMARY KEY, email TEXT)", ()) + .await + .unwrap(); + conn.execute("CREATE INDEX email_idx ON users(email)", ()) + .await + .unwrap(); + conn.execute( + "INSERT INTO users VALUES ('alice', 'a@b.c'), ('bob', 'b@d.e')", + (), + ) + .await + .unwrap(); + + let mut rows = conn + .query("SELECT * FROM users WHERE email = 'a@b.c'", ()) + .await + .unwrap(); + let row = rows.next().await.unwrap().unwrap(); + assert!(row.get::(0).unwrap() == "alice"); + assert!(row.get::(1).unwrap() == "a@b.c"); + assert!(rows.next().await.unwrap().is_none()); + + let mut rows = conn + .query("SELECT * FROM users WHERE email = 'b@d.e'", ()) + .await + .unwrap(); + let row = rows.next().await.unwrap().unwrap(); + assert!(row.get::(0).unwrap() == "bob"); + assert!(row.get::(1).unwrap() == "b@d.e"); + assert!(rows.next().await.unwrap().is_none()); +}