Modify the OP_Once opcode so that it works correctly in trigger sub-programs. This is a candidate fix for [7bbfb7d442].

FossilOrigin-Name: 557c69055a300b4082830b5f4803091dca1c3140
This commit is contained in:
dan 2011-12-09 13:24:16 +00:00
parent 254582f4c2
commit 1d8cb21fdb
12 changed files with 223 additions and 44 deletions

View File

@ -1,5 +1,5 @@
C The\sWindows\sOS\sflavor\s#ifdefs\smust\sbe\sperformed\safter\sthe\s'windows.h'\sfile\shas\sbeen\sincluded.
D 2011-12-09T05:52:16.276
C Modify\sthe\sOP_Once\sopcode\sso\sthat\sit\sworks\scorrectly\sin\strigger\ssub-programs.\sThis\sis\sa\scandidate\sfix\sfor\s[7bbfb7d442].
D 2011-12-09T13:24:16.480
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -134,7 +134,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
F src/expr.c 9ac5831769dddee6a55b07cdd439b21929bbe4e7
F src/expr.c 672c6e5fa3ad2eae9ff2588eac2317d76fcdfaef
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
@ -180,11 +180,11 @@ F src/printf.c 03104cbff6959ff45df69dc9060ba6212f60a869
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c 538b5f096f6aacedf717bf982f9ad9a901008514
F src/select.c e16b188449a7841e9e801a3c45c973c24e345199
F src/shell.c 29812a900a780eb0f835c4bc65e216272689def8
F src/sqlite.h.in 57081d8e6b53ce29541d7437c93bce6087ac53b5
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
F src/sqliteInt.h 142ef5ed3fe0e6c662e99dbf92f38715b29a53b6
F src/sqliteInt.h de3db02a1e4762a6ec9e1ab604ebc02d77948030
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@ -234,16 +234,16 @@ F src/test_vfstrace.c 0b884e06094a746da729119a2cabdc7aa790063d
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
F src/trigger.c 1cfb80e2290ef66ea89cb4e821caae65a02c0d56
F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
F src/update.c 25e046a8f69d5e557aabde2000487b8545509d8d
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
F src/util.c 01238e2b0f24a14779181dbf991fe02620a80e31
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
F src/vdbe.c a7ab9993ec5a4d9479dc99671faec061fbf9b889
F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755
F src/vdbeInt.h 9498fc98a2c9e349a4ef13455ff5a3e898f40176
F src/vdbe.c f49816f99b853c6cbdec950eedd4434cb2452376
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
F src/vdbeInt.h 23a9506c9ab31e7823d7257d1828d2d7843443a0
F src/vdbeapi.c 86189ebba2c49791d75eaa12929f3ce6527596bd
F src/vdbeaux.c 45713a5f8f4f36195f503b30153ddef292323f88
F src/vdbeaux.c cdd74a86c6281b4393c5f94d8f5e40e160e44e19
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
@ -252,7 +252,7 @@ F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
F src/wal.c 7e6e7fe68ee649505dca38c8ab83eda0d0d96ae5
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c f73752ca85c0ed221753fda98aeaf6b9d4616e0e
F src/where.c af623942514571895818b9b7ae11db95ae3b3d88
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@ -737,6 +737,7 @@ F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f
F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
F test/tkt-7bbfb7d442.test 8e7658f77d1ccea9d88dc9e255d3ed7fb68f8bdf
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
@ -977,7 +978,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 0d955c20c02da29582b5cd8df2b7124fb9d12ebb
R cba6a22ba8dad5c2320a7a8f8ac118be
U mistachkin
Z 5127f14a32c508056103def7f6cd4a97
P 3702a31e56fe02d14ce246109b318a124cad9f1a
R 212172c6a4e482305cc0a6c8ccb724d1
U dan
Z fd67074fb89c049640d6ea2f96341088

View File

@ -1 +1 @@
3702a31e56fe02d14ce246109b318a124cad9f1a
557c69055a300b4082830b5f4803091dca1c3140

View File

