diff --git a/src/box/sql.c b/src/box/sql.c index 4415b53939b1bfe16b3b48a96888299024f0756e..38eef7dfa90a01c081c066dc06b1b4c7d34e9591 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -1038,3 +1038,33 @@ sql_debug_info(struct info_handler *h) info_append_int(h, "sql_found_count", sql_found_count); info_end(h); } + +/** + * Extract maximum integer value from: + * @param index space_id + * @param index_id + * @param field number fieldno + * @param[out] fetched value in max_id + * + * @retval 0 on success, -1 otherwise. + * + * If index is empty - return 0 in max_id and success status + */ +int +tarantoolSqlGetMaxId(uint32_t space_id, uint32_t index_id, uint32_t fieldno, + uint64_t *max_id) +{ + char key[16]; + struct tuple *tuple; + char *key_end = mp_encode_array(key, 0); + if (box_index_max(space_id, index_id, key, key_end, &tuple) != 0) + return -1; + + /* Index is empty */ + if (tuple == NULL) { + *max_id = 0; + return 0; + } + + return tuple_field_u64(tuple, fieldno, max_id); +} diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 8adf054c409058c873c91c50386eed988006a7e2..848c6796cd2776cb28b63b1d315083d2fba5ef0e 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -851,12 +851,12 @@ void sqlite3StartTable( assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; - /* If this is the magic sqlite_sequence table used by autoincrement, + /* If this is the magic sql_sequence table used by autoincrement, ** then record a pointer to this table in the main database structure ** so that INSERT can find the table easily. */ #ifndef SQLITE_OMIT_AUTOINCREMENT - if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ + if( !pParse->nested && strcmp(zName, "sql_sequence")==0 ){ assert( sqlite3SchemaMutexHeld(db, 0) ); pTable->pSchema->pSeqTab = pTable; } @@ -1191,12 +1191,14 @@ void sqlite3AddPrimaryKey( } } } + pTab->iAutoIncPKey = -1; if( nTerm==1 && pCol && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0 && sortOrder!=SQLITE_SO_DESC ){ pTab->iPKey = iCol; + pTab->iAutoIncPKey = iCol; pTab->keyConf = (u8)onError; assert( autoInc==0 || autoInc==1 ); pTab->tabFlags |= autoInc*TF_Autoincrement; @@ -1954,11 +1956,6 @@ void sqlite3EndTable( /* Special processing for WITHOUT ROWID Tables */ if( tabOpts & TF_WithoutRowid ){ - if( (p->tabFlags & TF_Autoincrement) ){ - sqlite3ErrorMsg(pParse, - "AUTOINCREMENT not allowed on WITHOUT ROWID tables"); - return; - } if( (p->tabFlags & TF_HasPrimaryKey)==0 ){ sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName); }else{ @@ -2110,7 +2107,7 @@ void sqlite3EndTable( sqlite3VdbeAddOp1(v, OP_Close, iCursor); #ifndef SQLITE_OMIT_AUTOINCREMENT - /* Check to see if we need to create an sqlite_sequence table for + /* Check to see if we need to create an sql_sequence table for ** keeping track of autoincrement keys. */ if( (p->tabFlags & TF_Autoincrement)!=0 ){ @@ -2118,7 +2115,7 @@ void sqlite3EndTable( assert( sqlite3SchemaMutexHeld(db, 0) ); if( pDb->pSchema->pSeqTab==0 ){ sqlite3NestedParse(pParse, - "CREATE TABLE sqlite_sequence(name,seq)" + "CREATE TABLE sql_sequence(name PRIMARY KEY,seq)" ); } } @@ -2466,14 +2463,14 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int isView){ pParse->nested--; #ifndef SQLITE_OMIT_AUTOINCREMENT - /* Remove any entries of the sqlite_sequence table associated with + /* Remove any entries of the sql_sequence table associated with ** the table being dropped. This is done before the table is dropped - ** at the btree level, in case the sqlite_sequence table needs to + ** at the btree level, in case the sql_sequence table needs to ** move as a result of the drop (can happen in auto-vacuum mode). */ if( pTab->tabFlags & TF_Autoincrement ){ sqlite3NestedParse(pParse, - "DELETE FROM sqlite_sequence WHERE name=%Q", + "DELETE FROM sql_sequence WHERE name=%Q", pTab->zName ); } @@ -2591,7 +2588,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ } } #endif - if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 + if( sqlite3StrNICmp(pTab->zName, "sql_sequence", 12)==0 && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){ sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); goto exit_drop_table; diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c index d7cab4a09483e1ed28d1d4ab22586f69a8ad44f9..345afcc27a2eb6f857d6fdcd38e67c6a5e55a7f1 100644 --- a/src/box/sql/insert.c +++ b/src/box/sql/insert.c @@ -24,11 +24,11 @@ ** for that table that is actually opened. */ void -sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */ - int iCur, /* The cursor number of the table */ - Table * pTab, /* The table to be opened */ - int opcode /* OP_OpenRead or OP_OpenWrite */ - ) +sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */ + int iCur, /* The cursor number of the table */ + Table * pTab, /* The table to be opened */ + int opcode /* OP_OpenRead or OP_OpenWrite */ + ) { Vdbe *v; assert(!IsVirtual(pTab)); @@ -228,7 +228,7 @@ readsTable(Parse * p, Table * pTab) ** ** (1) Register to hold the name of the pTab table. ** (2) Register to hold the maximum ROWID of pTab. -** (3) Register to hold the rowid in sqlite_sequence of pTab +** (3) Register to hold the rowid in sql_sequence of pTab ** ** The 2nd register is the one that is returned. That is all the ** insert routine needs to know about. @@ -256,9 +256,9 @@ autoIncBegin(Parse * pParse, /* Parsing context */ pInfo->pNext = pToplevel->pAinc; pToplevel->pAinc = pInfo; pInfo->pTab = pTab; - pToplevel->nMem++; /* Register to hold name of table */ - pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ - pToplevel->nMem++; /* Rowid in sqlite_sequence */ + pToplevel->nMem++; /* Register to hold name of table */ + pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ + pToplevel->nMem++; /* Rowid in sql_sequence */ } memId = pInfo->regCtr; } @@ -290,13 +290,12 @@ sqlite3AutoincrementBegin(Parse * pParse) /* 0 */ {OP_Null, 0, 0, 0}, /* 1 */ {OP_Rewind, 0, 9, 0}, /* 2 */ {OP_Column, 0, 0, 0}, - /* 3 */ {OP_Ne, 0, 7, 0}, - /* 4 */ {OP_Rowid, 0, 0, 0}, - /* 5 */ {OP_Column, 0, 1, 0}, - /* 6 */ {OP_Goto, 0, 9, 0}, - /* 7 */ {OP_Next, 0, 2, 0}, - /* 8 */ {OP_Integer, 0, 0, 0}, - /* 9 */ {OP_Close, 0, 0, 0} + /* 3 */ {OP_Ne, 0, 6, 0}, + /* 4 */ {OP_Column, 0, 1, 0}, + /* 5 */ {OP_Goto, 0, 8, 0}, + /* 6 */ {OP_Next, 0, 2, 0}, + /* 7 */ {OP_Integer, 0, 0, 0}, + /* 8 */ {OP_Close, 0, 0, 0} }; VdbeOp *aOp; pDb = &db->mdb; @@ -314,9 +313,8 @@ sqlite3AutoincrementBegin(Parse * pParse) aOp[3].p1 = memId - 1; aOp[3].p3 = memId; aOp[3].p5 = SQLITE_JUMPIFNULL; - aOp[4].p2 = memId + 1; - aOp[5].p3 = memId; - aOp[8].p2 = memId; + aOp[4].p3 = memId; + aOp[7].p2 = memId; } } @@ -338,7 +336,7 @@ autoIncStep(Parse * pParse, int memId, int regRowid) /* ** This routine generates the code needed to write autoincrement -** maximum rowid values back into the sqlite_sequence register. +** maximum rowid values back into the sql_sequence register. ** Every statement that might do an INSERT into an autoincrement ** table (either directly or through triggers) needs to call this ** routine just before the "exit" code. @@ -354,11 +352,12 @@ autoIncrementEnd(Parse * pParse) for (p = pParse->pAinc; p; p = p->pNext) { static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList autoIncEnd[] = { - /* 0 */ {OP_NotNull, 0, 2, 0}, - /* 1 */ {OP_NewRowid, 0, 0, 0}, - /* 2 */ {OP_MakeRecord, 0, 2, 0}, - /* 3 */ {OP_Insert, 0, 0, 0}, - /* 4 */ {OP_Close, 0, 0, 0} + /* 0 */ {OP_SeekGE, 0, 3, 0}, + /* 1 */ {OP_IdxGT, 0, 3, 0}, + /* 2 */ {OP_Delete, 0, 0, 0}, + /* 3 */ {OP_MakeRecord, 0, 2, 0}, + /* 4 */ {OP_IdxInsert, 0, 0, 0}, + /* 5 */ {OP_Close, 0, 0, 0} }; VdbeOp *aOp; Db *pDb = &db->mdb; @@ -374,13 +373,18 @@ autoIncrementEnd(Parse * pParse) iLn); if (aOp == 0) break; - aOp[0].p1 = memId + 1; - aOp[1].p2 = memId + 1; - aOp[2].p1 = memId - 1; - aOp[2].p3 = iRec; - aOp[3].p2 = iRec; - aOp[3].p3 = memId + 1; - aOp[3].p5 = OPFLAG_APPEND; + aOp[0].p3 = memId - 1; + aOp[0].p4.i = 1; + aOp[0].p4type = P4_INT32; + aOp[1].p3 = memId - 1; + aOp[1].p4.i = 1; + aOp[1].p4type = P4_INT32; + aOp[2].p2 = memId + 1; + aOp[3].p1 = memId - 1; + aOp[3].p3 = iRec; + aOp[4].p2 = iRec; + aOp[4].p3 = memId + 1; + aOp[4].p5 = OPFLAG_APPEND; sqlite3ReleaseTempReg(pParse, iRec); } } @@ -541,6 +545,8 @@ sqlite3Insert(Parse * pParse, /* Parser context */ int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ + int regPK; /* register containing PK for autoinc prepared + for MakeRecord */ int *aRegIdx = 0; /* One register allocated to each index */ #ifndef SQLITE_OMIT_TRIGGER @@ -641,7 +647,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */ #endif /* SQLITE_OMIT_XFER_OPT */ /* If this is an AUTOINCREMENT table, look up the sequence number in the - ** sqlite_sequence table and store it in memory cell regAutoinc. + ** sql_sequence table and store it in memory cell regAutoinc. */ regAutoinc = autoIncBegin(pParse, pTab); @@ -1014,7 +1020,11 @@ sqlite3Insert(Parse * pParse, /* Parser context */ VdbeCoverage(v); } } else if (IsVirtual(pTab) || withoutRowid) { - sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid); + if (pTab->iAutoIncPKey >= 0) + sqlite3VdbeAddOp3(v, OP_MaxId, iDataCur, + pTab->iAutoIncPKey, regRowid); + else + sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid); } else { sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc); @@ -1026,16 +1036,26 @@ sqlite3Insert(Parse * pParse, /* Parser context */ ** with the first column. */ nHidden = 0; + regPK = -1; for (i = 0; i < pTab->nCol; i++) { int iRegStore = regRowid + 1 + i; - if (i == pTab->iPKey) { - /* The value of the INTEGER PRIMARY KEY column is always a NULL. - ** Whenever this column is read, the rowid will be substituted - ** in its place. Hence, fill this column with a NULL to avoid - ** taking up data space with information that will never be used. - ** As there may be shallow copies of this value, make it a soft-NULL */ - sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore); - continue; + if (i == pTab->iAutoIncPKey) { + /* PK was not specified in IDLIST and marked for + * autoincrementation. Extract max ID from the + * index and increment it. This'll be compared + * w/ corresponding value extracted from + * sql_sequence for given index later. + */ + if ((pTab->tabFlags & TF_Autoincrement) + && (ipkColumn == -1)) { + sqlite3VdbeAddOp2(v, + OP_FCopy, + regAutoinc, + iRegStore); + sqlite3VdbeAddOp2(v, OP_AddImm, + iRegStore, 1); + } + regPK = iRegStore; } if (pColumn == 0) { if (IsHiddenColumn(&pTab->aCol[i])) { @@ -1052,27 +1072,84 @@ sqlite3Insert(Parse * pParse, /* Parser context */ } if (j < 0 || nColumn == 0 || (pColumn && j >= pColumn->nId)) { + if (i == pTab->iAutoIncPKey) continue; sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); - } else if (useTempTable) { - sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, - iRegStore); + } else if (useTempTable) { + if ((pTab->tabFlags & TF_Autoincrement) + && (i == pTab->iAutoIncPKey)) { + int regTmp = ++pParse->nMem ; + /* Emit code which doesn't override + * autoinc-ed value with select result + * in case if result is NULL value. + */ + sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regTmp); + sqlite3VdbeAddOp2(v, OP_FCopy, regTmp, iRegStore); + sqlite3VdbeChangeP3(v, -1, OPFLAG_SAME_FRAME | OPFLAG_NOOP_IF_NULL); + } else { + sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, + iRegStore); + } } else if (pSelect) { if (regFromSelect != regData) { - sqlite3VdbeAddOp2(v, OP_SCopy, - regFromSelect + j, - iRegStore); + if ((pTab->tabFlags & TF_Autoincrement) + && (i == pTab->iAutoIncPKey)) { + /* Emit code which doesn't override + * autoinc-ed value with select result + * in case that result is NULL + */ + sqlite3VdbeAddOp2(v, OP_FCopy, + regFromSelect + j, + iRegStore); + sqlite3VdbeChangeP3(v, + -1, + OPFLAG_SAME_FRAME + | OPFLAG_NOOP_IF_NULL); + } else { + sqlite3VdbeAddOp2(v, OP_SCopy, + regFromSelect + j, + iRegStore); + } } } else { + if (i == pTab->iAutoIncPKey) { + if (pList->a[j].pExpr->op == TK_NULL) + continue; + + if (pList->a[j].pExpr->op == TK_REGISTER) { + /* Emit code which doesn't override + * autoinc-ed value with select result + * in case that result is NULL + */ + sqlite3VdbeAddOp2(v, OP_FCopy, + pList->a[j].pExpr->iTable, + iRegStore); + sqlite3VdbeChangeP3(v, + -1, + OPFLAG_SAME_FRAME + | OPFLAG_NOOP_IF_NULL); + continue; + } + } + sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore); } } - /* Generate code to check constraints and generate index keys and - ** do the insertion. - */ + /* If value in regAutoinc exceeds one contained in + regPK, need to update it. */ + if (pTab->tabFlags & TF_Autoincrement) { + /* No way there's no PK in the table. */ + assert( regPK >= 0); + + autoIncStep(pParse, regAutoinc, regPK); + } + + /* Generate code to check constraints and generate index keys + and do the insertion. + */ #ifndef SQLITE_OMIT_VIRTUALTABLE if (IsVirtual(pTab)) { const char *pVTab = @@ -1147,7 +1224,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */ } insert_end: - /* Update the sqlite_sequence table by storing the content of the + /* Update the sql_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ diff --git a/src/box/sql/opcodes.c b/src/box/sql/opcodes.c index 804c5a0fa5f0fedcfcd2c9ff4739a3499e3f8fe3..5d3b25e4b98ae31f764cecdc20f7d7cc32c28c80 100644 --- a/src/box/sql/opcodes.c +++ b/src/box/sql/opcodes.c @@ -126,57 +126,59 @@ const char *sqlite3OpcodeName(int i){ /* 112 */ "Close" OpHelp(""), /* 113 */ "ColumnsUsed" OpHelp(""), /* 114 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), - /* 115 */ "NewRowid" OpHelp("r[P2]=rowid"), - /* 116 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), - /* 117 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), - /* 118 */ "Delete" OpHelp(""), - /* 119 */ "ResetCount" OpHelp(""), - /* 120 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), - /* 121 */ "SorterData" OpHelp("r[P2]=data"), - /* 122 */ "RowData" OpHelp("r[P2]=data"), - /* 123 */ "Rowid" OpHelp("r[P2]=rowid"), - /* 124 */ "NullRow" OpHelp(""), - /* 125 */ "SorterInsert" OpHelp("key=r[P2]"), - /* 126 */ "IdxInsert" OpHelp("key=r[P2]"), - /* 127 */ "IdxDelete" OpHelp("key=r[P2@P3]"), - /* 128 */ "Seek" OpHelp("Move P3 to P1.rowid"), - /* 129 */ "IdxRowid" OpHelp("r[P2]=rowid"), - /* 130 */ "Destroy" OpHelp(""), + /* 115 */ "MaxId" OpHelp("r[P3]=get_max(space_index[P1]{Column[P2]})"), + /* 116 */ "FCopy" OpHelp("reg[P2@cur_frame]= reg[P1@root_frame(OPFLAG_SAME_FRAME)]"), + /* 117 */ "NewRowid" OpHelp("r[P2]=rowid"), + /* 118 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), + /* 119 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), + /* 120 */ "Delete" OpHelp(""), + /* 121 */ "ResetCount" OpHelp(""), + /* 122 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 123 */ "SorterData" OpHelp("r[P2]=data"), + /* 124 */ "RowData" OpHelp("r[P2]=data"), + /* 125 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 126 */ "NullRow" OpHelp(""), + /* 127 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 128 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 129 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 130 */ "Seek" OpHelp("Move P3 to P1.rowid"), /* 131 */ "Real" OpHelp("r[P2]=P4"), - /* 132 */ "Clear" OpHelp(""), - /* 133 */ "ResetSorter" OpHelp(""), - /* 134 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"), - /* 135 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"), - /* 136 */ "ParseSchema" OpHelp(""), - /* 137 */ "ParseSchema2" OpHelp("rows=r[P1@P2] iDb=P3"), - /* 138 */ "ParseSchema3" OpHelp("name=r[P1] sql=r[P1+1] iDb=P2"), - /* 139 */ "LoadAnalysis" OpHelp(""), - /* 140 */ "DropTable" OpHelp(""), - /* 141 */ "DropIndex" OpHelp(""), - /* 142 */ "DropTrigger" OpHelp(""), - /* 143 */ "IntegrityCk" OpHelp(""), - /* 144 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), - /* 145 */ "Param" OpHelp(""), - /* 146 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), - /* 147 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), - /* 148 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 149 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 150 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 151 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 152 */ "Expire" OpHelp(""), - /* 153 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 154 */ "VBegin" OpHelp(""), - /* 155 */ "VCreate" OpHelp(""), - /* 156 */ "VDestroy" OpHelp(""), - /* 157 */ "VOpen" OpHelp(""), - /* 158 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 159 */ "VRename" OpHelp(""), - /* 160 */ "Pagecount" OpHelp(""), - /* 161 */ "MaxPgcnt" OpHelp(""), - /* 162 */ "CursorHint" OpHelp(""), - /* 163 */ "IncMaxid" OpHelp(""), - /* 164 */ "Noop" OpHelp(""), - /* 165 */ "Explain" OpHelp(""), + /* 132 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 133 */ "Destroy" OpHelp(""), + /* 134 */ "Clear" OpHelp(""), + /* 135 */ "ResetSorter" OpHelp(""), + /* 136 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"), + /* 137 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"), + /* 138 */ "ParseSchema" OpHelp(""), + /* 139 */ "ParseSchema2" OpHelp("rows=r[P1@P2] iDb=P3"), + /* 140 */ "ParseSchema3" OpHelp("name=r[P1] sql=r[P1+1] iDb=P2"), + /* 141 */ "LoadAnalysis" OpHelp(""), + /* 142 */ "DropTable" OpHelp(""), + /* 143 */ "DropIndex" OpHelp(""), + /* 144 */ "DropTrigger" OpHelp(""), + /* 145 */ "IntegrityCk" OpHelp(""), + /* 146 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 147 */ "Param" OpHelp(""), + /* 148 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 149 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 150 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 151 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 152 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 153 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 154 */ "Expire" OpHelp(""), + /* 155 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 156 */ "VBegin" OpHelp(""), + /* 157 */ "VCreate" OpHelp(""), + /* 158 */ "VDestroy" OpHelp(""), + /* 159 */ "VOpen" OpHelp(""), + /* 160 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 161 */ "VRename" OpHelp(""), + /* 162 */ "Pagecount" OpHelp(""), + /* 163 */ "MaxPgcnt" OpHelp(""), + /* 164 */ "CursorHint" OpHelp(""), + /* 165 */ "IncMaxid" OpHelp(""), + /* 166 */ "Noop" OpHelp(""), + /* 167 */ "Explain" OpHelp(""), }; return azName[i]; } diff --git a/src/box/sql/opcodes.h b/src/box/sql/opcodes.h index 578783e1650d9bb042389ee74bbc65a6c313485e..5992f8d06385dc9d0bf7ccfa5c30f32087c07b12 100644 --- a/src/box/sql/opcodes.h +++ b/src/box/sql/opcodes.h @@ -115,57 +115,59 @@ #define OP_Close 112 #define OP_ColumnsUsed 113 #define OP_Sequence 114 /* synopsis: r[P2]=cursor[P1].ctr++ */ -#define OP_NewRowid 115 /* synopsis: r[P2]=rowid */ -#define OP_Insert 116 /* synopsis: intkey=r[P3] data=r[P2] */ -#define OP_InsertInt 117 /* synopsis: intkey=P3 data=r[P2] */ -#define OP_Delete 118 -#define OP_ResetCount 119 -#define OP_SorterCompare 120 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ -#define OP_SorterData 121 /* synopsis: r[P2]=data */ -#define OP_RowData 122 /* synopsis: r[P2]=data */ -#define OP_Rowid 123 /* synopsis: r[P2]=rowid */ -#define OP_NullRow 124 -#define OP_SorterInsert 125 /* synopsis: key=r[P2] */ -#define OP_IdxInsert 126 /* synopsis: key=r[P2] */ -#define OP_IdxDelete 127 /* synopsis: key=r[P2@P3] */ -#define OP_Seek 128 /* synopsis: Move P3 to P1.rowid */ -#define OP_IdxRowid 129 /* synopsis: r[P2]=rowid */ -#define OP_Destroy 130 +#define OP_MaxId 115 /* synopsis: r[P3]=get_max(space_index[P1]{Column[P2]}) */ +#define OP_FCopy 116 /* synopsis: reg[P2@cur_frame]= reg[P1@root_frame(OPFLAG_SAME_FRAME)] */ +#define OP_NewRowid 117 /* synopsis: r[P2]=rowid */ +#define OP_Insert 118 /* synopsis: intkey=r[P3] data=r[P2] */ +#define OP_InsertInt 119 /* synopsis: intkey=P3 data=r[P2] */ +#define OP_Delete 120 +#define OP_ResetCount 121 +#define OP_SorterCompare 122 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 123 /* synopsis: r[P2]=data */ +#define OP_RowData 124 /* synopsis: r[P2]=data */ +#define OP_Rowid 125 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 126 +#define OP_SorterInsert 127 /* synopsis: key=r[P2] */ +#define OP_IdxInsert 128 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 129 /* synopsis: key=r[P2@P3] */ +#define OP_Seek 130 /* synopsis: Move P3 to P1.rowid */ #define OP_Real 131 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_Clear 132 -#define OP_ResetSorter 133 -#define OP_CreateIndex 134 /* synopsis: r[P2]=root iDb=P1 */ -#define OP_CreateTable 135 /* synopsis: r[P2]=root iDb=P1 */ -#define OP_ParseSchema 136 -#define OP_ParseSchema2 137 /* synopsis: rows=r[P1@P2] iDb=P3 */ -#define OP_ParseSchema3 138 /* synopsis: name=r[P1] sql=r[P1+1] iDb=P2 */ -#define OP_LoadAnalysis 139 -#define OP_DropTable 140 -#define OP_DropIndex 141 -#define OP_DropTrigger 142 -#define OP_IntegrityCk 143 -#define OP_RowSetAdd 144 /* synopsis: rowset(P1)=r[P2] */ -#define OP_Param 145 -#define OP_FkCounter 146 /* synopsis: fkctr[P1]+=P2 */ -#define OP_MemMax 147 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -#define OP_OffsetLimit 148 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggStep0 149 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep 150 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggFinal 151 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 152 -#define OP_TableLock 153 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 154 -#define OP_VCreate 155 -#define OP_VDestroy 156 -#define OP_VOpen 157 -#define OP_VColumn 158 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 159 -#define OP_Pagecount 160 -#define OP_MaxPgcnt 161 -#define OP_CursorHint 162 -#define OP_IncMaxid 163 -#define OP_Noop 164 -#define OP_Explain 165 +#define OP_IdxRowid 132 /* synopsis: r[P2]=rowid */ +#define OP_Destroy 133 +#define OP_Clear 134 +#define OP_ResetSorter 135 +#define OP_CreateIndex 136 /* synopsis: r[P2]=root iDb=P1 */ +#define OP_CreateTable 137 /* synopsis: r[P2]=root iDb=P1 */ +#define OP_ParseSchema 138 +#define OP_ParseSchema2 139 /* synopsis: rows=r[P1@P2] iDb=P3 */ +#define OP_ParseSchema3 140 /* synopsis: name=r[P1] sql=r[P1+1] iDb=P2 */ +#define OP_LoadAnalysis 141 +#define OP_DropTable 142 +#define OP_DropIndex 143 +#define OP_DropTrigger 144 +#define OP_IntegrityCk 145 +#define OP_RowSetAdd 146 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 147 +#define OP_FkCounter 148 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 149 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 150 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggStep0 151 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep 152 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggFinal 153 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 154 +#define OP_TableLock 155 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 156 +#define OP_VCreate 157 +#define OP_VDestroy 158 +#define OP_VOpen 159 +#define OP_VColumn 160 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 161 +#define OP_Pagecount 162 +#define OP_MaxPgcnt 163 +#define OP_CursorHint 164 +#define OP_IncMaxid 165 +#define OP_Noop 166 +#define OP_Explain 167 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -192,13 +194,14 @@ /* 88 */ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\ /* 96 */ 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,\ /* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 112 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,\ -/* 120 */ 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x04, 0x00,\ -/* 128 */ 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\ -/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 144 */ 0x06, 0x10, 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00,\ +/* 112 */ 0x00, 0x00, 0x10, 0x20, 0x10, 0x10, 0x00, 0x00,\ +/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04,\ +/* 128 */ 0x04, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00,\ +/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 144 */ 0x00, 0x00, 0x06, 0x10, 0x00, 0x04, 0x1a, 0x00,\ /* 152 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 160 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,} +/* 160 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,\ +} /* The sqlite3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index eed00980e1a47291396da2f8f92179cc5ac5a7e4..e48d549a6c399364156252d5c50f811445c121ac 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -1785,6 +1785,8 @@ struct Table { int tnum; /* Root BTree page for this table */ u32 nTabRef; /* Number of pointers to this Table */ i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ + i16 iAutoIncPKey; /* If PK is marked INTEGER PRIMARY KEY AUTOINCREMENT, store + column number here, -1 otherwise Tarantool specifics */ i16 nCol; /* Number of columns in this table */ LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */ LogEst szTabRow; /* Estimated size of each table row in bytes */ @@ -3007,6 +3009,13 @@ struct AuthContext { #define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */ #define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */ +#define OPFLAG_SAME_FRAME 0x01 /* OP_FCopy: use same frame for source + * register + */ +#define OPFLAG_NOOP_IF_NULL 0x02 /* OP_FCopy: if source register is NULL + * then do nothing + */ + /* * Each trigger present in the database schema is stored as an instance of * struct Trigger. diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h index 3f8863bdbe19b124f1fa7d70df41e09c14825a7a..ba75d5e6807eba5ad438fee04c11b420469185ed 100644 --- a/src/box/sql/tarantoolInt.h +++ b/src/box/sql/tarantoolInt.h @@ -6,6 +6,8 @@ ** that's why we are using a weird naming schema. */ +#include <stdint.h> + /* * Tarantool system spaces. */ @@ -96,3 +98,9 @@ int tarantoolSqlite3MakeIdxParts(Index *index, void *buf); * If buf==NULL estimate result size. */ int tarantoolSqlite3MakeIdxOpts(Index *index, const char *zSql, void *buf); + +/* + * Fetch maximum value from ineger column number `fieldno` of space_id/index_id + * Return 0 on success, -1 otherwise + */ +int tarantoolSqlGetMaxId(uint32_t space_id, uint32_t index_id, uint32_t fieldno, uint64_t *max_id); diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index cef983eedf15791323eb415f352566916f39fcba..4db3e60cba7b3ba839a700daa368abcf84876b5a 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -4056,6 +4056,69 @@ case OP_Sequence: { /* out2 */ break; } +/* Opcode: MaxId P1 P2 P3 * * +** Synopsis: r[P3]=get_max(space_index[P1]{Column[P2]}) +** +** Get next Id of the table. P1 is a table cursor, P2 is column +** number. Return in P3 maximum id found in provided column. +** +** This opcode is Tarantool specific and will segfault in case +** of SQLite cursor. +*/ +case OP_MaxId: { /* out3 */ + VdbeCursor *pC; /* The VDBE cursor */ + int p2; /* Column number, which stores the id */ + int pgno; /* Page number of the cursor */ + pC = p->apCsr[pOp->p1]; + p2 = pOp->p2; + pOut = &aMem[pOp->p3]; + + /* This opcode is Tarantool specific. */ + assert( pC->uc.pCursor->curFlags & BTCF_TaCursor ); + + pgno = pC->pgnoRoot; + + tarantoolSqlGetMaxId(SQLITE_PAGENO_TO_SPACEID(pgno), SQLITE_PAGENO_TO_INDEXID(pgno), p2, &pOut->u.i); + + pOut->flags = MEM_Int; + break; +} + +/* Opcode: FCopy P1 P2 P3 * * +** Synopsis: reg[P2@cur_frame]= reg[P1@root_frame(OPFLAG_SAME_FRAME)] +** +** Copy integer value of register P1 in root frame in to register P2 of current +** frame. If current frame is topmost - copy within signle frame. +** Source register must hold integer value. +** +** If P3's flag OPFLAG_SAME_FRAME is set, do shallow copy of register within +** same frame, still making sure the value is integer. +** +** If P3's flag OPFLAG_NOOP_IF_NULL is set, then do nothing if reg[P1] is NULL +*/ +case OP_FCopy: { /* out2 */ + VdbeFrame *pFrame; + Mem *pIn1, *pOut; + if( p->pFrame && ((pOp->p3 & OPFLAG_SAME_FRAME) == 0)) { + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + pIn1 = &pFrame->aMem[pOp->p1]; + }else{ + pIn1 = &aMem[pOp->p1]; + } + + if ((pOp->p3 & OPFLAG_NOOP_IF_NULL) && (pIn1->flags & MEM_Null)) { + /* Flag is set and register is NULL -> do nothing */ + } else { + assert( memIsValid(pIn1) ); + assert( pIn1->flags & MEM_Int); + + pOut = &aMem[pOp->p2]; + MemSetTypeFlag(pOut, MEM_Int); + + pOut->u.i = pIn1->u.i; + } + break; +} /* Opcode: NewRowid P1 P2 P3 * * ** Synopsis: r[P2]=rowid diff --git a/test/sql-tap/autoinc.test.lua b/test/sql-tap/autoinc.test.lua new file mode 100755 index 0000000000000000000000000000000000000000..8c2dc2a8024821807dd515a24faff4b1ee5116a1 --- /dev/null +++ b/test/sql-tap/autoinc.test.lua @@ -0,0 +1,901 @@ +#!/usr/bin/env tarantool +test = require("sqltester") +test:plan(53) + +--!./tcltestrunner.lua +-- 2004 November 12 +-- +-- The author disclaims copyright to this source code. In place of +-- a legal notice, here is a blessing: +-- +-- May you do good and not evil. +-- May you find forgiveness for yourself and forgive others. +-- May you share freely, never taking more than you give. +-- +--------------------------------------------------------------------------- +-- This file implements regression tests for SQLite library. The +-- focus of this script is testing the AUTOINCREMENT features. +-- +-- $Id: autoinc.test,v 1.14 2009/06/23 20:28:54 drh Exp $ +-- +-- ["set","testdir",[["file","dirname",["argv0"]]]] +-- ["source",[["testdir"],"\/tester.tcl"]] +-- If the library is not compiled with autoincrement support then +-- skip all tests in this file. +-- + +-- Add a table with the AUTOINCREMENT feature. Verify that the +-- SQLITE_SEQUENCE table gets created. +-- +test:do_execsql_test( + "autoinc-1.2", + [[ + CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y); + ]], { + -- <autoinc-1.2> + + -- </autoinc-1.2> + }) + +-- The SQLITE_SEQUENCE table is initially empty +-- +test:do_execsql_test( + "autoinc-1.3", + [[ + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-1.3> + + -- </autoinc-1.3> + }) + +-- Close and reopen the database. Verify that everything is still there. +-- +-- test:do_test( +-- "autoinc-1.4", +-- function() +-- db("close") +-- sqlite3("db", "test.db") +-- return test:execsql([[ +-- SELECT * FROM sql_sequence; +-- ]]) +-- end, { +-- -- <autoinc-1.4> + +-- -- </autoinc-1.4> +-- }) + +-- We are not allowed to drop the sqlite_sequence table. +-- +test:do_catchsql_test( + "autoinc-1.5", + [[ + DROP TABLE sql_sequence + ]], { + -- <autoinc-1.5> + 1, "table sql_sequence may not be dropped" + -- </autoinc-1.5> + }) + +test:do_execsql_test( + "autoinc-1.6", + [[ + SELECT name FROM _space WHERE name NOT IN (SELECT name FROM _space WHERE name LIKE '\_%' ESCAPE '\') + ]], { + -- <autoinc-1.6> + "t1", "sql_sequence" + -- </autoinc-1.6> + }) + +-- Insert an entries into the t1 table and make sure the largest key +-- is always recorded in the sqlite_sequence table. +-- +test:do_execsql_test( + "autoinc-2.1", + [[ + --SELECT * FROM sql_sequence + ]], { + -- <autoinc-2.1> + + -- </autoinc-2.1> + }) + +test:do_execsql_test( + "autoinc-2.2", + [[ + INSERT INTO t1 VALUES(12,34); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.2> + "t1", 12 + -- </autoinc-2.2> + }) + +test:do_execsql_test( + "autoinc-2.3", + [[ + INSERT INTO t1 VALUES(1,23); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.3> + "t1", 12 + -- </autoinc-2.3> + }) + +test:do_execsql_test( + "autoinc-2.4", + [[ + INSERT INTO t1 VALUES(123,456); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.4> + "t1", 123 + -- </autoinc-2.4> + }) + +test:do_execsql_test( + "autoinc-2.5", + [[ + INSERT INTO t1 VALUES(NULL,567); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.5> + "t1", 124 + -- </autoinc-2.5> + }) + +test:do_execsql_test( + "autoinc-2.6", + [[ + DELETE FROM t1 WHERE y=567; + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.6> + "t1", 124 + -- </autoinc-2.6> + }) + +test:do_execsql_test( + "autoinc-2.7", + [[ + INSERT INTO t1 VALUES(NULL,567); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.7> + "t1", 125 + -- </autoinc-2.7> + }) + +test:do_execsql_test( + "autoinc-2.8", + [[ + DELETE FROM t1; + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.8> + "t1", 125 + -- </autoinc-2.8> + }) + +test:do_execsql_test( + "autoinc-2.9", + [[ + INSERT INTO t1 VALUES(12,34); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.9> + "t1", 125 + -- </autoinc-2.9> + }) + +test:do_execsql_test( + "autoinc-2.10", + [[ + INSERT INTO t1 VALUES(125,456); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.10> + "t1", 125 + -- </autoinc-2.10> + }) + +test:do_execsql_test( + "autoinc-2.11", + [[ + INSERT INTO t1 VALUES(-1234567,-1); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.11> + "t1", 125 + -- </autoinc-2.11> + }) + +test:do_execsql_test( + "autoinc-2.12", + [[ + INSERT INTO t1 VALUES(234,5678); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.12> + "t1", 234 + -- </autoinc-2.12> + }) + +test:do_execsql_test( + "autoinc-2.13", + [[ + DELETE FROM t1; + INSERT INTO t1 VALUES(NULL,1); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.13> + "t1", 235 + -- </autoinc-2.13> + }) + +test:do_execsql_test( + "autoinc-2.14", + [[ + SELECT * FROM t1; + ]], { + -- <autoinc-2.14> + 235, 1 + -- </autoinc-2.14> + }) + +-- # Manually change the autoincrement values in sqlite_sequence. +-- # +test:do_execsql_test( + "autoinc-2.20", + [[ + UPDATE sql_sequence SET seq=1234 WHERE name='t1'; + INSERT INTO t1 VALUES(NULL,2); + SELECT * FROM t1; + ]], { + -- <autoinc-2.20> + 235, 1, 1235, 2 + -- </autoinc-2.20> + }) + +test:do_execsql_test( + "autoinc-2.21", + [[ + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.21> + "t1", 1235 + -- </autoinc-2.21> + }) + +test:do_execsql_test( + "autoinc-2.22", + [[ + UPDATE sql_sequence SET seq=NULL WHERE name='t1'; + INSERT INTO t1 VALUES(NULL,3); + SELECT * FROM t1; + ]], { + -- <autoinc-2.22> + 235, 1, 1235, 2, 1236, 3 + -- </autoinc-2.22> + }) + +test:do_execsql_test( + "autoinc-2.23", + [[ + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.23> + "t1", 1236 + -- </autoinc-2.23> + }) + +test:do_execsql_test( + "autoinc-2.24", + [[ + UPDATE sql_sequence SET seq='a-string' WHERE name='t1'; + INSERT INTO t1 VALUES(NULL,4); + SELECT * FROM t1; + ]], { + -- <autoinc-2.24> + 235, 1, 1235, 2, 1236, 3, 1237, 4 + -- </autoinc-2.24> + }) + +test:do_execsql_test( + "autoinc-2.25", + [[ + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.25> + "t1", 1237 + -- </autoinc-2.25> + }) + +test:do_execsql_test( + "autoinc-2.26", + [[ + DELETE FROM sql_sequence WHERE name='t1'; + INSERT INTO t1 VALUES(NULL,5); + SELECT * FROM t1; + ]], { + -- <autoinc-2.26> + 235, 1, 1235, 2, 1236, 3, 1237, 4, 1238, 5 + -- </autoinc-2.26> + }) + +test:do_execsql_test( + "autoinc-2.27", + [[ + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.27> + "t1", 1238 + -- </autoinc-2.27> + }) + +test:do_execsql_test( + "autoinc-2.28", + [[ + UPDATE sql_sequence SET seq='-12345678901234567890' + WHERE name='t1'; + INSERT INTO t1 VALUES(NULL,6); + SELECT * FROM t1; + ]], { + -- <autoinc-2.28> + 235, 1, 1235, 2, 1236, 3, 1237, 4, 1238, 5, 1239, 6 + -- </autoinc-2.28> + }) + +test:do_execsql_test( + "autoinc-2.29", + [[ + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.29> + "t1", 1239 + -- </autoinc-2.29> + }) + +-- # Test multi-row inserts +-- # +test:do_execsql_test( + "autoinc-2.50", + [[ + DELETE FROM t1 WHERE y>=3; + INSERT INTO t1 SELECT NULL, y+2 FROM t1; + SELECT * FROM t1; + ]], { + -- <autoinc-2.50> + 235, 1, 1235, 2, 1240, 3, 1241, 4 + -- </autoinc-2.50> + }) + +test:do_execsql_test( + "autoinc-2.51", + [[ + SELECT * FROM sql_sequence + ]], { + -- <autoinc-2.51> + "t1", 1241 + -- </autoinc-2.51> + }) + +-- ifcapable tempdb { +-- do_test autoinc-2.52 { +-- execsql { +-- CREATE TEMP TABLE t2 AS SELECT y FROM t1; +-- } +-- execsql { +-- INSERT INTO t1 SELECT NULL, y+4 FROM t2; +-- SELECT * FROM t1; +-- } +-- } {235 1 1235 2 1240 3 1241 4 1242 5 1243 6 1244 7 1245 8} +-- do_test autoinc-2.53 { +-- execsql { +-- SELECT * FROM sqlite_sequence +-- } +-- } {t1 1245} +-- do_test autoinc-2.54 { +-- execsql { +-- DELETE FROM t1; +-- INSERT INTO t1 SELECT NULL, y FROM t2; +-- SELECT * FROM t1; +-- } +-- } {1246 1 1247 2 1248 3 1249 4} +-- do_test autoinc-2.55 { +-- execsql { +-- SELECT * FROM sqlite_sequence +-- } +-- } {t1 1249} +-- } +-- # Create multiple AUTOINCREMENT tables. Make sure all sequences are +-- # tracked separately and do not interfere with one another. +-- # +test:do_test( + "autoinc-2.70", + function() + test:catchsql([[ + DROP TABLE t2; + ]]) + return test:execsql([[ + CREATE TABLE t2(d, e INTEGER PRIMARY KEY AUTOINCREMENT, f); + INSERT INTO t2(d) VALUES(1); + SELECT * FROM sql_sequence; + ]]) + end, { + -- <autoinc-2.70> + "t1", 1241, "t2", 1 + -- </autoinc-2.70> + }) + +test:do_execsql_test( + "autoinc-2.71", + [[ + INSERT INTO t2(d) VALUES(2); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.71> + "t1", 1241, "t2", 2 + -- </autoinc-2.71> + }) + +test:do_execsql_test( + "autoinc-2.72", + [[ + INSERT INTO t1(x) VALUES(10000); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.72> + "t1", 10000, "t2", 2 + -- </autoinc-2.72> + }) + +test:do_execsql_test( + "autoinc-2.73", + [[ + CREATE TABLE t3(g INTEGER PRIMARY KEY AUTOINCREMENT, h); + INSERT INTO t3(h) VALUES(1); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.73> + "t1", 10000, "t2", 2, "t3", 1 + -- </autoinc-2.73> + }) + +test:do_execsql_test( + "autoinc-2.74", + [[ + INSERT INTO t2(d,e) VALUES(3,100); + SELECT * FROM sql_sequence; + ]], { + -- <autoinc-2.74> + "t1", 10000, "t2", 100, "t3", 1 + -- </autoinc-2.74> + }) + +-- When a table with an AUTOINCREMENT is deleted, the corresponding entry +-- in the SQLITE_SEQUENCE table should also be deleted. But the SQLITE_SEQUENCE +-- table itself should remain behind. +-- +test:do_execsql_test( + "autoinc-3.1", + [[ + SELECT name FROM sql_sequence + ]], { + -- <autoinc-3.1> + "t1", "t2", "t3" + -- </autoinc-3.1> + }) + +test:do_execsql_test( + "autoinc-3.2", + [[ + DROP TABLE t1; + SELECT name FROM sql_sequence; + ]], { + -- <autoinc-3.2> + "t2", "t3" + -- </autoinc-3.2> + }) + +test:do_execsql_test( + "autoinc-3.3", + [[ + DROP TABLE t3; + SELECT name FROM sql_sequence; + ]], { + -- <autoinc-3.3> + "t2" + -- </autoinc-3.3> + }) + +test:do_execsql_test( + "autoinc-3.4", + [[ + DROP TABLE t2; + SELECT name FROM sql_sequence; + ]], { + -- <autoinc-3.4> + + -- </autoinc-3.4> + }) + +-- AUTOINCREMENT on TEMP tables. +-- +-- Tarantool: TEMP tables are not supported yet. To be uncommented. #2119 +-- test:do_execsql_test( +-- "autoinc-4.1", +-- [[ +-- SELECT 1, name FROM sqlite_master WHERE type='table'; +-- SELECT 2, name FROM sqlite_temp_master WHERE type='table'; +-- ]], { +-- -- <autoinc-4.1> +-- 1, "sqlite_sequence" +-- -- </autoinc-4.1> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.2", +-- [[ +-- CREATE TABLE t1(x INTEGER PRIMARY KEY AUTOINCREMENT, y); +-- CREATE TEMP TABLE t3(a INTEGER PRIMARY KEY AUTOINCREMENT, b); +-- SELECT 1, name FROM sqlite_master WHERE type='table'; +-- SELECT 2, name FROM sqlite_temp_master WHERE type='table'; +-- ]], { +-- -- <autoinc-4.2> +-- 1, "sqlite_sequence", 1, "t1", 2, "t3", 2, "sqlite_sequence" +-- -- </autoinc-4.2> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.3", +-- [[ +-- SELECT 1, * FROM main.sqlite_sequence; +-- SELECT 2, * FROM temp.sqlite_sequence; +-- ]], { +-- -- <autoinc-4.3> + +-- -- </autoinc-4.3> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.4", +-- [[ +-- INSERT INTO t1 VALUES(10,1); +-- INSERT INTO t3 VALUES(20,2); +-- INSERT INTO t1 VALUES(NULL,3); +-- INSERT INTO t3 VALUES(NULL,4); +-- ]], { +-- -- <autoinc-4.4> + +-- -- </autoinc-4.4> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.4.1", +-- [[ +-- SELECT * FROM t1 UNION ALL SELECT * FROM t3; +-- ]], { +-- -- <autoinc-4.4.1> +-- 10, 1, 11, 3, 20, 2, 21, 4 +-- -- </autoinc-4.4.1> +-- }) + + + +-- -- ifcapable compound +-- test:do_execsql_test( +-- "autoinc-4.5", +-- [[ +-- SELECT 1, * FROM main.sqlite_sequence; +-- SELECT 2, * FROM temp.sqlite_sequence; +-- ]], { +-- -- <autoinc-4.5> +-- 1, "t1", 11, 2, "t3", 21 +-- -- </autoinc-4.5> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.6", +-- [[ +-- INSERT INTO t1 SELECT * FROM t3; +-- SELECT 1, * FROM main.sqlite_sequence; +-- SELECT 2, * FROM temp.sqlite_sequence; +-- ]], { +-- -- <autoinc-4.6> +-- 1, "t1", 21, 2, "t3", 21 +-- -- </autoinc-4.6> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.7", +-- [[ +-- INSERT INTO t3 SELECT x+100, y FROM t1; +-- SELECT 1, * FROM main.sqlite_sequence; +-- SELECT 2, * FROM temp.sqlite_sequence; +-- ]], { +-- -- <autoinc-4.7> +-- 1, "t1", 21, 2, "t3", 121 +-- -- </autoinc-4.7> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.8", +-- [[ +-- DROP TABLE t3; +-- SELECT 1, * FROM main.sqlite_sequence; +-- SELECT 2, * FROM temp.sqlite_sequence; +-- ]], { +-- -- <autoinc-4.8> +-- 1, "t1", 21 +-- -- </autoinc-4.8> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.9", +-- [[ +-- CREATE TEMP TABLE t2(p INTEGER PRIMARY KEY AUTOINCREMENT, q); +-- INSERT INTO t2 SELECT * FROM t1; +-- DROP TABLE t1; +-- SELECT 1, * FROM main.sqlite_sequence; +-- SELECT 2, * FROM temp.sqlite_sequence; +-- ]], { +-- -- <autoinc-4.9> +-- 2, "t2", 21 +-- -- </autoinc-4.9> +-- }) + +-- test:do_execsql_test( +-- "autoinc-4.10", +-- [[ +-- DROP TABLE t2; +-- SELECT 1, * FROM main.sqlite_sequence; +-- SELECT 2, * FROM temp.sqlite_sequence; +-- ]], { +-- -- <autoinc-4.10> + +-- -- </autoinc-4.10> +-- }) + + + +-- Requirement REQ00310: Make sure an insert fails if the sequence is +-- already at its maximum value. +-- +test:do_execsql_test( + "autoinc-6.1", + [[ + CREATE TABLE t6(v INTEGER PRIMARY KEY AUTOINCREMENT, w); + INSERT INTO t6 VALUES(9223372036854775808,1); + INSERT INTO t6 VALUES(NULL,1); + -- SELECT seq FROM sql_sequence WHERE name='t6'; + ]], { + -- <autoinc-6.1> + -- 9223372036854775807 + -- </autoinc-6.1> + }) + +test:do_catchsql_test( + "autoinc-6.2", + [[ + INSERT INTO t6 VALUES(NULL,1); + ]], { + -- <autoinc-6.2> + 1, "UNIQUE constraint failed: t6.v" + -- </autoinc-6.2> + }) + +-- Allow the AUTOINCREMENT keyword inside the parentheses +-- on a separate PRIMARY KEY designation. +-- +test:do_execsql_test( + "autoinc-7.1", + [[ + CREATE TABLE t7(x INTEGER, y REAL, PRIMARY KEY(x AUTOINCREMENT)); + INSERT INTO t7(y) VALUES(123); + INSERT INTO t7(y) VALUES(234); + DELETE FROM t7; + INSERT INTO t7(y) VALUES(345); + SELECT * FROM t7; + ]], { + -- <autoinc-7.1> + 3, 345.0 + -- </autoinc-7.1> + }) + +-- Test that if the AUTOINCREMENT is applied to a non integer primary key +-- the error message is sensible. +test:do_catchsql_test( + "autoinc-7.2", + [[ + CREATE TABLE t8(x TEXT PRIMARY KEY AUTOINCREMENT); + ]], { + -- <autoinc-7.2> + 1, "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY" + -- </autoinc-7.2> + }) + +-- Ticket #3148 +-- Make sure the sqlite_sequence table is not damaged when doing +-- an empty insert - an INSERT INTO ... SELECT ... where the SELECT +-- clause returns an empty set. +-- +test:do_test( + "autoinc-9.1", + function() + return test:execsql([[ + CREATE TABLE t2(x INTEGER PRIMARY KEY AUTOINCREMENT, y); + INSERT INTO t2 VALUES(NULL, 1); + CREATE TABLE t3(a INTEGER PRIMARY KEY AUTOINCREMENT, b); + INSERT INTO t3 SELECT * FROM t2 WHERE y>1; + + SELECT * FROM sql_sequence WHERE name='t3'; + ]]) + end, { + -- <autoinc-9.1> + "t3", 0 + -- </autoinc-9.1> + }) + +test:catchsql(" pragma recursive_triggers = off ") +-- Ticket #3928. Make sure that triggers to not make extra slots in +-- the SQLITE_SEQUENCE table. +-- +test:do_test( + "autoinc-3928.1", + function() + return test:execsql([[ + CREATE TABLE t3928(a INTEGER PRIMARY KEY AUTOINCREMENT, b); + CREATE TRIGGER t3928r1 BEFORE INSERT ON t3928 BEGIN + INSERT INTO t3928(b) VALUES('before1'); + INSERT INTO t3928(b) VALUES('before2'); + END; + CREATE TRIGGER t3928r2 AFTER INSERT ON t3928 BEGIN + INSERT INTO t3928(b) VALUES('after1'); + INSERT INTO t3928(b) VALUES('after2'); + END; + INSERT INTO t3928(b) VALUES('test'); + SELECT * FROM t3928 ORDER BY a; + ]]) + end, { + -- <autoinc-3928.1> + 1, "before1", 2, "after1", 3, "after2", 4, "before2", 5, "after1", 6, "after2", 7, "test", 8, "before1", 9, "before2", 10, "after1", 11, "before1", 12, "before2", 13, "after2" + -- </autoinc-3928.1> + }) + +test:do_test( + "autoinc-3928.2", + function() + return test:execsql([[ + SELECT * FROM sql_sequence WHERE name='t3928' + ]]) + end, { + -- <autoinc-3928.2> + "t3928", 13 + -- </autoinc-3928.2> + }) + +test:do_test( + "autoinc-3928.3", + function() + return test:execsql([[ + DROP TRIGGER t3928r1; + DROP TRIGGER t3928r2; + CREATE TRIGGER t3928r3 BEFORE UPDATE ON t3928 + WHEN typeof(new.b)=='integer' BEGIN + INSERT INTO t3928(b) VALUES('before-int-' || new.b); + END; + CREATE TRIGGER t3928r4 AFTER UPDATE ON t3928 + WHEN typeof(new.b)=='integer' BEGIN + INSERT INTO t3928(b) VALUES('after-int-' || new.b); + END; + DELETE FROM t3928 WHERE a!=1; + UPDATE t3928 SET b=456 WHERE a=1; + SELECT * FROM t3928 ORDER BY a; + ]]) + end, { + -- <autoinc-3928.3> + 1, 456, 14, "before-int-456", 15, "after-int-456" + -- </autoinc-3928.3> + }) + +test:do_test( + "autoinc-3928.4", + function() + return test:execsql([[ + SELECT * FROM sql_sequence WHERE name='t3928' + ]]) + end, { + -- <autoinc-3928.4> + "t3928", 15 + -- </autoinc-3928.4> + }) + +test:do_test( + "autoinc-3928.5", + function() + return test:execsql([[ + CREATE TABLE t3928b(x INTEGER PRIMARY KEY); + INSERT INTO t3928b VALUES(100); + INSERT INTO t3928b VALUES(200); + INSERT INTO t3928b VALUES(300); + DELETE FROM t3928; + CREATE TABLE t3928c(y INTEGER PRIMARY KEY AUTOINCREMENT, z); + CREATE TRIGGER t3928br1 BEFORE DELETE ON t3928b BEGIN + INSERT INTO t3928(b) VALUES('before-del-'||old.x); + INSERT INTO t3928c(z) VALUES('before-del-'||old.x); + END; + CREATE TRIGGER t3928br2 AFTER DELETE ON t3928b BEGIN + INSERT INTO t3928(b) VALUES('after-del-'||old.x); + INSERT INTO t3928c(z) VALUES('after-del-'||old.x); + END; + DELETE FROM t3928b; + SELECT * FROM t3928 ORDER BY a; + ]]) + end, { + -- <autoinc-3928.5> + 16, "before-del-100", 17, "after-del-100", 18, "before-del-200", 19, "after-del-200", 20, "before-del-300", 21, "after-del-300" + -- </autoinc-3928.5> + }) + +test:do_test( + "autoinc-3928.6", + function() + return test:execsql([[ + SELECT * FROM t3928c ORDER BY y; + ]]) + end, { + -- <autoinc-3928.6> + 1, "before-del-100", 2, "after-del-100", 3, "before-del-200", 4, "after-del-200", 5, "before-del-300", 6, "after-del-300" + -- </autoinc-3928.6> + }) + +test:do_test( + "autoinc-3928.7", + function() + return test:execsql([[ + SELECT * FROM sql_sequence WHERE name LIKE 't3928%' ORDER BY name; + ]]) + end, { + -- <autoinc-3928.7> + "t3928", 21, "t3928c", 6 + -- </autoinc-3928.7> + }) + +-- Ticket [a696379c1f0886615541a48b35bd8181a80e88f8] +test:do_test( + "autoinc-a69637.1", + function() + return test:execsql([[ + CREATE TABLE ta69637_1(x INTEGER PRIMARY KEY AUTOINCREMENT, y); + CREATE TABLE ta69637_2(z INTEGER PRIMARY KEY); + CREATE TRIGGER ra69637_1 AFTER INSERT ON ta69637_2 BEGIN + INSERT INTO ta69637_1(y) VALUES(new.z+1); + END; + INSERT INTO ta69637_2 VALUES(123); + SELECT * FROM ta69637_1; + ]]) + end, { + -- <autoinc-a69637.1> + 1, 124 + -- </autoinc-a69637.1> + }) + +test:do_test( + "autoinc-a69637.2", + function() + return test:execsql([[ + CREATE VIEW va69637_2 AS SELECT * FROM ta69637_2; + CREATE TRIGGER ra69637_2 INSTEAD OF INSERT ON va69637_2 BEGIN + INSERT INTO ta69637_1(y) VALUES(new.z+10000); + END; + INSERT INTO va69637_2 VALUES(123); + SELECT * FROM ta69637_1; + ]]) + end, { + -- <autoinc-a69637.2> + 1, 124, 2, 10123 + -- </autoinc-a69637.2> + }) + +test:finish_test()