AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdio.h>4#include <stdlib.h>5#include <string.h>6 7#ifdef SQLITE_ENABLE_STAT48/* Wrapper for the static function under test (provided by the source module). */9extern void test_sampleInsert(StatAccum *p, StatSample *pNew, int nEqZero);10#endif11 12static sqlite3 *gDb = NULL;13 14void setUp(void) {15 /* Setup executed before each test */16}17 18void tearDown(void) {19 /* Cleanup executed after each test */20}21 22#ifdef SQLITE_ENABLE_STAT423/* Helpers to allocate and free StatAccum and StatSample internals */24static void initStatAccum(StatAccum *p, int nCol, int mxSample){25 memset(p, 0, sizeof(*p));26 if( gDb==NULL ){27 /* Ensure a db connection exists for any sqlite3Db* helpers used by internals */28 int rc = sqlite3_open(":memory:", &gDb);29 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);30 }31 p->db = gDb;32 p->nCol = nCol;33 p->nKeyCol = nCol>0 ? nCol-1 : 0;34 p->mxSample = mxSample;35 p->nSample = 0;36 p->nMaxEqZero = 0;37 p->iMin = 0;38 /* allocate the array of samples */39 p->a = (StatSample*)sqlite3_malloc64(sizeof(StatSample) * (size_t)mxSample);40 TEST_ASSERT_NOT_NULL(p->a);41 memset(p->a, 0, sizeof(StatSample) * (size_t)mxSample);42 for(int i=0;i<mxSample;i++){43 p->a[i].anEq = (tRowcnt*)sqlite3_malloc64(sizeof(tRowcnt) * (size_t)nCol);44 p->a[i].anLt = (tRowcnt*)sqlite3_malloc64(sizeof(tRowcnt) * (size_t)nCol);45 p->a[i].anDLt = (tRowcnt*)sqlite3_malloc64(sizeof(tRowcnt) * (size_t)nCol);46 TEST_ASSERT_NOT_NULL(p->a[i].anEq);47 TEST_ASSERT_NOT_NULL(p->a[i].anLt);48 TEST_ASSERT_NOT_NULL(p->a[i].anDLt);49 memset(p->a[i].anEq, 0, sizeof(tRowcnt) * (size_t)nCol);50 memset(p->a[i].anLt, 0, sizeof(tRowcnt) * (size_t)nCol);51 memset(p->a[i].anDLt, 0, sizeof(tRowcnt) * (size_t)nCol);52 p->a[i].isPSample = 0;53 p->a[i].iCol = 0;54 p->a[i].u.iRowid = 0;55 p->a[i].nRowid = 0;56 }57}58 59static void freeStatAccum(StatAccum *p){60 if( p && p->a ){61 /* Free the per-sample arrays */62 for(int i=0; i<p->mxSample; i++){63 if( p->a[i].anEq ) sqlite3_free(p->a[i].anEq);64 if( p->a[i].anLt ) sqlite3_free(p->a[i].anLt);65 if( p->a[i].anDLt ) sqlite3_free(p->a[i].anDLt);66 /* Rowid memory (u.aRowid) would be freed by sampleClear via sqlite3DbFree if set. */67 p->a[i].anEq = NULL;68 p->a[i].anLt = NULL;69 p->a[i].anDLt = NULL;70 }71 sqlite3_free(p->a);72 p->a = NULL;73 }74}75 76static void initSampleArrays(StatSample *s, int nCol){77 memset(s, 0, sizeof(*s));78 s->anEq = (tRowcnt*)sqlite3_malloc64(sizeof(tRowcnt) * (size_t)nCol);79 s->anLt = (tRowcnt*)sqlite3_malloc64(sizeof(tRowcnt) * (size_t)nCol);80 s->anDLt = (tRowcnt*)sqlite3_malloc64(sizeof(tRowcnt) * (size_t)nCol);81 TEST_ASSERT_NOT_NULL(s->anEq);82 TEST_ASSERT_NOT_NULL(s->anLt);83 TEST_ASSERT_NOT_NULL(s->anDLt);84 memset(s->anEq, 0, sizeof(tRowcnt) * (size_t)nCol);85 memset(s->anLt, 0, sizeof(tRowcnt) * (size_t)nCol);86 memset(s->anDLt, 0, sizeof(tRowcnt) * (size_t)nCol);87 s->nRowid = 0;88 s->u.iRowid = 0;89}90 91static void freeSampleArrays(StatSample *s){92 if( s->anEq ) sqlite3_free(s->anEq);93 if( s->anLt ) sqlite3_free(s->anLt);94 if( s->anDLt ) sqlite3_free(s->anDLt);95 s->anEq = s->anLt = s->anDLt = NULL;96}97 98/* Test 1: Basic insertion and zeroing behavior with available capacity */99void test_sampleInsert_basic_insertion_and_zeroing(void){100 const int nCol = 3;101 const int mxSample = 4;102 StatAccum acc; initStatAccum(&acc, nCol, mxSample);103 104 /* Prepare new periodic sample (so we skip upgrade path) */105 StatSample newS; initSampleArrays(&newS, nCol);106 newS.isPSample = 1; /* periodic sample */107 newS.iCol = 0;108 newS.anEq[0] = 7; newS.anEq[1] = 8; newS.anEq[2] = 9;109 newS.anLt[0] = 0; newS.anLt[1] = 0; newS.anLt[2] = 5; /* last column orders samples */110 newS.anDLt[0] = 0; newS.anDLt[1] = 0; newS.anDLt[2] = 0;111 112 TEST_ASSERT_EQUAL_INT(0, acc.nSample);113 TEST_ASSERT_EQUAL_INT(0, acc.nMaxEqZero);114 115 test_sampleInsert(&acc, &newS, 2);116 117 TEST_ASSERT_EQUAL_INT(1, acc.nSample);118 TEST_ASSERT_EQUAL_INT(2, acc.nMaxEqZero);119 TEST_ASSERT_TRUE(acc.a[0].isPSample == 1);120 TEST_ASSERT_EQUAL_INT(0, acc.a[0].iCol);121 TEST_ASSERT_EQUAL_INT64(5, acc.a[0].anLt[nCol-1]);122 /* First 2 anEq entries zeroed, the remainder preserved */123 TEST_ASSERT_EQUAL_INT64(0, acc.a[0].anEq[0]);124 TEST_ASSERT_EQUAL_INT64(0, acc.a[0].anEq[1]);125 TEST_ASSERT_EQUAL_INT64(9, acc.a[0].anEq[2]);126 127 freeSampleArrays(&newS);128 freeStatAccum(&acc);129}130 131/* Test 2: When full, removal uses p->iMin index and reuses its buffer pointers */132void test_sampleInsert_full_array_removal_reuse_pointers(void){133 const int nCol = 3;134 const int mxSample = 3;135 StatAccum acc; initStatAccum(&acc, nCol, mxSample);136 137 /* Fill up acc.a with 3 non-PSample entries, increasing anLt last column */138 for(int i=0;i<mxSample;i++){139 acc.a[i].isPSample = 0;140 acc.a[i].iCol = 0;141 acc.a[i].anEq[0] = (tRowcnt)(1+i);142 acc.a[i].anEq[1] = 0;143 acc.a[i].anEq[2] = 0;144 acc.a[i].anLt[nCol-1] = (tRowcnt)((i+1)*10); /* 10,20,30 */145 }146 acc.nSample = mxSample;147 acc.iMin = 1; /* mark middle entry as least desirable to be removed */148 149 /* Keep pointers of the to-be-removed entry's buffers */150 tRowcnt *oldEq = acc.a[acc.iMin].anEq;151 tRowcnt *oldLt = acc.a[acc.iMin].anLt;152 tRowcnt *oldDLt = acc.a[acc.iMin].anDLt;153 154 /* New periodic sample with last column anLt > last remaining (30) */155 StatSample newS; initSampleArrays(&newS, nCol);156 newS.isPSample = 1;157 newS.iCol = 0;158 newS.anEq[0] = 55; newS.anEq[1] = 77; newS.anEq[2] = 99;159 newS.anLt[0] = 0; newS.anLt[1] = 0; newS.anLt[2] = 35; /* > 30 after removal */160 161 test_sampleInsert(&acc, &newS, 1);162 163 /* Still full */164 TEST_ASSERT_EQUAL_INT(mxSample, acc.nSample);165 /* The new last entry should reuse the removed entry's buffers */166 StatSample *last = &acc.a[mxSample-1];167 TEST_ASSERT_TRUE(last->anEq == oldEq);168 TEST_ASSERT_TRUE(last->anLt == oldLt);169 TEST_ASSERT_TRUE(last->anDLt == oldDLt);170 /* nEqZero=1 => first element zeroed, others copied from newS */171 TEST_ASSERT_EQUAL_INT64(0, last->anEq[0]);172 TEST_ASSERT_EQUAL_INT64(77, last->anEq[1]);173 TEST_ASSERT_EQUAL_INT64(99, last->anEq[2]);174 TEST_ASSERT_TRUE(last->isPSample == 1);175 TEST_ASSERT_EQUAL_INT(0, last->iCol);176 TEST_ASSERT_EQUAL_INT64(35, last->anLt[nCol-1]);177 /* iMin recomputed and must point to a non-PSample entry */178 TEST_ASSERT_TRUE(acc.iMin >= 0 && acc.iMin < mxSample);179 TEST_ASSERT_TRUE(acc.a[acc.iMin].isPSample == 0);180 181 freeSampleArrays(&newS);182 freeStatAccum(&acc);183}184 185/* Test 3: Upgrade an existing non-PSample sharing the prefix (no append) */186void test_sampleInsert_upgrade_existing_sample(void){187 const int nCol = 3;188 const int mxSample = 3;189 StatAccum acc; initStatAccum(&acc, nCol, mxSample);190 191 /* Existing non-PSample with iCol=1, anEq[1]=3, anEq[0]=0 so it shares prefix at col0 */192 acc.a[0].isPSample = 0;193 acc.a[0].iCol = 1;194 acc.a[0].anEq[0] = 0;195 acc.a[0].anEq[1] = 3;196 acc.a[0].anLt[nCol-1] = 10;197 acc.nSample = 1;198 199 /* New non-PSample prefers this prefix (anEq[0]=5 > old anEq[1]=3) */200 StatSample newS; initSampleArrays(&newS, nCol);201 newS.isPSample = 0;202 newS.iCol = 0;203 newS.anEq[0] = 5; /* > 3 so sampleIsBetter is true */204 newS.anLt[nCol-1] = 20; /* Must be > last a[n-1] for order, here 10 */205 206 test_sampleInsert(&acc, &newS, 0);207 208 /* No append: nSample unchanged */209 TEST_ASSERT_EQUAL_INT(1, acc.nSample);210 /* Upgraded existing sample's iCol and anEq at that column */211 TEST_ASSERT_EQUAL_INT(0, acc.a[0].iCol);212 TEST_ASSERT_EQUAL_INT64(5, acc.a[0].anEq[0]);213 /* Other fields (like anLt) unchanged for the upgraded sample */214 TEST_ASSERT_EQUAL_INT64(10, acc.a[0].anLt[nCol-1]);215 216 freeSampleArrays(&newS);217 freeStatAccum(&acc);218}219 220/* Test 4: Early return if an existing PSample shares the prefix */221void test_sampleInsert_skip_due_to_psample(void){222 const int nCol = 3;223 const int mxSample = 2;224 StatAccum acc; initStatAccum(&acc, nCol, mxSample);225 226 /* Existing PSample with iCol=1 and anEq[0]==0 so it shares prefix col0 */227 acc.a[0].isPSample = 1;228 acc.a[0].iCol = 1;229 acc.a[0].anEq[0] = 0;230 acc.a[0].anEq[1] = 3;231 acc.a[0].anLt[nCol-1] = 10;232 acc.nSample = 1;233 acc.nMaxEqZero = 1;234 235 /* New non-PSample attempts same prefix */236 StatSample newS; initSampleArrays(&newS, nCol);237 newS.isPSample = 0;238 newS.iCol = 0;239 newS.anEq[0] = 5; /* required by assert */240 newS.anLt[nCol-1] = 20; /* ordering OK */241 242 test_sampleInsert(&acc, &newS, 2);243 244 /* Function should return early: no change to samples count or contents */245 TEST_ASSERT_EQUAL_INT(1, acc.nSample);246 TEST_ASSERT_TRUE(acc.a[0].isPSample == 1);247 TEST_ASSERT_EQUAL_INT(1, acc.a[0].iCol);248 TEST_ASSERT_EQUAL_INT64(0, acc.a[0].anEq[0]);249 /* But nMaxEqZero is updated before the early return */250 TEST_ASSERT_EQUAL_INT(2, acc.nMaxEqZero);251 252 freeSampleArrays(&newS);253 freeStatAccum(&acc);254}255 256#endif /* SQLITE_ENABLE_STAT4 */257 258int main(void){259 UNITY_BEGIN();260#ifndef SQLITE_ENABLE_STAT4261 TEST_IGNORE_MESSAGE("SQLITE_ENABLE_STAT4 not defined; sampleInsert tests skipped.");262#else263 RUN_TEST(test_sampleInsert_basic_insertion_and_zeroing);264 RUN_TEST(test_sampleInsert_full_array_removal_reuse_pointers);265 RUN_TEST(test_sampleInsert_upgrade_existing_sample);266 RUN_TEST(test_sampleInsert_skip_due_to_psample);267#endif268 int rc = UNITY_END();269#ifdef SQLITE_ENABLE_STAT4270 if( gDb ){ sqlite3_close(gDb); gDb = NULL; }271#endif272 return rc;273}