aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.c9
-rw-r--r--src/global.c3
-rw-r--r--src/main.c7
-rw-r--r--src/resolve.c29
-rw-r--r--src/select.c16
-rw-r--r--src/shell.c.in5
-rw-r--r--src/sqlite.h.in11
-rw-r--r--src/sqliteInt.h14
-rw-r--r--src/test_config.c8
9 files changed, 90 insertions, 12 deletions
diff --git a/src/build.c b/src/build.c
index 15f8fe1d2..e7c23c173 100644
--- a/src/build.c
+++ b/src/build.c
@@ -3006,9 +3006,12 @@ void sqlite3CreateView(
** on a view, even though views do not have rowids. The following flag
** setting fixes this problem. But the fix can be disabled by compiling
** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that
- ** depend upon the old buggy behavior. */
-#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
- p->tabFlags |= TF_NoVisibleRowid;
+ ** depend upon the old buggy behavior. The ability can also be toggled
+ ** using SQLITE_CONFIG_NO_ROWID_IN_VIEW */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */
+#else
+ p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */
#endif
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
diff --git a/src/global.c b/src/global.c
index 7f27d91d1..121b3f6d6 100644
--- a/src/global.c
+++ b/src/global.c
@@ -289,6 +289,9 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
#ifndef SQLITE_UNTESTABLE
0, /* xTestCallback */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */
+#endif
0, /* bLocaltimeFault */
0, /* xAltLocaltime */
0x7ffffffe, /* iOnceResetThreshold */
diff --git a/src/main.c b/src/main.c
index 03429983d..6c8ae5b28 100644
--- a/src/main.c
+++ b/src/main.c
@@ -765,6 +765,13 @@ int sqlite3_config(int op, ...){
}
#endif /* SQLITE_OMIT_DESERIALIZE */
+ case SQLITE_CONFIG_NO_ROWID_IN_VIEW: {
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid;
+#endif
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
diff --git a/src/resolve.c b/src/resolve.c
index 2a128199d..6e0c9906a 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -469,8 +469,37 @@ static int lookupName(
}
}
if( 0==cnt && VisibleRowid(pTab) ){
+ /* pTab is a potential ROWID match. Keep track of it and match
+ ** the ROWID later if that seems appropriate. (Search for "cntTab"
+ ** to find related code.) Only allow a ROWID match if there is
+ ** a single ROWID match candidate.
+ */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match
+ ** if there is a single VIEW candidate or if there is a single
+ ** non-VIEW candidate plus multiple VIEW candidates. In other
+ ** words non-VIEW candidate terms take precedence over VIEWs.
+ */
+ if( cntTab==0
+ || (cntTab==1
+ && ALWAYS(pMatch!=0)
+ && ALWAYS(pMatch->pTab!=0)
+ && (pMatch->pTab->tabFlags & TF_Ephemeral)!=0
+ && (pTab->tabFlags & TF_Ephemeral)==0)
+ ){
+ cntTab = 1;
+ pMatch = pItem;
+ }else{
+ cntTab++;
+ }
+#else
+ /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is
+ ** simpler since we require exactly one candidate, which will
+ ** always be a non-VIEW
+ */
cntTab++;
pMatch = pItem;
+#endif
}
}
if( pMatch ){
diff --git a/src/select.c b/src/select.c
index 1085b9943..f71b71551 100644
--- a/src/select.c
+++ b/src/select.c
@@ -1953,11 +1953,7 @@ static const char *columnTypeImpl(
** data for the result-set column of the sub-select.
*/
if( iCol<pS->pEList->nExpr
-#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
- && iCol>=0
-#else
- && ALWAYS(iCol>=0)
-#endif
+ && (!ViewCanHaveRowid || iCol>=0)
){
/* If iCol is less than zero, then the expression requests the
** rowid of the sub-select or view. This expression is legal (see
@@ -5874,7 +5870,8 @@ int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
/* The usual case - do not allow ROWID on a subquery */
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
#else
- pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */
+ /* Legacy compatibility mode */
+ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid;
#endif
return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
}
@@ -6142,7 +6139,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pNestedFrom = pFrom->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
- assert( VisibleRowid(pTab)==0 );
+ assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid );
}else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
@@ -6174,7 +6171,8 @@ static int selectExpander(Walker *pWalker, Select *p){
pUsing = 0;
}
- nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
+ nAdd = pTab->nCol;
+ if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++;
for(j=0; j<nAdd; j++){
const char *zName;
struct ExprList_item *pX; /* Newly added ExprList term */
@@ -6256,7 +6254,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pX = &pNew->a[pNew->nExpr-1];
assert( pX->zEName==0 );
if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){
- if( pNestedFrom ){
+ if( pNestedFrom && j<pNestedFrom->nExpr ){
pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName);
testcase( pX->zEName==0 );
}else{
diff --git a/src/shell.c.in b/src/shell.c.in
index 9fdf90952..9a86a0665 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -12058,6 +12058,7 @@ static const char zOptions[] =
" -newline SEP set output row separator. Default: '\\n'\n"
" -nofollow refuse to open symbolic links to database files\n"
" -nonce STRING set the safe-mode escape nonce\n"
+ " -no-rowid-in-view Disable rowid-in-view using sqlite3_config()\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
" -pcachetrace trace all page cache operations\n"
@@ -12348,6 +12349,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
stdin_is_interactive = 0;
}else if( cli_strcmp(z,"-utf8")==0 ){
}else if( cli_strcmp(z,"-no-utf8")==0 ){
+ }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){
+ sqlite3_config(SQLITE_CONFIG_NO_ROWID_IN_VIEW);
}else if( cli_strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
const char *zSize;
@@ -12623,6 +12626,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
/* already handled */
}else if( cli_strcmp(z,"-no-utf8")==0 ){
/* already handled */
+ }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){
+ /* already handled */
}else if( cli_strcmp(z,"-heap")==0 ){
i++;
}else if( cli_strcmp(z,"-pagecache")==0 ){
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index e05d7b231..9c9a78c45 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -2143,6 +2143,16 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_NO_ROWID_IN_VIEW]]
+** <dt>SQLITE_CONFIG_NO_ROWID_IN_VIEW
+** <dd>The SQLITE_CONFIG_NO_ROWID_IN_VIEW option prohibits VIEWs from having
+** a ROWID. This is the default behavior and so test sqlite_config() option
+** is normally a no-op. However, if SQLite is compiled with the
+** -DSQLITE_ALLOW_ROWID_IN_VIEW option (not recommended!) then this configuration
+** option will disable that capability and make SQLite operate as it normally
+** would given default the default compile-time options. Once ROWIDs in VIEWs
+** have been disabled using this option, they cannot be reenabled.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2174,6 +2184,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_NO_ROWID_IN_VIEW 30 /* nil */
/*
** CAPI3REF: Database Connection Configuration Options
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index db1ede8e6..dba64c71b 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2535,6 +2535,15 @@ struct Table {
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
+/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is
+** available. By default, this macro is false
+*/
+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
+# define ViewCanHaveRowid 0
+#else
+# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0)
+#endif
+
/*
** Each foreign key constraint is an instance of the following structure.
**
@@ -4254,6 +4263,11 @@ struct Sqlite3Config {
#ifndef SQLITE_UNTESTABLE
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW
+ ** feature is disabled. 0 if rowids can
+ ** occur in views. */
+#endif
int bLocaltimeFault; /* True to fail localtime() calls */
int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */
int iOnceResetThreshold; /* When to reset OP_Once counters */
diff --git a/src/test_config.c b/src/test_config.c
index ee766a26d..76904e5bf 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -59,6 +59,14 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "rowid32", "0", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ Tcl_SetVar2(
+ interp, "sqlite_options", "allow_rowid_in_view", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(
+ interp, "sqlite_options", "allow_rowid_in_view", "0", TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_CASE_SENSITIVE_LIKE
Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","1",TCL_GLOBAL_ONLY);
#else