Enhance the built-in date/time functions so that they can be used in
CHECK constraints, in the WHERE clause or partial indexes, and index expressions, provided that none of the non-deterministic keywords ("now", "localtime", "utc") are used as arguments. FossilOrigin-Name: a90c062d46c63a1e6f83064b1c5afb26a16e93b6ee8620ca46d169fdb325c488
This commit is contained in:
commit
4562d0db0d
36
manifest
36
manifest
@ -1,5 +1,5 @@
|
||||
C Remove\ssome\s'breakpoint'\scommands\sin\stest\sfiles.
|
||||
D 2017-07-19T18:01:42.362
|
||||
C Enhance\sthe\sbuilt-in\sdate/time\sfunctions\sso\sthat\sthey\scan\sbe\sused\sin\nCHECK\sconstraints,\sin\sthe\sWHERE\sclause\sor\spartial\sindexes,\sand\sindex\nexpressions,\sprovided\sthat\snone\sof\sthe\snon-deterministic\skeywords\n("now",\s"localtime",\s"utc")\sare\sused\sas\sarguments.
|
||||
D 2017-07-20T15:08:43.378
|
||||
F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016
|
||||
@ -402,10 +402,10 @@ F src/build.c 74108007d286232fb4290464ee5452fa860c26215f8caa0e6c7cbf69a6fafe8f
|
||||
F src/callback.c 8e14b60d1ed1c87c02cb5f121ecda99224f2aea6524a77ee6f72c9b5c7110f84
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 928954802b1397d9fb1378c7eb702c94b4735bbab1d5793e21b6a77734f56a1b
|
||||
F src/date.c cc42a41c7422389860d40419a5e3bce5eaf6e7835c3ba2677751dc653550a5c7
|
||||
F src/date.c 48f743d88bbe88f848532d333cca84f26e52a4f217e86f86be7fc1b919c33d74
|
||||
F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
|
||||
F src/delete.c 3213547e97b676c6fa79948b7a9ede4801ea04a01a2043241deafedf132ecf5d
|
||||
F src/expr.c 68552ca7f1238c9661e60a3adb4bd28c93d5373895bed5a0293b3977518dc980
|
||||
F src/delete.c 939bd15e6b54b82b951e1c0ffc2ff2b4ab579196780a1f6d394e47bd6f799b6c
|
||||
F src/expr.c fdb2fc465cabbf372fecad1fc2b291758bec74150b4db0fb945332e09df28a0e
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 5ff2c895fe087756d8085dc1a9bc229b5670e2a65c3929dd87c71e43649af333
|
||||
F src/func.c e2854b19386b93ad6b498a3f3b7d6baa98ec14cfe84530fb12fce4414263d871
|
||||
@ -414,7 +414,7 @@ F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
|
||||
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
|
||||
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c bb70abf32c7c926745eb550938db9132309584a667a44c2db0e5fa3207600391
|
||||
F src/insert.c 487d2c1cd8d549f8d9492377ecc4d2f507b1bd5185e96c54389f6b607dce854c
|
||||
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
|
||||
F src/loadext.c 20865b183bb8a3723d59cf1efffc3c50217eb452c1021d077b908c94da26b0b2
|
||||
F src/main.c 3a9da9e3974d8a32ef6ca15b75503d540af22d284beb75bc7f0d93254ca3f8f7
|
||||
@ -445,7 +445,7 @@ F src/parse.y e384cb73f99e1b074085c974b37f4d830e885359e4b60837e30f7d67c16ba65b
|
||||
F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870
|
||||
F src/pcache.h 521bb9610d38ef17a3cc9b5ddafd4546c2ea67fa3d0e464823d73c2a28d50e11
|
||||
F src/pcache1.c 1195a21fe28e223e024f900b2011e80df53793f0356a24caace4188b098540dc
|
||||
F src/pragma.c d42b6b5af353bcd763f1901accb63e07855bb8d74c81d246be12488e424e6804
|
||||
F src/pragma.c cd6aeda3587be6c5c08f9b2d45eae6068666a03c9d077c8c43cdb85fb0aa70f2
|
||||
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
|
||||
F src/prepare.c dd250f904739b1dc449c131ac527c35e3424d94082dd111321bd83f80c6bb0fe
|
||||
F src/printf.c 8757834f1b54dae512fb25eb1acc8e94a0d15dd2290b58f2563f65973265adb2
|
||||
@ -453,12 +453,12 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 4324a94573b1e29286f8121e4881db59eaedc014afeb274c8d3e07ed282e0e20
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c c6bf96a7f9d7d68f929de84738c599a30d0a725ab0b54420e70545743cd5ee7b
|
||||
F src/shell.c dd4494287b22ac5ab0654fdd5acb1f2172d2fe621f673a39229ddc31bd8d598f
|
||||
F src/shell.c bd6a37cbe8bf64ef6a6a74fdc50f067d3148149b4ce2b4d03154663e66ded55f
|
||||
F src/shell.c.in b5725acacba95ccefa57b6d068f710e29ba8239c3aa704628a1902a1f729c175
|
||||
F src/sqlite.h.in dad804d4e1979a2ddec33cc8da6aa50c04e6ba0dcb4058e7b3609588d010e041
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 967154985ed2ae62f90d9029bb5b5071793d847f1696a2ebe9e8cc0b042ae60b
|
||||
F src/sqliteInt.h 0ba730cdc8afa723a5642380712f8bb33abd6a69218571c18b94acf3562de22a
|
||||
F src/sqliteInt.h 96197a18f041b9ab99e6cee0db39dbf771ac7762d9f0f63d9e719285f0478664
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
@ -520,11 +520,11 @@ F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc
|
||||
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
F src/util.c fc081ec6f63448dcd80d3dfad35baecfa104823254a815b081a4d9fe76e1db23
|
||||
F src/vacuum.c 874c0f2f15ab2908748297d587d22d485ea96d55aaec91d4775dddb2e24d2ecf
|
||||
F src/vdbe.c adc8a378710ec2376101483cc8a5f499539ee9bbebfb2a784f3370704d5d44ad
|
||||
F src/vdbe.h 7bf719031782823b915aff2c1f93d1944c1c6b300770a15339b7dbc9610b802e
|
||||
F src/vdbe.c 1e541ec7ff409bbabcc6b4f154957296fff5827c16c2ab0056348acae75685bf
|
||||
F src/vdbe.h d50cadf12bcf9fb99117ef392ce1ea283aa429270481426b6e8b0280c101fd97
|
||||
F src/vdbeInt.h 19bd04a4211fe56c712ab35b48be77fd5a0579b851e9dea2cb8deade359b72b9
|
||||
F src/vdbeapi.c 52844a5a71712197be45f1c63d730c48a745c7457c959465cfb2b969af40a266
|
||||
F src/vdbeaux.c 518d1cf6728ecb591390541c58b14902e8d61735ef574426b9971624c54d2c4b
|
||||
F src/vdbeaux.c 42e215cc6f69e91eebdd2b886ec1ff2b0d5e44331bd8b88a0a44e78bbeed7133
|
||||
F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
|
||||
F src/vdbemem.c fe8fce1cdc258320b465934039fe4b1230d63f81d6b81b1eac775b6eec00af0d
|
||||
F src/vdbesort.c f512c68d0bf7e0105316a5594c4329358c8ee9cae3b25138df041d97516c0372
|
||||
@ -697,6 +697,7 @@ F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807
|
||||
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
|
||||
F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b
|
||||
F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373
|
||||
F test/date2.test 74c234bece1b016e94dd4ef9c8cc7a199a8806c0e2291cab7ba64bace6350b10
|
||||
F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e
|
||||
F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5
|
||||
F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab
|
||||
@ -935,7 +936,7 @@ F test/index7.test 7feababe16f2091b229c22aff2bcc1d4d6b9d2bb
|
||||
F test/index8.test bc2e3db70e8e62459aaa1bd7e4a9b39664f8f9d7
|
||||
F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721
|
||||
F test/indexedby.test 9c4cd331224e57f79fbf411ae245e6272d415985
|
||||
F test/indexexpr1.test 038b3befa74e5a75126b6e9dd2ae5df61c1c7cf7
|
||||
F test/indexexpr1.test f06298de30f343f6a022c3b13ae82817bb52a169f2b23a13600688aa591be5d9
|
||||
F test/indexexpr2.test 3ddd7f23bc381b9f2b7a15f2d083b1a4078e7733dce8295602ecfa3c74a34cf9
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
@ -1636,7 +1637,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P c93284cd65b6471bffc38696e14dc24a2170698faf566cf91b727ba272f88c25
|
||||
R 9a0e35c22d772f08ad350059e0e65c0d
|
||||
U mistachkin
|
||||
Z 3b6354332c6aa6dc358fe07c8de8d63d
|
||||
P d14fc621e918915bbf8e04597eb238ea78dff3d9c5eb4402cb88692d00dbdfee b7f70c7fcabc10b8b3e62fe5ac68476cec23acaee037c7250ff70bca3f3ab541
|
||||
R 636bb2618e46215e27cfa1dc613ec36a
|
||||
T +closed b7f70c7fcabc10b8b3e62fe5ac68476cec23acaee037c7250ff70bca3f3ab541
|
||||
U drh
|
||||
Z 8d21c041010891259262753e78bdd45f
|
||||
|
@ -1 +1 @@
|
||||
d14fc621e918915bbf8e04597eb238ea78dff3d9c5eb4402cb88692d00dbdfee
|
||||
a90c062d46c63a1e6f83064b1c5afb26a16e93b6ee8620ca46d169fdb325c488
|
16
src/date.c
16
src/date.c
@ -386,7 +386,7 @@ static int parseDateOrTime(
|
||||
return 0;
|
||||
}else if( parseHhMmSs(zDate, p)==0 ){
|
||||
return 0;
|
||||
}else if( sqlite3StrICmp(zDate,"now")==0){
|
||||
}else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){
|
||||
return setDateTimeToCurrent(context, p);
|
||||
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
|
||||
setRawDateNumber(p, r);
|
||||
@ -669,7 +669,7 @@ static int parseModifier(
|
||||
** Assuming the current time value is UTC (a.k.a. GMT), shift it to
|
||||
** show local time.
|
||||
*/
|
||||
if( sqlite3_stricmp(z, "localtime")==0 ){
|
||||
if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){
|
||||
computeJD(p);
|
||||
p->iJD += localtimeOffset(p, pCtx, &rc);
|
||||
clearYMD_HMS_TZ(p);
|
||||
@ -695,7 +695,7 @@ static int parseModifier(
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
else if( sqlite3_stricmp(z, "utc")==0 ){
|
||||
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
|
||||
if( p->tzSet==0 ){
|
||||
sqlite3_int64 c1;
|
||||
computeJD(p);
|
||||
@ -1231,11 +1231,11 @@ static void currentTimeFunc(
|
||||
void sqlite3RegisterDateTimeFunctions(void){
|
||||
static FuncDef aDateTimeFuncs[] = {
|
||||
#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
||||
DFUNCTION(julianday, -1, 0, 0, juliandayFunc ),
|
||||
DFUNCTION(date, -1, 0, 0, dateFunc ),
|
||||
DFUNCTION(time, -1, 0, 0, timeFunc ),
|
||||
DFUNCTION(datetime, -1, 0, 0, datetimeFunc ),
|
||||
DFUNCTION(strftime, -1, 0, 0, strftimeFunc ),
|
||||
PURE_DATE(julianday, -1, 0, 0, juliandayFunc ),
|
||||
PURE_DATE(date, -1, 0, 0, dateFunc ),
|
||||
PURE_DATE(time, -1, 0, 0, timeFunc ),
|
||||
PURE_DATE(datetime, -1, 0, 0, datetimeFunc ),
|
||||
PURE_DATE(strftime, -1, 0, 0, strftimeFunc ),
|
||||
DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
|
||||
DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
|
||||
DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
|
||||
|
@ -852,10 +852,11 @@ int sqlite3GenerateIndexKey(
|
||||
if( piPartIdxLabel ){
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
*piPartIdxLabel = sqlite3VdbeMakeLabel(v);
|
||||
pParse->iSelfTab = iDataCur;
|
||||
pParse->iSelfTab = iDataCur + 1;
|
||||
sqlite3ExprCachePush(pParse);
|
||||
sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
|
||||
SQLITE_JUMPIFNULL);
|
||||
pParse->iSelfTab = 0;
|
||||
}else{
|
||||
*piPartIdxLabel = 0;
|
||||
}
|
||||
|
13
src/expr.c
13
src/expr.c
@ -3238,8 +3238,9 @@ void sqlite3ExprCodeLoadIndexColumn(
|
||||
if( iTabCol==XN_EXPR ){
|
||||
assert( pIdx->aColExpr );
|
||||
assert( pIdx->aColExpr->nExpr>iIdxCol );
|
||||
pParse->iSelfTab = iTabCur;
|
||||
pParse->iSelfTab = iTabCur + 1;
|
||||
sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut);
|
||||
pParse->iSelfTab = 0;
|
||||
}else{
|
||||
sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable, iTabCur,
|
||||
iTabCol, regOut);
|
||||
@ -3483,13 +3484,13 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
case TK_COLUMN: {
|
||||
int iTab = pExpr->iTable;
|
||||
if( iTab<0 ){
|
||||
if( pParse->ckBase>0 ){
|
||||
if( pParse->iSelfTab<0 ){
|
||||
/* Generating CHECK constraints or inserting into partial index */
|
||||
return pExpr->iColumn + pParse->ckBase;
|
||||
return pExpr->iColumn - pParse->iSelfTab;
|
||||
}else{
|
||||
/* Coding an expression that is part of an index where column names
|
||||
** in the index refer to the table to which the index belongs */
|
||||
iTab = pParse->iSelfTab;
|
||||
iTab = pParse->iSelfTab - 1;
|
||||
}
|
||||
}
|
||||
return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
|
||||
@ -3826,8 +3827,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, constMask, r1, target,
|
||||
(char*)pDef, P4_FUNCDEF);
|
||||
sqlite3VdbeAddOp4(v, pParse->iSelfTab ? OP_PureFunc0 : OP_Function0,
|
||||
constMask, r1, target, (char*)pDef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, (u8)nFarg);
|
||||
if( nFarg && constMask==0 ){
|
||||
sqlite3ReleaseTempRange(pParse, r1, nFarg);
|
||||
|
11
src/insert.c
11
src/insert.c
@ -1333,7 +1333,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
|
||||
ExprList *pCheck = pTab->pCheck;
|
||||
pParse->ckBase = regNewData+1;
|
||||
pParse->iSelfTab = -(regNewData+1);
|
||||
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
|
||||
for(i=0; i<pCheck->nExpr; i++){
|
||||
int allOk;
|
||||
@ -1353,6 +1353,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, allOk);
|
||||
}
|
||||
pParse->iSelfTab = 0;
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
||||
|
||||
@ -1497,10 +1498,10 @@ void sqlite3GenerateConstraintChecks(
|
||||
/* Skip partial indices for which the WHERE clause is not true */
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
|
||||
pParse->ckBase = regNewData+1;
|
||||
pParse->iSelfTab = -(regNewData+1);
|
||||
sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, addrUniqueOk,
|
||||
SQLITE_JUMPIFNULL);
|
||||
pParse->ckBase = 0;
|
||||
pParse->iSelfTab = 0;
|
||||
}
|
||||
|
||||
/* Create a record for this index entry as it should appear after
|
||||
@ -1511,9 +1512,9 @@ void sqlite3GenerateConstraintChecks(
|
||||
int iField = pIdx->aiColumn[i];
|
||||
int x;
|
||||
if( iField==XN_EXPR ){
|
||||
pParse->ckBase = regNewData+1;
|
||||
pParse->iSelfTab = -(regNewData+1);
|
||||
sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i);
|
||||
pParse->ckBase = 0;
|
||||
pParse->iSelfTab = 0;
|
||||
VdbeComment((v, "%s column %d", pIdx->zName, i));
|
||||
}else{
|
||||
if( iField==XN_ROWID || iField==pTab->iPKey ){
|
||||
|
@ -1594,7 +1594,7 @@ void sqlite3Pragma(
|
||||
int addrCkOk = sqlite3VdbeMakeLabel(v);
|
||||
char *zErr;
|
||||
int k;
|
||||
pParse->iSelfTab = iDataCur;
|
||||
pParse->iSelfTab = iDataCur + 1;
|
||||
sqlite3ExprCachePush(pParse);
|
||||
for(k=pCheck->nExpr-1; k>0; k--){
|
||||
sqlite3ExprIfFalse(pParse, pCheck->a[k].pExpr, addrCkFault, 0);
|
||||
@ -1602,6 +1602,7 @@ void sqlite3Pragma(
|
||||
sqlite3ExprIfTrue(pParse, pCheck->a[0].pExpr, addrCkOk,
|
||||
SQLITE_JUMPIFNULL);
|
||||
sqlite3VdbeResolveLabel(v, addrCkFault);
|
||||
pParse->iSelfTab = 0;
|
||||
zErr = sqlite3MPrintf(db, "CHECK constraint failed in %s",
|
||||
pTab->zName);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
|
||||
|
@ -8396,4 +8396,3 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1628,7 +1628,14 @@ struct FuncDestructor {
|
||||
** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and
|
||||
** adds the SQLITE_FUNC_SLOCHNG flag. Used for date & time functions
|
||||
** and functions like sqlite_version() that can change, but not during
|
||||
** a single query.
|
||||
** a single query. The iArg is ignored. The user-data is always set
|
||||
** to a NULL pointer. The bNC parameter is not used.
|
||||
**
|
||||
** PURE_DATE(zName, nArg, iArg, bNC, xFunc)
|
||||
** Used for "pure" date/time functions, this macro is like DFUNCTION
|
||||
** except that it does set the SQLITE_FUNC_CONSTANT flags. iArg is
|
||||
** ignored and the user-data for these functions is set to an
|
||||
** arbitrary non-NULL pointer. The bNC parameter is not used.
|
||||
**
|
||||
** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
|
||||
** Used to create an aggregate function definition implemented by
|
||||
@ -1651,8 +1658,11 @@ struct FuncDestructor {
|
||||
{nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
|
||||
#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
|
||||
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \
|
||||
0, 0, xFunc, 0, #zName, {0} }
|
||||
#define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
|
||||
(void*)xFunc, 0, xFunc, 0, #zName, {0} }
|
||||
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
|
||||
{nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
|
||||
@ -2953,8 +2963,8 @@ struct Parse {
|
||||
int nMem; /* Number of memory cells used so far */
|
||||
int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
|
||||
int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
|
||||
int ckBase; /* Base register of data during check constraints */
|
||||
int iSelfTab; /* Table of an index whose exprs are being coded */
|
||||
int iSelfTab; /* Table for associated with an index on expr, or negative
|
||||
** of the base register during check-constraint eval */
|
||||
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
|
||||
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
|
||||
int nLabel; /* Number of labels used */
|
||||
|
226
src/vdbe.c
226
src/vdbe.c
@ -1590,117 +1590,6 @@ case OP_CollSeq: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Function0 P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P3]=func(r[P2@P5])
|
||||
**
|
||||
** Invoke a user function (P4 is a pointer to a FuncDef object that
|
||||
** defines the function) with P5 arguments taken from register P2 and
|
||||
** successors. The result of the function is stored in register P3.
|
||||
** Register P3 must not be one of the function inputs.
|
||||
**
|
||||
** P1 is a 32-bit bitmask indicating whether or not each argument to the
|
||||
** function was determined to be constant at compile time. If the first
|
||||
** argument was constant then bit 0 of P1 is set. This is used to determine
|
||||
** whether meta data associated with a user function argument using the
|
||||
** sqlite3_set_auxdata() API may be safely retained until the next
|
||||
** invocation of this opcode.
|
||||
**
|
||||
** See also: Function, AggStep, AggFinal
|
||||
*/
|
||||
/* Opcode: Function P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P3]=func(r[P2@P5])
|
||||
**
|
||||
** Invoke a user function (P4 is a pointer to an sqlite3_context object that
|
||||
** contains a pointer to the function to be run) with P5 arguments taken
|
||||
** from register P2 and successors. The result of the function is stored
|
||||
** in register P3. Register P3 must not be one of the function inputs.
|
||||
**
|
||||
** P1 is a 32-bit bitmask indicating whether or not each argument to the
|
||||
** function was determined to be constant at compile time. If the first
|
||||
** argument was constant then bit 0 of P1 is set. This is used to determine
|
||||
** whether meta data associated with a user function argument using the
|
||||
** sqlite3_set_auxdata() API may be safely retained until the next
|
||||
** invocation of this opcode.
|
||||
**
|
||||
** SQL functions are initially coded as OP_Function0 with P4 pointing
|
||||
** to a FuncDef object. But on first evaluation, the P4 operand is
|
||||
** automatically converted into an sqlite3_context object and the operation
|
||||
** changed to this OP_Function opcode. In this way, the initialization of
|
||||
** the sqlite3_context object occurs only once, rather than once for each
|
||||
** evaluation of the function.
|
||||
**
|
||||
** See also: Function0, AggStep, AggFinal
|
||||
*/
|
||||
case OP_Function0: {
|
||||
int n;
|
||||
sqlite3_context *pCtx;
|
||||
|
||||
assert( pOp->p4type==P4_FUNCDEF );
|
||||
n = pOp->p5;
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
|
||||
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
|
||||
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
|
||||
pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
|
||||
if( pCtx==0 ) goto no_mem;
|
||||
pCtx->pOut = 0;
|
||||
pCtx->pFunc = pOp->p4.pFunc;
|
||||
pCtx->iOp = (int)(pOp - aOp);
|
||||
pCtx->pVdbe = p;
|
||||
pCtx->argc = n;
|
||||
pOp->p4type = P4_FUNCCTX;
|
||||
pOp->p4.pCtx = pCtx;
|
||||
pOp->opcode = OP_Function;
|
||||
/* Fall through into OP_Function */
|
||||
}
|
||||
case OP_Function: {
|
||||
int i;
|
||||
sqlite3_context *pCtx;
|
||||
|
||||
assert( pOp->p4type==P4_FUNCCTX );
|
||||
pCtx = pOp->p4.pCtx;
|
||||
|
||||
/* If this function is inside of a trigger, the register array in aMem[]
|
||||
** might change from one evaluation to the next. The next block of code
|
||||
** checks to see if the register array has changed, and if so it
|
||||
** reinitializes the relavant parts of the sqlite3_context object */
|
||||
pOut = &aMem[pOp->p3];
|
||||
if( pCtx->pOut != pOut ){
|
||||
pCtx->pOut = pOut;
|
||||
for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
|
||||
}
|
||||
|
||||
memAboutToChange(p, pOut);
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(i=0; i<pCtx->argc; i++){
|
||||
assert( memIsValid(pCtx->argv[i]) );
|
||||
REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
|
||||
}
|
||||
#endif
|
||||
MemSetTypeFlag(pOut, MEM_Null);
|
||||
pCtx->fErrorOrAux = 0;
|
||||
(*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
|
||||
|
||||
/* If the function returned an error, throw an exception */
|
||||
if( pCtx->fErrorOrAux ){
|
||||
if( pCtx->isError ){
|
||||
sqlite3VdbeError(p, "%s", sqlite3_value_text(pOut));
|
||||
rc = pCtx->isError;
|
||||
}
|
||||
sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
}
|
||||
|
||||
/* Copy the result of the function into register P3 */
|
||||
if( pOut->flags & (MEM_Str|MEM_Blob) ){
|
||||
sqlite3VdbeChangeEncoding(pOut, encoding);
|
||||
if( sqlite3VdbeMemTooBig(pOut) ) goto too_big;
|
||||
}
|
||||
|
||||
REGISTER_TRACE(pOp->p3, pOut);
|
||||
UPDATE_MAX_BLOBSIZE(pOut);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: BitAnd P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P1]&r[P2]
|
||||
**
|
||||
@ -7000,6 +6889,121 @@ case OP_MaxPgcnt: { /* out2 */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Opcode: Function0 P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P3]=func(r[P2@P5])
|
||||
**
|
||||
** Invoke a user function (P4 is a pointer to a FuncDef object that
|
||||
** defines the function) with P5 arguments taken from register P2 and
|
||||
** successors. The result of the function is stored in register P3.
|
||||
** Register P3 must not be one of the function inputs.
|
||||
**
|
||||
** P1 is a 32-bit bitmask indicating whether or not each argument to the
|
||||
** function was determined to be constant at compile time. If the first
|
||||
** argument was constant then bit 0 of P1 is set. This is used to determine
|
||||
** whether meta data associated with a user function argument using the
|
||||
** sqlite3_set_auxdata() API may be safely retained until the next
|
||||
** invocation of this opcode.
|
||||
**
|
||||
** See also: Function, AggStep, AggFinal
|
||||
*/
|
||||
/* Opcode: Function P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P3]=func(r[P2@P5])
|
||||
**
|
||||
** Invoke a user function (P4 is a pointer to an sqlite3_context object that
|
||||
** contains a pointer to the function to be run) with P5 arguments taken
|
||||
** from register P2 and successors. The result of the function is stored
|
||||
** in register P3. Register P3 must not be one of the function inputs.
|
||||
**
|
||||
** P1 is a 32-bit bitmask indicating whether or not each argument to the
|
||||
** function was determined to be constant at compile time. If the first
|
||||
** argument was constant then bit 0 of P1 is set. This is used to determine
|
||||
** whether meta data associated with a user function argument using the
|
||||
** sqlite3_set_auxdata() API may be safely retained until the next
|
||||
** invocation of this opcode.
|
||||
**
|
||||
** SQL functions are initially coded as OP_Function0 with P4 pointing
|
||||
** to a FuncDef object. But on first evaluation, the P4 operand is
|
||||
** automatically converted into an sqlite3_context object and the operation
|
||||
** changed to this OP_Function opcode. In this way, the initialization of
|
||||
** the sqlite3_context object occurs only once, rather than once for each
|
||||
** evaluation of the function.
|
||||
**
|
||||
** See also: Function0, AggStep, AggFinal
|
||||
*/
|
||||
case OP_PureFunc0:
|
||||
case OP_Function0: {
|
||||
int n;
|
||||
sqlite3_context *pCtx;
|
||||
|
||||
assert( pOp->p4type==P4_FUNCDEF );
|
||||
n = pOp->p5;
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
|
||||
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
|
||||
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
|
||||
pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
|
||||
if( pCtx==0 ) goto no_mem;
|
||||
pCtx->pOut = 0;
|
||||
pCtx->pFunc = pOp->p4.pFunc;
|
||||
pCtx->iOp = (int)(pOp - aOp);
|
||||
pCtx->pVdbe = p;
|
||||
pCtx->argc = n;
|
||||
pOp->p4type = P4_FUNCCTX;
|
||||
pOp->p4.pCtx = pCtx;
|
||||
assert( OP_PureFunc == OP_PureFunc0+2 );
|
||||
assert( OP_Function == OP_Function0+2 );
|
||||
pOp->opcode += 2;
|
||||
/* Fall through into OP_Function */
|
||||
}
|
||||
case OP_PureFunc:
|
||||
case OP_Function: {
|
||||
int i;
|
||||
sqlite3_context *pCtx;
|
||||
|
||||
assert( pOp->p4type==P4_FUNCCTX );
|
||||
pCtx = pOp->p4.pCtx;
|
||||
|
||||
/* If this function is inside of a trigger, the register array in aMem[]
|
||||
** might change from one evaluation to the next. The next block of code
|
||||
** checks to see if the register array has changed, and if so it
|
||||
** reinitializes the relavant parts of the sqlite3_context object */
|
||||
pOut = &aMem[pOp->p3];
|
||||
if( pCtx->pOut != pOut ){
|
||||
pCtx->pOut = pOut;
|
||||
for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
|
||||
}
|
||||
|
||||
memAboutToChange(p, pOut);
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(i=0; i<pCtx->argc; i++){
|
||||
assert( memIsValid(pCtx->argv[i]) );
|
||||
REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
|
||||
}
|
||||
#endif
|
||||
MemSetTypeFlag(pOut, MEM_Null);
|
||||
pCtx->fErrorOrAux = 0;
|
||||
(*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
|
||||
|
||||
/* If the function returned an error, throw an exception */
|
||||
if( pCtx->fErrorOrAux ){
|
||||
if( pCtx->isError ){
|
||||
sqlite3VdbeError(p, "%s", sqlite3_value_text(pOut));
|
||||
rc = pCtx->isError;
|
||||
}
|
||||
sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
}
|
||||
|
||||
/* Copy the result of the function into register P3 */
|
||||
if( pOut->flags & (MEM_Str|MEM_Blob) ){
|
||||
sqlite3VdbeChangeEncoding(pOut, encoding);
|
||||
if( sqlite3VdbeMemTooBig(pOut) ) goto too_big;
|
||||
}
|
||||
|
||||
REGISTER_TRACE(pOp->p3, pOut);
|
||||
UPDATE_MAX_BLOBSIZE(pOut);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: Init P1 P2 * P4 *
|
||||
** Synopsis: Start at P2
|
||||
|
@ -253,6 +253,8 @@ RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
|
||||
void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
|
||||
#endif
|
||||
|
||||
int sqlite3NotPureFunc(sqlite3_context*);
|
||||
|
||||
/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
|
||||
** each VDBE opcode.
|
||||
**
|
||||
|
@ -4583,6 +4583,25 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Cause a function to throw an error if it was call from OP_PureFunc
|
||||
** rather than OP_Function.
|
||||
**
|
||||
** OP_PureFunc means that the function must be deterministic, and should
|
||||
** throw an error if it is given inputs that would make it non-deterministic.
|
||||
** This routine is invoked by date/time functions that use non-deterministic
|
||||
** features such as 'now'.
|
||||
*/
|
||||
int sqlite3NotPureFunc(sqlite3_context *pCtx){
|
||||
if( pCtx->pVdbe->aOp[pCtx->iOp].opcode==OP_PureFunc ){
|
||||
sqlite3_result_error(pCtx,
|
||||
"non-deterministic function in index expression or CHECK constraint",
|
||||
-1);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
|
||||
|
134
test/date2.test
Normal file
134
test/date2.test
Normal file
@ -0,0 +1,134 @@
|
||||
# 2017-07-20
|
||||
#
|
||||
# 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. The
|
||||
# focus of this file is testing date and time functions used in
|
||||
# check constraints and index expressions.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Skip this whole file if date and time functions are omitted
|
||||
# at compile-time
|
||||
#
|
||||
ifcapable {!datetime} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test date2-100 {
|
||||
CREATE TABLE t1(x, y, CHECK( date(x) BETWEEN '2017-07-01' AND '2017-07-31' ));
|
||||
INSERT INTO t1(x,y) VALUES('2017-07-20','one');
|
||||
} {}
|
||||
do_catchsql_test date2-110 {
|
||||
INSERT INTO t1(x,y) VALUES('now','two');
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
do_execsql_test date2-120 {
|
||||
SELECT * FROM t1;
|
||||
} {2017-07-20 one}
|
||||
do_catchsql_test date2-130 {
|
||||
INSERT INTO t1(x,y) VALUES('2017-08-01','two');
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
|
||||
do_execsql_test date2-200 {
|
||||
CREATE TABLE t2(x,y);
|
||||
INSERT INTO t2(x,y) VALUES(1, '2017-07-20'), (2, 'xyzzy');
|
||||
CREATE INDEX t2y ON t2(date(y));
|
||||
}
|
||||
do_catchsql_test date2-210 {
|
||||
INSERT INTO t2(x,y) VALUES(3, 'now');
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
do_execsql_test date2-220 {
|
||||
SELECT x, y FROM t2 ORDER BY x;
|
||||
} {1 2017-07-20 2 xyzzy}
|
||||
|
||||
do_execsql_test date2-300 {
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY,b);
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
|
||||
INSERT INTO t3(a,b) SELECT x, julianday('2017-07-01')+x FROM c;
|
||||
UPDATE t3 SET b='now' WHERE a=500;
|
||||
}
|
||||
do_catchsql_test date2-310 {
|
||||
CREATE INDEX t3b1 ON t3(datetime(b));
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
do_catchsql_test date2-320 {
|
||||
CREATE INDEX t3b1 ON t3(datetime(b)) WHERE typeof(b)='real';
|
||||
} {0 {}}
|
||||
do_execsql_test date2-330 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT a FROM t3
|
||||
WHERE typeof(b)='real'
|
||||
AND datetime(b) BETWEEN '2017-07-04' AND '2017-07-08';
|
||||
} {/USING INDEX t3b/}
|
||||
do_execsql_test date2-331 {
|
||||
SELECT a FROM t3
|
||||
WHERE typeof(b)='real'
|
||||
AND datetime(b) BETWEEN '2017-07-04' AND '2017-07-08'
|
||||
ORDER BY a;
|
||||
} {3 4 5 6}
|
||||
|
||||
do_execsql_test date2-400 {
|
||||
CREATE TABLE t4(a INTEGER PRIMARY KEY,b);
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
|
||||
INSERT INTO t4(a,b) SELECT x, julianday('2017-07-01')+x FROM c;
|
||||
UPDATE t4 SET b='now' WHERE a=500;
|
||||
}
|
||||
do_catchsql_test date2-410 {
|
||||
CREATE INDEX t4b1 ON t4(b)
|
||||
WHERE date(b) BETWEEN '2017-06-01' AND '2017-08-31';
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
do_execsql_test date2-420 {
|
||||
DELETE FROM t4 WHERE a=500;
|
||||
CREATE INDEX t4b1 ON t4(b)
|
||||
WHERE date(b) BETWEEN '2017-06-01' AND '2017-08-31';
|
||||
}
|
||||
do_catchsql_test date2-430 {
|
||||
INSERT INTO t4(a,b) VALUES(9999,'now');
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
|
||||
do_execsql_test date2-500 {
|
||||
CREATE TABLE mods(x);
|
||||
INSERT INTO mods(x) VALUES
|
||||
('+10 days'),
|
||||
('-10 days'),
|
||||
('+10 hours'),
|
||||
('-10 hours'),
|
||||
('+10 minutes'),
|
||||
('-10 minutes'),
|
||||
('+10 seconds'),
|
||||
('-10 seconds'),
|
||||
('+10 months'),
|
||||
('-10 months'),
|
||||
('+10 years'),
|
||||
('-10 years'),
|
||||
('start of month'),
|
||||
('start of year'),
|
||||
('start of day'),
|
||||
('weekday 1'),
|
||||
('unixepoch');
|
||||
CREATE TABLE t5(y,m);
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5)
|
||||
INSERT INTO t5(y,m) SELECT julianday('2017-07-01')+c.x, mods.x FROM c, mods;
|
||||
CREATE INDEX t5x1 on t5(y) WHERE datetime(y,m) IS NOT NULL;
|
||||
}
|
||||
do_catchsql_test date2-510 {
|
||||
INSERT INTO t5(y,m) VALUES('2017-07-20','localtime');
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
do_catchsql_test date2-520 {
|
||||
INSERT INTO t5(y,m) VALUES('2017-07-20','utc');
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
@ -181,12 +181,12 @@ do_execsql_test indexexpr1-260eqp {
|
||||
|
||||
|
||||
do_catchsql_test indexexpr1-300 {
|
||||
CREATE TABLE t2(a,b,c);
|
||||
CREATE TABLE t2(a,b,c); INSERT INTO t2 VALUES(1,2,3);
|
||||
CREATE INDEX t2x1 ON t2(a,b+random());
|
||||
} {1 {non-deterministic functions prohibited in index expressions}}
|
||||
do_catchsql_test indexexpr1-301 {
|
||||
CREATE INDEX t2x1 ON t2(a+julianday('now'));
|
||||
} {1 {non-deterministic functions prohibited in index expressions}}
|
||||
CREATE INDEX t2x1 ON t2(julianday('now',a));
|
||||
} {1 {non-deterministic function in index expression or CHECK constraint}}
|
||||
do_catchsql_test indexexpr1-310 {
|
||||
CREATE INDEX t2x2 ON t2(a,b+(SELECT 15));
|
||||
} {1 {subqueries prohibited in index expressions}}
|
||||
@ -380,4 +380,7 @@ do_execsql_test indexexpr1-1300.1 {
|
||||
SELECT a FROM t1300 WHERE substr(b,4)='ess' COLLATE nocase ORDER BY +a;
|
||||
} {3 4}
|
||||
|
||||
# Date and time functions can participate in an index as long as they
|
||||
# do not contain
|
||||
|
||||
finish_test
|
||||
|
Loading…
x
Reference in New Issue
Block a user