diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c index 8c1c36b9bf2f0994fa0cb9a9959fe2afe3e16d8f..88fa81f7d0b113c4c82ac6dc543ca94d0bb66198 100644 --- a/src/box/sql/alter.c +++ b/src/box/sql/alter.c @@ -147,7 +147,6 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef) Table *pNew; /* Copy of pParse->pNewTable */ Table *pTab; /* Table being altered */ const char *zTab; /* Table name */ - Column *pCol; /* The new column */ Expr *pDflt; /* Default value for the new column */ sqlite3 *db; /* The database connection; */ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ @@ -162,7 +161,6 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef) /* Skip the "sqlite_altertab_" prefix on the name. */ zTab = &pNew->def->name[16]; - pCol = &pNew->aCol[pNew->def->field_count - 1]; assert(pNew->def != NULL); pDflt = space_column_default_expr(pNew->def->id, pNew->def->field_count - 1); @@ -182,7 +180,10 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef) * If there is a NOT NULL constraint, then the default value for the * column must not be NULL. */ - if (pCol->is_primkey) { + struct Index *pk = sqlite3PrimaryKeyIndex(pTab); + assert(pk != NULL); + struct key_def *pk_key_def = pk->def->key_def; + if (key_def_find(pk_key_def, pNew->def->field_count - 1) != NULL) { sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column"); return; } @@ -259,8 +260,6 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc) Table *pNew; Table *pTab; Vdbe *v; - uint32_t i; - int nAlloc; sqlite3 *db = pParse->db; /* Look up the table being altered. */ @@ -297,24 +296,10 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc) } pParse->pNewTable = pNew; assert(pNew->def->field_count > 0); - nAlloc = (((pNew->def->field_count - 1) / 8) * 8) + 8; - assert((uint32_t)nAlloc >= pNew->def->field_count && nAlloc % 8 == 0 && - nAlloc - pNew->def->field_count < 8); - pNew->aCol = - (Column *) sqlite3DbMallocZero(db, sizeof(Column) * nAlloc); /* FIXME: pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); */ /* FIXME: if (!pNew->aCol || !pNew->zName) { */ - if (!pNew->aCol) { - assert(db->mallocFailed); - goto exit_begin_add_column; - } - memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->def->field_count); - for (i = 0; i < pNew->def->field_count; i++) { - Column *pCol = &pNew->aCol[i]; - /* FIXME: pNew->def->name = sqlite3DbStrDup(db, pCol->zName); */ - pCol->coll = NULL; - pNew->def->fields[i].coll_id = COLL_NONE; - } + for (uint32_t i = 0; i < pNew->def->field_count; i++) + pNew->def->fields[i] = field_def_default; pNew->pSchema = db->pSchema; pNew->addColOffset = pTab->addColOffset; pNew->nTabRef = 1; diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 202e3a768d2186fbdf702e5c8c2a3df85daba138..5772ee5076507ac805e267df6113417f777c23f3 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -116,38 +116,48 @@ sql_finish_coding(struct Parse *parse_context) /** * This is a function which should be called during execution - * of sqlite3EndTable. It ensures that only PRIMARY KEY - * constraint may have ON CONFLICT REPLACE clause. + * of sqlite3EndTable. It set defaults for columns having no + * separate NULL/NOT NULL specifiers and ensures that only + * PRIMARY KEY constraint may have ON CONFLICT REPLACE clause. * + * @param parser SQL Parser object. * @param table Space which should be checked. - * @retval False, if only primary key constraint has - * ON CONFLICT REPLACE clause or if there are no indexes - * with REPLACE as error action. True otherwise. + * @retval -1 on error. Parser SQL_TARANTOOL_ERROR is set. + * @retval 0 on success. */ -static bool -check_on_conflict_replace_entries(struct Table *table) -{ - /* Check all NOT NULL constraints. */ - for (int i = 0; i < (int)table->def->field_count; i++) { - enum on_conflict_action on_error = - table->def->fields[i].nullable_action; - if (on_error == ON_CONFLICT_ACTION_REPLACE && - table->aCol[i].is_primkey == false) { - return true; +static int +actualize_on_conflict_actions(struct Parse *parser, struct Table *table) +{ + const char *err_msg = NULL; + struct field_def *field = table->def->fields; + struct Index *pk = sqlite3PrimaryKeyIndex(table); + for (uint32_t i = 0; i < table->def->field_count; ++i, ++field) { + if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) { + /* Set default nullability NONE. */ + field->nullable_action = ON_CONFLICT_ACTION_NONE; + field->is_nullable = true; } + if (field->nullable_action == ON_CONFLICT_ACTION_REPLACE && + (pk == NULL || key_def_find(pk->def->key_def, i) == NULL)) + goto non_pk_on_conflict_error; } - /* Check all UNIQUE constraints. */ + for (struct Index *idx = table->pIndex; idx; idx = idx->pNext) { if (idx->onError == ON_CONFLICT_ACTION_REPLACE && - !IsPrimaryKeyIndex(idx)) { - return true; - } + !IsPrimaryKeyIndex(idx)) + goto non_pk_on_conflict_error; } - /* - * CHECK constraints are not allowed to have REPLACE as - * error action and therefore can be skipped. - */ - return false; + + return 0; + +non_pk_on_conflict_error: + err_msg = tt_sprintf("only PRIMARY KEY constraint can have " + "ON CONFLICT REPLACE clause - %s", + table->def->name); + diag_set(ClientError, ER_SQL, err_msg); + parser->rc = SQL_TARANTOOL_ERROR; + parser->nErr++; + return -1; } /* @@ -338,7 +348,6 @@ deleteTable(sqlite3 * db, Table * pTable) /* Delete the Table structure itself. */ sqlite3HashClear(&pTable->idxHash); - sqlite3DbFree(db, pTable->aCol); sqlite3DbFree(db, pTable->zColAff); assert(pTable->def != NULL); /* Do not delete pTable->def allocated on region. */ @@ -601,7 +610,6 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType) int i; char *z; char *zType; - Column *pCol; sqlite3 *db = pParse->db; if ((p = pParse->pNewTable) == 0) return; @@ -639,17 +647,6 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType) return; } } - if ((p->def->field_count & 0x7) == 0) { - Column *aNew = - sqlite3DbRealloc(db, p->aCol, - (p->def->field_count + 8) * - sizeof(p->aCol[0])); - if (aNew == NULL) - return; - p->aCol = aNew; - } - pCol = &p->aCol[p->def->field_count]; - memset(pCol, 0, sizeof(p->aCol[0])); struct field_def *column_def = &p->def->fields[p->def->field_count]; memcpy(column_def, &field_def_default, sizeof(field_def_default)); column_def->name = z; @@ -900,7 +897,6 @@ sqlite3AddPrimaryKey(Parse * pParse, /* Parsing context */ ) { Table *pTab = pParse->pNewTable; - Column *pCol = 0; int iCol = -1, i; int nTerm; if (pTab == 0) @@ -914,8 +910,6 @@ sqlite3AddPrimaryKey(Parse * pParse, /* Parsing context */ pTab->tabFlags |= TF_HasPrimaryKey; if (pList == 0) { iCol = pTab->def->field_count - 1; - pCol = &pTab->aCol[iCol]; - pCol->is_primkey = 1; nTerm = 1; } else { nTerm = pList->nExpr; @@ -924,25 +918,22 @@ sqlite3AddPrimaryKey(Parse * pParse, /* Parsing context */ sqlite3ExprSkipCollate(pList->a[i].pExpr); assert(pCExpr != 0); if (pCExpr->op != TK_ID) { - sqlite3ErrorMsg(pParse, "expressions prohibited in PRIMARY KEY"); + sqlite3ErrorMsg(pParse, "expressions prohibited" + " in PRIMARY KEY"); goto primary_key_exit; } - const char *zCName = pCExpr->u.zToken; - for (iCol = 0; - iCol < (int)pTab->def->field_count; iCol++) { - if (strcmp - (zCName, - pTab->def->fields[iCol].name) == 0) { - pCol = &pTab->aCol[iCol]; - pCol->is_primkey = 1; + const char *name = pCExpr->u.zToken; + struct space_def *def = pTab->def; + for (uint32_t idx = 0; idx < def->field_count; idx++) { + if (strcmp(name, def->fields[idx].name) == 0) { + iCol = idx; break; } } } } - assert(pCol == NULL || pCol == &pTab->aCol[iCol]); - if (nTerm == 1 && pCol != NULL && - (pTab->def->fields[iCol].type == FIELD_TYPE_INTEGER) && + if (nTerm == 1 && iCol != -1 && + pTab->def->fields[iCol].type == FIELD_TYPE_INTEGER && sortOrder != SORT_ORDER_DESC) { assert(autoInc == 0 || autoInc == 1); if (autoInc) { @@ -966,19 +957,23 @@ sqlite3AddPrimaryKey(Parse * pParse, /* Parsing context */ } else if (autoInc) { sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " "INTEGER PRIMARY KEY or INT PRIMARY KEY"); + goto primary_key_exit; } else { sql_create_index(pParse, 0, 0, pList, onError, 0, 0, sortOrder, false, SQL_INDEX_TYPE_CONSTRAINT_PK); pList = 0; + if (pParse->nErr > 0) + goto primary_key_exit; } - /* Mark every PRIMARY KEY column as NOT NULL. */ - for (uint32_t i = 0; i < pTab->def->field_count; i++) { - if (pTab->aCol[i].is_primkey) { - field_def_create_for_pk(pParse, &pTab->def->fields[i], - pTab->def->name); - } + struct Index *pk = sqlite3PrimaryKeyIndex(pTab); + assert(pk != NULL); + struct key_def *pk_key_def = pk->def->key_def; + for (uint32_t i = 0; i < pk_key_def->part_count; i++) { + uint32_t idx = pk_key_def->parts[i].fieldno; + field_def_create_for_pk(pParse, &pTab->def->fields[idx], + pTab->def->name); } primary_key_exit: sql_expr_list_delete(pParse->db, pList); @@ -1028,9 +1023,8 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken) char *zColl = sqlite3NameFromToken(db, pToken); if (!zColl) return; - uint32_t *id = &p->def->fields[i].coll_id; - p->aCol[i].coll = sql_get_coll_seq(pParse, zColl, id); - if (p->aCol[i].coll != NULL) { + uint32_t *coll_id = &p->def->fields[i].coll_id; + if (sql_get_coll_seq(pParse, zColl, coll_id) != NULL) { /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>", * then an index may have been created on this column before the * collation type was added. Correct this if it is the case. @@ -1039,9 +1033,8 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken) pIdx = pIdx->pNext) { assert(pIdx->def->key_def->part_count == 1); if (pIdx->def->key_def->parts[0].fieldno == i) { - id = &pIdx->def->key_def->parts[0].coll_id; - pIdx->def->key_def->parts[0].coll = - sql_column_collation(p->def, i, id); + coll_id = &pIdx->def->key_def->parts[0].coll_id; + (void)sql_column_collation(p->def, i, coll_id); } } } @@ -1190,14 +1183,11 @@ identPut(char *z, int *pIdx, char *zSignedIdent) static char * createTableStmt(sqlite3 * db, Table * p) { - int i, k, n; char *zStmt; char *zSep, *zSep2, *zEnd; - Column *pCol; - n = 0; - for (pCol = p->aCol, i = 0; i < (int)p->def->field_count; i++, pCol++) { + int n = 0; + for (uint32_t i = 0; i < p->def->field_count; i++) n += identLength(p->def->fields[i].name) + 5; - } n += identLength(p->def->name); if (n < 50) { zSep = ""; @@ -1215,10 +1205,10 @@ createTableStmt(sqlite3 * db, Table * p) return 0; } sqlite3_snprintf(n, zStmt, "CREATE TABLE "); - k = sqlite3Strlen30(zStmt); + int k = sqlite3Strlen30(zStmt); identPut(zStmt, &k, p->def->name); zStmt[k++] = '('; - for (pCol = p->aCol, i = 0; i < (int)p->def->field_count; i++, pCol++) { + for (uint32_t i = 0; i < p->def->field_count; i++) { static const char *const azType[] = { /* AFFINITY_BLOB */ "", /* AFFINITY_TEXT */ " TEXT", @@ -1639,22 +1629,9 @@ sqlite3EndTable(Parse * pParse, /* Parse context */ } } - /* Set default on_nullable action if required. */ - struct field_def *field = p->def->fields; - for (uint32_t i = 0; i < p->def->field_count; ++i, ++field) { - if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) { - field->nullable_action = ON_CONFLICT_ACTION_NONE; - field->is_nullable = true; - } - } - - if (check_on_conflict_replace_entries(p)) { - sqlite3ErrorMsg(pParse, - "only PRIMARY KEY constraint can " - "have ON CONFLICT REPLACE clause " - "- %s", p->def->name); + if (actualize_on_conflict_actions(pParse, p)) goto cleanup; - } + if (db->init.busy) { /* * As rebuild creates a new ExpList tree and @@ -1829,12 +1806,9 @@ sql_create_view(struct Parse *parse_context, struct Token *begin, sqlite3SelectAddColumnTypeAndCollation(parse_context, p, select); } else { - assert(p->aCol == NULL); assert(sel_tab->def->opts.is_temporary); p->def->fields = sel_tab->def->fields; p->def->field_count = sel_tab->def->field_count; - p->aCol = sel_tab->aCol; - sel_tab->aCol = NULL; sel_tab->def->fields = NULL; sel_tab->def->field_count = 0; } diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index 43b2198a3912ae968b113157ccfe6399eee153f9..a4767f2693ac4627bc96f747733efdb7a01499b0 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -219,7 +219,6 @@ lookupName(Parse * pParse, /* The parsing context */ NameContext *pTopNC = pNC; /* First namecontext in the list */ int isTrigger = 0; /* True if resolved to a trigger column */ Table *pTab = 0; /* Table hold the row */ - Column *pCol; /* A column of pTab */ assert(pNC); /* the name context cannot be NULL. */ assert(zCol); /* The Z in X.Y.Z cannot be NULL */ @@ -272,9 +271,8 @@ lookupName(Parse * pParse, /* The parsing context */ if (0 == (cntTab++)) { pMatch = pItem; } - for (j = 0, pCol = pTab->aCol; - j < (int)pTab->def->field_count; - j++, pCol++) { + for (j = 0; j < (int)pTab->def->field_count; + j++) { if (strcmp(pTab->def->fields[j].name, zCol) == 0) { /* If there has been exactly one prior match and this match @@ -331,9 +329,8 @@ lookupName(Parse * pParse, /* The parsing context */ if (pTab) { int iCol; cntTab++; - for (iCol = 0, pCol = pTab->aCol; - iCol < (int)pTab->def->field_count; - iCol++, pCol++) { + for (iCol = 0; iCol < + (int)pTab->def->field_count; iCol++) { if (strcmp(pTab->def->fields[iCol].name, zCol) == 0) { break; diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 221ee0f15b6414e7dc50b6a208a907fe892979c3..d060a4696bb5d5ee053e0288905942760dee2020 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -1809,25 +1809,15 @@ sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table) { /* Database connection */ sqlite3 *db = parse->db; - int i, j; /* Loop counters */ u32 cnt; /* Index added to make the name unique */ - Column *aCol, *pCol; /* For looping over result columns */ - int nCol; /* Number of columns in the result set */ Expr *p; /* Expression for a single result column */ char *zName; /* Column name */ int nName; /* Size of name in zName[] */ Hash ht; /* Hash table of column names */ sqlite3HashInit(&ht); - if (expr_list) { - nCol = expr_list->nExpr; - aCol = sqlite3DbMallocZero(db, sizeof(aCol[0]) * nCol); - testcase(aCol == 0); - } else { - nCol = 0; - aCol = NULL; - } - assert(nCol == (i16) nCol); + uint32_t column_count = + expr_list != NULL ? (uint32_t)expr_list->nExpr : 0; /* * This should be a table without resolved columns. * sqlite3ViewGetColumnNames could use it to resolve @@ -1836,21 +1826,21 @@ sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table) assert(table->def->fields == NULL); struct region *region = &parse->region; table->def->fields = - region_alloc(region, nCol * sizeof(table->def->fields[0])); + region_alloc(region, + column_count * sizeof(table->def->fields[0])); if (table->def->fields == NULL) { sqlite3OomFault(db); goto cleanup; } - for (int i = 0; i < nCol; i++) { + for (uint32_t i = 0; i < column_count; i++) { memcpy(&table->def->fields[i], &field_def_default, sizeof(field_def_default)); table->def->fields[i].nullable_action = ON_CONFLICT_ACTION_NONE; table->def->fields[i].is_nullable = true; } - table->def->field_count = (uint32_t)nCol; - table->aCol = aCol; + table->def->field_count = column_count; - for (i = 0, pCol = aCol; i < nCol; i++, pCol++) { + for (uint32_t i = 0; i < column_count; i++) { /* Get an appropriate name for the column */ p = sqlite3ExprSkipCollate(expr_list->a[i].pExpr); @@ -1887,9 +1877,9 @@ sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table) while (zName && sqlite3HashFind(&ht, zName) != 0) { nName = sqlite3Strlen30(zName); if (nName > 0) { + int j; for (j = nName - 1; - j > 0 && sqlite3Isdigit(zName[j]); j--) { - } + j > 0 && sqlite3Isdigit(zName[j]); j--); if (zName[j] == ':') nName = j; } @@ -1899,7 +1889,9 @@ sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table) sqlite3_randomness(sizeof(cnt), &cnt); } size_t name_len = strlen(zName); - if (zName != NULL && sqlite3HashInsert(&ht, zName, pCol) == pCol) + void *field = &table->def->fields[i]; + if (zName != NULL && + sqlite3HashInsert(&ht, zName, field) == field) sqlite3OomFault(db); table->def->fields[i].name = region_alloc(region, name_len + 1); @@ -1919,10 +1911,8 @@ sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table) * pTable->def could be not temporal in * sqlite3ViewGetColumnNames so we need clean-up. */ - sqlite3DbFree(db, aCol); table->def->fields = NULL; table->def->field_count = 0; - table->aCol = NULL; rc = SQLITE_NOMEM_BKPT; } return rc; @@ -1947,8 +1937,6 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse, /* Parsing contexts */ { sqlite3 *db = pParse->db; NameContext sNC; - Column *pCol; - int i; Expr *p; struct ExprList_item *a; @@ -1961,8 +1949,7 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse, /* Parsing contexts */ memset(&sNC, 0, sizeof(sNC)); sNC.pSrcList = pSelect->pSrc; a = pSelect->pEList->a; - for (i = 0, pCol = pTab->aCol; - i < (int)pTab->def->field_count; i++, pCol++) { + for (uint32_t i = 0; i < pTab->def->field_count; i++) { enum field_type type; p = a[i].pExpr; type = columnType(&sNC, p, 0, 0); @@ -1972,13 +1959,11 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse, /* Parsing contexts */ if (affinity == 0) affinity = AFFINITY_BLOB; pTab->def->fields[i].affinity = affinity; - bool unused; - uint32_t id; - struct coll *coll = sql_expr_coll(pParse, p, &unused, &id); - if (coll != NULL && pCol->coll == NULL) { - pCol->coll = coll; - pTab->def->fields[i].coll_id = id; - } + bool is_found; + uint32_t coll_id; + if (pTab->def->fields[i].coll_id == COLL_NONE && + sql_expr_coll(pParse, p, &is_found, &coll_id) && is_found) + pTab->def->fields[i].coll_id = coll_id; } } diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index 62a86fc550bd7df507f005534b5ff100f1ac660c..8cc2288a391eda1dcedf98ad31338918a050010a 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -1872,16 +1872,6 @@ struct Savepoint { #define SAVEPOINT_RELEASE 1 #define SAVEPOINT_ROLLBACK 2 -/* - * information about each column of an SQL table is held in an instance - * of this structure. - */ -struct Column { - /** Collating sequence. */ - struct coll *coll; - u8 is_primkey; /* Boolean propertie for being PK */ -}; - #define sqlite3IsNumericAffinity(X) ((X)>=AFFINITY_NUMERIC) /* @@ -1910,7 +1900,6 @@ struct Column { * by an instance of the following structure. */ struct Table { - Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ diff --git a/test/sql-tap/conflict3.test.lua b/test/sql-tap/conflict3.test.lua index 345537eac2a94370b2719f215dc74d81c7343cf2..9b555501e72ad7277ab95491112bf01d76da9a0c 100755 --- a/test/sql-tap/conflict3.test.lua +++ b/test/sql-tap/conflict3.test.lua @@ -359,7 +359,7 @@ test:do_catchsql_test( CREATE TABLE t3(a PRIMARY KEY ON CONFLICT REPLACE, b UNIQUE ON CONFLICT REPLACE); ]], { - 1, "only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" + 1, "SQL error: only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" }) test:do_catchsql_test( @@ -368,7 +368,7 @@ test:do_catchsql_test( CREATE TABLE t3(a PRIMARY KEY, b UNIQUE ON CONFLICT REPLACE); ]], { - 1, "only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" + 1, "SQL error: only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" }) test:do_catchsql_test( @@ -378,7 +378,7 @@ test:do_catchsql_test( b UNIQUE ON CONFLICT REPLACE, c UNIQUE ON CONFLICT REPLACE); ]], { - 1, "only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" + 1, "SQL error: only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" }) test:do_catchsql_test( @@ -387,7 +387,7 @@ test:do_catchsql_test( CREATE TABLE t3(a PRIMARY KEY, b NOT NULL ON CONFLICT REPLACE DEFAULT 1488); ]], { - 1, "only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" + 1, "SQL error: only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" }) test:do_catchsql_test( @@ -396,7 +396,7 @@ test:do_catchsql_test( CREATE TABLE t3(a PRIMARY KEY ON CONFLICT REPLACE, b NOT NULL ON CONFLICT REPLACE DEFAULT 1488); ]], { - 1, "only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" + 1, "SQL error: only PRIMARY KEY constraint can have ON CONFLICT REPLACE clause - T3" }) test:finish_test()