Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_backup_sqlite3_backup_step.c292 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7 8/* Helper: assert rc==SQLITE_OK, else include sqlite3_errmsg in message */9static void assert_sqlite_ok_rc(int rc, sqlite3 *db, const char *msg){10  if( rc!=SQLITE_OK ){11    char buf[512];12    snprintf(buf, sizeof(buf), "%s (rc=%d, err=%s)", msg, rc,13             db ? sqlite3_errmsg(db) : "(null)");14    TEST_FAIL_MESSAGE(buf);15  }16}17 18static void exec_sql(sqlite3 *db, const char *sql){19  char *errmsg = NULL;20  int rc = sqlite3_exec(db, sql, 0, 0, &errmsg);21  if( rc!=SQLITE_OK ){22    char buf[512];23    snprintf(buf, sizeof(buf), "SQL error: %s; rc=%d; sql: %s",24             errmsg ? errmsg : "(null)", rc, sql);25    sqlite3_free(errmsg);26    TEST_FAIL_MESSAGE(buf);27  }28}29 30static int int_query(sqlite3 *db, const char *sql){31  sqlite3_stmt *stmt = NULL;32  int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);33  assert_sqlite_ok_rc(rc, db, "prepare failed");34  rc = sqlite3_step(stmt);35  if( rc!=SQLITE_ROW ){36    char buf[256];37    snprintf(buf, sizeof(buf), "Expected one row from: %s (rc=%d)", sql, rc);38    sqlite3_finalize(stmt);39    TEST_FAIL_MESSAGE(buf);40  }41  int v = sqlite3_column_int(stmt, 0);42  rc = sqlite3_finalize(stmt);43  assert_sqlite_ok_rc(rc, db, "finalize failed");44  return v;45}46 47static int pragma_page_size(sqlite3 *db){48  return int_query(db, "PRAGMA page_size");49}50 51void setUp(void) {52  /* Nothing needed */53}54void tearDown(void) {55  /* Nothing needed */56}57 58/* Test 1: Basic full backup from non-empty source to empty destination */59void test_sqlite3_backup_step_basic_copy_done(void){60  sqlite3 *src = NULL, *dst = NULL;61  int rc;62 63  assert_sqlite_ok_rc(sqlite3_open(":memory:", &src), src, "open src");64  assert_sqlite_ok_rc(sqlite3_open(":memory:", &dst), dst, "open dst");65 66  /* Prepare source with data */67  exec_sql(src, "CREATE TABLE t(a INTEGER PRIMARY KEY, b TEXT);"68                "INSERT INTO t(b) VALUES ('alpha'),('beta'),('gamma');");69 70  sqlite3_backup *p = sqlite3_backup_init(dst, "main", src, "main");71  TEST_ASSERT_NOT_NULL_MESSAGE(p, "sqlite3_backup_init returned NULL");72 73  rc = sqlite3_backup_step(p, -1);74  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_DONE, rc, "Expected SQLITE_DONE");75 76  /* Check page counts and remaining */77  TEST_ASSERT_EQUAL_INT(0, sqlite3_backup_remaining(p));78  TEST_ASSERT_TRUE_MESSAGE(sqlite3_backup_pagecount(p) >= 1, "Expected pagecount >= 1");79 80  rc = sqlite3_backup_finish(p);81  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);82 83  /* Verify data copied */84  int cnt = int_query(dst, "SELECT count(*) FROM t");85  TEST_ASSERT_EQUAL_INT(3, cnt);86 87  sqlite3_close(dst);88  sqlite3_close(src);89}90 91/* Test 2: Incremental backup with small nPage, ensure eventual DONE and remaining decreases */92void test_sqlite3_backup_step_partial_then_resume(void){93  sqlite3 *src = NULL, *dst = NULL;94  int rc;95 96  assert_sqlite_ok_rc(sqlite3_open(":memory:", &src), src, "open src");97  assert_sqlite_ok_rc(sqlite3_open(":memory:", &dst), dst, "open dst");98 99  /* Fill source with enough rows. Even if it fits in one page, the test tolerates DONE early. */100  exec_sql(src, "CREATE TABLE t(a INTEGER PRIMARY KEY, b BLOB);");101  /* Insert rows with some payload to possibly spill into multiple pages */102  exec_sql(src, "WITH RECURSIVE c(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM c WHERE x<200)"103                "INSERT INTO t(a,b) SELECT x, randomblob(200) FROM c;");104 105  sqlite3_backup *p = sqlite3_backup_init(dst, "main", src, "main");106  TEST_ASSERT_NOT_NULL(p);107 108  int lastRemaining = -1;109  int loops = 0;110  for(;;){111    rc = sqlite3_backup_step(p, 1); /* copy at most one page per call */112    if( rc==SQLITE_DONE ){113      break;114    }115    TEST_ASSERT_TRUE_MESSAGE(rc==SQLITE_OK || rc==SQLITE_BUSY, "Unexpected rc from partial step");116 117    /* Track remaining pages if available */118    int rem = sqlite3_backup_remaining(p);119    int pc  = sqlite3_backup_pagecount(p);120    TEST_ASSERT_TRUE_MESSAGE(pc >= 0, "pagecount should be >= 0");121    if( lastRemaining>=0 ){122      TEST_ASSERT_TRUE_MESSAGE(rem <= lastRemaining, "remaining did not decrease or stay the same");123    }124    lastRemaining = rem;125 126    /* Avoid infinite loop in pathological cases */127    loops++;128    TEST_ASSERT_TRUE_MESSAGE(loops < 100000, "Too many iterations without DONE");129  }130 131  rc = sqlite3_backup_finish(p);132  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);133 134  /* Verify data copied */135  int srcCnt = int_query(src, "SELECT count(*) FROM t");136  int dstCnt = int_query(dst, "SELECT count(*) FROM t");137  TEST_ASSERT_EQUAL_INT(srcCnt, dstCnt);138 139  sqlite3_close(dst);140  sqlite3_close(src);141}142 143/* Test 3: SQLITE_BUSY when source holds a write transaction, then success after rollback */144void test_sqlite3_backup_step_busy_when_src_write_txn(void){145  sqlite3 *src = NULL, *dst = NULL;146  int rc;147 148  assert_sqlite_ok_rc(sqlite3_open(":memory:", &src), src, "open src");149  assert_sqlite_ok_rc(sqlite3_open(":memory:", &dst), dst, "open dst");150 151  exec_sql(src, "CREATE TABLE t(a); INSERT INTO t VALUES (1),(2),(3);");152 153  /* Begin a write transaction on source to force BUSY */154  exec_sql(src, "BEGIN IMMEDIATE");155 156  sqlite3_backup *p = sqlite3_backup_init(dst, "main", src, "main");157  TEST_ASSERT_NOT_NULL(p);158 159  rc = sqlite3_backup_step(p, 1);160  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_BUSY, rc, "Expected SQLITE_BUSY with source write transaction");161 162  /* Rollback the write transaction and try again */163  exec_sql(src, "ROLLBACK");164 165  rc = sqlite3_backup_step(p, -1);166  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_DONE, rc, "Expected SQLITE_DONE after releasing write txn");167 168  rc = sqlite3_backup_finish(p);169  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);170 171  /* Verify data copied */172  int cnt = int_query(dst, "SELECT count(*) FROM t");173  TEST_ASSERT_EQUAL_INT(3, cnt);174 175  sqlite3_close(dst);176  sqlite3_close(src);177}178 179/* Test 4: SQLITE_READONLY when destination is memdb with fixed page size different from source */180void test_sqlite3_backup_step_memdb_pgsize_mismatch_readonly(void){181  sqlite3 *src = NULL, *dst = NULL;182  int rc;183 184  assert_sqlite_ok_rc(sqlite3_open(":memory:", &src), src, "open src");185  assert_sqlite_ok_rc(sqlite3_open(":memory:", &dst), dst, "open dst");186 187  /* Set source page size to 1024 and create content (fixes page size) */188  exec_sql(src, "PRAGMA page_size=1024;");189  exec_sql(src, "CREATE TABLE s(x); INSERT INTO s VALUES (1);");190 191  /* Set destination page size to 4096 and create any object to fix it */192  exec_sql(dst, "PRAGMA page_size=4096;");193  exec_sql(dst, "CREATE TABLE d(y); INSERT INTO d VALUES (99);");194 195  /* Sanity: ensure different page sizes */196  int pgsSrc = pragma_page_size(src);197  int pgsDst = pragma_page_size(dst);198  TEST_ASSERT_NOT_EQUAL(pgsSrc, pgsDst);199 200  sqlite3_backup *p = sqlite3_backup_init(dst, "main", src, "main");201  TEST_ASSERT_NOT_NULL(p);202 203  rc = sqlite3_backup_step(p, -1);204  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_READONLY, rc,205    "Expected SQLITE_READONLY for memdb destination with differing page size");206 207  rc = sqlite3_backup_finish(p);208  TEST_ASSERT_EQUAL_INT(SQLITE_READONLY, rc);209 210  /* Verify destination content unaffected (table d still there with 1 row) */211  int cnt = int_query(dst, "SELECT count(*) FROM d");212  TEST_ASSERT_EQUAL_INT(1, cnt);213 214  sqlite3_close(dst);215  sqlite3_close(src);216}217 218/* Test 5: Backup from empty source database */219void test_sqlite3_backup_step_empty_source(void){220  sqlite3 *src = NULL, *dst = NULL;221  int rc;222 223  assert_sqlite_ok_rc(sqlite3_open(":memory:", &src), src, "open src");224  assert_sqlite_ok_rc(sqlite3_open(":memory:", &dst), dst, "open dst");225 226  /* Source remains empty (no schema changes) */227 228  sqlite3_backup *p = sqlite3_backup_init(dst, "main", src, "main");229  TEST_ASSERT_NOT_NULL(p);230 231  rc = sqlite3_backup_step(p, -1);232  TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);233 234  TEST_ASSERT_EQUAL_INT(0, sqlite3_backup_remaining(p));235  TEST_ASSERT_TRUE(sqlite3_backup_pagecount(p) >= 0);236 237  rc = sqlite3_backup_finish(p);238  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);239 240  /* Destination should have empty schema */241  int sm = int_query(dst, "SELECT count(*) FROM sqlite_master WHERE type IN ('table','index','trigger','view')");242  TEST_ASSERT_EQUAL_INT(0, sm);243 244  sqlite3_close(dst);245  sqlite3_close(src);246}247 248/* Test 6: Destination page size is set to source page size for empty destination */249void test_sqlite3_backup_step_sets_dest_pagesize(void){250  sqlite3 *src = NULL, *dst = NULL;251  int rc;252 253  assert_sqlite_ok_rc(sqlite3_open(":memory:", &src), src, "open src");254  assert_sqlite_ok_rc(sqlite3_open(":memory:", &dst), dst, "open dst");255 256  /* Set source page size to 1024 and create data */257  exec_sql(src, "PRAGMA page_size=1024;");258  exec_sql(src, "CREATE TABLE s(a); INSERT INTO s VALUES (10),(20);");259 260  /* Destination left empty so its page size can be changed by backup_step */261  int destBefore = pragma_page_size(dst);262 263  sqlite3_backup *p = sqlite3_backup_init(dst, "main", src, "main");264  TEST_ASSERT_NOT_NULL(p);265 266  rc = sqlite3_backup_step(p, -1);267  TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);268 269  rc = sqlite3_backup_finish(p);270  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);271 272  int pgsSrc = pragma_page_size(src);273  int pgsDst = pragma_page_size(dst);274 275  TEST_ASSERT_NOT_EQUAL_MESSAGE(-1, destBefore, "Invalid initial dest page size");276  TEST_ASSERT_EQUAL_INT_MESSAGE(pgsSrc, 1024, "Expected source page size 1024");277  TEST_ASSERT_EQUAL_INT_MESSAGE(pgsDst, pgsSrc, "Destination page size should match source");278 279  sqlite3_close(dst);280  sqlite3_close(src);281}282 283int main(void){284  UNITY_BEGIN();285  RUN_TEST(test_sqlite3_backup_step_basic_copy_done);286  RUN_TEST(test_sqlite3_backup_step_partial_then_resume);287  RUN_TEST(test_sqlite3_backup_step_busy_when_src_write_txn);288  RUN_TEST(test_sqlite3_backup_step_memdb_pgsize_mismatch_readonly);289  RUN_TEST(test_sqlite3_backup_step_empty_source);290  RUN_TEST(test_sqlite3_backup_step_sets_dest_pagesize);291  return UNITY_END();292}