AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdio.h>6 7/* Unity setup/teardown */8void setUp(void) {9 /* No-op */10}11void tearDown(void) {12 /* No-op */13}14 15/* Helper: execute SQL and assert success */16static void assertExecSQL(sqlite3 *db, const char *sql){17 char *zErr = NULL;18 int rc = sqlite3_exec(db, sql, 0, 0, &zErr);19 if( rc!=SQLITE_OK ){20 fprintf(stderr, "SQL error rc=%d: %s\nSQL: %s\n", rc, zErr ? zErr : "(null)", sql);21 }22 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);23 if( zErr ) sqlite3_free(zErr);24}25 26/* Helper: get current page-cache memory used (bytes) */27static int getCacheUsed(sqlite3 *db){28 int cur = 0, hiwtr = 0;29 int rc = sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiwtr, 0);30 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);31 return cur;32}33 34/* Helper: populate a table with many large rows to grow the cache */35static void populateLargeTable(sqlite3 *db, const char *qualifiedTableName, int nRows, int blobSize){36 char sql[256];37 snprintf(sql, sizeof(sql), "CREATE TABLE IF NOT EXISTS %s(x);", qualifiedTableName);38 assertExecSQL(db, sql);39 assertExecSQL(db, "BEGIN");40 sqlite3_stmt *pStmt = NULL;41 snprintf(sql, sizeof(sql), "INSERT INTO %s(x) VALUES(randomblob(%d));", qualifiedTableName, blobSize);42 int rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);43 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);44 for(int i=0; i<nRows; i++){45 rc = sqlite3_step(pStmt);46 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);47 rc = sqlite3_reset(pStmt);48 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);49 }50 rc = sqlite3_finalize(pStmt);51 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);52 assertExecSQL(db, "COMMIT");53}54 55/* Test 1: Basic success on fresh database */56void test_sqlite3_db_release_memory_basic_returns_ok(void){57 sqlite3 *db = NULL;58 int rc = sqlite3_open(":memory:", &db);59 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);60 TEST_ASSERT_NOT_NULL(db);61 62 rc = sqlite3_db_release_memory(db);63 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);64 65 rc = sqlite3_close(db);66 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);67}68 69/* Test 2: After heavy use, release memory does not increase cache usage (and typically reduces it) */70void test_sqlite3_db_release_memory_non_increasing_cache_after_work(void){71 sqlite3 *db = NULL;72 int rc = sqlite3_open(":memory:", &db);73 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);74 75 /* Grow the cache by inserting many large blobs */76 populateLargeTable(db, "t", 1500, 4000); /* ~6MB of data requested at SQL layer */77 78 int usedBefore = getCacheUsed(db);79 /* Ensure we actually used some cache (should be > 0) */80 TEST_ASSERT_MESSAGE(usedBefore > 0, "Expected non-zero cache use after large inserts");81 82 rc = sqlite3_db_release_memory(db);83 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);84 85 int usedAfter = getCacheUsed(db);86 TEST_ASSERT_LESS_OR_EQUAL_INT(usedBefore, usedBefore); /* sanity no-op compare */87 TEST_ASSERT_TRUE_MESSAGE(usedAfter <= usedBefore, "Cache used should not increase after release");88 89 rc = sqlite3_close(db);90 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);91}92 93/* Test 3: Works with attached databases (multiple Btrees/Pagers) */94void test_sqlite3_db_release_memory_with_attached_databases(void){95 sqlite3 *db = NULL;96 int rc = sqlite3_open(":memory:", &db);97 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);98 99 /* Attach another in-memory database */100 assertExecSQL(db, "ATTACH DATABASE ':memory:' AS aux");101 102 /* Populate both main and aux to exercise multiple pagers */103 populateLargeTable(db, "main.t", 1000, 4096);104 populateLargeTable(db, "aux.u", 1000, 4096);105 106 int usedBefore = getCacheUsed(db);107 TEST_ASSERT_MESSAGE(usedBefore > 0, "Expected non-zero cache use after populating main and aux");108 109 rc = sqlite3_db_release_memory(db);110 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);111 112 int usedAfter = getCacheUsed(db);113 TEST_ASSERT_TRUE_MESSAGE(usedAfter <= usedBefore, "Cache used should not increase after release with attached DBs");114 115 /* Clean up */116 rc = sqlite3_close(db);117 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);118}119 120/* Test 4: Idempotent-ish behavior on repeated calls (no increase in cache usage) */121void test_sqlite3_db_release_memory_multiple_calls_idempotent(void){122 sqlite3 *db = NULL;123 int rc = sqlite3_open(":memory:", &db);124 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);125 126 populateLargeTable(db, "t", 1200, 4096);127 128 int usedBefore = getCacheUsed(db);129 TEST_ASSERT_MESSAGE(usedBefore > 0, "Expected non-zero cache use before release");130 131 rc = sqlite3_db_release_memory(db);132 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);133 int usedAfter1 = getCacheUsed(db);134 135 rc = sqlite3_db_release_memory(db);136 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);137 int usedAfter2 = getCacheUsed(db);138 139 TEST_ASSERT_TRUE_MESSAGE(usedAfter1 <= usedBefore, "First release should not increase cache usage");140 TEST_ASSERT_TRUE_MESSAGE(usedAfter2 <= usedAfter1, "Second release should not increase cache usage");141 142 rc = sqlite3_close(db);143 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);144}145 146/* Test 5: Calling while a statement is active still returns OK */147void test_sqlite3_db_release_memory_with_active_statement(void){148 sqlite3 *db = NULL;149 int rc = sqlite3_open(":memory:", &db);150 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);151 152 populateLargeTable(db, "t", 800, 4096);153 154 sqlite3_stmt *pStmt = NULL;155 rc = sqlite3_prepare_v2(db, "SELECT * FROM t;", -1, &pStmt, 0);156 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);157 TEST_ASSERT_NOT_NULL(pStmt);158 159 /* Do not finalize yet; keep resources potentially pinned */160 int usedBefore = getCacheUsed(db);161 rc = sqlite3_db_release_memory(db);162 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);163 int usedAfter = getCacheUsed(db);164 /* We only assert non-increase; pinned pages may prevent reductions */165 TEST_ASSERT_TRUE_MESSAGE(usedAfter <= usedBefore, "Cache used should not increase after release with active stmt");166 167 rc = sqlite3_finalize(pStmt);168 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);169 170 /* After finalizing, another release should remain OK and not increase usage */171 rc = sqlite3_db_release_memory(db);172 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);173 174 rc = sqlite3_close(db);175 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);176}177 178/* Main to run tests */179int main(void){180 UNITY_BEGIN();181 RUN_TEST(test_sqlite3_db_release_memory_basic_returns_ok);182 RUN_TEST(test_sqlite3_db_release_memory_non_increasing_cache_after_work);183 RUN_TEST(test_sqlite3_db_release_memory_with_attached_databases);184 RUN_TEST(test_sqlite3_db_release_memory_multiple_calls_idempotent);185 RUN_TEST(test_sqlite3_db_release_memory_with_active_statement);186 return UNITY_END();187}