AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdio.h>6 7/* Wrapper for the static function provided by the build as per instructions */8void test_codeTableLocks(Parse *pParse);9 10void setUp(void) {11 /* Setup code here, or leave empty */12}13 14void tearDown(void) {15 /* Cleanup code here, or leave empty */16}17 18/* Helper: initialize a Parse with a fresh DB and Vdbe */19static void initParseWithVdbe(Parse *pParse, sqlite3 **ppDb, Vdbe **ppVdbe){20 memset(pParse, 0, sizeof(*pParse));21 *ppDb = 0;22 *ppVdbe = 0;23 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", ppDb));24 TEST_ASSERT_NOT_NULL(*ppDb);25 26 *ppVdbe = sqlite3VdbeCreate(*ppDb);27 TEST_ASSERT_NOT_NULL(*ppVdbe);28 29 pParse->db = *ppDb;30 pParse->pVdbe = *ppVdbe;31}32 33/* Helper: cleanup db/vdbe */34static void cleanupParseVdbe(sqlite3 *db, Vdbe *v){35 if( v ) sqlite3VdbeDelete(v);36 if( db ) sqlite3_close(db);37}38 39/* Test 1: No locks -> no opcodes added */40void test_codeTableLocks_no_locks(void){41 sqlite3 *db = NULL;42 Vdbe *v = NULL;43 Parse parse;44 45 initParseWithVdbe(&parse, &db, &v);46 47 parse.aTableLock = NULL;48 parse.nTableLock = 0;49 50 int nOpBefore = v->nOp;51 test_codeTableLocks(&parse);52 TEST_ASSERT_EQUAL_INT(nOpBefore, v->nOp); /* No ops added */53 54 cleanupParseVdbe(db, v);55}56 57/* Test 2: Multiple locks -> one OP_TableLock per entry with correct fields */58void test_codeTableLocks_multiple_locks_correct_opcodes(void){59 sqlite3 *db = NULL;60 Vdbe *v = NULL;61 Parse parse;62 63 initParseWithVdbe(&parse, &db, &v);64 65 const char *z0 = "main.t1";66 const char *z1 = NULL; /* Test NULL zLockName */67 const char *z2 = "aux.t3";68 69 TableLock locks[3];70 locks[0].iDb = 0; locks[0].iTab = 1; locks[0].isWriteLock = 1; locks[0].zLockName = z0;71 locks[1].iDb = 2; locks[1].iTab = 99; locks[1].isWriteLock = 0; locks[1].zLockName = z1;72 locks[2].iDb = 1; locks[2].iTab = 12345; locks[2].isWriteLock = 1; locks[2].zLockName = z2;73 74 parse.aTableLock = locks;75 parse.nTableLock = 3;76 77 int nOpBefore = v->nOp;78 test_codeTableLocks(&parse);79 80 TEST_ASSERT_EQUAL_INT(nOpBefore + 3, v->nOp);81 82 /* Verify each appended opcode */83 int iStart = nOpBefore;84 Op *aOp = v->aOp;85 86 /* Check op 0 */87 TEST_ASSERT_EQUAL_INT(OP_TableLock, aOp[iStart+0].opcode);88 TEST_ASSERT_EQUAL_INT(locks[0].iDb, aOp[iStart+0].p1);89 TEST_ASSERT_EQUAL_INT(locks[0].iTab, aOp[iStart+0].p2);90 TEST_ASSERT_EQUAL_INT(locks[0].isWriteLock, aOp[iStart+0].p3);91 TEST_ASSERT_EQUAL_INT(P4_STATIC, aOp[iStart+0].p4type);92 TEST_ASSERT_TRUE(aOp[iStart+0].p4.z == z0);93 94 /* Check op 1 (NULL zLockName) */95 TEST_ASSERT_EQUAL_INT(OP_TableLock, aOp[iStart+1].opcode);96 TEST_ASSERT_EQUAL_INT(locks[1].iDb, aOp[iStart+1].p1);97 TEST_ASSERT_EQUAL_INT(locks[1].iTab, aOp[iStart+1].p2);98 TEST_ASSERT_EQUAL_INT(locks[1].isWriteLock, aOp[iStart+1].p3);99 TEST_ASSERT_EQUAL_INT(P4_STATIC, aOp[iStart+1].p4type);100 TEST_ASSERT_TRUE(aOp[iStart+1].p4.z == NULL);101 102 /* Check op 2 */103 TEST_ASSERT_EQUAL_INT(OP_TableLock, aOp[iStart+2].opcode);104 TEST_ASSERT_EQUAL_INT(locks[2].iDb, aOp[iStart+2].p1);105 TEST_ASSERT_EQUAL_INT(locks[2].iTab, aOp[iStart+2].p2);106 TEST_ASSERT_EQUAL_INT(locks[2].isWriteLock, aOp[iStart+2].p3);107 TEST_ASSERT_EQUAL_INT(P4_STATIC, aOp[iStart+2].p4type);108 TEST_ASSERT_TRUE(aOp[iStart+2].p4.z == z2);109 110 cleanupParseVdbe(db, v);111}112 113/* Test 3: Appending behavior: calling again appends more locks in order */114void test_codeTableLocks_appends_on_subsequent_calls(void){115 sqlite3 *db = NULL;116 Vdbe *v = NULL;117 Parse parse;118 119 initParseWithVdbe(&parse, &db, &v);120 121 const char *zA = "A";122 const char *zB = "B";123 const char *zC = "C";124 125 TableLock locks[3];126 /* First, a single lock */127 locks[0].iDb = 0; locks[0].iTab = 10; locks[0].isWriteLock = 1; locks[0].zLockName = zA;128 129 parse.aTableLock = locks;130 parse.nTableLock = 1;131 132 test_codeTableLocks(&parse);133 TEST_ASSERT_EQUAL_INT(1, v->nOp);134 Op *aOp = v->aOp;135 TEST_ASSERT_EQUAL_INT(OP_TableLock, aOp[0].opcode);136 TEST_ASSERT_EQUAL_INT(0, aOp[0].p1);137 TEST_ASSERT_EQUAL_INT(10, aOp[0].p2);138 TEST_ASSERT_EQUAL_INT(1, aOp[0].p3);139 TEST_ASSERT_EQUAL_INT(P4_STATIC, aOp[0].p4type);140 TEST_ASSERT_TRUE(aOp[0].p4.z == zA);141 142 /* Now, expand to three locks and call again: should append 3 more ops */143 locks[1].iDb = 1; locks[1].iTab = 20; locks[1].isWriteLock = 0; locks[1].zLockName = zB;144 locks[2].iDb = 2; locks[2].iTab = 30; locks[2].isWriteLock = 1; locks[2].zLockName = zC;145 parse.nTableLock = 3;146 147 int nOpBefore = v->nOp; /* 1 */148 test_codeTableLocks(&parse);149 TEST_ASSERT_EQUAL_INT(nOpBefore + 3, v->nOp);150 151 aOp = v->aOp; /* Might have been reallocated; refresh pointer */152 int iStart = nOpBefore;153 154 /* Verify the three appended ops correspond to locks[0], locks[1], locks[2] in order */155 for(int i=0; i<3; i++){156 TEST_ASSERT_EQUAL_INT(OP_TableLock, aOp[iStart+i].opcode);157 TEST_ASSERT_EQUAL_INT(locks[i].iDb, aOp[iStart+i].p1);158 TEST_ASSERT_EQUAL_INT(locks[i].iTab, aOp[iStart+i].p2);159 TEST_ASSERT_EQUAL_INT(locks[i].isWriteLock, aOp[iStart+i].p3);160 TEST_ASSERT_EQUAL_INT(P4_STATIC, aOp[iStart+i].p4type);161 TEST_ASSERT_TRUE(aOp[iStart+i].p4.z == locks[i].zLockName);162 }163 164 /* Ensure the original first op remains intact */165 TEST_ASSERT_EQUAL_INT(OP_TableLock, aOp[0].opcode);166 TEST_ASSERT_EQUAL_INT(0, aOp[0].p1);167 TEST_ASSERT_EQUAL_INT(10, aOp[0].p2);168 TEST_ASSERT_EQUAL_INT(1, aOp[0].p3);169 TEST_ASSERT_EQUAL_INT(P4_STATIC, aOp[0].p4type);170 TEST_ASSERT_TRUE(aOp[0].p4.z == zA);171 172 cleanupParseVdbe(db, v);173}174 175/* Test 4: Dynamic zLockName memory is preserved (P4_STATIC) and pointer equality holds */176void test_codeTableLocks_dynamic_zLockName_pointer_used(void){177 sqlite3 *db = NULL;178 Vdbe *v = NULL;179 Parse parse;180 181 initParseWithVdbe(&parse, &db, &v);182 183 /* Allocate dynamic name using sqlite3_mprintf (so we can free it after) */184 char *zDyn = sqlite3_mprintf("dyn_lock_%d", 42);185 TEST_ASSERT_NOT_NULL(zDyn);186 187 TableLock lock;188 lock.iDb = 3;189 lock.iTab = 777;190 lock.isWriteLock = 1;191 lock.zLockName = zDyn;192 193 parse.aTableLock = &lock;194 parse.nTableLock = 1;195 196 int nOpBefore = v->nOp;197 test_codeTableLocks(&parse);198 TEST_ASSERT_EQUAL_INT(nOpBefore + 1, v->nOp);199 200 Op *op = &v->aOp[nOpBefore];201 TEST_ASSERT_EQUAL_INT(OP_TableLock, op->opcode);202 TEST_ASSERT_EQUAL_INT(lock.iDb, op->p1);203 TEST_ASSERT_EQUAL_INT(lock.iTab, op->p2);204 TEST_ASSERT_EQUAL_INT(lock.isWriteLock, op->p3);205 TEST_ASSERT_EQUAL_INT(P4_STATIC, op->p4type);206 TEST_ASSERT_TRUE(op->p4.z == zDyn); /* Pointer equality expected with P4_STATIC */207 208 /* Since P4_STATIC was used, Vdbe will not free zDyn. We must free it here. */209 sqlite3_free(zDyn);210 211 cleanupParseVdbe(db, v);212}213 214int main(void) {215 UNITY_BEGIN();216 RUN_TEST(test_codeTableLocks_no_locks);217 RUN_TEST(test_codeTableLocks_multiple_locks_correct_opcodes);218 RUN_TEST(test_codeTableLocks_appends_on_subsequent_calls);219 RUN_TEST(test_codeTableLocks_dynamic_zLockName_pointer_used);220 return UNITY_END();221}