Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add support for multiple rows of VALUES in an INSERT statement. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
eb3b6a0ceb8bfb9fd59ff5fec420f863 |
User & Date: | drh 2012-01-28 15:22:22.673 |
Context
2012-01-28
| ||
15:26 | Update the version number to 3.7.11 and rerun autoconf. (check-in: 6f9b265dd0 user: drh tags: trunk) | |
15:22 | Add support for multiple rows of VALUES in an INSERT statement. (check-in: eb3b6a0ceb user: drh tags: trunk) | |
2012-01-25
| ||
20:43 | Only invalidate the schema when the OP_ParseSchema opcode fails, not on any general failure of a vdbe program. (check-in: 11f68d997d user: drh tags: trunk) | |
Changes
Changes to src/parse.y.
︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 | */ struct TrigEvent { int a; IdList * b; }; /* ** An instance of this structure holds the ATTACH key and the key type. */ struct AttachKey { int type; Token key; }; } // end %include // Input is a single SQL command input ::= cmdlist. cmdlist ::= cmdlist ecmd. cmdlist ::= ecmd. | > > > > > > > > | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | */ struct TrigEvent { int a; IdList * b; }; /* ** An instance of this structure holds the ATTACH key and the key type. */ struct AttachKey { int type; Token key; }; /* ** One or more VALUES claues */ struct ValueList { ExprList *pList; Select *pSelect; }; } // end %include // Input is a single SQL command input ::= cmdlist. cmdlist ::= cmdlist ecmd. cmdlist ::= ecmd. |
︙ | ︙ | |||
675 676 677 678 679 680 681 | setlist(A) ::= nm(X) EQ expr(Y). { A = sqlite3ExprListAppend(pParse, 0, Y.pExpr); sqlite3ExprListSetName(pParse, A, &X, 1); } ////////////////////////// The INSERT command ///////////////////////////////// // | | < | | > > > > > > > | | > | | | > > | | > > > > > > > > > > > > > > > > > | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 | setlist(A) ::= nm(X) EQ expr(Y). { A = sqlite3ExprListAppend(pParse, 0, Y.pExpr); sqlite3ExprListSetName(pParse, A, &X, 1); } ////////////////////////// The INSERT command ///////////////////////////////// // cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) valuelist(Y). {sqlite3Insert(pParse, X, Y.pList, Y.pSelect, F, R);} cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). {sqlite3Insert(pParse, X, 0, S, F, R);} cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. {sqlite3Insert(pParse, X, 0, 0, F, R);} %type insert_cmd {u8} insert_cmd(A) ::= INSERT orconf(R). {A = R;} insert_cmd(A) ::= REPLACE. {A = OE_Replace;} // A ValueList is either a single VALUES clause or a comma-separated list // of VALUES clauses. If it is a single VALUES clause then the // ValueList.pList field points to the expression list of that clause. // If it is a list of VALUES clauses, then those clauses are transformed // into a set of SELECT statements without FROM clauses and connected by // UNION ALL and the ValueList.pSelect points to the right-most SELECT in // that compound. %type valuelist {struct ValueList} %destructor valuelist { sqlite3ExprListDelete(pParse->db, $$.pList); sqlite3SelectDelete(pParse->db, $$.pSelect); } valuelist(A) ::= VALUES LP nexprlist(X) RP. { A.pList = X; A.pSelect = 0; } valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. { Select *pRight = sqlite3SelectNew(pParse, Y, 0, 0, 0, 0, 0, 0, 0, 0); if( X.pList ){ X.pSelect = sqlite3SelectNew(pParse, X.pList, 0, 0, 0, 0, 0, 0, 0, 0); X.pList = 0; } A.pList = 0; if( X.pSelect==0 || pRight==0 ){ sqlite3SelectDelete(pParse->db, pRight); sqlite3SelectDelete(pParse->db, X.pSelect); A.pSelect = 0; }else{ pRight->op = TK_ALL; pRight->pPrior = X.pSelect; pRight->selFlags |= SF_Values; pRight->pPrior->selFlags |= SF_Values; A.pSelect = pRight; } } %type inscollist_opt {IdList*} %destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);} %type inscollist {IdList*} %destructor inscollist {sqlite3IdListDelete(pParse->db, $$);} inscollist_opt(A) ::= . {A = 0;} |
︙ | ︙ | |||
1257 1258 1259 1260 1261 1262 1263 | // UPDATE trigger_cmd(A) ::= UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } // INSERT trigger_cmd(A) ::= | | | | 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 | // UPDATE trigger_cmd(A) ::= UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } // INSERT trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) valuelist(Y). {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y.pList, Y.pSelect, R);} trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S). {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);} // DELETE trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);} |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 79 80 81 82 | pNew = &standin; memset(pNew, 0, sizeof(*pNew)); } if( pEList==0 ){ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0)); } pNew->pEList = pEList; pNew->pSrc = pSrc; pNew->pWhere = pWhere; pNew->pGroupBy = pGroupBy; pNew->pHaving = pHaving; pNew->pOrderBy = pOrderBy; pNew->selFlags = isDistinct ? SF_Distinct : 0; pNew->op = TK_SELECT; | > | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | pNew = &standin; memset(pNew, 0, sizeof(*pNew)); } if( pEList==0 ){ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0)); } pNew->pEList = pEList; if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc)); pNew->pSrc = pSrc; pNew->pWhere = pWhere; pNew->pGroupBy = pGroupBy; pNew->pHaving = pHaving; pNew->pOrderBy = pOrderBy; pNew->selFlags = isDistinct ? SF_Distinct : 0; pNew->op = TK_SELECT; |
︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 | } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ | > > > | | > | 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 | } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ if( p->selFlags & SF_Values ){ sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); }else{ sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" " do not have the same number of result columns", selectOpName(p->op)); } rc = 1; goto multi_select_end; } /* Compound SELECTs that have an ORDER BY clause are handled separately. */ if( p->pOrderBy ){ |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 | #define SF_Distinct 0x01 /* Output should be DISTINCT */ #define SF_Resolved 0x02 /* Identifiers have been resolved */ #define SF_Aggregate 0x04 /* Contains aggregate functions */ #define SF_UsesEphemeral 0x08 /* Uses the OpenEphemeral opcode */ #define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */ #define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */ #define SF_UseSorter 0x40 /* Sort using a sorter */ /* ** The results of a select can be distributed in several ways. The ** "SRT" prefix means "SELECT Result Type". */ #define SRT_Union 1 /* Store result as keys in an index */ | > | 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 | #define SF_Distinct 0x01 /* Output should be DISTINCT */ #define SF_Resolved 0x02 /* Identifiers have been resolved */ #define SF_Aggregate 0x04 /* Contains aggregate functions */ #define SF_UsesEphemeral 0x08 /* Uses the OpenEphemeral opcode */ #define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */ #define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */ #define SF_UseSorter 0x40 /* Sort using a sorter */ #define SF_Values 0x80 /* Synthesized from VALUES clause */ /* ** The results of a select can be distributed in several ways. The ** "SRT" prefix means "SELECT Result Type". */ #define SRT_Union 1 /* Store result as keys in an index */ |
︙ | ︙ |
Changes to test/insert.test.
︙ | ︙ | |||
382 383 384 385 386 387 388 389 390 391 | INSERT INTO t6 VALUES(2,2); INSERT INTO t6 VALUES(3,3); INSERT INTO t6 SELECT nullif(y*2+10,14), y+100 FROM t6; SELECT x, y FROM t6; } } {1 1 2 2 3 3 12 101 13 102 16 103} integrity_check insert-99.0 finish_test | > > > > > > > > > > > > > > > | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | INSERT INTO t6 VALUES(2,2); INSERT INTO t6 VALUES(3,3); INSERT INTO t6 SELECT nullif(y*2+10,14), y+100 FROM t6; SELECT x, y FROM t6; } } {1 1 2 2 3 3 12 101 13 102 16 103} # Multiple VALUES clauses # do_test insert-10.1 { execsql { CREATE TABLE t10(a,b,c); INSERT INTO t10 VALUES(1,2,3), (4,5,6), (7,8,9); SELECT * FROM t10; } } {1 2 3 4 5 6 7 8 9} do_test insert-10.2 { catchsql { INSERT INTO t10 VALUES(11,12,13), (14,15); } } {1 {all VALUES must have the same number of terms}} integrity_check insert-99.0 finish_test |
Changes to test/trigger1.test.
︙ | ︙ | |||
286 287 288 289 290 291 292 | do_test trigger-3.5 { catchsql { CREATE TEMP TABLE t2(x,y); INSERT INTO t1 VALUES(1,2); SELECT * FROM t2; } } {1 {no such table: main.t2}} | | > > > > > > > > > > > > | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | do_test trigger-3.5 { catchsql { CREATE TEMP TABLE t2(x,y); INSERT INTO t1 VALUES(1,2); SELECT * FROM t2; } } {1 {no such table: main.t2}} do_test trigger-3.6.1 { catchsql { DROP TRIGGER r1; CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(NEW.a,NEW.b), (NEW.b*100, NEW.a*100); END; INSERT INTO t1 VALUES(1,2); SELECT * FROM t2; } } {0 {1 2 200 100}} do_test trigger-3.6.2 { catchsql { DROP TRIGGER r1; DELETE FROM t1; DELETE FROM t2; CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(NEW.a,NEW.b); END; INSERT INTO t1 VALUES(1,2); SELECT * FROM t2; } } {0 {1 2}} |
︙ | ︙ |