SQLite

Check-in [98b3816b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix the UPDATE-FROM logic so that it works the same as PostgreSQL when the FROM clause contains an outer join.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 98b3816bbaf539ea745456e1c0064e47d2903d33ee0ceb029bdb97d56fcde937
User & Date: drh 2022-05-25 11:09:07
References
2022-05-28
14:03
Apply the UPDATE-FROM file from check-in [98b3816bbaf539ea] to update-delete-limit builds. (check-in: 7e87892c user: drh tags: trunk)
2022-05-26
17:33
Correct handling of outer joins in the FROM clause of an UPDATE statement that occurs inside of a trigger. Follow-on to [98b3816bbaf539ea]. (check-in: 664a49fa user: drh tags: trunk)
Context
2022-05-25
11:17
Add in a VdbeCoverage() macro accidentally omitted from [d64ae49a1f251317]. (check-in: 18b5cea0 user: drh tags: trunk)
11:09
Fix the UPDATE-FROM logic so that it works the same as PostgreSQL when the FROM clause contains an outer join. (check-in: 98b3816b user: drh tags: trunk)
08:51
Got the sqlite3-api JS bits wrapped up in deferred-load module. Whether that's going to be easier to use, in practice, remains to be seen. Consolidated two of the test-related JS files. (check-in: dd83cc05 user: stephan tags: trunk)
02:32
An attempt to get UPDATE FROM working when the FROM clause contains a RIGHT or FULL JOIN. (Closed-Leaf check-in: a124e4f9 user: drh tags: update-from-right-join)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/parse.y.

936
937
938
939
940
941
942










943

944
945
946
947
948
949
950
  sqlite3Update(pParse,X,Y,W,R,O,L,0);
}
%else
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
        where_opt_ret(W). {
  sqlite3SrcListIndexedBy(pParse, X, &I);
  sqlite3ExprListCheckLength(pParse,Y,"set list"); 










  X = sqlite3SrcListAppendList(pParse, X, F);

  sqlite3Update(pParse,X,Y,W,R,0,0,0);
}
%endif



%type setlist {ExprList*}







>
>
>
>
>
>
>
>
>
>
|
>







936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
  sqlite3Update(pParse,X,Y,W,R,O,L,0);
}
%else
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
        where_opt_ret(W). {
  sqlite3SrcListIndexedBy(pParse, X, &I);
  sqlite3ExprListCheckLength(pParse,Y,"set list"); 
  if( F ){
    SrcList *pFromClause = F;
    if( pFromClause->nSrc>1 ){
      Select *pSubquery;
      Token as;
      pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0);
      as.n = 0;
      as.z = 0;
      pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
    }
    X = sqlite3SrcListAppendList(pParse, X, pFromClause);
  }
  sqlite3Update(pParse,X,Y,W,R,0,0,0);
}
%endif



%type setlist {ExprList*}

Changes to src/select.c.

6513
6514
6515
6516
6517
6518
6519























6520
6521
6522
6523
6524
6525
6526
    SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
  return 1;
}
#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */
























/*
** Generate code for the SELECT statement given in the p argument.  
**
** The results are returned according to the SelectDest structure.
** See comments in sqliteInt.h for further information.
**







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
    SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
  return 1;
}
#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */

/*
** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same
** as pSrcItem but has the same alias as p0, then return true.
** Otherwise return false.
*/
static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){
  int i;
  for(i=0; i<pSrc->nSrc; i++){
    SrcItem *p1 = &pSrc->a[i];
    if( p1==p0 ) continue;
    if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
      return 1;
    }
    if( p1->pSelect
     && (p1->pSelect->selFlags & SF_NestedFrom)!=0
     && sameSrcAlias(p0, p1->pSelect->pSrc)
    ){
      return 1;
    }
  }
  return 0;
}

