From d7939f05c20f5ab4a7ef05210df48729d6a50784 Mon Sep 17 00:00:00 2001 From: karan Date: Fri, 7 Mar 2025 19:09:58 +0530 Subject: [PATCH 1/3] Added tests in sqlite3 Signed-off-by: karan --- sqlite3/src/lib.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index c1cab5eb0..98cc17f3e 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -1079,3 +1079,103 @@ pub unsafe extern "C" fn sqlite3_wal_checkpoint_v2( } SQLITE_OK } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sqlite3_initialization() { + unsafe { + let result = sqlite3_initialize(); + assert_eq!(result, SQLITE_OK); + + // Test multiple initializations + let second_result = sqlite3_initialize(); + assert_eq!(second_result, SQLITE_OK); + } + } + + #[test] + fn test_sqlite3_open_memory() { + unsafe { + let mut db: *mut sqlite3 = std::ptr::null_mut(); + let filename = CString::new(":memory:").unwrap(); + + let result = sqlite3_open(filename.as_ptr(), &mut db); + assert_eq!(result, SQLITE_OK); + assert!(!db.is_null()); + + // Clean up + let close_result = sqlite3_close(db); + assert_eq!(close_result, SQLITE_OK); + } + } + + #[test] + fn test_sqlite3_error_codes() { + unsafe { + let mut db: *mut sqlite3 = std::ptr::null_mut(); + let filename = CString::new(":memory:").unwrap(); + + // Open database + let result = sqlite3_open(filename.as_ptr(), &mut db); + assert_eq!(result, SQLITE_OK); + + // Test error codes + let db_ref = &mut *db; + db_ref.err_code = SQLITE_ERROR; + assert_eq!(sqlite3_errcode(db), SQLITE_ERROR); + + // Test error messages + let error_msg = sqlite3_errmsg(db); + assert!(!error_msg.is_null()); + + // Clean up + sqlite3_close(db); + } + } + + #[test] + fn test_sqlite3_prepare_and_step() { + unsafe { + let mut db: *mut sqlite3 = std::ptr::null_mut(); + let filename = CString::new(":memory:").unwrap(); + + // Open database + let result = sqlite3_open(filename.as_ptr(), &mut db); + assert_eq!(result, SQLITE_OK); + + // Prepare a simple statement + let sql = CString::new("CREATE TABLE test (id INTEGER PRIMARY KEY)").unwrap(); + let mut stmt: *mut sqlite3_stmt = std::ptr::null_mut(); + let prepare_result = sqlite3_prepare_v2( + db, + sql.as_ptr(), + -1, + &mut stmt, + std::ptr::null_mut(), + ); + assert_eq!(prepare_result, SQLITE_OK); + + // Step through the statement + let step_result = sqlite3_step(stmt); + assert_eq!(step_result, SQLITE_DONE); + + // Clean up + sqlite3_finalize(stmt); + sqlite3_close(db); + } + } + + #[test] + fn test_sqlite3_version() { + unsafe { + let version = sqlite3_libversion(); + assert!(!version.is_null()); + + let version_num = sqlite3_libversion_number(); + assert!(version_num > 0); + } + } +} From eba5e74a2c4ddb71f7f15735aeb824d4706c797c Mon Sep 17 00:00:00 2001 From: karan Date: Fri, 7 Mar 2025 19:12:44 +0530 Subject: [PATCH 2/3] Cargo fmt Signed-off-by: karan --- sqlite3/src/lib.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index 98cc17f3e..eef3b285b 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -1089,7 +1089,7 @@ mod tests { unsafe { let result = sqlite3_initialize(); assert_eq!(result, SQLITE_OK); - + // Test multiple initializations let second_result = sqlite3_initialize(); assert_eq!(second_result, SQLITE_OK); @@ -1101,7 +1101,7 @@ mod tests { unsafe { let mut db: *mut sqlite3 = std::ptr::null_mut(); let filename = CString::new(":memory:").unwrap(); - + let result = sqlite3_open(filename.as_ptr(), &mut db); assert_eq!(result, SQLITE_OK); assert!(!db.is_null()); @@ -1117,7 +1117,7 @@ mod tests { unsafe { let mut db: *mut sqlite3 = std::ptr::null_mut(); let filename = CString::new(":memory:").unwrap(); - + // Open database let result = sqlite3_open(filename.as_ptr(), &mut db); assert_eq!(result, SQLITE_OK); @@ -1141,7 +1141,7 @@ mod tests { unsafe { let mut db: *mut sqlite3 = std::ptr::null_mut(); let filename = CString::new(":memory:").unwrap(); - + // Open database let result = sqlite3_open(filename.as_ptr(), &mut db); assert_eq!(result, SQLITE_OK); @@ -1149,13 +1149,8 @@ mod tests { // Prepare a simple statement let sql = CString::new("CREATE TABLE test (id INTEGER PRIMARY KEY)").unwrap(); let mut stmt: *mut sqlite3_stmt = std::ptr::null_mut(); - let prepare_result = sqlite3_prepare_v2( - db, - sql.as_ptr(), - -1, - &mut stmt, - std::ptr::null_mut(), - ); + let prepare_result = + sqlite3_prepare_v2(db, sql.as_ptr(), -1, &mut stmt, std::ptr::null_mut()); assert_eq!(prepare_result, SQLITE_OK); // Step through the statement @@ -1173,7 +1168,7 @@ mod tests { unsafe { let version = sqlite3_libversion(); assert!(!version.is_null()); - + let version_num = sqlite3_libversion_number(); assert!(version_num > 0); } From 34876c47114980bc6c2424ef10d0565d29e4bfb1 Mon Sep 17 00:00:00 2001 From: karan Date: Sat, 8 Mar 2025 09:52:43 +0530 Subject: [PATCH 3/3] fixing erro code for sqlite open Signed-off-by: karan --- sqlite3/README.md | 105 +++++++++++++++++++++++ sqlite3/include/sqlite3.h | 17 ++++ sqlite3/src/lib.rs | 173 +++++++++++++++++++++++++++++++++++++- sqlite3/tests/Makefile | 53 ++++++------ sqlite3/tests/main.c | 4 + sqlite3/tests/test-wal.c | 39 +++++++++ 6 files changed, 361 insertions(+), 30 deletions(-) create mode 100644 sqlite3/README.md create mode 100644 sqlite3/tests/test-wal.c diff --git a/sqlite3/README.md b/sqlite3/README.md new file mode 100644 index 000000000..e58ea8c4a --- /dev/null +++ b/sqlite3/README.md @@ -0,0 +1,105 @@ +# SQLite3 Implementation for Limbo + +This directory contains a Rust implementation of the SQLite3 C API. The implementation serves as a compatibility layer between SQLite's C API and Limbo's native Rust database implementation. + +## Purpose + +This implementation provides SQLite3 API compatibility for Limbo, allowing existing applications that use SQLite to work with Limbo without modification. The code: + +1. Implements the SQLite3 C API functions in Rust +2. Translates between C and Rust data structures +3. Maps SQLite operations to equivalent Limbo operations +4. Maintains API compatibility with SQLite version 3.42.0 + +## Testing Strategy + +We employ a dual-testing approach to ensure complete compatibility with SQLite: + +### Test Database Setup + +Before running tests, you need to set up a test database: + +```bash +# Create testing directory +mkdir -p ../../testing + +# Create and initialize test database +sqlite3 ../../testing/testing.db ".databases" +``` + +This creates an empty SQLite database that both test suites will use. + +### 1. C Test Suite (`/tests`) +- Written in C to test the exact same API that real applications use +- Can be compiled and run against both: + - Official SQLite library (for verification) + - Our Rust implementation (for validation) +- Serves as the "source of truth" for correct behavior + +To run C tests against official SQLite: +```bash +cd tests +make clean +make LIBS="-lsqlite3" +./sqlite3-tests +``` + +To run C tests against our implementation: +```bash +cd tests +make clean +make LIBS="-L../target/debug -lsqlite3" +./sqlite3-tests +``` + +### 2. Rust Tests (`src/lib.rs`) +- Unit tests written in Rust +- Test the same functionality as C tests +- Provide better debugging capabilities +- Help with development and implementation + +To run Rust tests: +```bash +cargo test +``` + +### Why Two Test Suites? + +1. **Behavior Verification**: C tests ensure our implementation matches SQLite's behavior exactly by running the same tests against both +2. **Development Efficiency**: Rust tests provide better debugging and development experience +3. **Complete Coverage**: Both test suites together provide comprehensive testing from both C and Rust perspectives + +### Common Test Issues + +1. **Missing Test Database** + - Error: `SQLITE_CANTOPEN (14)` in tests + - Solution: Create test database as shown in "Test Database Setup" + +2. **Wrong Database Path** + - Tests expect database at `../../testing/testing.db` + - Verify path relative to where tests are run + +3. **Permission Issues** + - Ensure test database is readable/writable + - Check directory permissions + +## Implementation Notes + +- All public functions are marked with `#[no_mangle]` and follow SQLite's C API naming convention +- Uses `unsafe` blocks for C API compatibility +- Implements error handling similar to SQLite +- Maintains thread safety guarantees of SQLite + +## Contributing + +When adding new features or fixing bugs: + +1. Add C tests that can run against both implementations +2. Add corresponding Rust tests +3. Verify behavior matches SQLite by running C tests against both implementations +4. Ensure all existing tests pass in both suites +5. Make sure test database exists and is accessible + +## Status + +This is an ongoing implementation. Some functions are marked with `stub!()` macro, indicating they're not yet implemented. Check individual function documentation for implementation status. \ No newline at end of file diff --git a/sqlite3/include/sqlite3.h b/sqlite3/include/sqlite3.h index 6ddf3938b..530eef5aa 100644 --- a/sqlite3/include/sqlite3.h +++ b/sqlite3/include/sqlite3.h @@ -31,6 +31,12 @@ #define SQLITE_STATE_BUSY 109 +/* WAL Checkpoint modes */ +#define SQLITE_CHECKPOINT_PASSIVE 0 +#define SQLITE_CHECKPOINT_FULL 1 +#define SQLITE_CHECKPOINT_RESTART 2 +#define SQLITE_CHECKPOINT_TRUNCATE 3 + typedef struct sqlite3 sqlite3; typedef struct sqlite3_stmt sqlite3_stmt; @@ -244,6 +250,17 @@ const char *sqlite3_libversion(void); int sqlite3_libversion_number(void); +/* WAL Checkpoint functions */ +int sqlite3_wal_checkpoint(sqlite3 *db, const char *db_name); + +int sqlite3_wal_checkpoint_v2( + sqlite3 *db, + const char *db_name, + int mode, + int *log_size, + int *checkpoint_count +); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index c1cab5eb0..b7459ba17 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -113,7 +113,7 @@ pub unsafe extern "C" fn sqlite3_open( ":memory:" => Arc::new(limbo_core::MemoryIO::new()), _ => match limbo_core::PlatformIO::new() { Ok(io) => Arc::new(io), - Err(_) => return SQLITE_MISUSE, + Err(_) => return SQLITE_CANTOPEN, }, }; match limbo_core::Database::open_file(io, filename, false) { @@ -1079,3 +1079,174 @@ pub unsafe extern "C" fn sqlite3_wal_checkpoint_v2( } SQLITE_OK } + +#[cfg(test)] +mod tests { + use super::*; + use std::ptr; + + #[test] + fn test_libversion() { + unsafe { + let version = sqlite3_libversion(); + assert!(!version.is_null()); + } + } + + #[test] + fn test_libversion_number() { + unsafe { + let version_num = sqlite3_libversion_number(); + assert_eq!(version_num, 3042000); + } + } + + #[test] + fn test_open_misuse() { + unsafe { + let mut db = ptr::null_mut(); + assert_eq!(sqlite3_open(ptr::null(), &mut db), SQLITE_MISUSE); + } + } + + #[test] + fn test_open_not_found() { + unsafe { + let mut db = ptr::null_mut(); + assert_eq!( + sqlite3_open(b"not-found/local.db\0".as_ptr() as *const i8, &mut db), + SQLITE_CANTOPEN + ); + } + } + + #[test] + fn test_open_existing() { + unsafe { + let mut db = ptr::null_mut(); + assert_eq!( + sqlite3_open(b"../../testing/testing.db\0".as_ptr() as *const i8, &mut db), + SQLITE_OK + ); + assert_eq!(sqlite3_close(db), SQLITE_OK); + } + } + + #[test] + fn test_close() { + unsafe { + assert_eq!(sqlite3_close(ptr::null_mut()), SQLITE_OK); + } + } + + #[test] + fn test_prepare_misuse() { + unsafe { + let mut db = ptr::null_mut(); + assert_eq!( + sqlite3_open(b"../../testing/testing.db\0".as_ptr() as *const i8, &mut db), + SQLITE_OK + ); + + let mut stmt = ptr::null_mut(); + assert_eq!( + sqlite3_prepare_v2(db, b"SELECT 1\0".as_ptr() as *const i8, -1, &mut stmt, ptr::null_mut()), + SQLITE_OK + ); + + assert_eq!(sqlite3_finalize(stmt), SQLITE_OK); + assert_eq!(sqlite3_close(db), SQLITE_OK); + } + } + + #[test] + fn test_wal_checkpoint() { + unsafe { + // Test with NULL db handle + assert_eq!(sqlite3_wal_checkpoint(ptr::null_mut(), ptr::null()), SQLITE_MISUSE); + + // Test with valid db + let mut db = ptr::null_mut(); + assert_eq!( + sqlite3_open(b"../../testing/testing.db\0".as_ptr() as *const i8, &mut db), + SQLITE_OK + ); + assert_eq!(sqlite3_wal_checkpoint(db, ptr::null()), SQLITE_OK); + assert_eq!(sqlite3_close(db), SQLITE_OK); + } + } + + #[test] + fn test_wal_checkpoint_v2() { + unsafe { + // Test with NULL db handle + assert_eq!( + sqlite3_wal_checkpoint_v2( + ptr::null_mut(), + ptr::null(), + SQLITE_CHECKPOINT_PASSIVE, + ptr::null_mut(), + ptr::null_mut() + ), + SQLITE_MISUSE + ); + + // Test with valid db + let mut db = ptr::null_mut(); + assert_eq!( + sqlite3_open(b"../../testing/testing.db\0".as_ptr() as *const i8, &mut db), + SQLITE_OK + ); + + let mut log_size = 0; + let mut checkpoint_count = 0; + + // Test different checkpoint modes + assert_eq!( + sqlite3_wal_checkpoint_v2( + db, + ptr::null(), + SQLITE_CHECKPOINT_PASSIVE, + &mut log_size, + &mut checkpoint_count + ), + SQLITE_OK + ); + + assert_eq!( + sqlite3_wal_checkpoint_v2( + db, + ptr::null(), + SQLITE_CHECKPOINT_FULL, + &mut log_size, + &mut checkpoint_count + ), + SQLITE_OK + ); + + assert_eq!( + sqlite3_wal_checkpoint_v2( + db, + ptr::null(), + SQLITE_CHECKPOINT_RESTART, + &mut log_size, + &mut checkpoint_count + ), + SQLITE_OK + ); + + assert_eq!( + sqlite3_wal_checkpoint_v2( + db, + ptr::null(), + SQLITE_CHECKPOINT_TRUNCATE, + &mut log_size, + &mut checkpoint_count + ), + SQLITE_OK + ); + + assert_eq!(sqlite3_close(db), SQLITE_OK); + } + } +} diff --git a/sqlite3/tests/Makefile b/sqlite3/tests/Makefile index ae1b7bb4a..44786a3f2 100644 --- a/sqlite3/tests/Makefile +++ b/sqlite3/tests/Makefile @@ -1,44 +1,39 @@ -V = -ifeq ($(strip $(V)),) - E = @echo - Q = @ -else - E = @\# - Q = -endif -export E Q - -PROGRAM = sqlite3-tests - -CFLAGS = -g -Wall -std=c17 -MMD -MP +# Compiler settings +CC = gcc +CFLAGS = -g -Wall -std=c17 -I../include +# Libraries LIBS ?= -lsqlite3 LIBS += -lm -OBJS += main.o -OBJS += test-aux.o -OBJS += test-close.o -OBJS += test-open.o -OBJS += test-prepare.o +# Target program +PROGRAM = sqlite3-tests +# Object files +OBJS = main.o \ + test-aux.o \ + test-close.o \ + test-open.o \ + test-prepare.o \ + test-wal.o + +# Default target all: $(PROGRAM) +# Test target test: $(PROGRAM) - $(E) " TEST" - $(Q) $(CURDIR)/$(PROGRAM) + ./$(PROGRAM) +# Compile source files %.o: %.c - $(E) " CC " $@ - $(Q) $(CC) $(CFLAGS) -c $< -o $@ -I$(HEADERS) + $(CC) $(CFLAGS) -c $< -o $@ +# Link program $(PROGRAM): $(OBJS) - $(E) " LINK " $@ - $(Q) $(CC) -o $@ $^ $(LIBS) + $(CC) -o $@ $(OBJS) $(LIBS) +# Clean target clean: - $(E) " CLEAN" - $(Q) rm -f $(PROGRAM) - $(Q) rm -f $(OBJS) *.d -.PHONY: clean + rm -f $(PROGRAM) $(OBJS) --include $(OBJS:.o=.d) +.PHONY: all test clean diff --git a/sqlite3/tests/main.c b/sqlite3/tests/main.c index 0166aa860..4cbf19f5e 100644 --- a/sqlite3/tests/main.c +++ b/sqlite3/tests/main.c @@ -5,6 +5,8 @@ extern void test_open_not_found(); extern void test_open_existing(); extern void test_close(); extern void test_prepare_misuse(); +extern void test_wal_checkpoint(); +extern void test_wal_checkpoint_v2(); int main(int argc, char *argv[]) { @@ -15,6 +17,8 @@ int main(int argc, char *argv[]) test_open_existing(); test_close(); test_prepare_misuse(); + test_wal_checkpoint(); + test_wal_checkpoint_v2(); return 0; } diff --git a/sqlite3/tests/test-wal.c b/sqlite3/tests/test-wal.c new file mode 100644 index 000000000..490277e02 --- /dev/null +++ b/sqlite3/tests/test-wal.c @@ -0,0 +1,39 @@ +#include "check.h" + +#include +#include +#include +#include + +void test_wal_checkpoint(void) +{ + sqlite3 *db; + + // Test with NULL db handle + CHECK_EQUAL(SQLITE_MISUSE, sqlite3_wal_checkpoint(NULL, NULL)); + + // Test with valid db + CHECK_EQUAL(SQLITE_OK, sqlite3_open("../../testing/testing.db", &db)); + CHECK_EQUAL(SQLITE_OK, sqlite3_wal_checkpoint(db, NULL)); + CHECK_EQUAL(SQLITE_OK, sqlite3_close(db)); +} + +void test_wal_checkpoint_v2(void) +{ + sqlite3 *db; + int log_size, checkpoint_count; + + // Test with NULL db handle + CHECK_EQUAL(SQLITE_MISUSE, sqlite3_wal_checkpoint_v2(NULL, NULL, SQLITE_CHECKPOINT_PASSIVE, NULL, NULL)); + + // Test with valid db + CHECK_EQUAL(SQLITE_OK, sqlite3_open("../../testing/testing.db", &db)); + + // Test different checkpoint modes + CHECK_EQUAL(SQLITE_OK, sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_PASSIVE, &log_size, &checkpoint_count)); + CHECK_EQUAL(SQLITE_OK, sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_FULL, &log_size, &checkpoint_count)); + CHECK_EQUAL(SQLITE_OK, sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_RESTART, &log_size, &checkpoint_count)); + CHECK_EQUAL(SQLITE_OK, sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_TRUNCATE, &log_size, &checkpoint_count)); + + CHECK_EQUAL(SQLITE_OK, sqlite3_close(db)); +} \ No newline at end of file