AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* The target function wrapper provided in the module */7extern int test_statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo);8 9static void initIndexInfo(10 sqlite3_index_info *p,11 sqlite3_index_constraint *aCons, int nCons,12 sqlite3_index_orderby *aOb, int nOb,13 sqlite3_index_constraint_usage *aUsage14){15 memset(p, 0, sizeof(*p));16 p->nConstraint = nCons;17 p->aConstraint = aCons;18 p->aConstraintUsage = aUsage;19 p->nOrderBy = nOb;20 p->aOrderBy = aOb;21 p->idxNum = 0;22 p->idxFlags = 0;23 p->orderByConsumed = 0;24 p->estimatedCost = 0.0;25}26 27void setUp(void) {28 /* Setup code here, or leave empty */29}30 31void tearDown(void) {32 /* Cleanup code here, or leave empty */33}34 35/* Test: No constraints or order by. Check defaults and flags. */36void test_statBestIndex_no_constraints_sets_flags_and_cost(void) {37 sqlite3_index_info idx;38 sqlite3_index_constraint_usage usage[1];39 sqlite3_index_constraint cons[1];40 sqlite3_index_orderby ob[1];41 memset(usage, 0, sizeof(usage));42 memset(cons, 0, sizeof(cons));43 memset(ob, 0, sizeof(ob));44 initIndexInfo(&idx, cons, 0, ob, 0, usage);45 46 int rc = test_statBestIndex(NULL, &idx);47 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);48 TEST_ASSERT_EQUAL_INT(0, idx.idxNum);49 TEST_ASSERT_EQUAL_INT(0, idx.orderByConsumed);50 TEST_ASSERT_EQUAL_DOUBLE(1.0, idx.estimatedCost);51 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);52}53 54/* Test: schema=? usable constraint */55void test_statBestIndex_schema_eq_sets_argvIndex_and_omit_and_idxNum(void) {56 sqlite3_index_info idx;57 sqlite3_index_constraint cons[1];58 sqlite3_index_constraint_usage usage[1];59 sqlite3_index_orderby ob[1];60 memset(usage, 0, sizeof(usage));61 memset(cons, 0, sizeof(cons));62 memset(ob, 0, sizeof(ob));63 64 cons[0].iColumn = 10; /* schema */65 cons[0].op = SQLITE_INDEX_CONSTRAINT_EQ;66 cons[0].usable = 1;67 68 initIndexInfo(&idx, cons, 1, ob, 0, usage);69 70 int rc = test_statBestIndex(NULL, &idx);71 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);72 TEST_ASSERT_EQUAL_INT(1, idx.aConstraintUsage[0].argvIndex);73 TEST_ASSERT_EQUAL_INT(1, idx.aConstraintUsage[0].omit);74 TEST_ASSERT_TRUE((idx.idxNum & 0x01) != 0);75 TEST_ASSERT_EQUAL_DOUBLE(1.0, idx.estimatedCost);76 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);77}78 79/* Test: schema=? and name=? constraints, regardless of array order */80void test_statBestIndex_schema_and_name_ordering(void) {81 sqlite3_index_info idx;82 sqlite3_index_constraint cons[2];83 sqlite3_index_constraint_usage usage[2];84 sqlite3_index_orderby ob[1];85 memset(usage, 0, sizeof(usage));86 memset(cons, 0, sizeof(cons));87 memset(ob, 0, sizeof(ob));88 89 /* Put name first then schema to ensure function still orders argvIndex90 as schema=1, name=2 */91 cons[0].iColumn = 0; /* name */92 cons[0].op = SQLITE_INDEX_CONSTRAINT_EQ;93 cons[0].usable = 1;94 95 cons[1].iColumn = 10; /* schema */96 cons[1].op = SQLITE_INDEX_CONSTRAINT_EQ;97 cons[1].usable = 1;98 99 initIndexInfo(&idx, cons, 2, ob, 0, usage);100 101 int rc = test_statBestIndex(NULL, &idx);102 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);103 104 TEST_ASSERT_EQUAL_INT(2, idx.aConstraintUsage[0].argvIndex); /* name gets 2 */105 TEST_ASSERT_EQUAL_INT(0, idx.aConstraintUsage[0].omit);106 107 TEST_ASSERT_EQUAL_INT(1, idx.aConstraintUsage[1].argvIndex); /* schema gets 1 */108 TEST_ASSERT_EQUAL_INT(1, idx.aConstraintUsage[1].omit);109 110 TEST_ASSERT_TRUE((idx.idxNum & 0x01) != 0); /* schema bit */111 TEST_ASSERT_TRUE((idx.idxNum & 0x02) != 0); /* name bit */112 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);113}114 115/* Test: All three constraints: schema, name, aggregate */116void test_statBestIndex_all_three_constraints(void) {117 sqlite3_index_info idx;118 sqlite3_index_constraint cons[3];119 sqlite3_index_constraint_usage usage[3];120 sqlite3_index_orderby ob[1];121 memset(usage, 0, sizeof(usage));122 memset(cons, 0, sizeof(cons));123 memset(ob, 0, sizeof(ob));124 125 cons[0].iColumn = 11; /* aggregate */126 cons[0].op = SQLITE_INDEX_CONSTRAINT_EQ;127 cons[0].usable = 1;128 129 cons[1].iColumn = 0; /* name */130 cons[1].op = SQLITE_INDEX_CONSTRAINT_EQ;131 cons[1].usable = 1;132 133 cons[2].iColumn = 10; /* schema */134 cons[2].op = SQLITE_INDEX_CONSTRAINT_EQ;135 cons[2].usable = 1;136 137 initIndexInfo(&idx, cons, 3, ob, 0, usage);138 139 int rc = test_statBestIndex(NULL, &idx);140 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);141 142 /* argvIndex assignment order: schema=1, name=2, aggregate=3 */143 TEST_ASSERT_EQUAL_INT(3, idx.aConstraintUsage[0].argvIndex); /* aggregate */144 TEST_ASSERT_EQUAL_INT(0, idx.aConstraintUsage[0].omit);145 146 TEST_ASSERT_EQUAL_INT(2, idx.aConstraintUsage[1].argvIndex); /* name */147 TEST_ASSERT_EQUAL_INT(0, idx.aConstraintUsage[1].omit);148 149 TEST_ASSERT_EQUAL_INT(1, idx.aConstraintUsage[2].argvIndex); /* schema */150 TEST_ASSERT_EQUAL_INT(1, idx.aConstraintUsage[2].omit);151 152 TEST_ASSERT_TRUE((idx.idxNum & 0x01) != 0);153 TEST_ASSERT_TRUE((idx.idxNum & 0x02) != 0);154 TEST_ASSERT_TRUE((idx.idxNum & 0x04) != 0);155 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);156}157 158/* Test: Unusable EQ constraint causes SQLITE_CONSTRAINT early return */159void test_statBestIndex_unusable_eq_causes_SQLITE_CONSTRAINT(void) {160 sqlite3_index_info idx;161 sqlite3_index_constraint cons[1];162 sqlite3_index_constraint_usage usage[1];163 sqlite3_index_orderby ob[1];164 memset(usage, 0, sizeof(usage));165 memset(cons, 0, sizeof(cons));166 memset(ob, 0, sizeof(ob));167 168 cons[0].iColumn = 0; /* name */169 cons[0].op = SQLITE_INDEX_CONSTRAINT_EQ;170 cons[0].usable = 0; /* unusable */171 172 initIndexInfo(&idx, cons, 1, ob, 0, usage);173 174 int rc = test_statBestIndex(NULL, &idx);175 TEST_ASSERT_EQUAL_INT(SQLITE_CONSTRAINT, rc);176}177 178/* Test: ORDER BY name ASC is consumed and sets bit 0x08 */179void test_statBestIndex_orderBy_consumed_one_column(void) {180 sqlite3_index_info idx;181 sqlite3_index_constraint cons[1];182 sqlite3_index_constraint_usage usage[1];183 sqlite3_index_orderby ob[1];184 memset(usage, 0, sizeof(usage));185 memset(cons, 0, sizeof(cons));186 memset(ob, 0, sizeof(ob));187 188 ob[0].iColumn = 0; /* name */189 ob[0].desc = 0; /* ASC */190 191 initIndexInfo(&idx, cons, 0, ob, 1, usage);192 193 int rc = test_statBestIndex(NULL, &idx);194 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);195 TEST_ASSERT_EQUAL_INT(1, idx.orderByConsumed);196 TEST_ASSERT_TRUE((idx.idxNum & 0x08) != 0);197 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);198}199 200/* Test: ORDER BY name ASC, path ASC is consumed and sets bit 0x08 */201void test_statBestIndex_orderBy_consumed_two_columns(void) {202 sqlite3_index_info idx;203 sqlite3_index_constraint cons[1];204 sqlite3_index_constraint_usage usage[1];205 sqlite3_index_orderby ob[2];206 memset(usage, 0, sizeof(usage));207 memset(cons, 0, sizeof(cons));208 memset(ob, 0, sizeof(ob));209 210 ob[0].iColumn = 0; /* name */211 ob[0].desc = 0; /* ASC */212 ob[1].iColumn = 1; /* path */213 ob[1].desc = 0; /* ASC */214 215 initIndexInfo(&idx, cons, 0, ob, 2, usage);216 217 int rc = test_statBestIndex(NULL, &idx);218 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);219 TEST_ASSERT_EQUAL_INT(1, idx.orderByConsumed);220 TEST_ASSERT_TRUE((idx.idxNum & 0x08) != 0);221 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);222}223 224/* Test: ORDER BY name DESC is not consumed */225void test_statBestIndex_orderBy_not_consumed_desc(void) {226 sqlite3_index_info idx;227 sqlite3_index_constraint cons[1];228 sqlite3_index_constraint_usage usage[1];229 sqlite3_index_orderby ob[1];230 memset(usage, 0, sizeof(usage));231 memset(cons, 0, sizeof(cons));232 memset(ob, 0, sizeof(ob));233 234 ob[0].iColumn = 0; /* name */235 ob[0].desc = 1; /* DESC */236 237 initIndexInfo(&idx, cons, 0, ob, 1, usage);238 239 int rc = test_statBestIndex(NULL, &idx);240 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);241 TEST_ASSERT_EQUAL_INT(0, idx.orderByConsumed);242 TEST_ASSERT_TRUE((idx.idxNum & 0x08) == 0);243 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);244}245 246/* Test: ORDER BY wrong columns is not consumed */247void test_statBestIndex_orderBy_not_consumed_wrong_columns(void) {248 sqlite3_index_info idx;249 sqlite3_index_constraint cons[1];250 sqlite3_index_constraint_usage usage[1];251 sqlite3_index_orderby ob[2];252 memset(usage, 0, sizeof(usage));253 memset(cons, 0, sizeof(cons));254 memset(ob, 0, sizeof(ob));255 256 /* First order by path, not name */257 ob[0].iColumn = 1; /* path */258 ob[0].desc = 0; /* ASC */259 ob[1].iColumn = 0; /* name */260 ob[1].desc = 0;261 262 initIndexInfo(&idx, cons, 0, ob, 2, usage);263 264 int rc = test_statBestIndex(NULL, &idx);265 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);266 TEST_ASSERT_EQUAL_INT(0, idx.orderByConsumed);267 TEST_ASSERT_TRUE((idx.idxNum & 0x08) == 0);268 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);269}270 271/* Test: Non-EQ unusable constraint should be ignored (no SQLITE_CONSTRAINT) */272void test_statBestIndex_non_eq_unusable_does_not_fail(void) {273 sqlite3_index_info idx;274 sqlite3_index_constraint cons[1];275 sqlite3_index_constraint_usage usage[1];276 sqlite3_index_orderby ob[1];277 memset(usage, 0, sizeof(usage));278 memset(cons, 0, sizeof(cons));279 memset(ob, 0, sizeof(ob));280 281 cons[0].iColumn = 0; /* name */282 cons[0].op = SQLITE_INDEX_CONSTRAINT_LT; /* not EQ */283 cons[0].usable = 0; /* unusable but should be ignored */284 285 initIndexInfo(&idx, cons, 1, ob, 0, usage);286 287 int rc = test_statBestIndex(NULL, &idx);288 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);289 TEST_ASSERT_EQUAL_INT(0, idx.idxNum);290 TEST_ASSERT_TRUE((idx.idxFlags & SQLITE_INDEX_SCAN_HEX) != 0);291}292 293int main(void) {294 UNITY_BEGIN();295 RUN_TEST(test_statBestIndex_no_constraints_sets_flags_and_cost);296 RUN_TEST(test_statBestIndex_schema_eq_sets_argvIndex_and_omit_and_idxNum);297 RUN_TEST(test_statBestIndex_schema_and_name_ordering);298 RUN_TEST(test_statBestIndex_all_three_constraints);299 RUN_TEST(test_statBestIndex_unusable_eq_causes_SQLITE_CONSTRAINT);300 RUN_TEST(test_statBestIndex_orderBy_consumed_one_column);301 RUN_TEST(test_statBestIndex_orderBy_consumed_two_columns);302 RUN_TEST(test_statBestIndex_orderBy_not_consumed_desc);303 RUN_TEST(test_statBestIndex_orderBy_not_consumed_wrong_columns);304 RUN_TEST(test_statBestIndex_non_eq_unusable_does_not_fail);305 return UNITY_END();306}