@ -1375,6 +1375,15 @@ static int isCandidateForInOpt(Select *p){
}
#endif /* SQLITE_OMIT_SUBQUERY */
/*
** Code an OP_Once instruction and allocate space for its flag. Return the
** address of the new instruction.
*/
int sqlite3CodeOnce(Parse *pParse){
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
}
/*
** This function is used by the implementation of the IN (...) operator.
** It's job is to find or create a b-tree structure that may be used
@ -1470,10 +1479,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
*/
assert(v);
if( iCol<0 ){
int iMem = ++pParse->nMem;
int iAddr;
iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
iAddr = sqlite3CodeOnce(pParse);
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
eType = IN_INDEX_ROWID;
@ -1499,12 +1507,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
&& sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
&& (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
){
int iMem = ++pParse->nMem;
int iAddr;
char *pKey;
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
iAddr = sqlite3CodeOnce(pParse);
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
pKey,P4_KEYINFO_HANDOFF);
@ -1601,9 +1608,8 @@ int sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
int mem = ++pParse->nMem;
testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){
testAddr = sqlite3CodeOnce(pParse);
}
#ifndef SQLITE_OMIT_EXPLAIN

View File

@ -3845,12 +3845,11 @@ int sqlite3Select(
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
if( pItem->isCorrelated==0 ){
/* If the subquery is no correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
int regOnce = ++pParse->nMem;
onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
onceAddr = sqlite3CodeOnce(pParse);
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);

View File

@ -2204,6 +2204,7 @@ struct Parse {
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
int nSet; /* Number of sets used so far */
int nOnce; /* Number of OP_Once instructions so far */
int ckBase; /* Base register of data during check constraints */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
@ -2692,6 +2693,7 @@ void sqlite3AddCollateType(Parse*, Token*);
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
int sqlite3CodeOnce(Parse *);
Bitvec *sqlite3BitvecCreate(u32);
int sqlite3BitvecTest(Bitvec*, u32);

View File

@ -904,6 +904,7 @@ static TriggerPrg *codeRowTrigger(
}
pProgram->nMem = pSubParse->nMem;
pProgram->nCsr = pSubParse->nTab;
pProgram->nOnce = pSubParse->nOnce;
pProgram->token = (void *)pTrigger;
pPrg->aColmask[0] = pSubParse->oldmask;
pPrg->aColmask[1] = pSubParse->newmask;

View File

@ -2023,14 +2023,19 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
/* Opcode: Once P1 P2 * * *
**
** Jump to P2 if the value in register P1 is a not null or zero. If
** the value is NULL or zero, fall through and change the P1 register
** to an integer 1.
**
** When P1 is not used otherwise in a program, this opcode falls through
** once and jumps on all subsequent invocations. It is the equivalent
** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise,
** set the flag and fall through to the next instruction.
*/
case OP_Once: { /* jump */
assert( pOp->p1<p->nOnceFlag );
if( p->aOnceFlag[pOp->p1] ){
pc = pOp->p2-1;
}else{
p->aOnceFlag[pOp->p1] = 1;
}
break;
}
/* Opcode: If P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is true. The value
@ -2043,7 +2048,6 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
** is considered true if it has a numeric value of zero. If the value
** in P1 is NULL then take the jump if P3 is true.
*/
case OP_Once: /* jump, in1 */
case OP_If: /* jump, in1 */
case OP_IfNot: { /* jump, in1 */
int c;
@ -2060,12 +2064,6 @@ case OP_IfNot: { /* jump, in1 */
}
if( c ){
pc = pOp->p2-1;
}else if( pOp->opcode==OP_Once ){
assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
memAboutToChange(p, pIn1);
pIn1->flags = MEM_Int;
pIn1->u.i = 1;
REGISTER_TRACE(pOp->p1, pIn1);
}
break;
}
@ -5110,7 +5108,8 @@ case OP_Program: { /* jump */
nMem = pProgram->nMem + pProgram->nCsr;
nByte = ROUND8(sizeof(VdbeFrame))
+ nMem * sizeof(Mem)
+ pProgram->nCsr * sizeof(VdbeCursor *);
+ pProgram->nCsr * sizeof(VdbeCursor *)
+ pProgram->nOnce * sizeof(u8);
pFrame = sqlite3DbMallocZero(db, nByte);
if( !pFrame ){
goto no_mem;
@ -5130,6 +5129,8 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
pFrame->aOnceFlag = p->aOnceFlag;
pFrame->nOnceFlag = p->nOnceFlag;
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
@ -5155,7 +5156,11 @@ case OP_Program: { /* jump */
p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
p->nOnceFlag = pProgram->nOnce;
p->nOp = pProgram->nOp;
pc = -1;
memset(p->aOnceFlag, 0, p->nOnceFlag);
break;
}

View File

@ -82,6 +82,7 @@ struct SubProgram {
int nOp; /* Elements in aOp[] */
int nMem; /* Number of memory cells required */
int nCsr; /* Number of cursors required */
int nOnce; /* Number of OP_Once instructions */
void *token; /* id that may be used to recursive triggers */
SubProgram *pNext; /* Next sub-program already visited */
};

View File

@ -117,6 +117,8 @@ struct VdbeFrame {
int nOp; /* Size of aOp array */
Mem *aMem; /* Array of memory cells for parent frame */
int nMem; /* Number of entries in aMem */
u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */
int nOnceFlag; /* Number of entries in aOnceFlag */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u16 nCursor; /* Number of entries in apCsr */
void *token; /* Copy of SubProgram.token */
@ -326,6 +328,8 @@ struct Vdbe {
int nFrame; /* Number of frames in pFrame list */
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
int nOnceFlag; /* Size of array aOnceFlag[] */
u8 *aOnceFlag; /* Flags for OP_Once */
};
/*

View File

@ -1469,6 +1469,7 @@ void sqlite3VdbeMakeReady(
int nMem; /* Number of VM memory registers */
int nCursor; /* Number of cursors required */
int nArg; /* Number of arguments in subprograms */
int nOnce; /* Number of OP_Once instructions */
int n; /* Loop counter */
u8 *zCsr; /* Memory available for allocation */
u8 *zEnd; /* First byte past allocated memory */
@ -1484,6 +1485,7 @@ void sqlite3VdbeMakeReady(
nMem = pParse->nMem;
nCursor = pParse->nTab;
nArg = pParse->nMaxArg;
nOnce = pParse->nOnce;
/* For each cursor required, also allocate a memory cell. Memory
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
@ -1530,6 +1532,8 @@ void sqlite3VdbeMakeReady(
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
&zCsr, zEnd, &nByte);
p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce*sizeof(u8),
&zCsr, zEnd, &nByte);
if( nByte ){
p->pFree = sqlite3DbMallocZero(db, nByte);
}
@ -1538,6 +1542,7 @@ void sqlite3VdbeMakeReady(
}while( nByte && !db->mallocFailed );
p->nCursor = (u16)nCursor;
p->nOnceFlag = nOnce;
if( p->aVar ){
p->nVar = (ynVar)nVar;
for(n=0; n<nVar; n++){
@ -1596,6 +1601,8 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
*/
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
v->aOnceFlag = pFrame->aOnceFlag;
v->nOnceFlag = pFrame->nOnceFlag;
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@ -1642,6 +1649,7 @@ static void closeAllCursors(Vdbe *p){
p->pDelFrame = pDel->pParent;
sqlite3VdbeFrameDelete(pDel);
}
memset(p->aOnceFlag, 0, p->nOnceFlag);
}
/*

View File

@ -2005,7 +2005,6 @@ static void constructAutomaticIndex(
int nByte; /* Byte of memory needed for pIdx */
Index *pIdx; /* Object describing the transient index */
Vdbe *v; /* Prepared statement under construction */
int regIsInit; /* Register set by initialization */
int addrInit; /* Address of the initialization bypass jump */
Table *pTable; /* The table being indexed */
KeyInfo *pKeyinfo; /* Key information for the index */
@ -2022,8 +2021,7 @@ static void constructAutomaticIndex(
** transient index on 2nd and subsequent iterations of the loop. */
v = pParse->pVdbe;
assert( v!=0 );
regIsInit = ++pParse->nMem;
addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit);
addrInit = sqlite3CodeOnce(pParse);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */

154
test/tkt-7bbfb7d442.test Normal file
View File

@ -0,0 +1,154 @@
# 2011 December 9
#
# 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests to verify that ticket [7bbfb7d442] has been
# fixed.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix tkt-7bbfb7d442
do_execsql_test 1.1 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
INSERT INTO t1 VALUES(3, 'three');
CREATE TABLE t2(c, d);
INSERT INTO t2 VALUES('one', 'I');
INSERT INTO t2 VALUES('two', 'II');
INSERT INTO t2 VALUES('three', 'III');
CREATE TABLE t3(t3_a PRIMARY KEY, t3_d);
CREATE TRIGGER t3t AFTER INSERT ON t3 WHEN new.t3_d IS NULL BEGIN
UPDATE t3 SET t3_d = (
SELECT d FROM
(SELECT * FROM t2 WHERE (new.t3_a%2)=(rowid%2) LIMIT 10),
(SELECT * FROM t1 WHERE (new.t3_a%2)=(rowid%2) LIMIT 10)
WHERE a = new.t3_a AND b = c
) WHERE t3_a = new.t3_a;
END;
}
do_execsql_test 1.2 {
INSERT INTO t3(t3_a) VALUES(1);
INSERT INTO t3(t3_a) VALUES(2);
INSERT INTO t3(t3_a) VALUES(3);
SELECT * FROM t3;
} {1 I 2 II 3 III}
do_execsql_test 1.3 { DELETE FROM t3 }
do_execsql_test 1.4 {
INSERT INTO t3(t3_a) SELECT 1 UNION SELECT 2 UNION SELECT 3;
SELECT * FROM t3;
} {1 I 2 II 3 III}
#-------------------------------------------------------------------------
# The following test case - 2.* - is from the original bug report as
# posted to the mailing list.
#
do_execsql_test 2.1 {
CREATE TABLE InventoryControl (
InventoryControlId INTEGER PRIMARY KEY AUTOINCREMENT,
SKU INTEGER NOT NULL,
Variant INTEGER NOT NULL DEFAULT 0,
ControlDate DATE NOT NULL,
ControlState INTEGER NOT NULL DEFAULT -1,
DeliveredQty VARCHAR(30)
);
CREATE TRIGGER TGR_InventoryControl_AfterInsert
AFTER INSERT ON InventoryControl
FOR EACH ROW WHEN NEW.ControlState=-1 BEGIN
INSERT OR REPLACE INTO InventoryControl(
InventoryControlId,SKU,Variant,ControlDate,ControlState,DeliveredQty
) SELECT
T1.InventoryControlId AS InventoryControlId,
T1.SKU AS SKU,
T1.Variant AS Variant,
T1.ControlDate AS ControlDate,
1 AS ControlState,
COALESCE(T2.DeliveredQty,0) AS DeliveredQty
FROM (
SELECT
NEW.InventoryControlId AS InventoryControlId,
II.SKU AS SKU,
II.Variant AS Variant,
COALESCE(LastClosedIC.ControlDate,NEW.ControlDate) AS ControlDate
FROM
InventoryItem II
LEFT JOIN
InventoryControl LastClosedIC
ON LastClosedIC.InventoryControlId IN ( SELECT 99999 )
WHERE
II.SKU=NEW.SKU AND
II.Variant=NEW.Variant
) T1
LEFT JOIN (
SELECT
TD.SKU AS SKU,
TD.Variant AS Variant,
10 AS DeliveredQty
FROM
TransactionDetail TD
WHERE
TD.SKU=NEW.SKU AND
TD.Variant=NEW.Variant
) T2
ON T2.SKU=T1.SKU AND
T2.Variant=T1.Variant;
END;
CREATE TABLE InventoryItem (
SKU INTEGER NOT NULL,
Variant INTEGER NOT NULL DEFAULT 0,
DeptCode INTEGER NOT NULL,
GroupCode INTEGER NOT NULL,
ItemDescription VARCHAR(120) NOT NULL,
PRIMARY KEY(SKU, Variant)
);
INSERT INTO InventoryItem VALUES(220,0,1,170,'Scoth Tampon Recurer');
INSERT INTO InventoryItem VALUES(31,0,1,110,'Fromage');
CREATE TABLE TransactionDetail (
TransactionId INTEGER NOT NULL,
SKU INTEGER NOT NULL,
Variant INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY(TransactionId, SKU, Variant)
);
INSERT INTO TransactionDetail(TransactionId, SKU, Variant) VALUES(44, 31, 0);
INSERT INTO InventoryControl(SKU, Variant, ControlDate) SELECT
II.SKU AS SKU, II.Variant AS Variant, '2011-08-30' AS ControlDate
FROM InventoryItem II;
}
do_execsql_test 2.2 {
SELECT SKU, DeliveredQty FROM InventoryControl WHERE SKU=31
} {31 10}
do_execsql_test 2.3 {
SELECT CASE WHEN DeliveredQty=10 THEN "TEST PASSED!" ELSE "TEST FAILED!" END
FROM InventoryControl WHERE SKU=31;
} {{TEST PASSED!}}
finish_test