Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -3.25.0 +3.25.3 Index: autoconf/configure.ac ================================================================== --- autoconf/configure.ac +++ autoconf/configure.ac @@ -170,11 +170,11 @@ # AC_ARG_ENABLE(session, [AS_HELP_STRING( [--enable-session], [enable the session extension [default=no]])], [], []) if test x"$enable_session" = "xyes"; then - BUILD_CFLAGS="$BUILD_CFLAGS-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK" + BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK" fi #----------------------------------------------------------------------- #----------------------------------------------------------------------- # --enable-debug Index: configure ================================================================== --- configure +++ configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlite 3.25.0. +# Generated by GNU Autoconf 2.69 for sqlite 3.25.3. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # @@ -724,12 +724,12 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.25.0' -PACKAGE_STRING='sqlite 3.25.0' +PACKAGE_VERSION='3.25.3' +PACKAGE_STRING='sqlite 3.25.3' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ @@ -1464,11 +1464,11 @@ # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.25.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.25.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. @@ -1529,11 +1529,11 @@ _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.25.0:";; + short | recursive ) echo "Configuration of sqlite 3.25.3:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options @@ -1655,11 +1655,11 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.25.0 +sqlite configure 3.25.3 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. @@ -2074,11 +2074,11 @@ } # ac_fn_c_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.25.0, which was +It was created by sqlite $as_me 3.25.3, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF @@ -12230,11 +12230,11 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.25.0, which was +This file was extended by sqlite $as_me 3.25.3, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS @@ -12296,11 +12296,11 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlite config.status 3.25.0 +sqlite config.status 3.25.3 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation Index: ext/fts3/fts3.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -1819,11 +1819,11 @@ ){ int rc = SQLITE_OK; /* Return code */ const char *zCsr = zNode; /* Cursor to iterate through node */ const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ char *zBuffer = 0; /* Buffer to load terms into */ - int nAlloc = 0; /* Size of allocated buffer */ + i64 nAlloc = 0; /* Size of allocated buffer */ int isFirstTerm = 1; /* True when processing first term on page */ sqlite3_int64 iChild; /* Block id of child node to descend to */ /* Skip over the 'height' varint that occurs at the start of every ** interior node. Then load the blockid of the left-child of the b-tree @@ -1857,18 +1857,18 @@ } isFirstTerm = 0; zCsr += fts3GetVarint32(zCsr, &nSuffix); assert( nPrefix>=0 && nSuffix>=0 ); - if( &zCsr[nSuffix]>zEnd ){ + if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){ rc = FTS_CORRUPT_VTAB; goto finish_scan; } - if( nPrefix+nSuffix>nAlloc ){ + if( (i64)nPrefix+nSuffix>nAlloc ){ char *zNew; - nAlloc = (nPrefix+nSuffix) * 2; - zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); + nAlloc = ((i64)nPrefix+nSuffix) * 2; + zNew = (char *)sqlite3_realloc64(zBuffer, nAlloc); if( !zNew ){ rc = SQLITE_NOMEM; goto finish_scan; } zBuffer = zNew; Index: ext/fts3/fts3_write.c ================================================================== --- ext/fts3/fts3_write.c +++ ext/fts3/fts3_write.c @@ -1372,19 +1372,23 @@ /* Because of the FTS3_NODE_PADDING bytes of padding, the following is ** safe (no risk of overread) even if the node data is corrupted. */ pNext += fts3GetVarint32(pNext, &nPrefix); pNext += fts3GetVarint32(pNext, &nSuffix); - if( nPrefix<0 || nSuffix<=0 - || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] + if( nSuffix<=0 + || (&pReader->aNode[pReader->nNode] - pNext)pReader->nTermAlloc ){ return FTS_CORRUPT_VTAB; } - if( nPrefix+nSuffix>pReader->nTermAlloc ){ - int nNew = (nPrefix+nSuffix)*2; - char *zNew = sqlite3_realloc(pReader->zTerm, nNew); + /* Both nPrefix and nSuffix were read by fts3GetVarint32() and so are + ** between 0 and 0x7FFFFFFF. But the sum of the two may cause integer + ** overflow - hence the (i64) casts. */ + if( (i64)nPrefix+nSuffix>(i64)pReader->nTermAlloc ){ + i64 nNew = ((i64)nPrefix+nSuffix)*2; + char *zNew = sqlite3_realloc64(pReader->zTerm, nNew); if( !zNew ){ return SQLITE_NOMEM; } pReader->zTerm = zNew; pReader->nTermAlloc = nNew; @@ -1402,11 +1406,11 @@ /* Check that the doclist does not appear to extend past the end of the ** b-tree node. And that the final byte of the doclist is 0x00. If either ** of these statements is untrue, then the data structure is corrupt. */ - if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] + if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)nDoclist || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) ){ return FTS_CORRUPT_VTAB; } return SQLITE_OK; @@ -3728,25 +3732,30 @@ if( bFirst==0 ){ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nPrefix); } p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); + if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){ + return SQLITE_CORRUPT_VTAB; + } blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); if( rc==SQLITE_OK ){ memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); p->term.n = nPrefix+nSuffix; p->iOff += nSuffix; if( p->iChild==0 ){ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); + if( (p->nNode-p->iOff)nDoclist ){ + return SQLITE_CORRUPT_VTAB; + } p->aDoclist = &p->aNode[p->iOff]; p->iOff += p->nDoclist; } } } assert( p->iOff<=p->nNode ); - return rc; } /* ** Release all dynamic resources held by node-reader object *p. Index: ext/userauth/userauth.c ================================================================== --- ext/userauth/userauth.c +++ ext/userauth/userauth.c @@ -208,11 +208,11 @@ if( db->auth.zAuthPW==0 ) return SQLITE_NOMEM; memcpy(db->auth.zAuthPW,zPW,nPW); db->auth.nAuthPW = nPW; rc = sqlite3UserAuthCheckLogin(db, "main", &authLevel); db->auth.authLevel = authLevel; - sqlite3ExpirePreparedStatements(db); + sqlite3ExpirePreparedStatements(db, 0); if( rc ){ return rc; /* OOM error, I/O error, etc. */ } if( authLevelnMem; - sqlite3VdbeLoadString(v, i, zName); - sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); - sqlite3MayAbort(pParse); - } -#endif - /* figure out how many UTF-8 characters are in zName */ zTabName = pTab->zName; nTabName = sqlite3Utf8CharLen(zTabName, -1); /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in @@ -237,10 +223,24 @@ " sqlite_rename_test(%Q, sql, type, name, 1) " "THEN %Q ELSE tbl_name END " "WHERE type IN ('view', 'trigger')" , zDb, zTabName, zName, zTabName, zDb, zName); } + + /* If this is a virtual table, invoke the xRename() function if + ** one is defined. The xRename() callback will modify the names + ** of any resources used by the v-table implementation (including other + ** SQLite tables) that are identified by the name of the virtual table. + */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( pVTab ){ + int i = ++pParse->nMem; + sqlite3VdbeLoadString(v, i, zName); + sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); + sqlite3MayAbort(pParse); + } +#endif renameReloadSchema(pParse, iDb); renameTestSchema(pParse, zDb, iDb==1); exit_rename_table: @@ -1074,11 +1074,11 @@ } if( rc==SQLITE_OK && pStep->zTarget ){ Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb); if( pTarget==0 ){ rc = SQLITE_ERROR; - }else{ + }else if( SQLITE_OK==(rc = sqlite3ViewGetColumnNames(pParse, pTarget)) ){ SrcList sSrc; memset(&sSrc, 0, sizeof(sSrc)); sSrc.nSrc = 1; sSrc.a[0].zName = pStep->zTarget; sSrc.a[0].pTab = pTarget; @@ -1420,21 +1420,24 @@ sWalker.u.pRename = &sCtx; rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp); if( rc==SQLITE_OK ){ + int isLegacy = (db->flags & SQLITE_LegacyAlter); if( sParse.pNewTable ){ Table *pTab = sParse.pNewTable; if( pTab->pSelect ){ - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = &sParse; + if( isLegacy==0 ){ + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = &sParse; - sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); - if( sParse.nErr ) rc = sParse.rc; - sqlite3WalkSelect(&sWalker, pTab->pSelect); + sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); + if( sParse.nErr ) rc = sParse.rc; + sqlite3WalkSelect(&sWalker, pTab->pSelect); + } }else{ /* Modify any FK definitions to point to the new table. */ #ifndef SQLITE_OMIT_FOREIGN_KEY if( db->flags & SQLITE_ForeignKeys ){ FKey *pFKey; @@ -1449,19 +1452,23 @@ /* If this is the table being altered, fix any table refs in CHECK ** expressions. Also update the name that appears right after the ** "CREATE [VIRTUAL] TABLE" bit. */ if( sqlite3_stricmp(zOld, pTab->zName)==0 ){ sCtx.pTab = pTab; - sqlite3WalkExprList(&sWalker, pTab->pCheck); + if( isLegacy==0 ){ + sqlite3WalkExprList(&sWalker, pTab->pCheck); + } renameTokenFind(&sParse, &sCtx, pTab->zName); } } } else if( sParse.pNewIndex ){ renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName); - sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + if( isLegacy==0 ){ + sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + } } #ifndef SQLITE_OMIT_TRIGGER else{ Trigger *pTrigger = sParse.pNewTrigger; @@ -1470,16 +1477,18 @@ && sCtx.pTab->pSchema==pTrigger->pTabSchema ){ renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table); } - rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb); - if( rc==SQLITE_OK ){ - renameWalkTrigger(&sWalker, pTrigger); - for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ - if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ - renameTokenFind(&sParse, &sCtx, pStep->zTarget); + if( isLegacy==0 ){ + rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb); + if( rc==SQLITE_OK ){ + renameWalkTrigger(&sWalker, pTrigger); + for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ + if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ + renameTokenFind(&sParse, &sCtx, pStep->zTarget); + } } } } } #endif @@ -1533,10 +1542,11 @@ ){ sqlite3 *db = sqlite3_context_db_handle(context); char const *zDb = (const char*)sqlite3_value_text(argv[0]); char const *zInput = (const char*)sqlite3_value_text(argv[1]); int bTemp = sqlite3_value_int(argv[4]); + int isLegacy = (db->flags & SQLITE_LegacyAlter); #ifndef SQLITE_OMIT_AUTHORIZATION sqlite3_xauth xAuth = db->xAuth; db->xAuth = 0; #endif @@ -1545,20 +1555,22 @@ if( zDb && zInput ){ int rc; Parse sParse; rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp); if( rc==SQLITE_OK ){ - if( sParse.pNewTable && sParse.pNewTable->pSelect ){ + if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = &sParse; sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); if( sParse.nErr ) rc = sParse.rc; } else if( sParse.pNewTrigger ){ - rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb); + if( isLegacy==0 ){ + rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb); + } if( rc==SQLITE_OK ){ int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema); int i2 = sqlite3FindDbName(db, zDb); if( i1==i2 ) sqlite3_result_int(context, 1); } Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -1769,14 +1769,10 @@ pTab->aCol[i].notNull = OE_Abort; } } } - /* The remaining transformations only apply to b-tree tables, not to - ** virtual tables */ - if( IN_DECLARE_VTAB ) return; - /* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY ** into BTREE_BLOBKEY. */ if( pParse->addrCrTab ){ assert( v ); @@ -2285,10 +2281,14 @@ ** statement that defines the view. */ assert( pTable->pSelect ); pSel = sqlite3SelectDup(db, pTable->pSelect, 0); if( pSel ){ +#ifndef SQLITE_OMIT_ALTERTABLE + u8 eParseMode = pParse->eParseMode; + pParse->eParseMode = PARSE_MODE_NORMAL; +#endif n = pParse->nTab; sqlite3SrcListAssignCursors(pParse, pSel->pSrc); pTable->nCol = -1; db->lookaside.bDisable++; #ifndef SQLITE_OMIT_AUTHORIZATION @@ -2330,10 +2330,13 @@ nErr++; } sqlite3DeleteTable(db, pSelTab); sqlite3SelectDelete(db, pSel); db->lookaside.bDisable--; +#ifndef SQLITE_OMIT_ALTERTABLE + pParse->eParseMode = eParseMode; +#endif } else { nErr++; } pTable->pSchema->schemaFlags |= DB_UnresetViews; if( db->mallocFailed ){ Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -1263,21 +1263,18 @@ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags); } } /* Fill in pNew->pLeft and pNew->pRight. */ + zAlloc += dupedExprNodeSize(p, dupFlags); if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){ - zAlloc += dupedExprNodeSize(p, dupFlags); if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){ pNew->pLeft = p->pLeft ? exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0; pNew->pRight = p->pRight ? exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0; } - if( pzBuffer ){ - *pzBuffer = zAlloc; - } }else{ #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(p, EP_Reduced|EP_TokenOnly) ){ pNew->pWin = 0; }else{ @@ -1293,10 +1290,13 @@ pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); } pNew->pRight = sqlite3ExprDup(db, p->pRight, 0); } } + if( pzBuffer ){ + *pzBuffer = zAlloc; + } } return pNew; } /* @@ -4844,22 +4844,19 @@ } /* ** This is the Expr node callback for sqlite3ExprImpliesNotNullRow(). ** If the expression node requires that the table at pWalker->iCur -** have a non-NULL column, then set pWalker->eCode to 1 and abort. +** have one or more non-NULL column, then set pWalker->eCode to 1 and abort. +** +** This routine controls an optimization. False positives (setting +** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives +** (never setting pWalker->eCode) is a harmless missed optimization. */ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ - /* This routine is only called for WHERE clause expressions and so it - ** cannot have any TK_AGG_COLUMN entries because those are only found - ** in HAVING clauses. We can get a TK_AGG_FUNCTION in a WHERE clause, - ** but that is an illegal construct and the query will be rejected at - ** a later stage of processing, so the TK_AGG_FUNCTION case does not - ** need to be considered here. */ - assert( pExpr->op!=TK_AGG_COLUMN ); + testcase( pExpr->op==TK_AGG_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); - if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune; switch( pExpr->op ){ case TK_ISNOT: case TK_NOT: case TK_ISNULL: Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -3019,10 +3019,11 @@ sqlite3_mutex_enter(db->mutex); db->errMask = 0xff; db->nDb = 2; db->magic = SQLITE_MAGIC_BUSY; db->aDb = db->aDbStatic; + db->lookaside.bDisable = 1; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS; db->autoCommit = 1; Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -519,11 +519,15 @@ { "lstat", (sqlite3_syscall_ptr)0, 0 }, #endif #define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent) #if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) +# ifdef __ANDROID__ + { "ioctl", (sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 }, +# else { "ioctl", (sqlite3_syscall_ptr)ioctl, 0 }, +# endif #else { "ioctl", (sqlite3_syscall_ptr)0, 0 }, #endif #define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent) Index: src/os_win.c ================================================================== --- src/os_win.c +++ src/os_win.c @@ -2904,10 +2904,11 @@ winFile *pFile = (winFile*)id; /* File handle object */ int rc = SQLITE_OK; /* Return code for this function */ DWORD lastErrno; #if SQLITE_MAX_MMAP_SIZE>0 sqlite3_int64 oldMmapSize; + if( pFile->nFetchOut>0 ) return SQLITE_OK; #endif assert( pFile ); SimulateIOError(return SQLITE_IOERR_TRUNCATE); OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n", Index: src/pragma.h ================================================================== --- src/pragma.h +++ src/pragma.h @@ -391,10 +391,15 @@ /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) + {/* zName: */ "legacy_alter_table", + /* ePragTyp: */ PragTyp_FLAG, + /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, + /* ColNames: */ 0, 0, + /* iArg: */ SQLITE_LegacyAlter }, {/* zName: */ "legacy_file_format", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_LegacyFileFmt }, @@ -644,6 +649,6 @@ /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_WriteSchema }, #endif }; -/* Number of pragmas: 60 on by default, 77 total. */ +/* Number of pragmas: 61 on by default, 78 total. */ Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -2315,10 +2315,17 @@ int i; /* Loop counter */ int rc; /* Result code */ ExprList *pOrderBy; /* The ORDER BY clause */ Expr *pLimit; /* Saved LIMIT and OFFSET */ int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */ + +#ifndef SQLITE_OMIT_WINDOWFUNC + if( p->pWin ){ + sqlite3ErrorMsg(pParse, "cannot use window functions in recursive queries"); + return; + } +#endif /* Obtain authorization to do a recursive query */ if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; /* Process the LIMIT and OFFSET clauses, if they exist */ @@ -4065,11 +4072,11 @@ return 1; } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ /* -** A structure to keep track of all of the column values that fixed to +** A structure to keep track of all of the column values that are fixed to ** a known value due to WHERE clause constraints of the form COLUMN=VALUE. */ typedef struct WhereConst WhereConst; struct WhereConst { Parse *pParse; /* Parsing context */ @@ -4077,17 +4084,32 @@ int nChng; /* Number of times a constant is propagated */ Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ }; /* -** Add a new entry to the pConst object +** Add a new entry to the pConst object. Except, do not add duplicate +** pColumn entires. */ static void constInsert( - WhereConst *pConst, - Expr *pColumn, - Expr *pValue + WhereConst *pConst, /* The WhereConst into which we are inserting */ + Expr *pColumn, /* The COLUMN part of the constraint */ + Expr *pValue /* The VALUE part of the constraint */ ){ + int i; + assert( pColumn->op==TK_COLUMN ); + + /* 2018-10-25 ticket [cf5ed20f] + ** Make sure the same pColumn is not inserted more than once */ + for(i=0; inConst; i++){ + const Expr *pExpr = pConst->apExpr[i*2]; + assert( pExpr->op==TK_COLUMN ); + if( pExpr->iTable==pColumn->iTable + && pExpr->iColumn==pColumn->iColumn + ){ + return; /* Already present. Return without doing anything. */ + } + } pConst->nConst++; pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, pConst->nConst*2*sizeof(Expr*)); if( pConst->apExpr==0 ){ @@ -6084,10 +6106,11 @@ sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub); sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); sqlite3VdbeResolveLabel(v, addrGosub); VdbeNoopComment((v, "inner-loop subroutine")); + sSort.labelOBLopt = 0; selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp1(v, OP_Return, regGosub); VdbeComment((v, "end inner-loop subroutine")); sqlite3VdbeResolveLabel(v, iBreak); Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1534,10 +1534,11 @@ #define SQLITE_CellSizeCk 0x00200000 /* Check btree cell sizes on load */ #define SQLITE_Fts3Tokenizer 0x00400000 /* Enable fts3_tokenizer(2) */ #define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee*/ #define SQLITE_TriggerEQP 0x01000000 /* Show trigger EXPLAIN QUERY PLAN */ #define SQLITE_ResetDatabase 0x02000000 /* Reset the database */ +#define SQLITE_LegacyAlter 0x04000000 /* Legacy ALTER TABLE behaviour */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG #define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */ #define SQLITE_VdbeListing 0x10000000 /* Debug listings of VDBE programs */ @@ -3176,10 +3177,11 @@ ** OPFLAG_SAVEPOSITION == BTREE_SAVEPOSITION ** OPFLAG_AUXDELETE == BTREE_AUXDELETE */ #define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */ /* Also used in P2 (not P5) of OP_Delete */ +#define OPFLAG_NOCHNG 0x01 /* OP_VColumn nochange for UPDATE */ #define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */ #define OPFLAG_LASTROWID 0x20 /* Set to update db->lastRowid */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */ #define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ Index: src/tclsqlite.c ================================================================== --- src/tclsqlite.c +++ src/tclsqlite.c @@ -3495,10 +3495,11 @@ flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; #else flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; #endif + if( objc==1 ) return sqliteCmdUsage(interp, objv); if( objc==2 ){ zArg = Tcl_GetStringFromObj(objv[1], 0); if( strcmp(zArg,"-version")==0 ){ Tcl_AppendResult(interp,sqlite3_libversion(), (char*)0); return TCL_OK; Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -861,11 +861,11 @@ for(i=0; inCol; i++){ if( aXRef[i]>=0 ){ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); }else{ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); - sqlite3VdbeChangeP5(v, 1); /* Enable sqlite3_vtab_nochange() */ + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* Enable sqlite3_vtab_nochange() */ } } if( HasRowid(pTab) ){ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); if( pRowid ){ Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -6955,14 +6955,15 @@ ** Store in register P3 the value of the P2-th column of ** the current row of the virtual-table of cursor P1. ** ** If the VColumn opcode is being used to fetch the value of ** an unchanging column during an UPDATE operation, then the P5 -** value is 1. Otherwise, P5 is 0. The P5 value is returned -** by sqlite3_vtab_nochange() routine and can be used -** by virtual table implementations to return special "no-change" -** marks which can be more efficient, depending on the virtual table. +** value is OPFLAG_NOCHNG. This will cause the sqlite3_vtab_nochange() +** function to return true inside the xColumn method of the virtual +** table implementation. The P5 column might also contain other +** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are +** unused by OP_VColumn. */ case OP_VColumn: { sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; @@ -6980,11 +6981,12 @@ pVtab = pCur->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; - if( pOp->p5 ){ + testcase( (pOp->p5 & OPFLAG_NOCHNG)==0 && pOp->p5!=0 ); + if( pOp->p5 & OPFLAG_NOCHNG ){ sqlite3VdbeMemSetNull(pDest); pDest->flags = MEM_Null|MEM_Zero; pDest->u.nZero = 0; }else{ MemSetTypeFlag(pDest, MEM_Null); @@ -7057,11 +7059,14 @@ ** in register P1 is passed as the zName argument to the xRename method. */ case OP_VRename: { sqlite3_vtab *pVtab; Mem *pName; - + int isLegacy; + + isLegacy = (db->flags & SQLITE_LegacyAlter); + db->flags |= SQLITE_LegacyAlter; pVtab = pOp->p4.pVtab->pVtab; pName = &aMem[pOp->p1]; assert( pVtab->pModule->xRename ); assert( memIsValid(pName) ); assert( p->readOnly==0 ); @@ -7071,10 +7076,11 @@ testcase( pName->enc==SQLITE_UTF16BE ); testcase( pName->enc==SQLITE_UTF16LE ); rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8); if( rc ) goto abort_due_to_error; rc = pVtab->pModule->xRename(pVtab, pName->z); + if( isLegacy==0 ) db->flags &= ~SQLITE_LegacyAlter; sqlite3VtabImportErrmsg(p, pVtab); p->expired = 0; if( rc ) goto abort_due_to_error; break; } Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -4555,11 +4555,13 @@ /* The index entry must begin with a header size */ (void)getVarint32((u8*)m.z, szHdr); testcase( szHdr==3 ); testcase( szHdr==m.n ); - if( unlikely(szHdr<3 || (int)szHdr>m.n) ){ + testcase( szHdr>0x7fffffff ); + assert( m.n>=0 ); + if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){ goto idx_rowid_corruption; } /* The last field of the index should be an integer - the ROWID. ** Verify that the last entry really is an integer. */ Index: src/wherecode.c ================================================================== --- src/wherecode.c +++ src/wherecode.c @@ -423,11 +423,11 @@ Select *pSelect; /* Pointer to the SELECT on the RHS */ for(i=iEq; inLTerm; i++){ if( pLoop->aLTerm[i]->pExpr==pX ){ int iField = pLoop->aLTerm[i]->iField - 1; - assert( pOrigRhs->a[iField].pExpr!=0 ); + if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); pOrigRhs->a[iField].pExpr = 0; assert( pOrigLhs->a[iField].pExpr!=0 ); pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr); pOrigLhs->a[iField].pExpr = 0; Index: src/whereexpr.c ================================================================== --- src/whereexpr.c +++ src/whereexpr.c @@ -1380,10 +1380,11 @@ ** Note that the virtual term must be tagged with TERM_VNULL. */ if( pExpr->op==TK_NOTNULL && pExpr->pLeft->op==TK_COLUMN && pExpr->pLeft->iColumn>=0 + && !ExprHasProperty(pExpr, EP_FromJoin) && OptimizationEnabled(db, SQLITE_Stat34) ){ Expr *pNewExpr; Expr *pLeft = pExpr->pLeft; int idxNew; Index: test/alter.test ================================================================== --- test/alter.test +++ test/alter.test @@ -873,7 +873,25 @@ do_execsql_test alter-16.2 { ALTER TABLE t16a RENAME TO t16a_rn; SELECT * FROM t16a_rn ORDER BY a; } {abc 1.25 99 xyzzy cba 5.5 98 fizzle} -finish_test +# 2018-09-16 ticket b41031ea2b5372378cb3d2d43cf9fe2a4a5c2510 +# +ifcapable rtree { + db close + sqlite3 db :memory: + do_execsql_test alter-17.100 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE VIRTUAL TABLE t2 USING rtree(id,x0,x1); + INSERT INTO t1 VALUES(1,'apple'),(2,'fig'),(3,'pear'); + INSERT INTO t2 VALUES(1,1.0,2.0),(2,2.0,3.0),(3,1.5,3.5); + CREATE TRIGGER r1 AFTER UPDATE ON t1 BEGIN + DELETE FROM t2 WHERE id = OLD.a; + END; + ALTER TABLE t1 RENAME TO t3; + UPDATE t3 SET b='peach' WHERE a=2; + SELECT * FROM t2 ORDER BY 1; + } {1 1.0 2.0 3 1.5 3.5} +} +finish_test Index: test/alterauth.test ================================================================== --- test/alterauth.test +++ test/alterauth.test @@ -22,11 +22,11 @@ set testprefix alterauth set ::auth [list] proc xAuth {type args} { if {$type == "SQLITE_ALTER_TABLE"} { - lappend ::auth [concat $type $args] + lappend ::auth [concat $type [lrange $args 0 3]] } return SQLITE_OK } db auth xAuth ADDED test/alterlegacy.test Index: test/alterlegacy.test ================================================================== --- /dev/null +++ test/alterlegacy.test @@ -0,0 +1,470 @@ +# 2018 September 20 +# +# 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. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix alterlegacy + +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + +do_execsql_test 1.0 { + PRAGMA legacy_alter_table = 1; + CREATE TABLE t1(a, b, CHECK(t1.a != t1.b)); + CREATE TABLE t2(a, b); + CREATE INDEX t2expr ON t2(a) WHERE t2.b>0; +} + +do_execsql_test 1.1 { + SELECT sql FROM sqlite_master +} { + {CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))} + {CREATE TABLE t2(a, b)} + {CREATE INDEX t2expr ON t2(a) WHERE t2.b>0} +} + +# Legacy behavior is to corrupt the schema in this case, as the table name in +# the CHECK constraint is incorrect after "t1" is renamed. This version is +# slightly different - it rejects the change and rolls back the transaction. +do_catchsql_test 1.2 { + ALTER TABLE t1 RENAME TO t1new; +} {1 {no such column: t1.a}} + +do_execsql_test 1.3 { + CREATE TABLE t3(c, d); + ALTER TABLE t3 RENAME TO t3new; + DROP TABLE t3new; +} + +do_execsql_test 1.4 { + SELECT sql FROM sqlite_master +} { + {CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))} + {CREATE TABLE t2(a, b)} + {CREATE INDEX t2expr ON t2(a) WHERE t2.b>0} +} + + +do_catchsql_test 1.3 { + ALTER TABLE t2 RENAME TO t2new; +} {1 {no such column: t2.b}} +do_execsql_test 1.4 { + SELECT sql FROM sqlite_master +} { + {CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))} + {CREATE TABLE t2(a, b)} + {CREATE INDEX t2expr ON t2(a) WHERE t2.b>0} +} + + +#------------------------------------------------------------------------- +reset_db +ifcapable vtab { + register_echo_module db + + do_execsql_test 2.0 { + PRAGMA legacy_alter_table = 1; + CREATE TABLE abc(a, b, c); + INSERT INTO abc VALUES(1, 2, 3); + CREATE VIRTUAL TABLE eee USING echo('abc'); + SELECT * FROM eee; + } {1 2 3} + + do_execsql_test 2.1 { + ALTER TABLE eee RENAME TO fff; + SELECT * FROM fff; + } {1 2 3} + + db close + sqlite3 db test.db + + do_catchsql_test 2.2 { + ALTER TABLE fff RENAME TO ggg; + } {1 {no such module: echo}} +} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 3.0 { + PRAGMA legacy_alter_table = 1; + CREATE TABLE txx(a, b, c); + INSERT INTO txx VALUES(1, 2, 3); + CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx; + CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one; + CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx; +} + +do_execsql_test 3.1.1 { + SELECT * FROM vvv; +} {1 2 3} +do_execsql_test 3.1.2a { + ALTER TABLE txx RENAME TO "t xx"; +} +do_catchsql_test 3.1.2b { + SELECT * FROM vvv; +} {1 {no such table: main.txx}} +do_execsql_test 3.1.3 { + SELECT sql FROM sqlite_master WHERE name='vvv'; +} {{CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx}} + + +do_catchsql_test 3.2.1 { + SELECT * FROM uuu; +} {1 {no such table: main.txx}} +do_execsql_test 3.2.2 { + SELECT sql FROM sqlite_master WHERE name='uuu';; +} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one}} + +do_catchsql_test 3.3.1 { + SELECT * FROM ttt; +} {1 {no such table: txx}} +do_execsql_test 3.3.2 { + SELECT sql FROM sqlite_temp_master WHERE name='ttt'; +} {{CREATE VIEW ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 4.0 { + PRAGMA legacy_alter_table = 1; + CREATE table t1(x, y); + CREATE table t2(a, b); + + CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN + SELECT t1.x, * FROM t1, t2; + INSERT INTO t2 VALUES(new.x, new.y); + END; +} + +do_execsql_test 4.1 { + INSERT INTO t1 VALUES(1, 1); + ALTER TABLE t1 RENAME TO t11; +} +do_catchsql_test 4.1a { + INSERT INTO t11 VALUES(2, 2); +} {1 {no such table: main.t1}} +do_execsql_test 4.1b { + ALTER TABLE t11 RENAME TO t1; + ALTER TABLE t2 RENAME TO t22; +} +do_catchsql_test 4.1c { + INSERT INTO t1 VALUES(3, 3); +} {1 {no such table: main.t2}} + +proc squish {a} { + string trim [regsub -all {[[:space:]][[:space:]]*} $a { }] +} +db func squish squish +do_test 4.2 { + execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' } +} [list [squish { + CREATE TRIGGER tr1 AFTER INSERT ON "t1" BEGIN + SELECT t1.x, * FROM t1, t2; + INSERT INTO t2 VALUES(new.x, new.y); + END +}]] + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 5.0 { + PRAGMA legacy_alter_table = 1; + CREATE TABLE t9(a, b, c); + CREATE TABLE t10(a, b, c); + CREATE TEMP TABLE t9(a, b, c); + + CREATE TRIGGER temp.t9t AFTER INSERT ON temp.t9 BEGIN + INSERT INTO t10 VALUES(new.a, new.b, new.c); + END; + + INSERT INTO temp.t9 VALUES(1, 2, 3); + SELECT * FROM t10; +} {1 2 3} + +do_execsql_test 5.1 { + ALTER TABLE temp.t9 RENAME TO 't1234567890' +} + +do_execsql_test 5.2 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t2 VALUES(3, 4); + CREATE VIEW v AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2; + SELECT * FROM v; +} {1 2 3 4} + +do_execsql_test 5.3 { + ALTER TABLE t2 RENAME TO one; +} {} + +do_catchsql_test 5.4 { + SELECT * FROM v +} {1 {no such table: main.t2}} + +do_execsql_test 5.5 { + ALTER TABLE one RENAME TO t2; + DROP VIEW v; + CREATE VIEW temp.vv AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2; + SELECT * FROM vv; +} {1 2 3 4} + +do_execsql_test 5.6 { + ALTER TABLE t2 RENAME TO one; +} {} +do_catchsql_test 5.7 { + SELECT * FROM vv +} {1 {no such table: t2}} + +#------------------------------------------------------------------------- + +ifcapable vtab { + register_tcl_module db + proc tcl_command {method args} { + switch -- $method { + xConnect { + return "CREATE TABLE t1(a, b, c)" + } + } + return {} + } + + do_execsql_test 6.0 { + CREATE VIRTUAL TABLE x1 USING tcl(tcl_command); + } + + do_execsql_test 6.1 { + ALTER TABLE x1 RENAME TO x2; + SELECT sql FROM sqlite_master WHERE name = 'x2' + } {{CREATE VIRTUAL TABLE "x2" USING tcl(tcl_command)}} + + do_execsql_test 7.1 { + CREATE TABLE ddd(db, sql, zOld, zNew, bTemp); + INSERT INTO ddd VALUES( + 'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', 'ddd', NULL, 0 + ), ( + 'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', NULL, 'eee', 0 + ), ( + 'main', NULL, 'ddd', 'eee', 0 + ); + } {} +} + +#------------------------------------------------------------------------- +# +reset_db +forcedelete test.db2 +do_execsql_test 8.1 { + PRAGMA legacy_alter_table = 1; + ATTACH 'test.db2' AS aux; + PRAGMA foreign_keys = on; + CREATE TABLE aux.p1(a INTEGER PRIMARY KEY, b); + CREATE TABLE aux.c1(x INTEGER PRIMARY KEY, y REFERENCES p1(a)); + INSERT INTO aux.p1 VALUES(1, 1); + INSERT INTO aux.p1 VALUES(2, 2); + INSERT INTO aux.c1 VALUES(NULL, 2); + CREATE TABLE aux.c2(x INTEGER PRIMARY KEY, y REFERENCES c1(a)); +} + +do_execsql_test 8.2 { + ALTER TABLE aux.p1 RENAME TO ppp; +} + +do_execsql_test 8.2 { + INSERT INTO aux.c1 VALUES(NULL, 1); + SELECT sql FROM aux.sqlite_master WHERE name = 'c1'; +} {{CREATE TABLE c1(x INTEGER PRIMARY KEY, y REFERENCES "ppp"(a))}} + +reset_db +do_execsql_test 9.0 { + PRAGMA legacy_alter_table = 1; + CREATE TABLE t1(a, b, c); + CREATE VIEW v1 AS SELECT * FROM t2; +} +do_execsql_test 9.1 { + ALTER TABLE t1 RENAME TO t3; +} {} +do_execsql_test 9.1b { + ALTER TABLE t3 RENAME TO t1; +} {} +do_execsql_test 9.2 { + DROP VIEW v1; + CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN + INSERT INTO t2 VALUES(new.a); + END; +} +do_execsql_test 9.3 { + ALTER TABLE t1 RENAME TO t3; +} {} + +forcedelete test.db2 +do_execsql_test 9.4 { + ALTER TABLE t3 RENAME TO t1; + DROP TRIGGER tr; + + ATTACH 'test.db2' AS aux; + CREATE TRIGGER tr AFTER INSERT ON t1 WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END; + + CREATE TABLE aux.t1(x); + CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END; +} +do_execsql_test 9.5 { + ALTER TABLE main.t1 RENAME TO t3; +} +do_execsql_test 9.6 { + SELECT sql FROM sqlite_temp_master; + SELECT sql FROM sqlite_master WHERE type='trigger'; +} { + {CREATE TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END} + {CREATE TRIGGER tr AFTER INSERT ON "t3" WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END} +} + +#------------------------------------------------------------------------- +reset_db +ifcapable fts5 { + do_execsql_test 10.0 { + PRAGMA legacy_alter_table = 1; + CREATE VIRTUAL TABLE fff USING fts5(x, y, z); + } + + do_execsql_test 10.1 { + BEGIN; + INSERT INTO fff VALUES('a', 'b', 'c'); + ALTER TABLE fff RENAME TO ggg; + COMMIT; + } + + do_execsql_test 10.2 { + SELECT * FROM ggg; + } {a b c} +} + +#------------------------------------------------------------------------- +reset_db +forcedelete test.db2 +db func trigger trigger +set ::trigger [list] +proc trigger {args} { + lappend ::trigger $args +} +do_execsql_test 11.0 { + PRAGMA legacy_alter_table = 1; + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t1(a, b, c); + CREATE TABLE main.t1(a, b, c); + CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN + SELECT trigger(new.a, new.b, new.c); + END; +} + +do_execsql_test 11.1 { + INSERT INTO main.t1 VALUES(1, 2, 3); + INSERT INTO aux.t1 VALUES(4, 5, 6); +} +do_test 11.2 { set ::trigger } {{4 5 6}} + +do_execsql_test 11.3 { + SELECT name, tbl_name FROM sqlite_temp_master; +} {tr t1} + +do_execsql_test 11.4 { + ALTER TABLE main.t1 RENAME TO t2; + SELECT name, tbl_name FROM sqlite_temp_master; +} {tr t1} + +do_execsql_test 11.5 { + ALTER TABLE aux.t1 RENAME TO t2; + SELECT name, tbl_name FROM sqlite_temp_master; +} {tr t2} + +do_execsql_test 11.6 { + INSERT INTO aux.t2 VALUES(7, 8, 9); +} +do_test 11.7 { set ::trigger } {{4 5 6} {7 8 9}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 12.0 { + PRAGMA legacy_alter_table = 1; + CREATE TABLE t1(a); + CREATE TABLE t2(w); + CREATE TRIGGER temp.r1 AFTER INSERT ON main.t2 BEGIN + INSERT INTO t1(a) VALUES(new.w); + END; + CREATE TEMP TABLE t2(x); +} + +do_execsql_test 12.1 { + ALTER TABLE main.t2 RENAME TO t3; +} + +do_execsql_test 12.2 { + INSERT INTO t3 VALUES('WWW'); + SELECT * FROM t1; +} {WWW} + + +#------------------------------------------------------------------------- +reset_db + +ifcapable rtree { + do_execsql_test 14.0 { + PRAGMA legacy_alter_table = 1; + CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy); + + CREATE TABLE "mytable" ( "fid" INTEGER PRIMARY KEY, "geom" BLOB); + + CREATE TRIGGER tr1 AFTER UPDATE OF "geom" ON "mytable" + WHEN OLD."fid" = NEW."fid" AND NEW."geom" IS NULL BEGIN + DELETE FROM rt WHERE id = OLD."fid"; + END; + + INSERT INTO mytable VALUES(1, X'abcd'); + } + + do_execsql_test 14.1 { + UPDATE mytable SET geom = X'1234' + } + + do_execsql_test 14.2 { + ALTER TABLE mytable RENAME TO mytable_renamed; + } + + do_execsql_test 14.3 { + CREATE TRIGGER tr2 AFTER INSERT ON mytable_renamed BEGIN + DELETE FROM rt WHERE id=(SELECT min(id) FROM rt); + END; + } + + do_execsql_test 14.4 { + ALTER TABLE mytable_renamed RENAME TO mytable2; + } +} + +reset_db +do_execsql_test 14.5 { + PRAGMA legacy_alter_table = 1; + CREATE TABLE t1(a, b, c); + CREATE VIEW v1 AS SELECT * FROM t1; + CREATE TRIGGER xyz AFTER INSERT ON t1 BEGIN + SELECT a, b FROM v1; + END; +} +do_execsql_test 14.6 { + ALTER TABLE t1 RENAME TO tt1; +} + + +finish_test + Index: test/altermalloc2.test ================================================================== --- test/altermalloc2.test +++ test/altermalloc2.test @@ -70,6 +70,32 @@ ALTER TABLE t1 RENAME abcd TO dcba } } -test { faultsim_test_result {0 {}} } + +reset_db +do_execsql_test 4.0 { + CREATE TABLE rr(a, b); + CREATE VIEW vv AS SELECT * FROM rr; + + CREATE TRIGGER vv1 INSTEAD OF INSERT ON vv BEGIN + SELECT 1, 2, 3; + END; + CREATE TRIGGER tr1 AFTER INSERT ON rr BEGIN + INSERT INTO vv VALUES(new.a, new.b); + END; +} {} + +faultsim_save_and_close +do_faultsim_test 4 -faults oom-* -prep { + faultsim_restore_and_reopen + execsql { SELECT * FROM sqlite_master } +} -body { + execsql { + ALTER TABLE rr RENAME a TO c; + } +} -test { + faultsim_test_result {0 {}} +} + finish_test Index: test/altertab.test ================================================================== --- test/altertab.test +++ test/altertab.test @@ -408,8 +408,58 @@ } do_catchsql_test 13.2 { ALTER TABLE t2 RENAME b TO y; } {1 {error in trigger tr1 after rename: ambiguous column name: y}} + +#------------------------------------------------------------------------- +reset_db + +ifcapable rtree { + do_execsql_test 14.0 { + CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy); + + CREATE TABLE "mytable" ( "fid" INTEGER PRIMARY KEY, "geom" BLOB); + + CREATE TRIGGER tr1 AFTER UPDATE OF "geom" ON "mytable" + WHEN OLD."fid" = NEW."fid" AND NEW."geom" IS NULL BEGIN + DELETE FROM rt WHERE id = OLD."fid"; + END; + + INSERT INTO mytable VALUES(1, X'abcd'); + } + + do_execsql_test 14.1 { + UPDATE mytable SET geom = X'1234' + } + + do_execsql_test 14.2 { + ALTER TABLE mytable RENAME TO mytable_renamed; + } + + do_execsql_test 14.3 { + CREATE TRIGGER tr2 AFTER INSERT ON mytable_renamed BEGIN + DELETE FROM rt WHERE id=(SELECT min(id) FROM rt); + END; + } + + do_execsql_test 14.4 { + ALTER TABLE mytable_renamed RENAME TO mytable2; + } +} + +reset_db +do_execsql_test 14.5 { + CREATE TABLE t1(a, b, c); + CREATE VIEW v1 AS SELECT * FROM t1; + CREATE TRIGGER xyz AFTER INSERT ON t1 BEGIN + SELECT a, b FROM v1; + END; +} +do_execsql_test 14.6 { + ALTER TABLE t1 RENAME TO tt1; +} + + finish_test ADDED test/altertab2.test Index: test/altertab2.test ================================================================== --- /dev/null +++ test/altertab2.test @@ -0,0 +1,46 @@ +# 2018 September 30 +# +# 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. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix altertab + +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + +ifcapable fts5 { + do_execsql_test 1.0 { + CREATE TABLE rr(a, b); + CREATE VIRTUAL TABLE ff USING fts5(a, b); + CREATE TRIGGER tr1 AFTER INSERT ON rr BEGIN + INSERT INTO ff VALUES(new.a, new.b); + END; + INSERT INTO rr VALUES('hello', 'world'); + SELECT * FROM ff; + } {hello world} + + do_execsql_test 1.1 { + ALTER TABLE ff RENAME TO ffff; + } + + do_execsql_test 1.2 { + INSERT INTO rr VALUES('in', 'tcl'); + SELECT * FROM ffff; + } {hello world in tcl} +} + + +finish_test + Index: test/auth.test ================================================================== --- test/auth.test +++ test/auth.test @@ -2596,11 +2596,12 @@ # Test also that if SQLITE_DENY is returned from an SQLITE_READ authorizer # invocation with no column name specified, compilation fails. # set ::authargs [list] -proc auth {op a b c d} { +proc auth {op args} { + foreach {a b c d} $args break lappend ::authargs $op $a $b $c $d if {$op == "SQLITE_READ"} { return "SQLITE_DENY" } return "SQLITE_OK" } set ::authargs [list] ADDED test/fts3corrupt4.test Index: test/fts3corrupt4.test ================================================================== --- /dev/null +++ test/fts3corrupt4.test @@ -0,0 +1,147 @@ +# 2006 September 9 +# +# 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 FTS3 module. +# +# $Id: fts3aa.test,v 1.1 2007/08/20 17:38:42 shess Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix fts3corrupt4 + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +do_execsql_test 1.0 { + BEGIN; + CREATE VIRTUAL TABLE ft USING fts3; + INSERT INTO ft VALUES('aback'); + INSERT INTO ft VALUES('abaft'); + INSERT INTO ft VALUES('abandon'); + COMMIT; +} + +proc blob {a} { binary decode hex $a } +db func blob blob + +do_execsql_test 1.1 { + SELECT quote(root) FROM ft_segdir; +} {X'0005616261636B03010200030266740302020003046E646F6E03030200'} + +do_execsql_test 1.2 { + UPDATE ft_segdir SET root = blob( + '0005616261636B03010200 FFFFFFFF0702 66740302020003046E646F6E03030200' + ); +} + +do_catchsql_test 1.3 { + SELECT * FROM ft WHERE ft MATCH 'abandon'; +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 2.0.0 { + CREATE VIRTUAL TABLE ft USING fts3; + INSERT INTO ft(ft) VALUES('nodesize=32'); +} +do_test 2.0.1 { + for {set i 0} {$i < 12} {incr i} { + execsql { + BEGIN; + INSERT INTO ft VALUES('abc' || $i); + INSERT INTO ft VALUES('abc' || $i || 'x' ); + INSERT INTO ft VALUES('abc' || $i || 'xx' ); + COMMIT + } + } + execsql { + SELECT count(*) FROM ft_segdir; + SELECT count(*) FROM ft_segments; + } +} {12 0} + +do_execsql_test 2.1 { + INSERT INTO ft(ft) VALUES('merge=1,4'); + SELECT count(*) FROM ft_segdir; + SELECT count(*) FROM ft_segments; +} {12 3} + +do_execsql_test 2.2 { + SELECT quote(block) FROM ft_segments WHERE blockid=2 +} {X'00056162633130031F0200'} + +db func blob blob +do_execsql_test 2.3.1 { + UPDATE ft_segments SET block = + blob('00056162633130031F0200 FFFFFFFF07FF55 66740302020003046E646F6E03030200') + WHERE blockid=2; +} {} +do_catchsql_test 2.3.2 { + INSERT INTO ft(ft) VALUES('merge=1,4'); +} {1 {database disk image is malformed}} + +do_execsql_test 2.4.1 { + UPDATE ft_segments SET block = + blob('00056162633130031F0200 02FFFFFFFF07 66740302020003046E646F6E03030200') + WHERE blockid=2; +} {} +do_catchsql_test 2.4.2 { + INSERT INTO ft(ft) VALUES('merge=1,4'); +} {1 {database disk image is malformed}} + +do_execsql_test 2.5.1 { + UPDATE ft_segments SET block = + blob('00056162633130031F0200 0202 6674 FFFFFF070302020003046E646F6E030200') + WHERE blockid=2; +} {} +do_catchsql_test 2.5.2 { + INSERT INTO ft(ft) VALUES('merge=1,4'); +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 3.0.0 { + CREATE VIRTUAL TABLE ft USING fts3; + INSERT INTO ft(ft) VALUES('nodesize=32'); +} +do_test 3.0.1 { + execsql BEGIN + for {set i 0} {$i < 20} {incr i} { + execsql { INSERT INTO ft VALUES('abc' || $i) } + } + execsql { + COMMIT; + SELECT count(*) FROM ft_segdir; + SELECT count(*) FROM ft_segments; + } +} {1 5} + +do_execsql_test 3.1 { + SELECT quote(root) FROM ft_segdir +} {X'0101056162633132040136030132030136'} + +db func blob blob +do_execsql_test 3.2 { + UPDATE ft_segdir + SET root = blob('0101056162633132FFFFFFFF070236030132030136'); +} + +do_catchsql_test 3.1 { + SELECT * FROM ft WHERE ft MATCH 'abc20' +} {1 {database disk image is malformed}} + +finish_test + + Index: test/fuzzdata5.db ================================================================== --- test/fuzzdata5.db +++ test/fuzzdata5.db cannot compute difference between binary files Index: test/incrvacuum.test ================================================================== --- test/incrvacuum.test +++ test/incrvacuum.test @@ -780,7 +780,55 @@ execsql { COMMIT; PRAGMA integrity_check; } } {ok} + +#------------------------------------------------------------------------- +# At one point it was unsafe to truncate a db file on windows while there +# were outstanding xFetch() references. This test case attempts to hit +# that case. +# +reset_db +do_execsql_test incrvacuum-16.0 { + PRAGMA auto_vacuum = 2; + CREATE TABLE t3(a); + INSERT INTO t3 VALUES(1), (2), (3), (4); + + CREATE TABLE t2(x); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); +} {} + +# Reopen db to ensure the page-cache is empty. +# +db close +sqlite3 db test.db + +# Open db in mmap-mode. Open a transaction, delete some data, then run +# incremental-vacuum. Do not commit the transaction. +# +do_execsql_test incrvacuum-16.1 { + PRAGMA mmap_size = 1000000; + BEGIN; + DELETE FROM t2; + PRAGMA incremental_vacuum = 1000; +} {1000000} + +# Scan through table t3 (which is all clean pages - so mmap is used). Then, +# midway through, commit the transaction. This causes the db to be truncated +# while there are outstanding xFetch pages. +# +do_test incrvacuum-16.2 { + set res [list] + db eval { SELECT a FROM t3 } { + if {$a==3} { db eval COMMIT } + lappend res $a + } + set res +} {1 2 3 4} finish_test Index: test/lookaside.test ================================================================== --- test/lookaside.test +++ test/lookaside.test @@ -22,11 +22,11 @@ } # The tests in this file configure the lookaside allocator after a # connection is opened. This will not work if there is any "presql" # configured (SQL run within the [sqlite3] wrapper in tester.tcl). -if {[info exists ::G(perm:presql)]} { +if {[info exists ::G(perm:dbconfig)] && $::G(perm:dbconfig)!=""} { finish_test return } test_set_config_pagecache 0 0 @@ -33,11 +33,13 @@ catch {db close} sqlite3_shutdown sqlite3_initialize autoinstall_test_functions + sqlite3 db test.db +db cache size 4 # Make sure sqlite3_db_config() and sqlite3_db_status are working. # do_test lookaside-1.1 { catch {sqlite3_config_error db} @@ -97,10 +99,11 @@ db eval {CREATE TABLE t2(x);} foreach {x y z} [sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 0] break expr {$x==0 && $y<$z && $z>10 && $z<100} } {1} do_test lookaside-2.3 { + db eval {SELECT 1} sqlite3_db_config_lookaside db 0 50 50 } {5} ;# SQLITE_BUSY do_test lookaside-2.4 { db cache flush sqlite3_db_config_lookaside db 0 50 50 Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -253,10 +253,11 @@ fts3ae.test fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3atoken.test fts3auto.test fts3aux1.test fts3aux2.test fts3b.test fts3comp1.test fts3conf.test fts3corrupt2.test fts3corrupt.test + fts3corrupt4.test fts3cov.test fts3c.test fts3defer2.test fts3defer3.test fts3defer.test fts3drop.test fts3d.test fts3e.test fts3expr2.test fts3expr3.test fts3expr4.test fts3expr5.test fts3expr.test fts3fault2.test fts3fault.test fts3first.test fts3join.test fts3malloc.test fts3matchinfo.test fts3near.test Index: test/releasetest.tcl ================================================================== --- test/releasetest.tcl +++ test/releasetest.tcl @@ -77,10 +77,11 @@ "Unlock-Notify" { -O2 -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_THREADSAFE -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + -DSQLITE_USER_AUTHENTICATION=1 } "Secure-Delete" { -O2 -DSQLITE_SECURE_DELETE=1 -DSQLITE_SOUNDEX=1 Index: test/rowvalue.test ================================================================== --- test/rowvalue.test +++ test/rowvalue.test @@ -543,7 +543,18 @@ # 2018-02-18: Memory leak nexted row-value. Detected by OSSFuzz. # do_catchsql_test 20.1 { SELECT 1 WHERE (2,(2,0)) IS (2,(2,0)); } {0 1} + +# 2018-11-03: Ticket https://www.sqlite.org/src/info/1a84668dcfdebaf1 +# Assertion fault when doing row-value operations on a primary key +# containing duplicate columns. +# +do_execsql_test 21.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a,b,PRIMARY KEY(b,b)); + INSERT INTO t1 VALUES(1,2),(3,4),(5,6); + SELECT * FROM t1 WHERE (a,b) IN (VALUES(1,2)); +} {1 2} finish_test Index: test/tclsqlite.test ================================================================== --- test/tclsqlite.test +++ test/tclsqlite.test @@ -14,10 +14,12 @@ # Actually, all tests are based on the TCL interface, so the main # interface is pretty well tested. This file contains some addition # tests for fringe issues that the main test suite does not cover. # # $Id: tclsqlite.test,v 1.73 2009/03/16 13:19:36 danielk1977 Exp $ + +catch {sqlite3} set testdir [file dirname $argv0] source $testdir/tester.tcl # Check the error messages generated by tclsqlite @@ -28,10 +30,15 @@ } do_test tcl-1.1 { set v [catch {sqlite3 -bogus} msg] regsub {really_sqlite3} $msg {sqlite3} msg lappend v $msg +} [list 1 "wrong # args: should be \"$r\""] +do_test tcl-1.1.1 { + set v [catch {sqlite3} msg] + regsub {really_sqlite3} $msg {sqlite3} msg + lappend v $msg } [list 1 "wrong # args: should be \"$r\""] do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg } {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, deserialize, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, serialize, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}} Index: test/view.test ================================================================== --- test/view.test +++ test/view.test @@ -680,22 +680,23 @@ INSERT INTO t25 (x) VALUES (1); ANALYZE; } proc authLogDelete {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DELETE" && [string match sqlite_stat* $arg1]} { - lappend ::log [list $code $arg1 $arg2 $arg3 $arg4 $args] + # lappend ::log [list $code $arg1 $arg2 $arg3 $arg4 $args] + lappend ::log [list $code $arg1 $arg2 $arg3 $arg4] } return SQLITE_OK } set log "" db authorizer ::authLogDelete db eval {DROP VIEW x1;} set log } {} -set res [list {SQLITE_DELETE sqlite_stat1 {} main {} {}}] -ifcapable stat4 { lappend res {SQLITE_DELETE sqlite_stat4 {} main {} {}} } +set res [list {SQLITE_DELETE sqlite_stat1 {} main {}}] +ifcapable stat4 { lappend res {SQLITE_DELETE sqlite_stat4 {} main {}} } do_test view-25.2 { set log "" db eval {DROP TABLE t25;} set log } $res Index: test/where.test ================================================================== --- test/where.test +++ test/where.test @@ -1410,7 +1410,20 @@ AND (b=1 OR c=1); } { 4 1 0 4 0 1 } + +# 2018-11-05: ticket [https://www.sqlite.org/src/tktview/65eb38f6e46de8c75e188a] +# Incorrect result in LEFT JOIN when STAT4 is enabled. +# +sqlite3 db :memory: +do_execsql_test where-22.1 { + CREATE TABLE t1(a INT); + CREATE INDEX t1a ON t1(a); + INSERT INTO t1(a) VALUES(NULL),(NULL),(42),(NULL),(NULL); + CREATE TABLE t2(dummy INT); + SELECT count(*) FROM t1 LEFT JOIN t2 ON a IS NOT NULL; +} {5} + finish_test Index: test/whereL.test ================================================================== --- test/whereL.test +++ test/whereL.test @@ -108,7 +108,18 @@ B WHERE A.id='1' AND A.id=subq.yy AND B.id=subq.zz; } {1} + +# 2018-10-25: Ticket [cf5ed20f] +# Incorrect join result with duplicate WHERE clause constraint. +# +do_execsql_test 400 { + CREATE TABLE x(a, b, c); + CREATE TABLE y(a, b); + INSERT INTO x VALUES (1, 0, 1); + INSERT INTO y VALUES (1, 2); + SELECT x.a FROM x JOIN y ON x.c = y.a WHERE x.b = 1 AND x.b = 1; +} {} finish_test Index: test/window1.test ================================================================== --- test/window1.test +++ test/window1.test @@ -524,7 +524,29 @@ } {1 {misuse of window function sum()}} do_catchsql_test 11.4 { CREATE INDEX t6i ON t6(lead(b) OVER ()); } {1 {misuse of window function lead()}} -finish_test +# 2018-09-17 ticket 510cde277783b5fb5de628393959849dff377eb3 +# Endless loop on a query with window functions and a limit +# +do_execsql_test 12.100 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(id INT, b VARCHAR, c VARCHAR); + INSERT INTO t1 VALUES(1, 'A', 'one'); + INSERT INTO t1 VALUES(2, 'B', 'two'); + INSERT INTO t1 VALUES(3, 'C', 'three'); + INSERT INTO t1 VALUES(4, 'D', 'one'); + INSERT INTO t1 VALUES(5, 'E', 'two'); + SELECT id, b, lead(c,1) OVER(ORDER BY c) AS x + FROM t1 WHERE id>1 + ORDER BY b LIMIT 1; +} {2 B two} +do_execsql_test 12.110 { + INSERT INTO t1 VALUES(6, 'F', 'three'); + INSERT INTO t1 VALUES(7, 'G', 'one'); + SELECT id, b, lead(c,1) OVER(ORDER BY c) AS x + FROM t1 WHERE id>1 + ORDER BY b LIMIT 2; +} {2 B two 3 C three} +finish_test Index: test/window4.tcl ================================================================== --- test/window4.tcl +++ test/window4.tcl @@ -356,9 +356,35 @@ } execsql_test 10.3 { SELECT id, lag(b, -1) OVER (PARTITION BY a ORDER BY id) FROM t7; } +execsql_test 11.0 { + DROP VIEW IF EXISTS v8; + DROP TABLE IF EXISTS t8; + CREATE TABLE t8(t INT, total INT); + INSERT INTO t8 VALUES(0,2); + INSERT INTO t8 VALUES(5,1); + INSERT INTO t8 VALUES(10,1); +} + +execsql_test 11.1 { + SELECT NTILE(256) OVER (ORDER BY total) - 1 AS nt FROM t8; +} + +execsql_test 11.2 { + CREATE VIEW v8 AS SELECT NTILE(256) OVER (ORDER BY total) - 1 AS nt FROM t8; +} + +execsql_test 11.3 { + SELECT * FROM v8; +} + +execsql_test 11.4 { + SELECT * FROM ( + SELECT NTILE(256) OVER (ORDER BY total) - 1 AS nt FROM t8 + ) sub; +} finish_test Index: test/window4.test ================================================================== --- test/window4.test +++ test/window4.test @@ -1214,42 +1214,63 @@ } {1 1 1 1 1 1 1} do_execsql_test 9.2 { SELECT dense_rank() OVER (PARTITION BY x) FROM t2 } {1 1 1 1 1 1 1} + do_test 9.3 { set myres {} foreach r [db eval {SELECT x, percent_rank() OVER (PARTITION BY x ORDER BY x) FROM t2}] { - lappend myres [format %.2f [set r]] + lappend myres [format %.4f [set r]] + } + set res2 {1.0000 0.0000 1.0000 0.0000 1.0000 0.0000 4.0000 0.0000 4.0000 0.0000 6.0000 0.0000 7.0000 0.0000} + foreach r [set myres] r2 [set res2] { + if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { + error "list element [set i] does not match: got=[set r] expected=[set r2]" + } } - set myres -} {1.00 0.00 1.00 0.00 1.00 0.00 4.00 0.00 4.00 0.00 6.00 0.00 7.00 0.00} + set {} {} +} {} do_execsql_test 9.4 { SELECT x, rank() OVER (ORDER BY x) FROM t2 ORDER BY 1,2 } {1 1 1 1 1 1 4 4 4 4 6 6 7 7} do_execsql_test 9.5 { SELECT DISTINCT x, rank() OVER (ORDER BY x) FROM t2 ORDER BY 1,2 } {1 1 4 4 6 6 7 7} + do_test 9.6 { set myres {} foreach r [db eval {SELECT percent_rank() OVER () FROM t1}] { - lappend myres [format %.2f [set r]] + lappend myres [format %.4f [set r]] + } + set res2 {0.0000 0.0000 0.0000} + foreach r [set myres] r2 [set res2] { + if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { + error "list element [set i] does not match: got=[set r] expected=[set r2]" + } } - set myres -} {0.00 0.00 0.00} + set {} {} +} {} + do_test 9.7 { set myres {} foreach r [db eval {SELECT cume_dist() OVER () FROM t1}] { - lappend myres [format %.2f [set r]] + lappend myres [format %.4f [set r]] + } + set res2 {1.0000 1.0000 1.0000} + foreach r [set myres] r2 [set res2] { + if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { + error "list element [set i] does not match: got=[set r] expected=[set r2]" + } } - set myres -} {1.00 1.00 1.00} + set {} {} +} {} do_execsql_test 10.0 { DROP TABLE IF EXISTS t7; CREATE TABLE t7(id INTEGER PRIMARY KEY, a INTEGER, b INTEGER); INSERT INTO t7(id, a, b) VALUES @@ -1266,7 +1287,34 @@ } {1 {} 2 2 3 {} 4 {} 5 {} 6 8} do_execsql_test 10.3 { SELECT id, lag(b, -1) OVER (PARTITION BY a ORDER BY id) FROM t7; } {1 {} 2 4 3 {} 4 8 5 1 6 {}} + +do_execsql_test 11.0 { + DROP VIEW IF EXISTS v8; + DROP TABLE IF EXISTS t8; + CREATE TABLE t8(t INT, total INT); + INSERT INTO t8 VALUES(0,2); + INSERT INTO t8 VALUES(5,1); + INSERT INTO t8 VALUES(10,1); +} {} + +do_execsql_test 11.1 { + SELECT NTILE(256) OVER (ORDER BY total) - 1 AS nt FROM t8; +} {0 1 2} + +do_execsql_test 11.2 { + CREATE VIEW v8 AS SELECT NTILE(256) OVER (ORDER BY total) - 1 AS nt FROM t8; +} {} + +do_execsql_test 11.3 { + SELECT * FROM v8; +} {0 1 2} + +do_execsql_test 11.4 { + SELECT * FROM ( + SELECT NTILE(256) OVER (ORDER BY total) - 1 AS nt FROM t8 + ) sub; +} {0 1 2} finish_test Index: test/with1.test ================================================================== --- test/with1.test +++ test/with1.test @@ -862,10 +862,31 @@ do_catchsql_test 16.1 { WITH RECURSIVE i(x) AS (VALUES(1) UNION SELECT count(*) FROM i) SELECT * FROM i; } {1 {recursive aggregate queries not supported}} + +# Or window-function recursive queries. Ticket e8275b41. +# +ifcapable windowfunc { + do_catchsql_test 16.2 { + WITH RECURSIVE + i(x) AS (VALUES(1) UNION SELECT count(*) OVER () FROM i) + SELECT * FROM i; + } {1 {cannot use window functions in recursive queries}} + do_catchsql_test 16.3 { + WITH RECURSIVE + t(id, parent) AS (VALUES(1,2)), + q(id, parent, rn) AS ( + VALUES(1,2,3) + UNION ALL + SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.id) AS rn + FROM q JOIN t ON t.parent = q.id + ) + SELECT * FROM q; + } {1 {cannot use window functions in recursive queries}} +} #------------------------------------------------------------------------- do_execsql_test 17.1 { WITH x(a) AS ( WITH y(b) AS (SELECT 10) Index: tool/mkpragmatab.tcl ================================================================== --- tool/mkpragmatab.tcl +++ tool/mkpragmatab.tcl @@ -380,10 +380,15 @@ NAME: threads FLAG: Result0 NAME: optimize FLAG: Result1 NeedSchema + + NAME: legacy_alter_table + TYPE: FLAG + ARG: SQLITE_LegacyAlter + IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) } # Open the output file # set destfile "[file dir [file dir [file normal $argv0]]]/src/pragma.h"