AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Global DB handle for tests */7static sqlite3 *gdb = NULL;8 9static Vdbe* makeVdbeWithMakeRecord(Parse *pParse, int *pAddr){10 Vdbe *v = sqlite3VdbeCreate(pParse);11 TEST_ASSERT_NOT_NULL_MESSAGE(v, "Failed to create Vdbe");12 int addr = sqlite3VdbeAddOp3(v, OP_MakeRecord, 0, 0, 0);13 if( pAddr ) *pAddr = addr;14 /* Sanity: ensure we added something */15 TEST_ASSERT_TRUE(addr >= 0);16 return v;17}18 19static VdbeOp* getOp(Vdbe *v, int addr){20 VdbeOp *op = sqlite3VdbeGetOp(v, addr);21 TEST_ASSERT_NOT_NULL_MESSAGE(op, "sqlite3VdbeGetOp returned NULL");22 return op;23}24 25void setUp(void) {26 int rc = sqlite3_open(":memory:", &gdb);27 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory sqlite3 DB");28}29 30void tearDown(void) {31 if( gdb ){32 sqlite3_close(gdb);33 gdb = NULL;34 }35}36 37/* Helper to allocate and initialize a Table with N columns and a Schema with given file_format.38 Caller must sqlite3DbFree(gdb, t.aCol) after use if allocated. */39static void initTableAndSchema(Table *pTab, Schema *pSchema, int nCol, int file_format){40 memset(pTab, 0, sizeof(*pTab));41 memset(pSchema, 0, sizeof(*pSchema));42 pSchema->file_format = file_format;43 pTab->pSchema = pSchema;44 pTab->nCol = (u16)nCol;45 pTab->aCol = (Column*)sqlite3DbMallocZero(gdb, sizeof(Column) * (nCol>0?nCol:1));46 TEST_ASSERT_NOT_NULL_MESSAGE(pTab->aCol, "Failed to allocate columns");47}48 49/* Test: file_format < 2 leaves P5 unchanged */50void test_sqlite3SetMakeRecordP5_file_format_lt2_leaves_p5_unchanged(void){51 Parse parse;52 memset(&parse, 0, sizeof(parse));53 parse.db = gdb;54 55 int addr = -1;56 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);57 58 /* Pre-set P5 to a sentinel to verify it does not change */59 sqlite3VdbeChangeP5(v, (u16)77);60 61 Schema s;62 Table t;63 initTableAndSchema(&t, &s, 4, 1); /* file_format < 2 */64 65 /* Populate columns with no defaults/PKs; shouldn't matter due to early return */66 for(int i=0; i<4; i++){67 t.aCol[i].iDflt = 0;68 t.aCol[i].colFlags = 0;69 }70 71 sqlite3SetMakeRecordP5(v, &t);72 73 VdbeOp *op = getOp(v, addr);74 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);75 TEST_ASSERT_EQUAL_UINT8(77, op->p5);76 77 sqlite3DbFree(gdb, t.aCol);78 sqlite3VdbeDelete(v);79}80 81/* Test: No defaults or PKs -> P5 becomes 1 */82void test_sqlite3SetMakeRecordP5_no_defaults_no_pk_sets_p5_1(void){83 Parse parse;84 memset(&parse, 0, sizeof(parse));85 parse.db = gdb;86 87 int addr;88 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);89 90 Schema s;91 Table t;92 initTableAndSchema(&t, &s, 5, 4); /* file_format >= 2 */93 for(int i=0; i<5; i++){94 t.aCol[i].iDflt = 0;95 t.aCol[i].colFlags = 0;96 }97 98 sqlite3SetMakeRecordP5(v, &t);99 100 VdbeOp *op = getOp(v, addr);101 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);102 TEST_ASSERT_EQUAL_UINT8(1, op->p5);103 104 sqlite3DbFree(gdb, t.aCol);105 sqlite3VdbeDelete(v);106}107 108/* Test: Last column has default -> P5 = nCol */109void test_sqlite3SetMakeRecordP5_last_col_default_sets_p5_ncol(void){110 Parse parse;111 memset(&parse, 0, sizeof(parse));112 parse.db = gdb;113 114 int addr;115 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);116 117 const int N = 5;118 Schema s;119 Table t;120 initTableAndSchema(&t, &s, N, 4);121 for(int i=0; i<N; i++){122 t.aCol[i].iDflt = 0;123 t.aCol[i].colFlags = 0;124 }125 t.aCol[N-1].iDflt = 1; /* rightmost column has default */126 127 sqlite3SetMakeRecordP5(v, &t);128 129 VdbeOp *op = getOp(v, addr);130 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);131 TEST_ASSERT_EQUAL_UINT8(N, op->p5);132 133 sqlite3DbFree(gdb, t.aCol);134 sqlite3VdbeDelete(v);135}136 137/* Test: Middle column has default -> P5 = index+1 */138void test_sqlite3SetMakeRecordP5_middle_col_default_sets_p5_index_plus_1(void){139 Parse parse;140 memset(&parse, 0, sizeof(parse));141 parse.db = gdb;142 143 int addr;144 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);145 146 const int N = 6;147 const int idx = 3; /* zero-based index of default-bearing column */148 Schema s;149 Table t;150 initTableAndSchema(&t, &s, N, 4);151 for(int i=0; i<N; i++){152 t.aCol[i].iDflt = 0;153 t.aCol[i].colFlags = 0;154 }155 t.aCol[idx].iDflt = 1;156 157 sqlite3SetMakeRecordP5(v, &t);158 159 VdbeOp *op = getOp(v, addr);160 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);161 TEST_ASSERT_EQUAL_UINT8(idx+1, op->p5);162 163 sqlite3DbFree(gdb, t.aCol);164 sqlite3VdbeDelete(v);165}166 167/* Test: Primary key flag in a column -> P5 = index+1 */168void test_sqlite3SetMakeRecordP5_pk_flag_sets_p5_index_plus_1(void){169 Parse parse;170 memset(&parse, 0, sizeof(parse));171 parse.db = gdb;172 173 int addr;174 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);175 176 const int N = 4;177 const int pkIdx = 2;178 Schema s;179 Table t;180 initTableAndSchema(&t, &s, N, 4);181 for(int i=0; i<N; i++){182 t.aCol[i].iDflt = 0;183 t.aCol[i].colFlags = 0;184 }185 t.aCol[pkIdx].colFlags |= COLFLAG_PRIMKEY;186 187 sqlite3SetMakeRecordP5(v, &t);188 189 VdbeOp *op = getOp(v, addr);190 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);191 TEST_ASSERT_EQUAL_UINT8(pkIdx+1, op->p5);192 193 sqlite3DbFree(gdb, t.aCol);194 sqlite3VdbeDelete(v);195}196 197/* Test: Only first column has default -> P5 should be 1 */198void test_sqlite3SetMakeRecordP5_only_first_col_default_results_in_p5_1(void){199 Parse parse;200 memset(&parse, 0, sizeof(parse));201 parse.db = gdb;202 203 int addr;204 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);205 206 const int N = 5;207 Schema s;208 Table t;209 initTableAndSchema(&t, &s, N, 4);210 for(int i=0; i<N; i++){211 t.aCol[i].iDflt = 0;212 t.aCol[i].colFlags = 0;213 }214 t.aCol[0].iDflt = 1; /* default only on first column */215 216 sqlite3SetMakeRecordP5(v, &t);217 218 VdbeOp *op = getOp(v, addr);219 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);220 TEST_ASSERT_EQUAL_UINT8(1, op->p5);221 222 sqlite3DbFree(gdb, t.aCol);223 sqlite3VdbeDelete(v);224}225 226/* Test: Single-column table -> P5 should be 1 */227void test_sqlite3SetMakeRecordP5_single_column_table_p5_1(void){228 Parse parse;229 memset(&parse, 0, sizeof(parse));230 parse.db = gdb;231 232 int addr;233 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);234 235 const int N = 1;236 Schema s;237 Table t;238 initTableAndSchema(&t, &s, N, 4);239 t.aCol[0].iDflt = 0;240 t.aCol[0].colFlags = 0;241 242 sqlite3SetMakeRecordP5(v, &t);243 244 VdbeOp *op = getOp(v, addr);245 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);246 TEST_ASSERT_EQUAL_UINT8(1, op->p5);247 248 sqlite3DbFree(gdb, t.aCol);249 sqlite3VdbeDelete(v);250}251 252/* Test: Multiple flagged columns; rightmost flagged dictates P5 */253void test_sqlite3SetMakeRecordP5_rightmost_flagged_column_dominates(void){254 Parse parse;255 memset(&parse, 0, sizeof(parse));256 parse.db = gdb;257 258 int addr;259 Vdbe *v = makeVdbeWithMakeRecord(&parse, &addr);260 261 const int N = 5;262 Schema s;263 Table t;264 initTableAndSchema(&t, &s, N, 4);265 for(int i=0; i<N; i++){266 t.aCol[i].iDflt = 0;267 t.aCol[i].colFlags = 0;268 }269 /* Set multiple flags: col 2 has PK, col 4 has default. Rightmost (4) should win -> P5=5 */270 t.aCol[2].colFlags |= COLFLAG_PRIMKEY;271 t.aCol[4].iDflt = 1;272 273 sqlite3SetMakeRecordP5(v, &t);274 275 VdbeOp *op = getOp(v, addr);276 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, op->opcode);277 TEST_ASSERT_EQUAL_UINT8(5, op->p5);278 279 sqlite3DbFree(gdb, t.aCol);280 sqlite3VdbeDelete(v);281}282 283int main(void) {284 UNITY_BEGIN();285 RUN_TEST(test_sqlite3SetMakeRecordP5_file_format_lt2_leaves_p5_unchanged);286 RUN_TEST(test_sqlite3SetMakeRecordP5_no_defaults_no_pk_sets_p5_1);287 RUN_TEST(test_sqlite3SetMakeRecordP5_last_col_default_sets_p5_ncol);288 RUN_TEST(test_sqlite3SetMakeRecordP5_middle_col_default_sets_p5_index_plus_1);289 RUN_TEST(test_sqlite3SetMakeRecordP5_pk_flag_sets_p5_index_plus_1);290 RUN_TEST(test_sqlite3SetMakeRecordP5_only_first_col_default_results_in_p5_1);291 RUN_TEST(test_sqlite3SetMakeRecordP5_single_column_table_p5_1);292 RUN_TEST(test_sqlite3SetMakeRecordP5_rightmost_flagged_column_dominates);293 return UNITY_END();294}