Fix resolution of "rowid" and similar identifiers in queries that use nested joins.
FossilOrigin-Name: 37ec43d92bde13efc71fa57ff5e59c4a95b9054c298f844aefeb06d4a91ad0d4
This commit is contained in:
commit
f99a27fab9
21
manifest
21
manifest
@ -1,5 +1,5 @@
|
||||
C Enable\s"OR\sIGNORE"\ssupport\svia\sSQLITE_VTAB_CONSTRAINT_SUPPORT\sfor\sinternal-content\s(not\scontentless\sor\sexternal-content)\sfts5\stables.
|
||||
D 2023-09-16T17:11:44.961
|
||||
C Fix\sresolution\sof\s"rowid"\sand\ssimilar\sidentifiers\sin\squeries\sthat\suse\snested\sjoins.
|
||||
D 2023-09-16T18:18:57.522
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -660,7 +660,7 @@ F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574
|
||||
F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
|
||||
F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
|
||||
F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500
|
||||
F src/expr.c c8c436f3c71179d1ecafc69623d562a1da006c2641dc0ef2884028317c7c584c
|
||||
F src/expr.c 9902bebcc9fa2b2c4cc94b7aa5615afe1affc98a986553aa7b239971c54ddea8
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36
|
||||
F src/func.c 154f08966f8a3a7cad6c438205df1abf58fb2826961a0683e82e120fa647e84c
|
||||
@ -708,14 +708,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
|
||||
F src/prepare.c 80548297dc0e1fb3139cdebffb5a1bcac3dfac66d791012dd74838e70445072d
|
||||
F src/printf.c e3ba080e2f409f9bfcc8d34724e6fc160e9c718dc92d0548f6b71b8b6f860ce2
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
|
||||
F src/resolve.c 0c3046b88901336709cd09f474303a16fc54bce13a2befcab66d0fa6b44ca869
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c e9fb48546ab1882639a3a960383f6342dddb776c0227615f8e19de51f0102f68
|
||||
F src/select.c 7f9155185be78902818b21c2cd3e33f01b4306279a15d6719eb1bbb9779034aa
|
||||
F src/shell.c.in 62708bea44d4e43aa7b1270ed422d1d29e82297924d4e0f223c39336a3f582f8
|
||||
F src/sqlite.h.in 931a58d119d5cf87110648f39fa0bb9f1738b0068cb68250d893304a471bd6c0
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 2f30b2671f4c03cd27a43f039e11251391066c97d11385f5f963bb40b03038ac
|
||||
F src/sqliteInt.h 0c33d256c0f7de824c7cef1aef14b66c94e4f0de77d598284048e73be6bb4f39
|
||||
F src/sqliteInt.h f2d713fac835f32b131d8a334595b0c471ede3796dab527c705d2b03c32d14e9
|
||||
F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6
|
||||
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@ -1290,7 +1290,7 @@ F test/joinC.test 1f1a602c2127f55f136e2cbd3bf2d26546614bf8cffe5902ec1ac9c07f87f2
|
||||
F test/joinD.test 2ce62e7353a0702ca5e70008faf319c1d4686aa19fba34275c6d1da0e960be28
|
||||
F test/joinE.test d5d182f3812771e2c0d97c9dcf5dbe4c41c8e21c82560e59358731c4a3981d6b
|
||||
F test/joinF.test 53dd66158806823ea680dd7543b5406af151b5aafa5cd06a7f3231cd94938127
|
||||
F test/joinH.test c9550bb6a0257cf99668a28485bb309bac542081702e89261b95542ab5f676b1
|
||||
F test/joinH.test 5f6107246b8509f9df4745fbdc2107a16f07594770a5c473a25f6c7f672edd86
|
||||
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
|
||||
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
|
||||
F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e
|
||||
@ -2121,8 +2121,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 04a333f5faf6b90592f1f69889ac6c28949955e186f39037cd639480b06feae8
|
||||
R 2ca122bb4f5ba7fdf7ce920860d7e991
|
||||
P c362bde4f4b8489947f080154d7fddcfd6e8e21d742a483c496fb7fbe59969d2 6b6eb38979d68c06e382620c8813d6b67a3de02c4a7a029c84f924b9a2e380c6
|
||||
R 41ef20fc783874cad2321b0304c25547
|
||||
T +closed 6b6eb38979d68c06e382620c8813d6b67a3de02c4a7a029c84f924b9a2e380c6
|
||||
U dan
|
||||
Z dc8ad7cb6d6511a31fbab9f13cc1476f
|
||||
Z a54b0e7aba892d4d1c6dff72b9ecc478
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
c362bde4f4b8489947f080154d7fddcfd6e8e21d742a483c496fb7fbe59969d2
|
||||
37ec43d92bde13efc71fa57ff5e59c4a95b9054c298f844aefeb06d4a91ad0d4
|
21
src/expr.c
21
src/expr.c
@ -2694,6 +2694,27 @@ int sqlite3IsRowid(const char *z){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to a buffer containing a usable rowid alias for table
|
||||
** pTab. An alias is usable if there is not an explicit user-defined column
|
||||
** of the same name.
|
||||
*/
|
||||
const char *sqlite3RowidAlias(Table *pTab){
|
||||
const char *azOpt[] = {"_ROWID_", "ROWID", "OID"};
|
||||
int ii;
|
||||
assert( VisibleRowid(pTab) );
|
||||
for(ii=0; ii<ArraySize(azOpt); ii++){
|
||||
int iCol;
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break;
|
||||
}
|
||||
if( iCol==pTab->nCol ){
|
||||
return azOpt[ii];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** pX is the RHS of an IN operator. If pX is a SELECT statement
|
||||
** that can be simplified to a direct table access, then return
|
||||
|
103
src/resolve.c
103
src/resolve.c
@ -104,21 +104,36 @@ static void resolveAlias(
|
||||
}
|
||||
|
||||
/*
|
||||
** Subqueries stores the original database, table and column names for their
|
||||
** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
|
||||
** Check to see if the zSpan given to this routine matches the zDb, zTab,
|
||||
** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
|
||||
** match anything.
|
||||
** Subqueries store the original database, table and column names for their
|
||||
** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN",
|
||||
** and mark the expression-list item by setting ExprList.a[].fg.eEName
|
||||
** to ENAME_TAB.
|
||||
**
|
||||
** Check to see if the zSpan/eEName of the expression-list item passed to this
|
||||
** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are
|
||||
** NULL then those fields will match anything. Return true if there is a match,
|
||||
** or false otherwise.
|
||||
**
|
||||
** SF_NestedFrom subqueries also store an entry for the implicit rowid (or
|
||||
** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID,
|
||||
** and setting zSpan to "DATABASE.TABLE.<rowid-alias>". This type of pItem
|
||||
** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid)
|
||||
** is set to 1 if there is this kind of match.
|
||||
*/
|
||||
int sqlite3MatchEName(
|
||||
const struct ExprList_item *pItem,
|
||||
const char *zCol,
|
||||
const char *zTab,
|
||||
const char *zDb
|
||||
const char *zDb,
|
||||
int *pbRowid
|
||||
){
|
||||
int n;
|
||||
const char *zSpan;
|
||||
if( pItem->fg.eEName!=ENAME_TAB ) return 0;
|
||||
int eEName = pItem->fg.eEName;
|
||||
if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){
|
||||
return 0;
|
||||
}
|
||||
assert( pbRowid==0 || *pbRowid==0 );
|
||||
zSpan = pItem->zEName;
|
||||
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
|
||||
if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
|
||||
@ -130,9 +145,11 @@ int sqlite3MatchEName(
|
||||
return 0;
|
||||
}
|
||||
zSpan += n+1;
|
||||
if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
|
||||
return 0;
|
||||
if( zCol ){
|
||||
if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0;
|
||||
if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0;
|
||||
}
|
||||
if( eEName==ENAME_ROWID ) *pbRowid = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -265,7 +282,7 @@ static int lookupName(
|
||||
){
|
||||
int i, j; /* Loop counters */
|
||||
int cnt = 0; /* Number of matching column names */
|
||||
int cntTab = 0; /* Number of matching table names */
|
||||
int cntTab = 0; /* Number of potential "rowid" matches */
|
||||
int nSubquery = 0; /* How many levels of subquery */
|
||||
sqlite3 *db = pParse->db; /* The database connection */
|
||||
SrcItem *pItem; /* Use for looping over pSrcList items */
|
||||
@ -342,39 +359,49 @@ static int lookupName(
|
||||
assert( pEList!=0 );
|
||||
assert( pEList->nExpr==pTab->nCol );
|
||||
for(j=0; j<pEList->nExpr; j++){
|
||||
if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){
|
||||
int bRowid = 0; /* True if possible rowid match */
|
||||
if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){
|
||||
continue;
|
||||
}
|
||||
if( cnt>0 ){
|
||||
if( pItem->fg.isUsing==0
|
||||
|| sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
|
||||
){
|
||||
/* Two or more tables have the same column name which is
|
||||
** not joined by USING. This is an error. Signal as much
|
||||
** by clearing pFJMatch and letting cnt go above 1. */
|
||||
sqlite3ExprListDelete(db, pFJMatch);
|
||||
pFJMatch = 0;
|
||||
}else
|
||||
if( (pItem->fg.jointype & JT_RIGHT)==0 ){
|
||||
/* An INNER or LEFT JOIN. Use the left-most table */
|
||||
continue;
|
||||
}else
|
||||
if( (pItem->fg.jointype & JT_LEFT)==0 ){
|
||||
/* A RIGHT JOIN. Use the right-most table */
|
||||
cnt = 0;
|
||||
sqlite3ExprListDelete(db, pFJMatch);
|
||||
pFJMatch = 0;
|
||||
}else{
|
||||
/* For a FULL JOIN, we must construct a coalesce() func */
|
||||
extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
|
||||
if( bRowid==0 ){
|
||||
if( cnt>0 ){
|
||||
if( pItem->fg.isUsing==0
|
||||
|| sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
|
||||
){
|
||||
/* Two or more tables have the same column name which is
|
||||
** not joined by USING. This is an error. Signal as much
|
||||
** by clearing pFJMatch and letting cnt go above 1. */
|
||||
sqlite3ExprListDelete(db, pFJMatch);
|
||||
pFJMatch = 0;
|
||||
}else
|
||||
if( (pItem->fg.jointype & JT_RIGHT)==0 ){
|
||||
/* An INNER or LEFT JOIN. Use the left-most table */
|
||||
continue;
|
||||
}else
|
||||
if( (pItem->fg.jointype & JT_LEFT)==0 ){
|
||||
/* A RIGHT JOIN. Use the right-most table */
|
||||
cnt = 0;
|
||||
sqlite3ExprListDelete(db, pFJMatch);
|
||||
pFJMatch = 0;
|
||||
}else{
|
||||
/* For a FULL JOIN, we must construct a coalesce() func */
|
||||
extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
hit = 1;
|
||||
}else if( cnt>0 ){
|
||||
/* This is a potential rowid match, but there has already been
|
||||
** a real match found. So this can be ignored. */
|
||||
continue;
|
||||
}
|
||||
cnt++;
|
||||
cntTab = 2;
|
||||
cntTab++;
|
||||
pMatch = pItem;
|
||||
pExpr->iColumn = j;
|
||||
pEList->a[j].fg.bUsed = 1;
|
||||
hit = 1;
|
||||
|
||||
/* rowid cannot be part of a USING clause - assert() this. */
|
||||
assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 );
|
||||
if( pEList->a[j].fg.bUsingTerm ) break;
|
||||
}
|
||||
if( hit || zTab==0 ) continue;
|
||||
@ -569,10 +596,10 @@ static int lookupName(
|
||||
&& pMatch
|
||||
&& (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
|
||||
&& sqlite3IsRowid(zCol)
|
||||
&& ALWAYS(VisibleRowid(pMatch->pTab))
|
||||
&& ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
|
||||
){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1;
|
||||
if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
}
|
||||
|
||||
|
68
src/select.c
68
src/select.c
@ -6114,6 +6114,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
char *zTName = 0; /* text of name of TABLE */
|
||||
int iErrOfst;
|
||||
if( pE->op==TK_DOT ){
|
||||
assert( (selFlags & SF_NestedFrom)==0 );
|
||||
assert( pE->pLeft!=0 );
|
||||
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
|
||||
zTName = pE->pLeft->u.zToken;
|
||||
@ -6124,6 +6125,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
iErrOfst = pE->w.iOfst;
|
||||
}
|
||||
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
||||
int nAdd; /* Number of cols including rowid */
|
||||
Table *pTab = pFrom->pTab; /* Table for this data source */
|
||||
ExprList *pNestedFrom; /* Result-set of a nested FROM clause */
|
||||
char *zTabName; /* AS name for this data source */
|
||||
@ -6141,6 +6143,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
pNestedFrom = pFrom->pSelect->pEList;
|
||||
assert( pNestedFrom!=0 );
|
||||
assert( pNestedFrom->nExpr==pTab->nCol );
|
||||
assert( VisibleRowid(pTab)==0 );
|
||||
}else{
|
||||
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
|
||||
continue;
|
||||
@ -6171,33 +6174,48 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
}else{
|
||||
pUsing = 0;
|
||||
}
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
char *zName = pTab->aCol[j].zCnName;
|
||||
|
||||
nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
|
||||
for(j=0; j<nAdd; j++){
|
||||
const char *zName;
|
||||
struct ExprList_item *pX; /* Newly added ExprList term */
|
||||
|
||||
assert( zName );
|
||||
if( zTName
|
||||
&& pNestedFrom
|
||||
&& sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0)==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
if( j==pTab->nCol ){
|
||||
zName = sqlite3RowidAlias(pTab);
|
||||
if( zName==0 ) continue;
|
||||
}else{
|
||||
zName = pTab->aCol[j].zCnName;
|
||||
|
||||
/* If a column is marked as 'hidden', omit it from the expanded
|
||||
** result-set list unless the SELECT has the SF_IncludeHidden
|
||||
** bit set.
|
||||
*/
|
||||
if( (p->selFlags & SF_IncludeHidden)==0
|
||||
&& IsHiddenColumn(&pTab->aCol[j])
|
||||
){
|
||||
continue;
|
||||
}
|
||||
if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
|
||||
&& zTName==0
|
||||
&& (selFlags & (SF_NestedFrom))==0
|
||||
){
|
||||
continue;
|
||||
/* If pTab is actually an SF_NestedFrom sub-select, do not
|
||||
** expand any ENAME_ROWID columns. */
|
||||
if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){
|
||||
continue;
|
||||
}
|
||||
|
||||
if( zTName
|
||||
&& pNestedFrom
|
||||
&& sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If a column is marked as 'hidden', omit it from the expanded
|
||||
** result-set list unless the SELECT has the SF_IncludeHidden
|
||||
** bit set.
|
||||
*/
|
||||
if( (p->selFlags & SF_IncludeHidden)==0
|
||||
&& IsHiddenColumn(&pTab->aCol[j])
|
||||
){
|
||||
continue;
|
||||
}
|
||||
if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
|
||||
&& zTName==0
|
||||
&& (selFlags & (SF_NestedFrom))==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
assert( zName );
|
||||
tableSeen = 1;
|
||||
|
||||
if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){
|
||||
@ -6247,11 +6265,11 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
zSchemaName, zTabName, zName);
|
||||
testcase( pX->zEName==0 );
|
||||
}
|
||||
pX->fg.eEName = ENAME_TAB;
|
||||
pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB);
|
||||
if( (pFrom->fg.isUsing
|
||||
&& sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0)
|
||||
|| (pUsing && sqlite3IdListIndex(pUsing, zName)>=0)
|
||||
|| (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
|
||||
|| (j<pTab->nCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND))
|
||||
){
|
||||
pX->fg.bNoExpand = 1;
|
||||
}
|
||||
|
@ -3181,6 +3181,7 @@ struct ExprList {
|
||||
#define ENAME_NAME 0 /* The AS clause of a result set */
|
||||
#define ENAME_SPAN 1 /* Complete text of the result set expression */
|
||||
#define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */
|
||||
#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */
|
||||
|
||||
/*
|
||||
** An instance of this structure can hold a simple list of identifiers,
|
||||
@ -5010,6 +5011,7 @@ int sqlite3ExprIsInteger(const Expr*, int*);
|
||||
int sqlite3ExprCanBeNull(const Expr*);
|
||||
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
|
||||
int sqlite3IsRowid(const char*);
|
||||
const char *sqlite3RowidAlias(Table *pTab);
|
||||
void sqlite3GenerateRowDelete(
|
||||
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
|
||||
@ -5281,7 +5283,8 @@ int sqlite3MatchEName(
|
||||
const struct ExprList_item*,
|
||||
const char*,
|
||||
const char*,
|
||||
const char*
|
||||
const char*,
|
||||
int*
|
||||
);
|
||||
Bitmask sqlite3ExprColUsed(Expr*);
|
||||
u8 sqlite3StrIHash(const char*);
|
||||
|
120
test/joinH.test
120
test/joinH.test
@ -132,4 +132,124 @@ do_execsql_test 6.0 {
|
||||
do_execsql_test 6.1 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON true WHERE CASE WHEN t2.b THEN 0 ELSE 1 END;
|
||||
} {3 NULL}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(c);
|
||||
CREATE TABLE t3(d);
|
||||
|
||||
INSERT INTO t1 VALUES ('a', 'a');
|
||||
INSERT INTO t2 VALUES ('ddd');
|
||||
INSERT INTO t3 VALUES(1234);
|
||||
}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT t2.rowid FROM t1 JOIN (t2 JOIN t3);
|
||||
} {1}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
UPDATE t1 SET b = t2.rowid FROM t2, t3;
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
SELECT * FROM t1
|
||||
} {a 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE x1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE x2(c, d);
|
||||
CREATE TABLE x3(rowid, _rowid_);
|
||||
|
||||
CREATE TABLE x4(rowid, _rowid_, oid);
|
||||
|
||||
INSERT INTO x1 VALUES(1000, 'thousand');
|
||||
INSERT INTO x2 VALUES('c', 'd');
|
||||
INSERT INTO x3(oid, rowid, _rowid_) VALUES(43, 'hello', 'world');
|
||||
INSERT INTO x4(oid, rowid, _rowid_) VALUES('forty three', 'hello', 'world');
|
||||
}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
SELECT x3.oid FROM x1 JOIN (x2 JOIN x3 ON c='c')
|
||||
} 43
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 8.2 {
|
||||
SELECT x3.rowid FROM x1 JOIN (x2 JOIN x3 ON c='c')
|
||||
} {hello}
|
||||
|
||||
do_execsql_test 8.3 {
|
||||
SELECT x4.oid FROM x1 JOIN (x2 JOIN x4 ON c='c')
|
||||
} {{forty three}}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.0 {
|
||||
CREATE TABLE x1(a);
|
||||
CREATE TABLE x2(b);
|
||||
CREATE TABLE x3(c);
|
||||
|
||||
CREATE TABLE wo1(a PRIMARY KEY, b) WITHOUT ROWID;
|
||||
CREATE TABLE wo2(a PRIMARY KEY, rowid) WITHOUT ROWID;
|
||||
CREATE TABLE wo3(a PRIMARY KEY, b) WITHOUT ROWID;
|
||||
}
|
||||
|
||||
do_catchsql_test 9.1 {
|
||||
SELECT rowid FROM wo1, x1, x2;
|
||||
} {1 {no such column: rowid}}
|
||||
do_catchsql_test 9.2 {
|
||||
SELECT rowid FROM wo1, (x1, x2);
|
||||
} {1 {no such column: rowid}}
|
||||
do_catchsql_test 9.3 {
|
||||
SELECT rowid FROM wo1 JOIN (x1 JOIN x2);
|
||||
} {1 {no such column: rowid}}
|
||||
do_catchsql_test 9.4 {
|
||||
SELECT a FROM wo1, x1, x2;
|
||||
} {1 {ambiguous column name: a}}
|
||||
|
||||
|
||||
# It is not possible to use "rowid" in a USING clause.
|
||||
#
|
||||
do_catchsql_test 9.5 {
|
||||
SELECT * FROM x1 JOIN x2 USING (rowid);
|
||||
} {1 {cannot join using column rowid - column not present in both tables}}
|
||||
do_catchsql_test 9.6 {
|
||||
SELECT * FROM wo2 JOIN x2 USING (rowid);
|
||||
} {1 {cannot join using column rowid - column not present in both tables}}
|
||||
|
||||
# "rowid" columns are not matched by NATURAL JOIN. If they were, then
|
||||
# the SELECT below would return zero rows.
|
||||
do_execsql_test 9.7 {
|
||||
INSERT INTO x1(rowid, a) VALUES(101, 'A');
|
||||
INSERT INTO x2(rowid, b) VALUES(55, 'B');
|
||||
SELECT * FROM x1 NATURAL JOIN x2;
|
||||
} {A B}
|
||||
|
||||
do_execsql_test 9.8 {
|
||||
INSERT INTO wo1(a, b) VALUES('mya', 'myb');
|
||||
INSERT INTO wo2(a, rowid) VALUES('mypk', 'myrowid');
|
||||
INSERT INTO wo3(a, b) VALUES('MYA', 'MYB');
|
||||
INSERT INTO x3(rowid, c) VALUES(99, 'x3B');
|
||||
}
|
||||
|
||||
do_catchsql_test 9.8 {
|
||||
SELECT rowid FROM x1 JOIN (x2 JOIN wo2);
|
||||
} {0 myrowid}
|
||||
do_catchsql_test 9.9 {
|
||||
SELECT _rowid_ FROM wo1 JOIN (wo3 JOIN x3)
|
||||
} {0 99}
|
||||
do_catchsql_test 9.10 {
|
||||
SELECT oid FROM wo1 JOIN (wo3 JOIN x3)
|
||||
} {0 99}
|
||||
do_catchsql_test 9.11 {
|
||||
SELECT oid FROM wo2 JOIN (wo3 JOIN x3)
|
||||
} {0 99}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user