Now supports result sets of the form "TABLE.*" with nested FROM clauses.
FossilOrigin-Name: 4cf5ed7ea198abc32f8118e79490e77f847f08c1
This commit is contained in:
parent
fb7773276b
commit
3e3f1a5b49
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C When\sresolving\sresult-set\sname\scollisions,\smake\sthem\sx:1,\sx:2,\sx:3,\setc.\ninstead\sof\sx:1,\sx:1:1,\sx:1;1;1.
|
||||
D 2013-01-02T14:57:32.750
|
||||
C Now\ssupports\sresult\ssets\sof\sthe\sform\s"TABLE.*"\swith\snested\sFROM\sclauses.
|
||||
D 2013-01-03T00:45:56.842
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -130,7 +130,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 72a70dcfda75d3a1f81041ce4573e7afddcd8e4e
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c 9b8d308979114991e5dc7cee958316e07186941d
|
||||
F src/expr.c 0e41d66d868b37dbc0e041c342e0036fad27e705
|
||||
F src/expr.c 4dff0b04eaaf133789279c6b8cd69175dfbb1691
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 5b7a12e2f8620e855b0478a9a6798df9967bb277
|
||||
F src/func.c 8147799b048065a1590805be464d05b4913e652c
|
||||
@ -172,14 +172,14 @@ F src/pragma.c 8907c559d3127729d3bcedb1fe5c59fc196d3a17
|
||||
F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c
|
||||
F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 3104a5e4c2cb6e3a813b7266cbd541dc91e26fad
|
||||
F src/resolve.c e00f17ac321b48f022e0154e5c2fb90456b6d3a6
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c bdede5fc109ab090dd6ce5edf6090434dc41bc0e
|
||||
F src/select.c 9a76144e137fbe26bad3c5ef06e0acd953de9f59
|
||||
F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843
|
||||
F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177
|
||||
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
|
||||
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
|
||||
F src/sqliteInt.h 2ce2d742e32be86bf1fc8e29c91ef91c29c32e60
|
||||
F src/sqliteInt.h e998703742455b2241731424c6ec142fd8d0258f
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@ -701,7 +701,7 @@ F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea
|
||||
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
|
||||
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
|
||||
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
|
||||
F test/selectD.test 243db87cd12f62aca4fe3f8b9465dfeab30b4d7a
|
||||
F test/selectD.test 5e05091a755b12e0afd350137c49b25f7d9bc61b
|
||||
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
|
||||
F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746
|
||||
F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879
|
||||
@ -1031,7 +1031,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P a5f4d2b641f7fafb6f1a312efeffb10f213d2d0a
|
||||
R c34797fdba8f90f34f8f47a91737b456
|
||||
P ef01e30456670e6b1bc67fe41ec27e52c182efaf
|
||||
R 0fd360c4969bf18ffc741f29481411d6
|
||||
U drh
|
||||
Z 110af146cb022182bd89fc0a02fd1d9b
|
||||
Z a4217b32c27253cef4a6339df637b923
|
||||
|
@ -1 +1 @@
|
||||
ef01e30456670e6b1bc67fe41ec27e52c182efaf
|
||||
4cf5ed7ea198abc32f8118e79490e77f847f08c1
|
@ -3281,6 +3281,12 @@ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
|
||||
sqlite3ExplainPush(pOut);
|
||||
sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
|
||||
sqlite3ExplainPop(pOut);
|
||||
if( pList->a[i].zName ){
|
||||
sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
|
||||
}
|
||||
if( pList->a[i].bSpanIsTab ){
|
||||
sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
|
||||
}
|
||||
if( i<pList->nExpr-1 ){
|
||||
sqlite3ExplainNL(pOut);
|
||||
}
|
||||
|
@ -150,6 +150,35 @@ static int nameInUsingClause(IdList *pUsing, const char *zCol){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
*/
|
||||
int sqlite3MatchSpanName(
|
||||
const char *zSpan,
|
||||
const char *zCol,
|
||||
const char *zTab,
|
||||
const char *zDb
|
||||
){
|
||||
int n;
|
||||
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
|
||||
if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
zSpan += n+1;
|
||||
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
|
||||
if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
zSpan += n+1;
|
||||
if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
|
||||
@ -240,8 +269,7 @@ static int lookupName(
|
||||
ExprList *pEList = pItem->pSelect->pEList;
|
||||
int hit = 0;
|
||||
for(j=0; j<pEList->nExpr; j++){
|
||||
if( zTab && sqlite3StrICmp(pEList->a[j].zSpan, zTab)!=0 ) continue;
|
||||
if( sqlite3StrICmp(pEList->a[j].zName, zCol)==0 ){
|
||||
if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){
|
||||
cnt++;
|
||||
cntTab = 2;
|
||||
pMatch = pItem;
|
||||
|
44
src/select.c
44
src/select.c
@ -3294,6 +3294,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
ExprList *pEList;
|
||||
struct SrcList_item *pFrom;
|
||||
sqlite3 *db = pParse->db;
|
||||
Expr *pE, *pRight, *pExpr;
|
||||
|
||||
if( db->mallocFailed ){
|
||||
return WRC_Abort;
|
||||
@ -3379,7 +3380,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
** that need expanding.
|
||||
*/
|
||||
for(k=0; k<pEList->nExpr; k++){
|
||||
Expr *pE = pEList->a[k].pExpr;
|
||||
pE = pEList->a[k].pExpr;
|
||||
if( pE->op==TK_ALL ) break;
|
||||
assert( pE->op!=TK_DOT || pE->pRight!=0 );
|
||||
assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
|
||||
@ -3399,9 +3400,10 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
&& (p->selFlags & SF_NestedFrom)==0;
|
||||
|
||||
for(k=0; k<pEList->nExpr; k++){
|
||||
Expr *pE = a[k].pExpr;
|
||||
assert( pE->op!=TK_DOT || pE->pRight!=0 );
|
||||
if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){
|
||||
pE = a[k].pExpr;
|
||||
pRight = pE->pRight;
|
||||
assert( pE->op!=TK_DOT || pRight!=0 );
|
||||
if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){
|
||||
/* This particular expression does not need to be expanded.
|
||||
*/
|
||||
pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
|
||||
@ -3416,32 +3418,42 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
/* This expression is a "*" or a "TABLE.*" and needs to be
|
||||
** expanded. */
|
||||
int tableSeen = 0; /* Set to 1 when TABLE matches */
|
||||
char *zTName; /* text of name of TABLE */
|
||||
char *zTName = 0; /* text of name of TABLE */
|
||||
if( pE->op==TK_DOT ){
|
||||
assert( pE->pLeft!=0 );
|
||||
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
|
||||
zTName = pE->pLeft->u.zToken;
|
||||
}else{
|
||||
zTName = 0;
|
||||
}
|
||||
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
||||
Table *pTab = pFrom->pTab;
|
||||
Select *pSub = pFrom->pSelect;
|
||||
char *zTabName = pFrom->zAlias;
|
||||
const char *zSchemaName = 0;
|
||||
if( zTabName==0 ){
|
||||
zTabName = pTab->zName;
|
||||
}
|
||||
if( db->mallocFailed ) break;
|
||||
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
|
||||
continue;
|
||||
if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){
|
||||
int iDb;
|
||||
pSub = 0;
|
||||
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
|
||||
continue;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
zSchemaName = iDb>=0 ? db->aDb[i].zName : "*";
|
||||
}
|
||||
tableSeen = 1;
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
Expr *pExpr, *pRight;
|
||||
char *zName = pTab->aCol[j].zName;
|
||||
char *zColname; /* The computed column name */
|
||||
char *zToFree; /* Malloced string that needs to be freed */
|
||||
Token sColname; /* Computed column name as a token */
|
||||
|
||||
if( zTName && pSub
|
||||
&& sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If a column is marked as 'hidden' (currently only possible
|
||||
** for virtual tables), do not include it in the expanded
|
||||
** result-set list.
|
||||
@ -3450,6 +3462,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
assert(IsVirtual(pTab));
|
||||
continue;
|
||||
}
|
||||
tableSeen = 1;
|
||||
|
||||
if( i>0 && zTName==0 ){
|
||||
if( (pFrom->jointype & JT_NATURAL)!=0
|
||||
@ -3484,7 +3497,14 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
sColname.n = sqlite3Strlen30(zColname);
|
||||
sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
|
||||
if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
|
||||
pNew->a[pNew->nExpr-1].zSpan = sqlite3DbStrDup(db, zTabName);
|
||||
struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
|
||||
if( pSub ){
|
||||
pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan);
|
||||
}else{
|
||||
pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s",
|
||||
zSchemaName, zTabName, zColname);
|
||||
}
|
||||
pX->bSpanIsTab = 1;
|
||||
}
|
||||
sqlite3DbFree(db, zToFree);
|
||||
}
|
||||
|
@ -1777,7 +1777,8 @@ struct Expr {
|
||||
** column labels. In this case, Expr.zSpan is typically the text of a
|
||||
** column expression as it exists in a SELECT statement. However, if
|
||||
** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name
|
||||
** of the table to which the column of a FROM-clause subquery refers.
|
||||
** of the result column in the form: DATABASE.TABLE.COLUMN. This later
|
||||
** form is used for name resolution with nested FROM clauses.
|
||||
*/
|
||||
struct ExprList {
|
||||
int nExpr; /* Number of expressions on the list */
|
||||
@ -1788,7 +1789,7 @@ struct ExprList {
|
||||
char *zSpan; /* Original text of the expression */
|
||||
u8 sortOrder; /* 1 for DESC or 0 for ASC */
|
||||
unsigned done :1; /* A flag to indicate when processing is finished */
|
||||
unsigned bSpanIsTab :1; /* zSpan holds table name, not the span */
|
||||
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
|
||||
u16 iOrderByCol; /* For ORDER BY, column number in result set */
|
||||
u16 iAlias; /* Index into Parse.aAlias[] for zName */
|
||||
} *a; /* Alloc a power of two greater or equal to nExpr */
|
||||
@ -3080,6 +3081,7 @@ void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
void sqlite3ExpirePreparedStatements(sqlite3*);
|
||||
int sqlite3CodeSubselect(Parse *, Expr *, int, int);
|
||||
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
|
||||
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
|
||||
int sqlite3ResolveExprNames(NameContext*, Expr*);
|
||||
void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
|
||||
int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
|
||||
|
@ -15,64 +15,107 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test selectD-1.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1');
|
||||
CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2');
|
||||
CREATE TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3');
|
||||
CREATE TABLE t4(a,b); INSERT INTO t4 VALUES(444,'x4');
|
||||
|
||||
SELECT *
|
||||
FROM (t1), (t2), (t3), (t4)
|
||||
WHERE t4.a=t3.a+111
|
||||
AND t3.a=t2.a+111
|
||||
AND t2.a=t1.a+111;
|
||||
for {set i 1} {$i<=2} {incr i} {
|
||||
db close
|
||||
forcedelete test$i.db
|
||||
sqlite3 db test$i.db
|
||||
if {$i==2} {
|
||||
optimization_control db query-flattener off
|
||||
}
|
||||
} {111 x1 222 x2 333 x3 444 x4}
|
||||
do_test selectD-1.2 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {111 x1 222 x2 333 x3 444 x4}
|
||||
do_test selectD-1.3 {
|
||||
db eval {
|
||||
UPDATE t2 SET a=111;
|
||||
UPDATE t3 SET a=111;
|
||||
UPDATE t4 SET a=111;
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 USING(a)) USING (a)) USING (a);
|
||||
}
|
||||
} {111 x1 x2 x3 x4}
|
||||
do_test selectD-1.4 {
|
||||
db eval {
|
||||
UPDATE t2 SET a=111;
|
||||
UPDATE t3 SET a=111;
|
||||
UPDATE t4 SET a=111;
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
USING (a))
|
||||
USING (a);
|
||||
}
|
||||
} {111 x1 x2 x3 x4}
|
||||
do_test selectD-1.5 {
|
||||
db eval {
|
||||
UPDATE t3 SET a=222;
|
||||
UPDATE t4 SET a=222;
|
||||
SELECT *
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 x2 222 x3 x4}
|
||||
do_test selectD-1.6 {
|
||||
db eval {
|
||||
UPDATE t4 SET a=333;
|
||||
SELECT *
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 x2 222 x3 {}}
|
||||
do_test selectD-$i.0 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1');
|
||||
CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2');
|
||||
CREATE TEMP TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3');
|
||||
CREATE TABLE t4(a,b); INSERT INTO t4 VALUES(444,'x4');
|
||||
}
|
||||
} {}
|
||||
do_test selectD-$i.1 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM (t1), (t2), (t3), (t4)
|
||||
WHERE t4.a=t3.a+111
|
||||
AND t3.a=t2.a+111
|
||||
AND t2.a=t1.a+111;
|
||||
}
|
||||
} {111 x1 222 x2 333 x3 444 x4}
|
||||
do_test selectD-$i.2.1 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {111 x1 222 x2 333 x3 444 x4}
|
||||
do_test selectD-$i.2.2 {
|
||||
db eval {
|
||||
SELECT t3.a
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {333}
|
||||
do_test selectD-$i.2.3 {
|
||||
db eval {
|
||||
SELECT t3.*
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {333 x3}
|
||||
do_test selectD-$i.2.3 {
|
||||
db eval {
|
||||
SELECT t3.*, t2.*
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {333 x3 222 x2}
|
||||
do_test selectD-$i.3 {
|
||||
db eval {
|
||||
UPDATE t2 SET a=111;
|
||||
UPDATE t3 SET a=111;
|
||||
UPDATE t4 SET a=111;
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 USING(a)) USING (a)) USING (a);
|
||||
}
|
||||
} {111 x1 x2 x3 x4}
|
||||
do_test selectD-$i.4 {
|
||||
db eval {
|
||||
UPDATE t2 SET a=111;
|
||||
UPDATE t3 SET a=111;
|
||||
UPDATE t4 SET a=111;
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
USING (a))
|
||||
USING (a);
|
||||
}
|
||||
} {111 x1 x2 x3 x4}
|
||||
do_test selectD-$i.5 {
|
||||
db eval {
|
||||
UPDATE t3 SET a=222;
|
||||
UPDATE t4 SET a=222;
|
||||
SELECT *
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 x2 222 x3 x4}
|
||||
do_test selectD-$i.6 {
|
||||
db eval {
|
||||
UPDATE t4 SET a=333;
|
||||
SELECT *
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 x2 222 x3 {}}
|
||||
do_test selectD-$i.7 {
|
||||
db eval {
|
||||
SELECT t1.*, t2.*, t3.*, t4.b
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 111 x2 222 x3 {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user