/*
** Generate code for the SELECT statement given in the p argument.  
**
** The results are returned according to the SelectDest structure.
** See comments in sqliteInt.h for further information.
**
6618
6619
6620
6621
6622
6623
6624
6625
6626
6627
6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
  **
  ** Postgres disallows this case too. The reason is that some other 
  ** systems handle this case differently, and not all the same way, 
  ** which is just confusing. To avoid this, we follow PG's lead and
  ** disallow it altogether.  */
  if( p->selFlags & SF_UFSrcCheck ){
    SrcItem *p0 = &p->pSrc->a[0];
    for(i=1; i<p->pSrc->nSrc; i++){
      SrcItem *p1 = &p->pSrc->a[i];
      if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
        sqlite3ErrorMsg(pParse, 
            "target object/alias may not appear in FROM clause: %s", 
            p0->zAlias ? p0->zAlias : p0->pTab->zName
        );
        goto select_end;
      }
    }

    /* Clear the SF_UFSrcCheck flag. The check has already been performed,
    ** and leaving this flag set can cause errors if a compound sub-query
    ** in p->pSrc is flattened into this query and this function called
    ** again as part of compound SELECT processing.  */
    p->selFlags &= ~SF_UFSrcCheck;







|
<
<
|
|
|
|
|
<







6641
6642
6643
6644
6645
6646
6647
6648


6649
6650
6651
6652
6653

6654
6655
6656
6657
6658
6659
6660
  **
  ** Postgres disallows this case too. The reason is that some other 
  ** systems handle this case differently, and not all the same way, 
  ** which is just confusing. To avoid this, we follow PG's lead and
  ** disallow it altogether.  */
  if( p->selFlags & SF_UFSrcCheck ){
    SrcItem *p0 = &p->pSrc->a[0];
    if( sameSrcAlias(p0, p->pSrc) ){


      sqlite3ErrorMsg(pParse, 
          "target object/alias may not appear in FROM clause: %s", 
          p0->zAlias ? p0->zAlias : p0->pTab->zName
      );
      goto select_end;

    }

    /* Clear the SF_UFSrcCheck flag. The check has already been performed,
    ** and leaving this flag set can cause errors if a compound sub-query
    ** in p->pSrc is flattened into this query and this function called
    ** again as part of compound SELECT processing.  */
    p->selFlags &= ~SF_UFSrcCheck;

Added test/upfrom4.test.















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 2022-05-24
#
# 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 upfrom4

do_execsql_test 100 {
  DROP TABLE IF EXISTS t5;
  DROP TABLE IF EXISTS m1;
  DROP TABLE IF EXISTS m2;
  CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT, c TEXT);
  CREATE TABLE m1(x INTEGER PRIMARY KEY, y TEXT);
  CREATE TABLE m2(u INTEGER PRIMARY KEY, v TEXT);

  INSERT INTO t5 VALUES(1, 'one', 'ONE');
  INSERT INTO t5 VALUES(2, 'two', 'TWO');
  INSERT INTO t5 VALUES(3, 'three', 'THREE');
  INSERT INTO t5 VALUES(4, 'four', 'FOUR');

  INSERT INTO m1 VALUES(1, 'i');
  INSERT INTO m1 VALUES(2, 'ii');
  INSERT INTO m1 VALUES(3, 'iii');

  INSERT INTO m2 VALUES(1, 'I');
  INSERT INTO m2 VALUES(3, 'II');
  INSERT INTO m2 VALUES(4, 'III');
  SELECT * FROM t5;
} {1 one ONE 2 two TWO 3 three THREE 4 four FOUR}

do_execsql_test 110 {
  BEGIN;
  UPDATE t5 SET b=y, c=v FROM m1 LEFT JOIN m2 ON (x=u) WHERE x=a;
  SELECT * FROM t5 ORDER BY a;
  ROLLBACK;
} {1 i I 2 ii {} 3 iii II 4 four FOUR}

do_execsql_test 120 {
  BEGIN;
  UPDATE t5 SET b=y, c=v FROM m2 RIGHT JOIN m1 ON (x=u) WHERE x=a;
  SELECT * FROM t5 ORDER BY a;
  ROLLBACK;
} {1 i I 2 ii {} 3 iii II 4 four FOUR}


finish_test