AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Helpers to construct test data */7 8static Table* makeTableWithCols(sqlite3 *db, Schema *pSchema, const char **azCol, int nCol){9 Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));10 TEST_ASSERT_NOT_NULL(pTab);11 pTab->pSchema = pSchema;12 pTab->nCol = nCol;13 pTab->tabFlags = 0; /* Ordinary table */14 pTab->u.tab.pFKey = 0;15 16 pTab->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nCol);17 TEST_ASSERT_NOT_NULL(pTab->aCol);18 for(int i=0; i<nCol; i++){19 pTab->aCol[i].zCnName = sqlite3DbStrDup(db, azCol[i]);20 TEST_ASSERT_NOT_NULL(pTab->aCol[i].zCnName);21 }22 return pTab;23}24 25/* Construct a minimal ExprList with n items, setting zEName strings if names provided.26 Items' pExpr fields are left NULL since sqlite3CreateForeignKey only uses zEName and nExpr. */27static ExprList* makeExprList(sqlite3 *db, const char **azName, int n){28 if( n<=0 ) return 0;29 int nByte = sizeof(ExprList) + (n-1)*(int)sizeof(struct ExprList_item);30 ExprList *pList = (ExprList*)sqlite3DbMallocZero(db, nByte);31 TEST_ASSERT_NOT_NULL(pList);32 pList->nExpr = n;33 pList->nAlloc = n;34 for(int i=0; i<n; i++){35 if( azName && azName[i] ){36 pList->a[i].zEName = sqlite3DbStrDup(db, azName[i]);37 TEST_ASSERT_NOT_NULL(pList->a[i].zEName);38 }else{39 pList->a[i].zEName = 0;40 }41 pList->a[i].pExpr = 0;42 }43 return pList;44}45 46static void freeTableAndFKeys(sqlite3 *db, Table *pTab){47 if( !pTab ) return;48 /* Remove and free all FKeys linked to this table and from schema hash */49 sqlite3FkDelete(db, pTab);50 /* Free column name strings and array, then table itself */51 if( pTab->aCol ){52 for(int i=0; i<pTab->nCol; i++){53 if( pTab->aCol[i].zCnName ){54 sqlite3DbFree(db, pTab->aCol[i].zCnName);55 }56 }57 sqlite3DbFree(db, pTab->aCol);58 }59 sqlite3DbFree(db, pTab);60}61 62/* Unity setUp/tearDown */63void setUp(void) {64 /* Nothing global to set up */65}66void tearDown(void) {67 /* Nothing global to tear down */68}69 70/* Tests */71 72void test_sqlite3CreateForeignKey_basic_singlecol_lastcol(void){73 sqlite3 *db = 0;74 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));75 Schema *pSchema = db->aDb[0].pSchema;76 77 const char *cols[] = {"c1","c2"};78 Table *pTab = makeTableWithCols(db, pSchema, cols, 2);79 80 Parse pParse;81 memset(&pParse, 0, sizeof(pParse));82 pParse.db = db;83 pParse.pNewTable = pTab;84 85 Token tTo;86 tTo.z = "parent";87 tTo.n = (int)strlen(tTo.z);88 89 int flags = ((int)0x77 << 8) | 0x55; /* ON UPDATE=0x77, ON DELETE=0x55 */90 sqlite3CreateForeignKey(&pParse, 0, &tTo, 0, flags);91 92 FKey *pFk = pTab->u.tab.pFKey;93 TEST_ASSERT_NOT_NULL(pFk);94 TEST_ASSERT_EQUAL_PTR(pTab, pFk->pFrom);95 TEST_ASSERT_EQUAL_INT(1, pFk->nCol);96 TEST_ASSERT_EQUAL_INT(pTab->nCol - 1, pFk->aCol[0].iFrom);97 TEST_ASSERT_EQUAL_UINT8(0x55, pFk->aAction[0]);98 TEST_ASSERT_EQUAL_UINT8(0x77, pFk->aAction[1]);99 TEST_ASSERT_EQUAL_STRING("parent", pFk->zTo);100 101 /* Verify it is visible from the schema hash */102 FKey *pHead = (FKey*)sqlite3HashFind(&pSchema->fkeyHash, pFk->zTo);103 TEST_ASSERT_EQUAL_PTR(pFk, pHead);104 105 freeTableAndFKeys(db, pTab);106 sqlite3_close(db);107}108 109void test_sqlite3CreateForeignKey_mismatch_counts_error(void){110 sqlite3 *db = 0;111 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));112 Schema *pSchema = db->aDb[0].pSchema;113 114 const char *cols[] = {"a","b","c"};115 Table *pTab = makeTableWithCols(db, pSchema, cols, 3);116 117 Parse pParse;118 memset(&pParse, 0, sizeof(pParse));119 pParse.db = db;120 pParse.pNewTable = pTab;121 122 Token tTo;123 tTo.z = "parent2";124 tTo.n = (int)strlen(tTo.z);125 126 /* from-list size 2, to-list size 1 -> mismatch */127 ExprList *pFrom = makeExprList(db, 0, 2);128 ExprList *pTo = makeExprList(db, 0, 1);129 130 sqlite3CreateForeignKey(&pParse, pFrom, &tTo, pTo, 0);131 132 TEST_ASSERT_NULL(pTab->u.tab.pFKey);133 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, pParse.rc);134 135 freeTableAndFKeys(db, pTab);136 sqlite3_close(db);137}138 139void test_sqlite3CreateForeignKey_unknown_fromcol_error(void){140 sqlite3 *db = 0;141 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));142 Schema *pSchema = db->aDb[0].pSchema;143 144 const char *cols[] = {"x","y"};145 Table *pTab = makeTableWithCols(db, pSchema, cols, 2);146 147 Parse pParse;148 memset(&pParse, 0, sizeof(pParse));149 pParse.db = db;150 pParse.pNewTable = pTab;151 152 Token tTo;153 tTo.z = "p";154 tTo.n = (int)strlen(tTo.z);155 156 const char *fromNames[] = {"does_not_exist"};157 ExprList *pFrom = makeExprList(db, fromNames, 1);158 159 sqlite3CreateForeignKey(&pParse, pFrom, &tTo, 0, 0);160 161 TEST_ASSERT_NULL(pTab->u.tab.pFKey);162 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, pParse.rc);163 164 freeTableAndFKeys(db, pTab);165 sqlite3_close(db);166}167 168void test_sqlite3CreateForeignKey_pFromCol_null_pToCol_multi_error(void){169 sqlite3 *db = 0;170 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));171 Schema *pSchema = db->aDb[0].pSchema;172 173 const char *cols[] = {"id"};174 Table *pTab = makeTableWithCols(db, pSchema, cols, 1);175 176 Parse pParse;177 memset(&pParse, 0, sizeof(pParse));178 pParse.db = db;179 pParse.pNewTable = pTab;180 181 Token tTo;182 tTo.z = "parent3";183 tTo.n = (int)strlen(tTo.z);184 185 const char *toNames[] = {"pcol1","pcol2"};186 ExprList *pTo = makeExprList(db, toNames, 2);187 188 sqlite3CreateForeignKey(&pParse, 0, &tTo, pTo, 0);189 190 TEST_ASSERT_NULL(pTab->u.tab.pFKey);191 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, pParse.rc);192 193 freeTableAndFKeys(db, pTab);194 sqlite3_close(db);195}196 197void test_sqlite3CreateForeignKey_multi_col_with_to_names(void){198 sqlite3 *db = 0;199 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));200 Schema *pSchema = db->aDb[0].pSchema;201 202 const char *cols[] = {"cA","cB","cC"};203 Table *pTab = makeTableWithCols(db, pSchema, cols, 3);204 205 Parse pParse;206 memset(&pParse, 0, sizeof(pParse));207 pParse.db = db;208 pParse.pNewTable = pTab;209 210 Token tTo;211 tTo.z = "parent4";212 tTo.n = (int)strlen(tTo.z);213 214 const char *fromNames[] = {"cB","cA"};215 const char *toNames[] = {"pB","pA"};216 ExprList *pFrom = makeExprList(db, fromNames, 2);217 ExprList *pTo = makeExprList(db, toNames, 2);218 219 sqlite3CreateForeignKey(&pParse, pFrom, &tTo, pTo, 0);220 221 FKey *pFk = pTab->u.tab.pFKey;222 TEST_ASSERT_NOT_NULL(pFk);223 TEST_ASSERT_EQUAL_INT(2, pFk->nCol);224 /* cB is index 1, cA is index 0 */225 TEST_ASSERT_EQUAL_INT(1, pFk->aCol[0].iFrom);226 TEST_ASSERT_EQUAL_INT(0, pFk->aCol[1].iFrom);227 TEST_ASSERT_NOT_NULL(pFk->aCol[0].zCol);228 TEST_ASSERT_NOT_NULL(pFk->aCol[1].zCol);229 TEST_ASSERT_EQUAL_STRING("pB", pFk->aCol[0].zCol);230 TEST_ASSERT_EQUAL_STRING("pA", pFk->aCol[1].zCol);231 232 freeTableAndFKeys(db, pTab);233 sqlite3_close(db);234}235 236void test_sqlite3CreateForeignKey_multiple_chain_same_parent(void){237 sqlite3 *db = 0;238 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));239 Schema *pSchema = db->aDb[0].pSchema;240 241 const char *cols[] = {"a","b","c"};242 Table *pTab = makeTableWithCols(db, pSchema, cols, 3);243 244 Parse pParse;245 memset(&pParse, 0, sizeof(pParse));246 pParse.db = db;247 pParse.pNewTable = pTab;248 249 Token tTo;250 tTo.z = "parent_chain";251 tTo.n = (int)strlen(tTo.z);252 253 /* First FK uses last column (c) */254 sqlite3CreateForeignKey(&pParse, 0, &tTo, 0, 0x01 | (0x02<<8));255 FKey *pFirst = pTab->u.tab.pFKey;256 TEST_ASSERT_NOT_NULL(pFirst);257 TEST_ASSERT_EQUAL_STRING("parent_chain", pFirst->zTo);258 259 /* Second FK uses explicit column 'a' */260 const char *fromNames[] = {"a"};261 ExprList *pFrom = makeExprList(db, fromNames, 1);262 sqlite3CreateForeignKey(&pParse, pFrom, &tTo, 0, 0x03 | (0x04<<8));263 FKey *pSecond = pTab->u.tab.pFKey;264 TEST_ASSERT_NOT_NULL(pSecond);265 TEST_ASSERT_NOT_EQUAL(pSecond, pFirst);266 TEST_ASSERT_EQUAL_PTR(pFirst, pSecond->pNextFrom);267 268 /* Schema hash head should now be pSecond, and linkage between them maintained */269 FKey *pHead = (FKey*)sqlite3HashFind(&pSchema->fkeyHash, "parent_chain");270 TEST_ASSERT_EQUAL_PTR(pSecond, pHead);271 TEST_ASSERT_EQUAL_PTR(pFirst, pSecond->pNextTo);272 TEST_ASSERT_EQUAL_PTR(pSecond, pFirst->pPrevTo);273 274 freeTableAndFKeys(db, pTab);275 sqlite3_close(db);276}277 278void test_sqlite3CreateForeignKey_parent_name_dequote(void){279 sqlite3 *db = 0;280 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));281 Schema *pSchema = db->aDb[0].pSchema;282 283 const char *cols[] = {"onlycol"};284 Table *pTab = makeTableWithCols(db, pSchema, cols, 1);285 286 Parse pParse;287 memset(&pParse, 0, sizeof(pParse));288 pParse.db = db;289 pParse.pNewTable = pTab;290 291 Token tTo;292 tTo.z = "\"ParEnt\"";293 tTo.n = (int)strlen(tTo.z);294 295 sqlite3CreateForeignKey(&pParse, 0, &tTo, 0, 0);296 297 FKey *pFk = pTab->u.tab.pFKey;298 TEST_ASSERT_NOT_NULL(pFk);299 /* Name should be dequoted */300 TEST_ASSERT_EQUAL_STRING("ParEnt", pFk->zTo);301 302 freeTableAndFKeys(db, pTab);303 sqlite3_close(db);304}305 306int main(void){307 UNITY_BEGIN();308 RUN_TEST(test_sqlite3CreateForeignKey_basic_singlecol_lastcol);309 RUN_TEST(test_sqlite3CreateForeignKey_mismatch_counts_error);310 RUN_TEST(test_sqlite3CreateForeignKey_unknown_fromcol_error);311 RUN_TEST(test_sqlite3CreateForeignKey_pFromCol_null_pToCol_multi_error);312 RUN_TEST(test_sqlite3CreateForeignKey_multi_col_with_to_names);313 RUN_TEST(test_sqlite3CreateForeignKey_multiple_chain_same_parent);314 RUN_TEST(test_sqlite3CreateForeignKey_parent_name_dequote);315 return UNITY_END();316}