From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 7FA7E219E9 for ; Wed, 18 Jul 2018 12:52:29 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qNGq5BQkQfPO for ; Wed, 18 Jul 2018 12:52:29 -0400 (EDT) Received: from smtpng3.m.smailru.net (smtpng3.m.smailru.net [94.100.177.149]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 04DFC2157F for ; Wed, 18 Jul 2018 12:52:29 -0400 (EDT) From: Kirill Shcherbatov Subject: [tarantool-patches] [PATCH v1 3/3] sql: get rid of Column structure Date: Wed, 18 Jul 2018 19:52:22 +0300 Message-Id: <72990d0ecbba60f0551c254eb33a3282b645cd5d.1531932662.git.kshcherbatov@tarantool.org> In-Reply-To: References: In-Reply-To: References: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: korablev@tarantool.org, Kirill Shcherbatov Get rid of is_primkey in Column structure as it become redundant. Moved the last member coll with collation pointer to field_def structure. Finally, dropped Column. --- src/box/alter.cc | 3 + src/box/field_def.c | 1 + src/box/field_def.h | 2 + src/box/sql/alter.c | 27 ++------- src/box/sql/build.c | 130 ++++++++++++++++------------------------ src/box/sql/resolve.c | 11 ++-- src/box/sql/select.c | 43 +++++-------- src/box/sql/sqliteInt.h | 11 ---- test/sql-tap/conflict3.test.lua | 10 ++-- 9 files changed, 87 insertions(+), 151 deletions(-) diff --git a/src/box/alter.cc b/src/box/alter.cc index 7b6bd1a..444bc48 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -407,6 +407,9 @@ field_def_decode(struct field_def *field, const char **data, tt_sprintf("collation is reasonable only for " "string, scalar and any fields")); } + struct coll_id *collation = coll_by_id(field->coll_id); + if (collation != NULL) + field->coll = collation->coll; const char *dv = field->default_value; if (dv != NULL) { diff --git a/src/box/field_def.c b/src/box/field_def.c index 8dbead6..12cc3b2 100644 --- a/src/box/field_def.c +++ b/src/box/field_def.c @@ -106,6 +106,7 @@ const struct field_def field_def_default = { .is_nullable = false, .nullable_action = ON_CONFLICT_ACTION_DEFAULT, .coll_id = COLL_NONE, + .coll = NULL, .default_value = NULL, .default_value_expr = NULL }; diff --git a/src/box/field_def.h b/src/box/field_def.h index 05f80d4..c8f158c 100644 --- a/src/box/field_def.h +++ b/src/box/field_def.h @@ -123,6 +123,8 @@ struct field_def { enum on_conflict_action nullable_action; /** Collation ID for string comparison. */ uint32_t coll_id; + /** Collating sequence for string comparison. */ + struct coll *coll; /** 0-terminated SQL expression for DEFAULT value. */ char *default_value; /** AST for parsed default value. */ diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c index fe54e55..38c36e8 100644 --- a/src/box/sql/alter.c +++ b/src/box/sql/alter.c @@ -146,7 +146,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 */ @@ -161,7 +160,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(SQLITE_PAGENO_TO_SPACEID(pNew->tnum), pNew->def->field_count - 1); @@ -181,7 +179,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; } @@ -258,8 +259,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. */ @@ -296,24 +295,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 ee97ef9..ed104e4 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_MAX) { + /* Set default. */ + 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; } /* @@ -340,7 +350,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. */ @@ -604,7 +613,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; @@ -642,17 +650,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; @@ -890,7 +887,6 @@ sqlite3AddPrimaryKey(Parse * pParse, /* Parsing context */ ) { Table *pTab = pParse->pNewTable; - Column *pCol = 0; int iCol = -1, i; int nTerm; if (pTab == 0) @@ -904,8 +900,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; @@ -914,25 +908,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); pTab->iPKey = iCol; @@ -1002,8 +993,8 @@ sqlite3AddCollateType(Parse * pParse, Token * 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) { + p->def->fields[i].coll = sql_get_coll_seq(pParse, zColl, id); + if (p->def->fields[i].coll != NULL) { /* If the column is declared as " PRIMARY KEY COLLATE ", * then an index may have been created on this column before the * collation type was added. Correct this if it is the case. @@ -1163,14 +1154,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 = ""; @@ -1188,10 +1176,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", @@ -1692,22 +1680,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_MAX) { - 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 @@ -1882,12 +1857,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 a185473..2c49e2c 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) { if (iCol == pTab->iPKey) { diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 34d5329..d577648 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -1811,25 +1811,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 @@ -1838,21 +1828,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); @@ -1889,9 +1879,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; } @@ -1901,7 +1891,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); @@ -1921,10 +1913,8 @@ cleanup: * 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; @@ -1949,8 +1939,6 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse, /* Parsing contexts */ { sqlite3 *db = pParse->db; NameContext sNC; - Column *pCol; - int i; Expr *p; struct ExprList_item *a; @@ -1963,8 +1951,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); @@ -1977,8 +1964,8 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse, /* Parsing contexts */ bool unused; uint32_t id; struct coll *coll = sql_expr_coll(pParse, p, &unused, &id); - if (coll != NULL && pCol->coll == NULL) { - pCol->coll = coll; + if (coll != NULL && pTab->def->fields[i].coll == NULL) { + pTab->def->fields[i].coll = coll; pTab->def->fields[i].coll_id = id; } } diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index 9f6e97e..984bec9 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 345537e..9b55550 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() -- 2.7.4