Fossil SCM

Merge from trunk. Sorry if this errors.

brickviking 2024-12-16 11:09 bv-infotool merge
Commit 91639f39e23e8bad5e052f27694557806d776b28297e12178f9f4e103d20527f
+254 -49
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -2367,11 +2367,11 @@
23672367
**
23682368
******************************************************************************
23692369
**
23702370
** This SQLite extension implements functions that compute SHA3 hashes
23712371
** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard.
2372
-** Two SQL functions are implemented:
2372
+** Three SQL functions are implemented:
23732373
**
23742374
** sha3(X,SIZE)
23752375
** sha3_agg(Y,SIZE)
23762376
** sha3_query(Z,SIZE)
23772377
**
@@ -14193,11 +14193,11 @@
1419314193
/* A view. Or a trigger on a view. */
1419414194
if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
1419514195
}else{
1419614196
IdxTable *pTab;
1419714197
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
14198
- if( rc==SQLITE_OK ){
14198
+ if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){
1419914199
int i;
1420014200
char *zInner = 0;
1420114201
char *zOuter = 0;
1420214202
pTab->pNext = p->pTable;
1420314203
p->pTable = pTab;
@@ -16261,11 +16261,31 @@
1626116261
**
1626216262
** Similar compiler commands will work on different systems. The key
1626316263
** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that
1626416264
** the shell.c source file will know to include the -vfstrace command-line
1626516265
** option and (2) you must compile and link the three source files
16266
-** shell,c, test_vfstrace.c, and sqlite3.c.
16266
+** shell,c, test_vfstrace.c, and sqlite3.c.
16267
+**
16268
+** RUNTIME CONTROL OF VFSTRACE OUTPUT
16269
+**
16270
+** The application can use the "vfstrace" pragma to control which VFS
16271
+** APIs are traced. To disable all output:
16272
+**
16273
+** PRAGMA vfstrace('-all');
16274
+**
16275
+** To enable all output (which is the default setting):
16276
+**
16277
+** PRAGMA vfstrace('+all');
16278
+**
16279
+** Individual APIs can be enabled or disabled by name, with or without
16280
+** the initial "x" character. For example, to set up for tracing lock
16281
+** primatives only:
16282
+**
16283
+** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
16284
+**
16285
+** The argument to the vfstrace pragma ignores capitalization and any
16286
+** characters other than alphabetics, '+', and '-'.
1626716287
*/
1626816288
#include <stdlib.h>
1626916289
#include <string.h>
1627016290
/* #include "sqlite3.h" */
1627116291
@@ -16275,10 +16295,12 @@
1627516295
*/
1627616296
typedef struct vfstrace_info vfstrace_info;
1627716297
struct vfstrace_info {
1627816298
sqlite3_vfs *pRootVfs; /* The underlying real VFS */
1627916299
int (*xOut)(const char*, void*); /* Send output here */
16300
+ unsigned int mTrace; /* Mask of interfaces to trace */
16301
+ u8 bOn; /* Tracing on/off */
1628016302
void *pOutArg; /* First argument to xOut */
1628116303
const char *zVfsName; /* Name of this trace-VFS */
1628216304
sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */
1628316305
};
1628416306
@@ -16291,10 +16313,42 @@
1629116313
vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */
1629216314
const char *zFName; /* Base name of the file */
1629316315
sqlite3_file *pReal; /* The real underlying file */
1629416316
};
1629516317
16318
+/*
16319
+** Bit values for vfstrace_info.mTrace.
16320
+*/
16321
+#define VTR_CLOSE 0x00000001
16322
+#define VTR_READ 0x00000002
16323
+#define VTR_WRITE 0x00000004
16324
+#define VTR_TRUNC 0x00000008
16325
+#define VTR_SYNC 0x00000010
16326
+#define VTR_FSIZE 0x00000020
16327
+#define VTR_LOCK 0x00000040
16328
+#define VTR_UNLOCK 0x00000080
16329
+#define VTR_CRL 0x00000100
16330
+#define VTR_FCTRL 0x00000200
16331
+#define VTR_SECSZ 0x00000400
16332
+#define VTR_DEVCHAR 0x00000800
16333
+#define VTR_SHMLOCK 0x00001000
16334
+#define VTR_SHMMAP 0x00002000
16335
+#define VTR_SHMBAR 0x00004000
16336
+#define VTR_SHMUNMAP 0x00008000
16337
+#define VTR_OPEN 0x00010000
16338
+#define VTR_DELETE 0x00020000
16339
+#define VTR_ACCESS 0x00040000
16340
+#define VTR_FULLPATH 0x00080000
16341
+#define VTR_DLOPEN 0x00100000
16342
+#define VTR_DLERR 0x00200000
16343
+#define VTR_DLSYM 0x00400000
16344
+#define VTR_DLCLOSE 0x00800000
16345
+#define VTR_RAND 0x01000000
16346
+#define VTR_SLEEP 0x02000000
16347
+#define VTR_CURTIME 0x04000000
16348
+#define VTR_LASTERR 0x08000000
16349
+
1629616350
/*
1629716351
** Method declarations for vfstrace_file.
1629816352
*/
1629916353
static int vfstraceClose(sqlite3_file*);
1630016354
static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
@@ -16355,15 +16409,17 @@
1635516409
const char *zFormat,
1635616410
...
1635716411
){
1635816412
va_list ap;
1635916413
char *zMsg;
16360
- va_start(ap, zFormat);
16361
- zMsg = sqlite3_vmprintf(zFormat, ap);
16362
- va_end(ap);
16363
- pInfo->xOut(zMsg, pInfo->pOutArg);
16364
- sqlite3_free(zMsg);
16414
+ if( pInfo->bOn ){
16415
+ va_start(ap, zFormat);
16416
+ zMsg = sqlite3_vmprintf(zFormat, ap);
16417
+ va_end(ap);
16418
+ pInfo->xOut(zMsg, pInfo->pOutArg);
16419
+ sqlite3_free(zMsg);
16420
+ }
1636516421
}
1636616422
1636716423
/*
1636816424
** Try to convert an error code into a symbolic name for that error code.
1636916425
*/
@@ -16457,18 +16513,26 @@
1645716513
int i = *pI;
1645816514
while( zAppend[0] ){ z[i++] = *(zAppend++); }
1645916515
z[i] = 0;
1646016516
*pI = i;
1646116517
}
16518
+
16519
+/*
16520
+** Turn tracing output on or off according to mMask.
16521
+*/
16522
+static void vfstraceOnOff(vfstrace_info *pInfo, unsigned int mMask){
16523
+ pInfo->bOn = (pInfo->mTrace & mMask)!=0;
16524
+}
1646216525
1646316526
/*
1646416527
** Close an vfstrace-file.
1646516528
*/
1646616529
static int vfstraceClose(sqlite3_file *pFile){
1646716530
vfstrace_file *p = (vfstrace_file *)pFile;
1646816531
vfstrace_info *pInfo = p->pInfo;
1646916532
int rc;
16533
+ vfstraceOnOff(pInfo, VTR_CLOSE);
1647016534
vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName);
1647116535
rc = p->pReal->pMethods->xClose(p->pReal);
1647216536
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1647316537
if( rc==SQLITE_OK ){
1647416538
sqlite3_free((void*)p->base.pMethods);
@@ -16487,10 +16551,11 @@
1648716551
sqlite_int64 iOfst
1648816552
){
1648916553
vfstrace_file *p = (vfstrace_file *)pFile;
1649016554
vfstrace_info *pInfo = p->pInfo;
1649116555
int rc;
16556
+ vfstraceOnOff(pInfo, VTR_READ);
1649216557
vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)",
1649316558
pInfo->zVfsName, p->zFName, iAmt, iOfst);
1649416559
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
1649516560
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1649616561
return rc;
@@ -16506,10 +16571,11 @@
1650616571
sqlite_int64 iOfst
1650716572
){
1650816573
vfstrace_file *p = (vfstrace_file *)pFile;
1650916574
vfstrace_info *pInfo = p->pInfo;
1651016575
int rc;
16576
+ vfstraceOnOff(pInfo, VTR_WRITE);
1651116577
vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)",
1651216578
pInfo->zVfsName, p->zFName, iAmt, iOfst);
1651316579
rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
1651416580
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1651516581
return rc;
@@ -16520,10 +16586,11 @@
1652016586
*/
1652116587
static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){
1652216588
vfstrace_file *p = (vfstrace_file *)pFile;
1652316589
vfstrace_info *pInfo = p->pInfo;
1652416590
int rc;
16591
+ vfstraceOnOff(pInfo, VTR_TRUNC);
1652516592
vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName,
1652616593
size);
1652716594
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
1652816595
vfstrace_printf(pInfo, " -> %d\n", rc);
1652916596
return rc;
@@ -16544,10 +16611,11 @@
1654416611
else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL");
1654516612
if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY");
1654616613
if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){
1654716614
sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags);
1654816615
}
16616
+ vfstraceOnOff(pInfo, VTR_SYNC);
1654916617
vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName,
1655016618
&zBuf[1]);
1655116619
rc = p->pReal->pMethods->xSync(p->pReal, flags);
1655216620
vfstrace_printf(pInfo, " -> %d\n", rc);
1655316621
return rc;
@@ -16558,10 +16626,11 @@
1655816626
*/
1655916627
static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
1656016628
vfstrace_file *p = (vfstrace_file *)pFile;
1656116629
vfstrace_info *pInfo = p->pInfo;
1656216630
int rc;
16631
+ vfstraceOnOff(pInfo, VTR_FSIZE);
1656316632
vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName);
1656416633
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
1656516634
vfstrace_print_errcode(pInfo, " -> %s,", rc);
1656616635
vfstrace_printf(pInfo, " size=%lld\n", *pSize);
1656716636
return rc;
@@ -16586,10 +16655,11 @@
1658616655
*/
1658716656
static int vfstraceLock(sqlite3_file *pFile, int eLock){
1658816657
vfstrace_file *p = (vfstrace_file *)pFile;
1658916658
vfstrace_info *pInfo = p->pInfo;
1659016659
int rc;
16660
+ vfstraceOnOff(pInfo, VTR_LOCK);
1659116661
vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName,
1659216662
lockName(eLock));
1659316663
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
1659416664
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1659516665
return rc;
@@ -16600,10 +16670,11 @@
1660016670
*/
1660116671
static int vfstraceUnlock(sqlite3_file *pFile, int eLock){
1660216672
vfstrace_file *p = (vfstrace_file *)pFile;
1660316673
vfstrace_info *pInfo = p->pInfo;
1660416674
int rc;
16675
+ vfstraceOnOff(pInfo, VTR_UNLOCK);
1660516676
vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName,
1660616677
lockName(eLock));
1660716678
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
1660816679
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1660916680
return rc;
@@ -16614,10 +16685,11 @@
1661416685
*/
1661516686
static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){
1661616687
vfstrace_file *p = (vfstrace_file *)pFile;
1661716688
vfstrace_info *pInfo = p->pInfo;
1661816689
int rc;
16690
+ vfstraceOnOff(pInfo, VTR_CRL);
1661916691
vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)",
1662016692
pInfo->zVfsName, p->zFName);
1662116693
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
1662216694
vfstrace_print_errcode(pInfo, " -> %s", rc);
1662316695
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
@@ -16633,10 +16705,11 @@
1663316705
int rc;
1663416706
char zBuf[100];
1663516707
char zBuf2[100];
1663616708
char *zOp;
1663716709
char *zRVal = 0;
16710
+ vfstraceOnOff(pInfo, VTR_FCTRL);
1663816711
switch( op ){
1663916712
case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
1664016713
case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
1664116714
case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
1664216715
case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
@@ -16661,10 +16734,83 @@
1666116734
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
1666216735
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
1666316736
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
1666416737
case SQLITE_FCNTL_PRAGMA: {
1666516738
const char *const* a = (const char*const*)pArg;
16739
+ if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
16740
+ const u8 *zArg = (const u8*)a[2];
16741
+ if( zArg[0]>='0' && zArg[0]<=9 ){
16742
+ pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
16743
+ }else{
16744
+ static const struct {
16745
+ const char *z;
16746
+ unsigned int m;
16747
+ } aKw[] = {
16748
+ { "all", 0xffffffff },
16749
+ { "close", VTR_CLOSE },
16750
+ { "read", VTR_READ },
16751
+ { "write", VTR_WRITE },
16752
+ { "truncate", VTR_TRUNC },
16753
+ { "sync", VTR_SYNC },
16754
+ { "filesize", VTR_FSIZE },
16755
+ { "lock", VTR_LOCK },
16756
+ { "unlock", VTR_UNLOCK },
16757
+ { "checkreservedlock", VTR_CRL },
16758
+ { "filecontrol", VTR_FCTRL },
16759
+ { "sectorsize", VTR_SECSZ },
16760
+ { "devicecharacteristics", VTR_DEVCHAR },
16761
+ { "shmlock", VTR_SHMLOCK },
16762
+ { "shmmap", VTR_SHMMAP },
16763
+ { "shmummap", VTR_SHMUNMAP },
16764
+ { "shmbarrier", VTR_SHMBAR },
16765
+ { "open", VTR_OPEN },
16766
+ { "delete", VTR_DELETE },
16767
+ { "access", VTR_ACCESS },
16768
+ { "fullpathname", VTR_FULLPATH },
16769
+ { "dlopen", VTR_DLOPEN },
16770
+ { "dlerror", VTR_DLERR },
16771
+ { "dlsym", VTR_DLSYM },
16772
+ { "dlclose", VTR_DLCLOSE },
16773
+ { "randomness", VTR_RAND },
16774
+ { "sleep", VTR_SLEEP },
16775
+ { "currenttime", VTR_CURTIME },
16776
+ { "currenttimeint64", VTR_CURTIME },
16777
+ { "getlasterror", VTR_LASTERR },
16778
+ };
16779
+ int onOff = 1;
16780
+ while( zArg[0] ){
16781
+ int jj, n;
16782
+ while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+'
16783
+ && !isalpha(zArg[0]) ) zArg++;
16784
+ if( zArg[0]==0 ) break;
16785
+ if( zArg[0]=='-' ){
16786
+ onOff = 0;
16787
+ zArg++;
16788
+ }else if( zArg[0]=='+' ){
16789
+ onOff = 1;
16790
+ zArg++;
16791
+ }
16792
+ while( !isalpha(zArg[0]) ){
16793
+ if( zArg[0]==0 ) break;
16794
+ zArg++;
16795
+ }
16796
+ if( zArg[0]=='x' && isalpha(zArg[1]) ) zArg++;
16797
+ for(n=0; isalpha(zArg[n]); n++){}
16798
+ for(jj=0; jj<(int)(sizeof(aKw)/sizeof(aKw[0])); jj++){
16799
+ if( sqlite3_strnicmp(aKw[jj].z,(const char*)zArg,n)==0 ){
16800
+ if( onOff ){
16801
+ pInfo->mTrace |= aKw[jj].m;
16802
+ }else{
16803
+ pInfo->mTrace &= ~aKw[jj].m;
16804
+ }
16805
+ break;
16806
+ }
16807
+ }
16808
+ zArg += n;
16809
+ }
16810
+ }
16811
+ }
1666616812
sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
1666716813
zOp = zBuf;
1666816814
break;
1666916815
}
1667016816
case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break;
@@ -16756,10 +16902,11 @@
1675616902
*/
1675716903
static int vfstraceSectorSize(sqlite3_file *pFile){
1675816904
vfstrace_file *p = (vfstrace_file *)pFile;
1675916905
vfstrace_info *pInfo = p->pInfo;
1676016906
int rc;
16907
+ vfstraceOnOff(pInfo, VTR_SECSZ);
1676116908
vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName);
1676216909
rc = p->pReal->pMethods->xSectorSize(p->pReal);
1676316910
vfstrace_printf(pInfo, " -> %d\n", rc);
1676416911
return rc;
1676516912
}
@@ -16769,10 +16916,11 @@
1676916916
*/
1677016917
static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){
1677116918
vfstrace_file *p = (vfstrace_file *)pFile;
1677216919
vfstrace_info *pInfo = p->pInfo;
1677316920
int rc;
16921
+ vfstraceOnOff(pInfo, VTR_DEVCHAR);
1677416922
vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)",
1677516923
pInfo->zVfsName, p->zFName);
1677616924
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
1677716925
vfstrace_printf(pInfo, " -> 0x%08x\n", rc);
1677816926
return rc;
@@ -16795,19 +16943,20 @@
1679516943
vfstrace_file *p = (vfstrace_file *)pFile;
1679616944
vfstrace_info *pInfo = p->pInfo;
1679716945
int rc;
1679816946
char zLck[100];
1679916947
int i = 0;
16948
+ vfstraceOnOff(pInfo, VTR_SHMLOCK);
1680016949
memcpy(zLck, "|0", 3);
1680116950
if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK");
1680216951
if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK");
1680316952
if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED");
1680416953
if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE");
1680516954
if( flags & ~(0xf) ){
1680616955
sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags);
1680716956
}
16808
- if( ofst>=0 && ofst<sizeof(azLockName)/sizeof(azLockName[0]) ){
16957
+ if( ofst>=0 && ofst<(int)(sizeof(azLockName)/sizeof(azLockName[0])) ){
1680916958
vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d(%s),n=%d,%s)",
1681016959
pInfo->zVfsName, p->zFName, ofst, azLockName[ofst],
1681116960
n, &zLck[1]);
1681216961
}else{
1681316962
vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=5d,n=%d,%s)",
@@ -16826,26 +16975,29 @@
1682616975
void volatile **pp
1682716976
){
1682816977
vfstrace_file *p = (vfstrace_file *)pFile;
1682916978
vfstrace_info *pInfo = p->pInfo;
1683016979
int rc;
16980
+ vfstraceOnOff(pInfo, VTR_SHMMAP);
1683116981
vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)",
1683216982
pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite);
1683316983
rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
1683416984
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1683516985
return rc;
1683616986
}
1683716987
static void vfstraceShmBarrier(sqlite3_file *pFile){
1683816988
vfstrace_file *p = (vfstrace_file *)pFile;
1683916989
vfstrace_info *pInfo = p->pInfo;
16990
+ vfstraceOnOff(pInfo, VTR_SHMBAR);
1684016991
vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName);
1684116992
p->pReal->pMethods->xShmBarrier(p->pReal);
1684216993
}
1684316994
static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){
1684416995
vfstrace_file *p = (vfstrace_file *)pFile;
1684516996
vfstrace_info *pInfo = p->pInfo;
1684616997
int rc;
16998
+ vfstraceOnOff(pInfo, VTR_SHMUNMAP);
1684716999
vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
1684817000
pInfo->zVfsName, p->zFName, delFlag);
1684917001
rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
1685017002
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1685117003
return rc;
@@ -16869,10 +17021,11 @@
1686917021
sqlite3_vfs *pRoot = pInfo->pRootVfs;
1687017022
p->pInfo = pInfo;
1687117023
p->zFName = zName ? fileTail(zName) : "<temp>";
1687217024
p->pReal = (sqlite3_file *)&p[1];
1687317025
rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags);
17026
+ vfstraceOnOff(pInfo, VTR_OPEN);
1687417027
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
1687517028
pInfo->zVfsName, p->zFName, flags);
1687617029
if( p->pReal->pMethods ){
1687717030
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
1687817031
const sqlite3_io_methods *pSub = p->pReal->pMethods;
@@ -16914,10 +17067,11 @@
1691417067
*/
1691517068
static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
1691617069
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1691717070
sqlite3_vfs *pRoot = pInfo->pRootVfs;
1691817071
int rc;
17072
+ vfstraceOnOff(pInfo, VTR_DELETE);
1691917073
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
1692017074
pInfo->zVfsName, zPath, dirSync);
1692117075
rc = pRoot->xDelete(pRoot, zPath, dirSync);
1692217076
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
1692317077
return rc;
@@ -16934,10 +17088,11 @@
1693417088
int *pResOut
1693517089
){
1693617090
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1693717091
sqlite3_vfs *pRoot = pInfo->pRootVfs;
1693817092
int rc;
17093
+ vfstraceOnOff(pInfo, VTR_ACCESS);
1693917094
vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
1694017095
pInfo->zVfsName, zPath, flags);
1694117096
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
1694217097
vfstrace_print_errcode(pInfo, " -> %s", rc);
1694317098
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
@@ -16956,10 +17111,11 @@
1695617111
char *zOut
1695717112
){
1695817113
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1695917114
sqlite3_vfs *pRoot = pInfo->pRootVfs;
1696017115
int rc;
17116
+ vfstraceOnOff(pInfo, VTR_FULLPATH);
1696117117
vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")",
1696217118
pInfo->zVfsName, zPath);
1696317119
rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut);
1696417120
vfstrace_print_errcode(pInfo, " -> %s", rc);
1696517121
vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut);
@@ -16970,10 +17126,11 @@
1697017126
** Open the dynamic library located at zPath and return a handle.
1697117127
*/
1697217128
static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){
1697317129
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1697417130
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17131
+ vfstraceOnOff(pInfo, VTR_DLOPEN);
1697517132
vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath);
1697617133
return pRoot->xDlOpen(pRoot, zPath);
1697717134
}
1697817135
1697917136
/*
@@ -16982,10 +17139,11 @@
1698217139
** with dynamic libraries.
1698317140
*/
1698417141
static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
1698517142
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1698617143
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17144
+ vfstraceOnOff(pInfo, VTR_DLERR);
1698717145
vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte);
1698817146
pRoot->xDlError(pRoot, nByte, zErrMsg);
1698917147
vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg);
1699017148
}
1699117149
@@ -17003,10 +17161,11 @@
1700317161
** Close the dynamic library handle pHandle.
1700417162
*/
1700517163
static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
1700617164
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1700717165
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17166
+ vfstraceOnOff(pInfo, VTR_DLCLOSE);
1700817167
vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
1700917168
pRoot->xDlClose(pRoot, pHandle);
1701017169
}
1701117170
1701217171
/*
@@ -17014,10 +17173,11 @@
1701417173
** random data.
1701517174
*/
1701617175
static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
1701717176
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1701817177
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17178
+ vfstraceOnOff(pInfo, VTR_RAND);
1701917179
vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte);
1702017180
return pRoot->xRandomness(pRoot, nByte, zBufOut);
1702117181
}
1702217182
1702317183
/*
@@ -17025,34 +17185,52 @@
1702517185
** actually slept.
1702617186
*/
1702717187
static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){
1702817188
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1702917189
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17190
+ vfstraceOnOff(pInfo, VTR_SLEEP);
17191
+ vfstrace_printf(pInfo, "%s.xSleep(%d)\n", pInfo->zVfsName, nMicro);
1703017192
return pRoot->xSleep(pRoot, nMicro);
1703117193
}
1703217194
1703317195
/*
1703417196
** Return the current time as a Julian Day number in *pTimeOut.
1703517197
*/
1703617198
static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
1703717199
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1703817200
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17039
- return pRoot->xCurrentTime(pRoot, pTimeOut);
17201
+ int rc;
17202
+ vfstraceOnOff(pInfo, VTR_CURTIME);
17203
+ vfstrace_printf(pInfo, "%s.xCurrentTime()", pInfo->zVfsName);
17204
+ rc = pRoot->xCurrentTime(pRoot, pTimeOut);
17205
+ vfstrace_printf(pInfo, " -> %.17g\n", *pTimeOut);
17206
+ return rc;
1704017207
}
1704117208
static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
1704217209
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1704317210
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17044
- return pRoot->xCurrentTimeInt64(pRoot, pTimeOut);
17211
+ int rc;
17212
+ vfstraceOnOff(pInfo, VTR_CURTIME);
17213
+ vfstrace_printf(pInfo, "%s.xCurrentTimeInt64()", pInfo->zVfsName);
17214
+ rc = pRoot->xCurrentTimeInt64(pRoot, pTimeOut);
17215
+ vfstrace_printf(pInfo, " -> %lld\n", *pTimeOut);
17216
+ return rc;
1704517217
}
1704617218
1704717219
/*
17048
-** Return th3 most recent error code and message
17220
+** Return the most recent error code and message
1704917221
*/
17050
-static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
17222
+static int vfstraceGetLastError(sqlite3_vfs *pVfs, int nErr, char *zErr){
1705117223
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
1705217224
sqlite3_vfs *pRoot = pInfo->pRootVfs;
17053
- return pRoot->xGetLastError(pRoot, iErr, zErr);
17225
+ int rc;
17226
+ vfstraceOnOff(pInfo, VTR_LASTERR);
17227
+ vfstrace_printf(pInfo, "%s.xGetLastError(%d,zBuf)", pInfo->zVfsName, nErr);
17228
+ if( nErr ) zErr[0] = 0;
17229
+ rc = pRoot->xGetLastError(pRoot, nErr, zErr);
17230
+ vfstrace_printf(pInfo, " -> zBuf[] = \"%s\", rc = %d\n", nErr?zErr:"", rc);
17231
+ return rc;
1705417232
}
1705517233
1705617234
/*
1705717235
** Override system calls.
1705817236
*/
@@ -17142,10 +17320,12 @@
1714217320
pInfo->pRootVfs = pRoot;
1714317321
pInfo->xOut = xOut;
1714417322
pInfo->pOutArg = pOutArg;
1714517323
pInfo->zVfsName = pNew->zName;
1714617324
pInfo->pTraceVfs = pNew;
17325
+ pInfo->mTrace = 0xffffffff;
17326
+ pInfo->bOn = 1;
1714717327
vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n",
1714817328
pInfo->zVfsName, pRoot->zName);
1714917329
return sqlite3_vfs_register(pNew, makeDefault);
1715017330
}
1715117331
@@ -26444,18 +26624,24 @@
2644426624
#endif
2644526625
2644626626
/*
2644726627
** Run an SQL command and return the single integer result.
2644826628
*/
26449
-static int db_int(sqlite3 *db, const char *zSql){
26629
+static int db_int(sqlite3 *db, const char *zSql, ...){
2645026630
sqlite3_stmt *pStmt;
2645126631
int res = 0;
26452
- sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
26632
+ char *z;
26633
+ va_list ap;
26634
+ va_start(ap, zSql);
26635
+ z = sqlite3_vmprintf(zSql, ap);
26636
+ va_end(ap);
26637
+ sqlite3_prepare_v2(db, z, -1, &pStmt, 0);
2645326638
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
2645426639
res = sqlite3_column_int(pStmt,0);
2645526640
}
2645626641
sqlite3_finalize(pStmt);
26642
+ sqlite3_free(z);
2645726643
return res;
2645826644
}
2645926645
2646026646
#if SQLITE_SHELL_HAVE_RECOVER
2646126647
/*
@@ -26554,13 +26740,11 @@
2655426740
zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
2655526741
}else{
2655626742
zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
2655726743
}
2655826744
for(i=0; i<ArraySize(aQuery); i++){
26559
- char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
26560
- int val = db_int(p->db, zSql);
26561
- sqlite3_free(zSql);
26745
+ int val = db_int(p->db, aQuery[i].zSql, zSchemaTab);
2656226746
sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
2656326747
}
2656426748
sqlite3_free(zSchemaTab);
2656526749
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
2656626750
sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion);
@@ -26575,16 +26759,17 @@
2657526759
*/
2657626760
static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
2657726761
sqlite3_stmt *pStmt = 0;
2657826762
sqlite3_int64 nPage = 0;
2657926763
int pgSz = 0;
26580
- const char *zFilename;
2658126764
const char *zTail;
2658226765
char *zName = 0;
2658326766
int rc, i, j;
2658426767
unsigned char bShow[256]; /* Characters ok to display */
2658526768
26769
+ UNUSED_PARAMETER(nArg);
26770
+ UNUSED_PARAMETER(azArg);
2658626771
memset(bShow, '.', sizeof(bShow));
2658726772
for(i=' '; i<='~'; i++){
2658826773
if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
2658926774
}
2659026775
rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0);
@@ -26603,21 +26788,19 @@
2660326788
sqlite3_finalize(pStmt);
2660426789
pStmt = 0;
2660526790
if( nPage<1 ) goto dbtotxt_error;
2660626791
rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
2660726792
if( rc ) goto dbtotxt_error;
26608
- rc = 0;
2660926793
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
26610
- zTail = zFilename = "unk.db";
26794
+ zTail = "unk.db";
2661126795
}else{
26612
- zFilename = (const char*)sqlite3_column_text(pStmt, 2);
26796
+ const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
2661326797
if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
2661426798
zTail = strrchr(zFilename, '/');
2661526799
#if defined(_WIN32)
2661626800
if( zTail==0 ) zTail = strrchr(zFilename, '\\');
2661726801
#endif
26618
- if( zTail ) zFilename = zTail;
2661926802
}
2662026803
zName = strdup(zTail);
2662126804
shell_check_oom(zName);
2662226805
sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
2662326806
nPage*pgSz, pgSz, zName);
@@ -26624,11 +26807,10 @@
2662426807
sqlite3_finalize(pStmt);
2662526808
pStmt = 0;
2662626809
rc = sqlite3_prepare_v2(p->db,
2662726810
"SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0);
2662826811
if( rc ) goto dbtotxt_error;
26629
- rc = 0;
2663026812
while( sqlite3_step(pStmt)==SQLITE_ROW ){
2663126813
sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0);
2663226814
const u8 *aData = sqlite3_column_blob(pStmt, 1);
2663326815
int seenPageLabel = 0;
2663426816
for(i=0; i<pgSz; i+=16){
@@ -28216,12 +28398,12 @@
2821628398
}else if( *pDb==0 ){
2821728399
return 0;
2821828400
}else{
2821928401
/* Formulate the columns spec, close the DB, zero *pDb. */
2822028402
char *zColsSpec = 0;
28221
- int hasDupes = db_int(*pDb, zHasDupes);
28222
- int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0;
28403
+ int hasDupes = db_int(*pDb, "%s", zHasDupes);
28404
+ int nDigits = (hasDupes)? db_int(*pDb, "%s", zColDigits) : 0;
2822328405
if( hasDupes ){
2822428406
#ifdef SHELL_COLUMN_RENAME_CLEAN
2822528407
rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
2822628408
rc_err_oom_die(rc);
2822728409
#endif
@@ -28232,11 +28414,11 @@
2823228414
sqlite3_bind_int(pStmt, 1, nDigits);
2823328415
rc = sqlite3_step(pStmt);
2823428416
sqlite3_finalize(pStmt);
2823528417
if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM);
2823628418
}
28237
- assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
28419
+ assert(db_int(*pDb, "%s", zHasDupes)==0); /* Consider: remove this */
2823828420
rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
2823928421
rc_err_oom_die(rc);
2824028422
rc = sqlite3_step(pStmt);
2824128423
if( rc==SQLITE_ROW ){
2824228424
zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
@@ -29282,11 +29464,15 @@
2928229464
/* Below, resources must be freed before exit. */
2928329465
while( (nSkip--)>0 ){
2928429466
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
2928529467
}
2928629468
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
29287
- if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){
29469
+ if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
29470
+ && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
29471
+ " WHERE name=%Q AND type='view'",
29472
+ zSchema ? zSchema : "main", zTable)
29473
+ ){
2928829474
/* Table does not exist. Create it. */
2928929475
sqlite3 *dbCols = 0;
2929029476
char *zRenames = 0;
2929129477
char *zColDefs;
2929229478
zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
@@ -30354,11 +30540,14 @@
3035430540
}
3035530541
close_db(pSrc);
3035630542
}else
3035730543
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
3035830544
30359
- if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){
30545
+ if( c=='s' &&
30546
+ (cli_strncmp(azArg[0], "scanstats", n)==0 ||
30547
+ cli_strncmp(azArg[0], "scanstatus", n)==0)
30548
+ ){
3036030549
if( nArg==2 ){
3036130550
if( cli_strcmp(azArg[1], "vm")==0 ){
3036230551
p->scanstatsOn = 3;
3036330552
}else
3036430553
if( cli_strcmp(azArg[1], "est")==0 ){
@@ -31409,11 +31598,13 @@
3140931598
{ 0x10000000, 1, "OrderBySubq" },
3141031599
{ 0xffffffff, 0, "All" },
3141131600
};
3141231601
unsigned int curOpt;
3141331602
unsigned int newOpt;
31603
+ unsigned int m;
3141431604
int ii;
31605
+ int nOff;
3141531606
sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
3141631607
newOpt = curOpt;
3141731608
for(ii=2; ii<nArg; ii++){
3141831609
const char *z = azArg[ii];
3141931610
int useLabel = 0;
@@ -31450,28 +31641,32 @@
3145031641
}
3145131642
}
3145231643
}
3145331644
if( curOpt!=newOpt ){
3145431645
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
31455
- }else if( nArg<3 ){
31456
- curOpt = ~newOpt;
31646
+ }
31647
+ for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){
31648
+ if( m & newOpt ) nOff++;
3145731649
}
31458
- if( newOpt==0 ){
31459
- sqlite3_fputs("+All\n", p->out);
31460
- }else if( newOpt==0xffffffff ){
31461
- sqlite3_fputs("-All\n", p->out);
31650
+ if( nOff<12 ){
31651
+ sqlite3_fputs("+All", p->out);
31652
+ for(ii=0; ii<ArraySize(aLabel); ii++){
31653
+ if( !aLabel[ii].bDsply ) continue;
31654
+ if( (newOpt & aLabel[ii].mask)!=0 ){
31655
+ sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel);
31656
+ }
31657
+ }
3146231658
}else{
31463
- int jj;
31464
- for(jj=0; jj<ArraySize(aLabel); jj++){
31465
- unsigned int m = aLabel[jj].mask;
31466
- if( !aLabel[jj].bDsply ) continue;
31467
- if( (curOpt&m)!=(newOpt&m) ){
31468
- sqlite3_fprintf(p->out, "%c%s\n", (newOpt & m)==0 ? '+' : '-',
31469
- aLabel[jj].zLabel);
31659
+ sqlite3_fputs("-All", p->out);
31660
+ for(ii=0; ii<ArraySize(aLabel); ii++){
31661
+ if( !aLabel[ii].bDsply ) continue;
31662
+ if( (newOpt & aLabel[ii].mask)==0 ){
31663
+ sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel);
3147031664
}
3147131665
}
3147231666
}
31667
+ sqlite3_fputs("\n", p->out);
3147331668
rc2 = isOk = 3;
3147431669
break;
3147531670
}
3147631671
3147731672
/* sqlite3_test_control(int, db, int) */
@@ -31928,11 +32123,10 @@
3192832123
SCAN_TRACKER_REFTYPE pst){
3192932124
char cin;
3193032125
char cWait = (char)qss; /* intentional narrowing loss */
3193132126
if( cWait==0 ){
3193232127
PlainScan:
31933
- assert( cWait==0 );
3193432128
while( (cin = *zLine++)!=0 ){
3193532129
if( IsSpace(cin) )
3193632130
continue;
3193732131
switch (cin){
3193832132
case '-':
@@ -31980,11 +32174,10 @@
3198032174
switch( cWait ){
3198132175
case '*':
3198232176
if( *zLine != '/' )
3198332177
continue;
3198432178
++zLine;
31985
- cWait = 0;
3198632179
CONTINUE_PROMPT_AWAITC(pst, 0);
3198732180
qss = QSS_SETV(qss, 0);
3198832181
goto PlainScan;
3198932182
case '`': case '\'': case '"':
3199032183
if(*zLine==cWait){
@@ -31992,11 +32185,10 @@
3199232185
++zLine;
3199332186
continue;
3199432187
}
3199532188
deliberate_fall_through;
3199632189
case ']':
31997
- cWait = 0;
3199832190
CONTINUE_PROMPT_AWAITC(pst, 0);
3199932191
qss = QSS_SETV(qss, 0);
3200032192
goto PlainScan;
3200132193
default: assert(0);
3200232194
}
@@ -32180,11 +32372,14 @@
3218032372
if( doAutoDetectRestore(p, zSql) ) return 1;
3218132373
return 0;
3218232374
}
3218332375
3218432376
static void echo_group_input(ShellState *p, const char *zDo){
32185
- if( ShellHasFlag(p, SHFLG_Echo) ) sqlite3_fprintf(p->out, "%s\n", zDo);
32377
+ if( ShellHasFlag(p, SHFLG_Echo) ){
32378
+ sqlite3_fprintf(p->out, "%s\n", zDo);
32379
+ fflush(p->out);
32380
+ }
3218632381
}
3218732382
3218832383
#ifdef SQLITE_SHELL_FIDDLE
3218932384
/*
3219032385
** Alternate one_input_line() impl for wasm mode. This is not in the primary
@@ -32640,10 +32835,19 @@
3264032835
}
3264132836
3264232837
static void sayAbnormalExit(void){
3264332838
if( seenInterrupt ) eputz("Program interrupted.\n");
3264432839
}
32840
+
32841
+/* Routine to output from vfstrace
32842
+*/
32843
+static int vfstraceOut(const char *z, void *pArg){
32844
+ ShellState *p = (ShellState*)pArg;
32845
+ sqlite3_fputs(z, p->out);
32846
+ fflush(p->out);
32847
+ return 1;
32848
+}
3264532849
3264632850
#ifndef SQLITE_SHELL_IS_UTF8
3264732851
# if (defined(_WIN32) || defined(WIN32)) \
3264832852
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
3264932853
# define SQLITE_SHELL_IS_UTF8 (0)
@@ -32877,12 +33081,10 @@
3287733081
case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
3287833082
case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
3287933083
default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
3288033084
}
3288133085
}else if( cli_strcmp(z,"-vfstrace")==0 ){
32882
- vfstrace_register("trace",0,(int(*)(const char*,void*))sqlite3_fputs,
32883
- stderr,1);
3288433086
bEnableVfstrace = 1;
3288533087
#ifdef SQLITE_ENABLE_MULTIPLEX
3288633088
}else if( cli_strcmp(z,"-multiplex")==0 ){
3288733089
extern int sqlite3_multiplex_initialize(const char*,int);
3288833090
sqlite3_multiplex_initialize(0, 1);
@@ -32975,10 +33177,13 @@
3297533177
"%s: Error: no database filename specified\n", Argv0);
3297633178
return 1;
3297733179
#endif
3297833180
}
3297933181
data.out = stdout;
33182
+ if( bEnableVfstrace ){
33183
+ vfstrace_register("trace",0,vfstraceOut, &data, 1);
33184
+ }
3298033185
#ifndef SQLITE_SHELL_FIDDLE
3298133186
sqlite3_appendvfs_init(0,0,0);
3298233187
#endif
3298333188
3298433189
/* Go ahead and open the database file if it already exists. If the
3298533190
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -2367,11 +2367,11 @@
2367 **
2368 ******************************************************************************
2369 **
2370 ** This SQLite extension implements functions that compute SHA3 hashes
2371 ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard.
2372 ** Two SQL functions are implemented:
2373 **
2374 ** sha3(X,SIZE)
2375 ** sha3_agg(Y,SIZE)
2376 ** sha3_query(Z,SIZE)
2377 **
@@ -14193,11 +14193,11 @@
14193 /* A view. Or a trigger on a view. */
14194 if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
14195 }else{
14196 IdxTable *pTab;
14197 rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
14198 if( rc==SQLITE_OK ){
14199 int i;
14200 char *zInner = 0;
14201 char *zOuter = 0;
14202 pTab->pNext = p->pTable;
14203 p->pTable = pTab;
@@ -16261,11 +16261,31 @@
16261 **
16262 ** Similar compiler commands will work on different systems. The key
16263 ** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that
16264 ** the shell.c source file will know to include the -vfstrace command-line
16265 ** option and (2) you must compile and link the three source files
16266 ** shell,c, test_vfstrace.c, and sqlite3.c.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16267 */
16268 #include <stdlib.h>
16269 #include <string.h>
16270 /* #include "sqlite3.h" */
16271
@@ -16275,10 +16295,12 @@
16275 */
16276 typedef struct vfstrace_info vfstrace_info;
16277 struct vfstrace_info {
16278 sqlite3_vfs *pRootVfs; /* The underlying real VFS */
16279 int (*xOut)(const char*, void*); /* Send output here */
 
 
16280 void *pOutArg; /* First argument to xOut */
16281 const char *zVfsName; /* Name of this trace-VFS */
16282 sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */
16283 };
16284
@@ -16291,10 +16313,42 @@
16291 vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */
16292 const char *zFName; /* Base name of the file */
16293 sqlite3_file *pReal; /* The real underlying file */
16294 };
16295
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16296 /*
16297 ** Method declarations for vfstrace_file.
16298 */
16299 static int vfstraceClose(sqlite3_file*);
16300 static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
@@ -16355,15 +16409,17 @@
16355 const char *zFormat,
16356 ...
16357 ){
16358 va_list ap;
16359 char *zMsg;
16360 va_start(ap, zFormat);
16361 zMsg = sqlite3_vmprintf(zFormat, ap);
16362 va_end(ap);
16363 pInfo->xOut(zMsg, pInfo->pOutArg);
16364 sqlite3_free(zMsg);
 
 
16365 }
16366
16367 /*
16368 ** Try to convert an error code into a symbolic name for that error code.
16369 */
@@ -16457,18 +16513,26 @@
16457 int i = *pI;
16458 while( zAppend[0] ){ z[i++] = *(zAppend++); }
16459 z[i] = 0;
16460 *pI = i;
16461 }
 
 
 
 
 
 
 
16462
16463 /*
16464 ** Close an vfstrace-file.
16465 */
16466 static int vfstraceClose(sqlite3_file *pFile){
16467 vfstrace_file *p = (vfstrace_file *)pFile;
16468 vfstrace_info *pInfo = p->pInfo;
16469 int rc;
 
16470 vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName);
16471 rc = p->pReal->pMethods->xClose(p->pReal);
16472 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16473 if( rc==SQLITE_OK ){
16474 sqlite3_free((void*)p->base.pMethods);
@@ -16487,10 +16551,11 @@
16487 sqlite_int64 iOfst
16488 ){
16489 vfstrace_file *p = (vfstrace_file *)pFile;
16490 vfstrace_info *pInfo = p->pInfo;
16491 int rc;
 
16492 vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)",
16493 pInfo->zVfsName, p->zFName, iAmt, iOfst);
16494 rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
16495 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16496 return rc;
@@ -16506,10 +16571,11 @@
16506 sqlite_int64 iOfst
16507 ){
16508 vfstrace_file *p = (vfstrace_file *)pFile;
16509 vfstrace_info *pInfo = p->pInfo;
16510 int rc;
 
16511 vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)",
16512 pInfo->zVfsName, p->zFName, iAmt, iOfst);
16513 rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
16514 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16515 return rc;
@@ -16520,10 +16586,11 @@
16520 */
16521 static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){
16522 vfstrace_file *p = (vfstrace_file *)pFile;
16523 vfstrace_info *pInfo = p->pInfo;
16524 int rc;
 
16525 vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName,
16526 size);
16527 rc = p->pReal->pMethods->xTruncate(p->pReal, size);
16528 vfstrace_printf(pInfo, " -> %d\n", rc);
16529 return rc;
@@ -16544,10 +16611,11 @@
16544 else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL");
16545 if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY");
16546 if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){
16547 sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags);
16548 }
 
16549 vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName,
16550 &zBuf[1]);
16551 rc = p->pReal->pMethods->xSync(p->pReal, flags);
16552 vfstrace_printf(pInfo, " -> %d\n", rc);
16553 return rc;
@@ -16558,10 +16626,11 @@
16558 */
16559 static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
16560 vfstrace_file *p = (vfstrace_file *)pFile;
16561 vfstrace_info *pInfo = p->pInfo;
16562 int rc;
 
16563 vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName);
16564 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
16565 vfstrace_print_errcode(pInfo, " -> %s,", rc);
16566 vfstrace_printf(pInfo, " size=%lld\n", *pSize);
16567 return rc;
@@ -16586,10 +16655,11 @@
16586 */
16587 static int vfstraceLock(sqlite3_file *pFile, int eLock){
16588 vfstrace_file *p = (vfstrace_file *)pFile;
16589 vfstrace_info *pInfo = p->pInfo;
16590 int rc;
 
16591 vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName,
16592 lockName(eLock));
16593 rc = p->pReal->pMethods->xLock(p->pReal, eLock);
16594 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16595 return rc;
@@ -16600,10 +16670,11 @@
16600 */
16601 static int vfstraceUnlock(sqlite3_file *pFile, int eLock){
16602 vfstrace_file *p = (vfstrace_file *)pFile;
16603 vfstrace_info *pInfo = p->pInfo;
16604 int rc;
 
16605 vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName,
16606 lockName(eLock));
16607 rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
16608 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16609 return rc;
@@ -16614,10 +16685,11 @@
16614 */
16615 static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){
16616 vfstrace_file *p = (vfstrace_file *)pFile;
16617 vfstrace_info *pInfo = p->pInfo;
16618 int rc;
 
16619 vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)",
16620 pInfo->zVfsName, p->zFName);
16621 rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
16622 vfstrace_print_errcode(pInfo, " -> %s", rc);
16623 vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
@@ -16633,10 +16705,11 @@
16633 int rc;
16634 char zBuf[100];
16635 char zBuf2[100];
16636 char *zOp;
16637 char *zRVal = 0;
 
16638 switch( op ){
16639 case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
16640 case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
16641 case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
16642 case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
@@ -16661,10 +16734,83 @@
16661 case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
16662 case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
16663 case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
16664 case SQLITE_FCNTL_PRAGMA: {
16665 const char *const* a = (const char*const*)pArg;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16666 sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
16667 zOp = zBuf;
16668 break;
16669 }
16670 case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break;
@@ -16756,10 +16902,11 @@
16756 */
16757 static int vfstraceSectorSize(sqlite3_file *pFile){
16758 vfstrace_file *p = (vfstrace_file *)pFile;
16759 vfstrace_info *pInfo = p->pInfo;
16760 int rc;
 
16761 vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName);
16762 rc = p->pReal->pMethods->xSectorSize(p->pReal);
16763 vfstrace_printf(pInfo, " -> %d\n", rc);
16764 return rc;
16765 }
@@ -16769,10 +16916,11 @@
16769 */
16770 static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){
16771 vfstrace_file *p = (vfstrace_file *)pFile;
16772 vfstrace_info *pInfo = p->pInfo;
16773 int rc;
 
16774 vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)",
16775 pInfo->zVfsName, p->zFName);
16776 rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
16777 vfstrace_printf(pInfo, " -> 0x%08x\n", rc);
16778 return rc;
@@ -16795,19 +16943,20 @@
16795 vfstrace_file *p = (vfstrace_file *)pFile;
16796 vfstrace_info *pInfo = p->pInfo;
16797 int rc;
16798 char zLck[100];
16799 int i = 0;
 
16800 memcpy(zLck, "|0", 3);
16801 if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK");
16802 if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK");
16803 if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED");
16804 if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE");
16805 if( flags & ~(0xf) ){
16806 sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags);
16807 }
16808 if( ofst>=0 && ofst<sizeof(azLockName)/sizeof(azLockName[0]) ){
16809 vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d(%s),n=%d,%s)",
16810 pInfo->zVfsName, p->zFName, ofst, azLockName[ofst],
16811 n, &zLck[1]);
16812 }else{
16813 vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=5d,n=%d,%s)",
@@ -16826,26 +16975,29 @@
16826 void volatile **pp
16827 ){
16828 vfstrace_file *p = (vfstrace_file *)pFile;
16829 vfstrace_info *pInfo = p->pInfo;
16830 int rc;
 
16831 vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)",
16832 pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite);
16833 rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
16834 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16835 return rc;
16836 }
16837 static void vfstraceShmBarrier(sqlite3_file *pFile){
16838 vfstrace_file *p = (vfstrace_file *)pFile;
16839 vfstrace_info *pInfo = p->pInfo;
 
16840 vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName);
16841 p->pReal->pMethods->xShmBarrier(p->pReal);
16842 }
16843 static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){
16844 vfstrace_file *p = (vfstrace_file *)pFile;
16845 vfstrace_info *pInfo = p->pInfo;
16846 int rc;
 
16847 vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
16848 pInfo->zVfsName, p->zFName, delFlag);
16849 rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
16850 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16851 return rc;
@@ -16869,10 +17021,11 @@
16869 sqlite3_vfs *pRoot = pInfo->pRootVfs;
16870 p->pInfo = pInfo;
16871 p->zFName = zName ? fileTail(zName) : "<temp>";
16872 p->pReal = (sqlite3_file *)&p[1];
16873 rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags);
 
16874 vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
16875 pInfo->zVfsName, p->zFName, flags);
16876 if( p->pReal->pMethods ){
16877 sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
16878 const sqlite3_io_methods *pSub = p->pReal->pMethods;
@@ -16914,10 +17067,11 @@
16914 */
16915 static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
16916 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
16917 sqlite3_vfs *pRoot = pInfo->pRootVfs;
16918 int rc;
 
16919 vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
16920 pInfo->zVfsName, zPath, dirSync);
16921 rc = pRoot->xDelete(pRoot, zPath, dirSync);
16922 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16923 return rc;
@@ -16934,10 +17088,11 @@
16934 int *pResOut
16935 ){
16936 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
16937 sqlite3_vfs *pRoot = pInfo->pRootVfs;
16938 int rc;
 
16939 vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
16940 pInfo->zVfsName, zPath, flags);
16941 rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
16942 vfstrace_print_errcode(pInfo, " -> %s", rc);
16943 vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
@@ -16956,10 +17111,11 @@
16956 char *zOut
16957 ){
16958 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
16959 sqlite3_vfs *pRoot = pInfo->pRootVfs;
16960 int rc;
 
16961 vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")",
16962 pInfo->zVfsName, zPath);
16963 rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut);
16964 vfstrace_print_errcode(pInfo, " -> %s", rc);
16965 vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut);
@@ -16970,10 +17126,11 @@
16970 ** Open the dynamic library located at zPath and return a handle.
16971 */
16972 static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){
16973 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
16974 sqlite3_vfs *pRoot = pInfo->pRootVfs;
 
16975 vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath);
16976 return pRoot->xDlOpen(pRoot, zPath);
16977 }
16978
16979 /*
@@ -16982,10 +17139,11 @@
16982 ** with dynamic libraries.
16983 */
16984 static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
16985 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
16986 sqlite3_vfs *pRoot = pInfo->pRootVfs;
 
16987 vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte);
16988 pRoot->xDlError(pRoot, nByte, zErrMsg);
16989 vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg);
16990 }
16991
@@ -17003,10 +17161,11 @@
17003 ** Close the dynamic library handle pHandle.
17004 */
17005 static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
17006 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17007 sqlite3_vfs *pRoot = pInfo->pRootVfs;
 
17008 vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
17009 pRoot->xDlClose(pRoot, pHandle);
17010 }
17011
17012 /*
@@ -17014,10 +17173,11 @@
17014 ** random data.
17015 */
17016 static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
17017 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17018 sqlite3_vfs *pRoot = pInfo->pRootVfs;
 
17019 vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte);
17020 return pRoot->xRandomness(pRoot, nByte, zBufOut);
17021 }
17022
17023 /*
@@ -17025,34 +17185,52 @@
17025 ** actually slept.
17026 */
17027 static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){
17028 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17029 sqlite3_vfs *pRoot = pInfo->pRootVfs;
 
 
17030 return pRoot->xSleep(pRoot, nMicro);
17031 }
17032
17033 /*
17034 ** Return the current time as a Julian Day number in *pTimeOut.
17035 */
17036 static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
17037 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17038 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17039 return pRoot->xCurrentTime(pRoot, pTimeOut);
 
 
 
 
 
17040 }
17041 static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
17042 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17043 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17044 return pRoot->xCurrentTimeInt64(pRoot, pTimeOut);
 
 
 
 
 
17045 }
17046
17047 /*
17048 ** Return th3 most recent error code and message
17049 */
17050 static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
17051 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17052 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17053 return pRoot->xGetLastError(pRoot, iErr, zErr);
 
 
 
 
 
 
17054 }
17055
17056 /*
17057 ** Override system calls.
17058 */
@@ -17142,10 +17320,12 @@
17142 pInfo->pRootVfs = pRoot;
17143 pInfo->xOut = xOut;
17144 pInfo->pOutArg = pOutArg;
17145 pInfo->zVfsName = pNew->zName;
17146 pInfo->pTraceVfs = pNew;
 
 
17147 vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n",
17148 pInfo->zVfsName, pRoot->zName);
17149 return sqlite3_vfs_register(pNew, makeDefault);
17150 }
17151
@@ -26444,18 +26624,24 @@
26444 #endif
26445
26446 /*
26447 ** Run an SQL command and return the single integer result.
26448 */
26449 static int db_int(sqlite3 *db, const char *zSql){
26450 sqlite3_stmt *pStmt;
26451 int res = 0;
26452 sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
 
 
 
 
 
26453 if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
26454 res = sqlite3_column_int(pStmt,0);
26455 }
26456 sqlite3_finalize(pStmt);
 
26457 return res;
26458 }
26459
26460 #if SQLITE_SHELL_HAVE_RECOVER
26461 /*
@@ -26554,13 +26740,11 @@
26554 zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
26555 }else{
26556 zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
26557 }
26558 for(i=0; i<ArraySize(aQuery); i++){
26559 char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
26560 int val = db_int(p->db, zSql);
26561 sqlite3_free(zSql);
26562 sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
26563 }
26564 sqlite3_free(zSchemaTab);
26565 sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
26566 sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion);
@@ -26575,16 +26759,17 @@
26575 */
26576 static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
26577 sqlite3_stmt *pStmt = 0;
26578 sqlite3_int64 nPage = 0;
26579 int pgSz = 0;
26580 const char *zFilename;
26581 const char *zTail;
26582 char *zName = 0;
26583 int rc, i, j;
26584 unsigned char bShow[256]; /* Characters ok to display */
26585
 
 
26586 memset(bShow, '.', sizeof(bShow));
26587 for(i=' '; i<='~'; i++){
26588 if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
26589 }
26590 rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0);
@@ -26603,21 +26788,19 @@
26603 sqlite3_finalize(pStmt);
26604 pStmt = 0;
26605 if( nPage<1 ) goto dbtotxt_error;
26606 rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
26607 if( rc ) goto dbtotxt_error;
26608 rc = 0;
26609 if( sqlite3_step(pStmt)!=SQLITE_ROW ){
26610 zTail = zFilename = "unk.db";
26611 }else{
26612 zFilename = (const char*)sqlite3_column_text(pStmt, 2);
26613 if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
26614 zTail = strrchr(zFilename, '/');
26615 #if defined(_WIN32)
26616 if( zTail==0 ) zTail = strrchr(zFilename, '\\');
26617 #endif
26618 if( zTail ) zFilename = zTail;
26619 }
26620 zName = strdup(zTail);
26621 shell_check_oom(zName);
26622 sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
26623 nPage*pgSz, pgSz, zName);
@@ -26624,11 +26807,10 @@
26624 sqlite3_finalize(pStmt);
26625 pStmt = 0;
26626 rc = sqlite3_prepare_v2(p->db,
26627 "SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0);
26628 if( rc ) goto dbtotxt_error;
26629 rc = 0;
26630 while( sqlite3_step(pStmt)==SQLITE_ROW ){
26631 sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0);
26632 const u8 *aData = sqlite3_column_blob(pStmt, 1);
26633 int seenPageLabel = 0;
26634 for(i=0; i<pgSz; i+=16){
@@ -28216,12 +28398,12 @@
28216 }else if( *pDb==0 ){
28217 return 0;
28218 }else{
28219 /* Formulate the columns spec, close the DB, zero *pDb. */
28220 char *zColsSpec = 0;
28221 int hasDupes = db_int(*pDb, zHasDupes);
28222 int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0;
28223 if( hasDupes ){
28224 #ifdef SHELL_COLUMN_RENAME_CLEAN
28225 rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
28226 rc_err_oom_die(rc);
28227 #endif
@@ -28232,11 +28414,11 @@
28232 sqlite3_bind_int(pStmt, 1, nDigits);
28233 rc = sqlite3_step(pStmt);
28234 sqlite3_finalize(pStmt);
28235 if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM);
28236 }
28237 assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
28238 rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
28239 rc_err_oom_die(rc);
28240 rc = sqlite3_step(pStmt);
28241 if( rc==SQLITE_ROW ){
28242 zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
@@ -29282,11 +29464,15 @@
29282 /* Below, resources must be freed before exit. */
29283 while( (nSkip--)>0 ){
29284 while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
29285 }
29286 import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
29287 if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){
 
 
 
 
29288 /* Table does not exist. Create it. */
29289 sqlite3 *dbCols = 0;
29290 char *zRenames = 0;
29291 char *zColDefs;
29292 zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
@@ -30354,11 +30540,14 @@
30354 }
30355 close_db(pSrc);
30356 }else
30357 #endif /* !defined(SQLITE_SHELL_FIDDLE) */
30358
30359 if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){
 
 
 
30360 if( nArg==2 ){
30361 if( cli_strcmp(azArg[1], "vm")==0 ){
30362 p->scanstatsOn = 3;
30363 }else
30364 if( cli_strcmp(azArg[1], "est")==0 ){
@@ -31409,11 +31598,13 @@
31409 { 0x10000000, 1, "OrderBySubq" },
31410 { 0xffffffff, 0, "All" },
31411 };
31412 unsigned int curOpt;
31413 unsigned int newOpt;
 
31414 int ii;
 
31415 sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
31416 newOpt = curOpt;
31417 for(ii=2; ii<nArg; ii++){
31418 const char *z = azArg[ii];
31419 int useLabel = 0;
@@ -31450,28 +31641,32 @@
31450 }
31451 }
31452 }
31453 if( curOpt!=newOpt ){
31454 sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
31455 }else if( nArg<3 ){
31456 curOpt = ~newOpt;
 
31457 }
31458 if( newOpt==0 ){
31459 sqlite3_fputs("+All\n", p->out);
31460 }else if( newOpt==0xffffffff ){
31461 sqlite3_fputs("-All\n", p->out);
 
 
 
 
31462 }else{
31463 int jj;
31464 for(jj=0; jj<ArraySize(aLabel); jj++){
31465 unsigned int m = aLabel[jj].mask;
31466 if( !aLabel[jj].bDsply ) continue;
31467 if( (curOpt&m)!=(newOpt&m) ){
31468 sqlite3_fprintf(p->out, "%c%s\n", (newOpt & m)==0 ? '+' : '-',
31469 aLabel[jj].zLabel);
31470 }
31471 }
31472 }
 
31473 rc2 = isOk = 3;
31474 break;
31475 }
31476
31477 /* sqlite3_test_control(int, db, int) */
@@ -31928,11 +32123,10 @@
31928 SCAN_TRACKER_REFTYPE pst){
31929 char cin;
31930 char cWait = (char)qss; /* intentional narrowing loss */
31931 if( cWait==0 ){
31932 PlainScan:
31933 assert( cWait==0 );
31934 while( (cin = *zLine++)!=0 ){
31935 if( IsSpace(cin) )
31936 continue;
31937 switch (cin){
31938 case '-':
@@ -31980,11 +32174,10 @@
31980 switch( cWait ){
31981 case '*':
31982 if( *zLine != '/' )
31983 continue;
31984 ++zLine;
31985 cWait = 0;
31986 CONTINUE_PROMPT_AWAITC(pst, 0);
31987 qss = QSS_SETV(qss, 0);
31988 goto PlainScan;
31989 case '`': case '\'': case '"':
31990 if(*zLine==cWait){
@@ -31992,11 +32185,10 @@
31992 ++zLine;
31993 continue;
31994 }
31995 deliberate_fall_through;
31996 case ']':
31997 cWait = 0;
31998 CONTINUE_PROMPT_AWAITC(pst, 0);
31999 qss = QSS_SETV(qss, 0);
32000 goto PlainScan;
32001 default: assert(0);
32002 }
@@ -32180,11 +32372,14 @@
32180 if( doAutoDetectRestore(p, zSql) ) return 1;
32181 return 0;
32182 }
32183
32184 static void echo_group_input(ShellState *p, const char *zDo){
32185 if( ShellHasFlag(p, SHFLG_Echo) ) sqlite3_fprintf(p->out, "%s\n", zDo);
 
 
 
32186 }
32187
32188 #ifdef SQLITE_SHELL_FIDDLE
32189 /*
32190 ** Alternate one_input_line() impl for wasm mode. This is not in the primary
@@ -32640,10 +32835,19 @@
32640 }
32641
32642 static void sayAbnormalExit(void){
32643 if( seenInterrupt ) eputz("Program interrupted.\n");
32644 }
 
 
 
 
 
 
 
 
 
32645
32646 #ifndef SQLITE_SHELL_IS_UTF8
32647 # if (defined(_WIN32) || defined(WIN32)) \
32648 && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
32649 # define SQLITE_SHELL_IS_UTF8 (0)
@@ -32877,12 +33081,10 @@
32877 case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
32878 case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
32879 default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
32880 }
32881 }else if( cli_strcmp(z,"-vfstrace")==0 ){
32882 vfstrace_register("trace",0,(int(*)(const char*,void*))sqlite3_fputs,
32883 stderr,1);
32884 bEnableVfstrace = 1;
32885 #ifdef SQLITE_ENABLE_MULTIPLEX
32886 }else if( cli_strcmp(z,"-multiplex")==0 ){
32887 extern int sqlite3_multiplex_initialize(const char*,int);
32888 sqlite3_multiplex_initialize(0, 1);
@@ -32975,10 +33177,13 @@
32975 "%s: Error: no database filename specified\n", Argv0);
32976 return 1;
32977 #endif
32978 }
32979 data.out = stdout;
 
 
 
32980 #ifndef SQLITE_SHELL_FIDDLE
32981 sqlite3_appendvfs_init(0,0,0);
32982 #endif
32983
32984 /* Go ahead and open the database file if it already exists. If the
32985
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -2367,11 +2367,11 @@
2367 **
2368 ******************************************************************************
2369 **
2370 ** This SQLite extension implements functions that compute SHA3 hashes
2371 ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard.
2372 ** Three SQL functions are implemented:
2373 **
2374 ** sha3(X,SIZE)
2375 ** sha3_agg(Y,SIZE)
2376 ** sha3_query(Z,SIZE)
2377 **
@@ -14193,11 +14193,11 @@
14193 /* A view. Or a trigger on a view. */
14194 if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
14195 }else{
14196 IdxTable *pTab;
14197 rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
14198 if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){
14199 int i;
14200 char *zInner = 0;
14201 char *zOuter = 0;
14202 pTab->pNext = p->pTable;
14203 p->pTable = pTab;
@@ -16261,11 +16261,31 @@
16261 **
16262 ** Similar compiler commands will work on different systems. The key
16263 ** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that
16264 ** the shell.c source file will know to include the -vfstrace command-line
16265 ** option and (2) you must compile and link the three source files
16266 ** shell,c, test_vfstrace.c, and sqlite3.c.
16267 **
16268 ** RUNTIME CONTROL OF VFSTRACE OUTPUT
16269 **
16270 ** The application can use the "vfstrace" pragma to control which VFS
16271 ** APIs are traced. To disable all output:
16272 **
16273 ** PRAGMA vfstrace('-all');
16274 **
16275 ** To enable all output (which is the default setting):
16276 **
16277 ** PRAGMA vfstrace('+all');
16278 **
16279 ** Individual APIs can be enabled or disabled by name, with or without
16280 ** the initial "x" character. For example, to set up for tracing lock
16281 ** primatives only:
16282 **
16283 ** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
16284 **
16285 ** The argument to the vfstrace pragma ignores capitalization and any
16286 ** characters other than alphabetics, '+', and '-'.
16287 */
16288 #include <stdlib.h>
16289 #include <string.h>
16290 /* #include "sqlite3.h" */
16291
@@ -16275,10 +16295,12 @@
16295 */
16296 typedef struct vfstrace_info vfstrace_info;
16297 struct vfstrace_info {
16298 sqlite3_vfs *pRootVfs; /* The underlying real VFS */
16299 int (*xOut)(const char*, void*); /* Send output here */
16300 unsigned int mTrace; /* Mask of interfaces to trace */
16301 u8 bOn; /* Tracing on/off */
16302 void *pOutArg; /* First argument to xOut */
16303 const char *zVfsName; /* Name of this trace-VFS */
16304 sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */
16305 };
16306
@@ -16291,10 +16313,42 @@
16313 vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */
16314 const char *zFName; /* Base name of the file */
16315 sqlite3_file *pReal; /* The real underlying file */
16316 };
16317
16318 /*
16319 ** Bit values for vfstrace_info.mTrace.
16320 */
16321 #define VTR_CLOSE 0x00000001
16322 #define VTR_READ 0x00000002
16323 #define VTR_WRITE 0x00000004
16324 #define VTR_TRUNC 0x00000008
16325 #define VTR_SYNC 0x00000010
16326 #define VTR_FSIZE 0x00000020
16327 #define VTR_LOCK 0x00000040
16328 #define VTR_UNLOCK 0x00000080
16329 #define VTR_CRL 0x00000100
16330 #define VTR_FCTRL 0x00000200
16331 #define VTR_SECSZ 0x00000400
16332 #define VTR_DEVCHAR 0x00000800
16333 #define VTR_SHMLOCK 0x00001000
16334 #define VTR_SHMMAP 0x00002000
16335 #define VTR_SHMBAR 0x00004000
16336 #define VTR_SHMUNMAP 0x00008000
16337 #define VTR_OPEN 0x00010000
16338 #define VTR_DELETE 0x00020000
16339 #define VTR_ACCESS 0x00040000
16340 #define VTR_FULLPATH 0x00080000
16341 #define VTR_DLOPEN 0x00100000
16342 #define VTR_DLERR 0x00200000
16343 #define VTR_DLSYM 0x00400000
16344 #define VTR_DLCLOSE 0x00800000
16345 #define VTR_RAND 0x01000000
16346 #define VTR_SLEEP 0x02000000
16347 #define VTR_CURTIME 0x04000000
16348 #define VTR_LASTERR 0x08000000
16349
16350 /*
16351 ** Method declarations for vfstrace_file.
16352 */
16353 static int vfstraceClose(sqlite3_file*);
16354 static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
@@ -16355,15 +16409,17 @@
16409 const char *zFormat,
16410 ...
16411 ){
16412 va_list ap;
16413 char *zMsg;
16414 if( pInfo->bOn ){
16415 va_start(ap, zFormat);
16416 zMsg = sqlite3_vmprintf(zFormat, ap);
16417 va_end(ap);
16418 pInfo->xOut(zMsg, pInfo->pOutArg);
16419 sqlite3_free(zMsg);
16420 }
16421 }
16422
16423 /*
16424 ** Try to convert an error code into a symbolic name for that error code.
16425 */
@@ -16457,18 +16513,26 @@
16513 int i = *pI;
16514 while( zAppend[0] ){ z[i++] = *(zAppend++); }
16515 z[i] = 0;
16516 *pI = i;
16517 }
16518
16519 /*
16520 ** Turn tracing output on or off according to mMask.
16521 */
16522 static void vfstraceOnOff(vfstrace_info *pInfo, unsigned int mMask){
16523 pInfo->bOn = (pInfo->mTrace & mMask)!=0;
16524 }
16525
16526 /*
16527 ** Close an vfstrace-file.
16528 */
16529 static int vfstraceClose(sqlite3_file *pFile){
16530 vfstrace_file *p = (vfstrace_file *)pFile;
16531 vfstrace_info *pInfo = p->pInfo;
16532 int rc;
16533 vfstraceOnOff(pInfo, VTR_CLOSE);
16534 vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName);
16535 rc = p->pReal->pMethods->xClose(p->pReal);
16536 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16537 if( rc==SQLITE_OK ){
16538 sqlite3_free((void*)p->base.pMethods);
@@ -16487,10 +16551,11 @@
16551 sqlite_int64 iOfst
16552 ){
16553 vfstrace_file *p = (vfstrace_file *)pFile;
16554 vfstrace_info *pInfo = p->pInfo;
16555 int rc;
16556 vfstraceOnOff(pInfo, VTR_READ);
16557 vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)",
16558 pInfo->zVfsName, p->zFName, iAmt, iOfst);
16559 rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
16560 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16561 return rc;
@@ -16506,10 +16571,11 @@
16571 sqlite_int64 iOfst
16572 ){
16573 vfstrace_file *p = (vfstrace_file *)pFile;
16574 vfstrace_info *pInfo = p->pInfo;
16575 int rc;
16576 vfstraceOnOff(pInfo, VTR_WRITE);
16577 vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)",
16578 pInfo->zVfsName, p->zFName, iAmt, iOfst);
16579 rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
16580 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16581 return rc;
@@ -16520,10 +16586,11 @@
16586 */
16587 static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){
16588 vfstrace_file *p = (vfstrace_file *)pFile;
16589 vfstrace_info *pInfo = p->pInfo;
16590 int rc;
16591 vfstraceOnOff(pInfo, VTR_TRUNC);
16592 vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName,
16593 size);
16594 rc = p->pReal->pMethods->xTruncate(p->pReal, size);
16595 vfstrace_printf(pInfo, " -> %d\n", rc);
16596 return rc;
@@ -16544,10 +16611,11 @@
16611 else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL");
16612 if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY");
16613 if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){
16614 sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags);
16615 }
16616 vfstraceOnOff(pInfo, VTR_SYNC);
16617 vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName,
16618 &zBuf[1]);
16619 rc = p->pReal->pMethods->xSync(p->pReal, flags);
16620 vfstrace_printf(pInfo, " -> %d\n", rc);
16621 return rc;
@@ -16558,10 +16626,11 @@
16626 */
16627 static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
16628 vfstrace_file *p = (vfstrace_file *)pFile;
16629 vfstrace_info *pInfo = p->pInfo;
16630 int rc;
16631 vfstraceOnOff(pInfo, VTR_FSIZE);
16632 vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName);
16633 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
16634 vfstrace_print_errcode(pInfo, " -> %s,", rc);
16635 vfstrace_printf(pInfo, " size=%lld\n", *pSize);
16636 return rc;
@@ -16586,10 +16655,11 @@
16655 */
16656 static int vfstraceLock(sqlite3_file *pFile, int eLock){
16657 vfstrace_file *p = (vfstrace_file *)pFile;
16658 vfstrace_info *pInfo = p->pInfo;
16659 int rc;
16660 vfstraceOnOff(pInfo, VTR_LOCK);
16661 vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName,
16662 lockName(eLock));
16663 rc = p->pReal->pMethods->xLock(p->pReal, eLock);
16664 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16665 return rc;
@@ -16600,10 +16670,11 @@
16670 */
16671 static int vfstraceUnlock(sqlite3_file *pFile, int eLock){
16672 vfstrace_file *p = (vfstrace_file *)pFile;
16673 vfstrace_info *pInfo = p->pInfo;
16674 int rc;
16675 vfstraceOnOff(pInfo, VTR_UNLOCK);
16676 vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName,
16677 lockName(eLock));
16678 rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
16679 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16680 return rc;
@@ -16614,10 +16685,11 @@
16685 */
16686 static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){
16687 vfstrace_file *p = (vfstrace_file *)pFile;
16688 vfstrace_info *pInfo = p->pInfo;
16689 int rc;
16690 vfstraceOnOff(pInfo, VTR_CRL);
16691 vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)",
16692 pInfo->zVfsName, p->zFName);
16693 rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
16694 vfstrace_print_errcode(pInfo, " -> %s", rc);
16695 vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
@@ -16633,10 +16705,11 @@
16705 int rc;
16706 char zBuf[100];
16707 char zBuf2[100];
16708 char *zOp;
16709 char *zRVal = 0;
16710 vfstraceOnOff(pInfo, VTR_FCTRL);
16711 switch( op ){
16712 case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
16713 case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
16714 case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
16715 case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
@@ -16661,10 +16734,83 @@
16734 case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
16735 case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
16736 case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
16737 case SQLITE_FCNTL_PRAGMA: {
16738 const char *const* a = (const char*const*)pArg;
16739 if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
16740 const u8 *zArg = (const u8*)a[2];
16741 if( zArg[0]>='0' && zArg[0]<=9 ){
16742 pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
16743 }else{
16744 static const struct {
16745 const char *z;
16746 unsigned int m;
16747 } aKw[] = {
16748 { "all", 0xffffffff },
16749 { "close", VTR_CLOSE },
16750 { "read", VTR_READ },
16751 { "write", VTR_WRITE },
16752 { "truncate", VTR_TRUNC },
16753 { "sync", VTR_SYNC },
16754 { "filesize", VTR_FSIZE },
16755 { "lock", VTR_LOCK },
16756 { "unlock", VTR_UNLOCK },
16757 { "checkreservedlock", VTR_CRL },
16758 { "filecontrol", VTR_FCTRL },
16759 { "sectorsize", VTR_SECSZ },
16760 { "devicecharacteristics", VTR_DEVCHAR },
16761 { "shmlock", VTR_SHMLOCK },
16762 { "shmmap", VTR_SHMMAP },
16763 { "shmummap", VTR_SHMUNMAP },
16764 { "shmbarrier", VTR_SHMBAR },
16765 { "open", VTR_OPEN },
16766 { "delete", VTR_DELETE },
16767 { "access", VTR_ACCESS },
16768 { "fullpathname", VTR_FULLPATH },
16769 { "dlopen", VTR_DLOPEN },
16770 { "dlerror", VTR_DLERR },
16771 { "dlsym", VTR_DLSYM },
16772 { "dlclose", VTR_DLCLOSE },
16773 { "randomness", VTR_RAND },
16774 { "sleep", VTR_SLEEP },
16775 { "currenttime", VTR_CURTIME },
16776 { "currenttimeint64", VTR_CURTIME },
16777 { "getlasterror", VTR_LASTERR },
16778 };
16779 int onOff = 1;
16780 while( zArg[0] ){
16781 int jj, n;
16782 while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+'
16783 && !isalpha(zArg[0]) ) zArg++;
16784 if( zArg[0]==0 ) break;
16785 if( zArg[0]=='-' ){
16786 onOff = 0;
16787 zArg++;
16788 }else if( zArg[0]=='+' ){
16789 onOff = 1;
16790 zArg++;
16791 }
16792 while( !isalpha(zArg[0]) ){
16793 if( zArg[0]==0 ) break;
16794 zArg++;
16795 }
16796 if( zArg[0]=='x' && isalpha(zArg[1]) ) zArg++;
16797 for(n=0; isalpha(zArg[n]); n++){}
16798 for(jj=0; jj<(int)(sizeof(aKw)/sizeof(aKw[0])); jj++){
16799 if( sqlite3_strnicmp(aKw[jj].z,(const char*)zArg,n)==0 ){
16800 if( onOff ){
16801 pInfo->mTrace |= aKw[jj].m;
16802 }else{
16803 pInfo->mTrace &= ~aKw[jj].m;
16804 }
16805 break;
16806 }
16807 }
16808 zArg += n;
16809 }
16810 }
16811 }
16812 sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
16813 zOp = zBuf;
16814 break;
16815 }
16816 case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break;
@@ -16756,10 +16902,11 @@
16902 */
16903 static int vfstraceSectorSize(sqlite3_file *pFile){
16904 vfstrace_file *p = (vfstrace_file *)pFile;
16905 vfstrace_info *pInfo = p->pInfo;
16906 int rc;
16907 vfstraceOnOff(pInfo, VTR_SECSZ);
16908 vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName);
16909 rc = p->pReal->pMethods->xSectorSize(p->pReal);
16910 vfstrace_printf(pInfo, " -> %d\n", rc);
16911 return rc;
16912 }
@@ -16769,10 +16916,11 @@
16916 */
16917 static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){
16918 vfstrace_file *p = (vfstrace_file *)pFile;
16919 vfstrace_info *pInfo = p->pInfo;
16920 int rc;
16921 vfstraceOnOff(pInfo, VTR_DEVCHAR);
16922 vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)",
16923 pInfo->zVfsName, p->zFName);
16924 rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
16925 vfstrace_printf(pInfo, " -> 0x%08x\n", rc);
16926 return rc;
@@ -16795,19 +16943,20 @@
16943 vfstrace_file *p = (vfstrace_file *)pFile;
16944 vfstrace_info *pInfo = p->pInfo;
16945 int rc;
16946 char zLck[100];
16947 int i = 0;
16948 vfstraceOnOff(pInfo, VTR_SHMLOCK);
16949 memcpy(zLck, "|0", 3);
16950 if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK");
16951 if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK");
16952 if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED");
16953 if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE");
16954 if( flags & ~(0xf) ){
16955 sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags);
16956 }
16957 if( ofst>=0 && ofst<(int)(sizeof(azLockName)/sizeof(azLockName[0])) ){
16958 vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d(%s),n=%d,%s)",
16959 pInfo->zVfsName, p->zFName, ofst, azLockName[ofst],
16960 n, &zLck[1]);
16961 }else{
16962 vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=5d,n=%d,%s)",
@@ -16826,26 +16975,29 @@
16975 void volatile **pp
16976 ){
16977 vfstrace_file *p = (vfstrace_file *)pFile;
16978 vfstrace_info *pInfo = p->pInfo;
16979 int rc;
16980 vfstraceOnOff(pInfo, VTR_SHMMAP);
16981 vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)",
16982 pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite);
16983 rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
16984 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
16985 return rc;
16986 }
16987 static void vfstraceShmBarrier(sqlite3_file *pFile){
16988 vfstrace_file *p = (vfstrace_file *)pFile;
16989 vfstrace_info *pInfo = p->pInfo;
16990 vfstraceOnOff(pInfo, VTR_SHMBAR);
16991 vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName);
16992 p->pReal->pMethods->xShmBarrier(p->pReal);
16993 }
16994 static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){
16995 vfstrace_file *p = (vfstrace_file *)pFile;
16996 vfstrace_info *pInfo = p->pInfo;
16997 int rc;
16998 vfstraceOnOff(pInfo, VTR_SHMUNMAP);
16999 vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
17000 pInfo->zVfsName, p->zFName, delFlag);
17001 rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
17002 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
17003 return rc;
@@ -16869,10 +17021,11 @@
17021 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17022 p->pInfo = pInfo;
17023 p->zFName = zName ? fileTail(zName) : "<temp>";
17024 p->pReal = (sqlite3_file *)&p[1];
17025 rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags);
17026 vfstraceOnOff(pInfo, VTR_OPEN);
17027 vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
17028 pInfo->zVfsName, p->zFName, flags);
17029 if( p->pReal->pMethods ){
17030 sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
17031 const sqlite3_io_methods *pSub = p->pReal->pMethods;
@@ -16914,10 +17067,11 @@
17067 */
17068 static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
17069 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17070 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17071 int rc;
17072 vfstraceOnOff(pInfo, VTR_DELETE);
17073 vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
17074 pInfo->zVfsName, zPath, dirSync);
17075 rc = pRoot->xDelete(pRoot, zPath, dirSync);
17076 vfstrace_print_errcode(pInfo, " -> %s\n", rc);
17077 return rc;
@@ -16934,10 +17088,11 @@
17088 int *pResOut
17089 ){
17090 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17091 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17092 int rc;
17093 vfstraceOnOff(pInfo, VTR_ACCESS);
17094 vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
17095 pInfo->zVfsName, zPath, flags);
17096 rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
17097 vfstrace_print_errcode(pInfo, " -> %s", rc);
17098 vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
@@ -16956,10 +17111,11 @@
17111 char *zOut
17112 ){
17113 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17114 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17115 int rc;
17116 vfstraceOnOff(pInfo, VTR_FULLPATH);
17117 vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")",
17118 pInfo->zVfsName, zPath);
17119 rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut);
17120 vfstrace_print_errcode(pInfo, " -> %s", rc);
17121 vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut);
@@ -16970,10 +17126,11 @@
17126 ** Open the dynamic library located at zPath and return a handle.
17127 */
17128 static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){
17129 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17130 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17131 vfstraceOnOff(pInfo, VTR_DLOPEN);
17132 vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath);
17133 return pRoot->xDlOpen(pRoot, zPath);
17134 }
17135
17136 /*
@@ -16982,10 +17139,11 @@
17139 ** with dynamic libraries.
17140 */
17141 static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
17142 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17143 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17144 vfstraceOnOff(pInfo, VTR_DLERR);
17145 vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte);
17146 pRoot->xDlError(pRoot, nByte, zErrMsg);
17147 vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg);
17148 }
17149
@@ -17003,10 +17161,11 @@
17161 ** Close the dynamic library handle pHandle.
17162 */
17163 static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
17164 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17165 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17166 vfstraceOnOff(pInfo, VTR_DLCLOSE);
17167 vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
17168 pRoot->xDlClose(pRoot, pHandle);
17169 }
17170
17171 /*
@@ -17014,10 +17173,11 @@
17173 ** random data.
17174 */
17175 static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
17176 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17177 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17178 vfstraceOnOff(pInfo, VTR_RAND);
17179 vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte);
17180 return pRoot->xRandomness(pRoot, nByte, zBufOut);
17181 }
17182
17183 /*
@@ -17025,34 +17185,52 @@
17185 ** actually slept.
17186 */
17187 static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){
17188 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17189 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17190 vfstraceOnOff(pInfo, VTR_SLEEP);
17191 vfstrace_printf(pInfo, "%s.xSleep(%d)\n", pInfo->zVfsName, nMicro);
17192 return pRoot->xSleep(pRoot, nMicro);
17193 }
17194
17195 /*
17196 ** Return the current time as a Julian Day number in *pTimeOut.
17197 */
17198 static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
17199 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17200 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17201 int rc;
17202 vfstraceOnOff(pInfo, VTR_CURTIME);
17203 vfstrace_printf(pInfo, "%s.xCurrentTime()", pInfo->zVfsName);
17204 rc = pRoot->xCurrentTime(pRoot, pTimeOut);
17205 vfstrace_printf(pInfo, " -> %.17g\n", *pTimeOut);
17206 return rc;
17207 }
17208 static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
17209 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17210 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17211 int rc;
17212 vfstraceOnOff(pInfo, VTR_CURTIME);
17213 vfstrace_printf(pInfo, "%s.xCurrentTimeInt64()", pInfo->zVfsName);
17214 rc = pRoot->xCurrentTimeInt64(pRoot, pTimeOut);
17215 vfstrace_printf(pInfo, " -> %lld\n", *pTimeOut);
17216 return rc;
17217 }
17218
17219 /*
17220 ** Return the most recent error code and message
17221 */
17222 static int vfstraceGetLastError(sqlite3_vfs *pVfs, int nErr, char *zErr){
17223 vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
17224 sqlite3_vfs *pRoot = pInfo->pRootVfs;
17225 int rc;
17226 vfstraceOnOff(pInfo, VTR_LASTERR);
17227 vfstrace_printf(pInfo, "%s.xGetLastError(%d,zBuf)", pInfo->zVfsName, nErr);
17228 if( nErr ) zErr[0] = 0;
17229 rc = pRoot->xGetLastError(pRoot, nErr, zErr);
17230 vfstrace_printf(pInfo, " -> zBuf[] = \"%s\", rc = %d\n", nErr?zErr:"", rc);
17231 return rc;
17232 }
17233
17234 /*
17235 ** Override system calls.
17236 */
@@ -17142,10 +17320,12 @@
17320 pInfo->pRootVfs = pRoot;
17321 pInfo->xOut = xOut;
17322 pInfo->pOutArg = pOutArg;
17323 pInfo->zVfsName = pNew->zName;
17324 pInfo->pTraceVfs = pNew;
17325 pInfo->mTrace = 0xffffffff;
17326 pInfo->bOn = 1;
17327 vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n",
17328 pInfo->zVfsName, pRoot->zName);
17329 return sqlite3_vfs_register(pNew, makeDefault);
17330 }
17331
@@ -26444,18 +26624,24 @@
26624 #endif
26625
26626 /*
26627 ** Run an SQL command and return the single integer result.
26628 */
26629 static int db_int(sqlite3 *db, const char *zSql, ...){
26630 sqlite3_stmt *pStmt;
26631 int res = 0;
26632 char *z;
26633 va_list ap;
26634 va_start(ap, zSql);
26635 z = sqlite3_vmprintf(zSql, ap);
26636 va_end(ap);
26637 sqlite3_prepare_v2(db, z, -1, &pStmt, 0);
26638 if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
26639 res = sqlite3_column_int(pStmt,0);
26640 }
26641 sqlite3_finalize(pStmt);
26642 sqlite3_free(z);
26643 return res;
26644 }
26645
26646 #if SQLITE_SHELL_HAVE_RECOVER
26647 /*
@@ -26554,13 +26740,11 @@
26740 zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
26741 }else{
26742 zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
26743 }
26744 for(i=0; i<ArraySize(aQuery); i++){
26745 int val = db_int(p->db, aQuery[i].zSql, zSchemaTab);
 
 
26746 sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
26747 }
26748 sqlite3_free(zSchemaTab);
26749 sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
26750 sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion);
@@ -26575,16 +26759,17 @@
26759 */
26760 static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
26761 sqlite3_stmt *pStmt = 0;
26762 sqlite3_int64 nPage = 0;
26763 int pgSz = 0;
 
26764 const char *zTail;
26765 char *zName = 0;
26766 int rc, i, j;
26767 unsigned char bShow[256]; /* Characters ok to display */
26768
26769 UNUSED_PARAMETER(nArg);
26770 UNUSED_PARAMETER(azArg);
26771 memset(bShow, '.', sizeof(bShow));
26772 for(i=' '; i<='~'; i++){
26773 if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
26774 }
26775 rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0);
@@ -26603,21 +26788,19 @@
26788 sqlite3_finalize(pStmt);
26789 pStmt = 0;
26790 if( nPage<1 ) goto dbtotxt_error;
26791 rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
26792 if( rc ) goto dbtotxt_error;
 
26793 if( sqlite3_step(pStmt)!=SQLITE_ROW ){
26794 zTail = "unk.db";
26795 }else{
26796 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
26797 if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
26798 zTail = strrchr(zFilename, '/');
26799 #if defined(_WIN32)
26800 if( zTail==0 ) zTail = strrchr(zFilename, '\\');
26801 #endif
 
26802 }
26803 zName = strdup(zTail);
26804 shell_check_oom(zName);
26805 sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
26806 nPage*pgSz, pgSz, zName);
@@ -26624,11 +26807,10 @@
26807 sqlite3_finalize(pStmt);
26808 pStmt = 0;
26809 rc = sqlite3_prepare_v2(p->db,
26810 "SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0);
26811 if( rc ) goto dbtotxt_error;
 
26812 while( sqlite3_step(pStmt)==SQLITE_ROW ){
26813 sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0);
26814 const u8 *aData = sqlite3_column_blob(pStmt, 1);
26815 int seenPageLabel = 0;
26816 for(i=0; i<pgSz; i+=16){
@@ -28216,12 +28398,12 @@
28398 }else if( *pDb==0 ){
28399 return 0;
28400 }else{
28401 /* Formulate the columns spec, close the DB, zero *pDb. */
28402 char *zColsSpec = 0;
28403 int hasDupes = db_int(*pDb, "%s", zHasDupes);
28404 int nDigits = (hasDupes)? db_int(*pDb, "%s", zColDigits) : 0;
28405 if( hasDupes ){
28406 #ifdef SHELL_COLUMN_RENAME_CLEAN
28407 rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
28408 rc_err_oom_die(rc);
28409 #endif
@@ -28232,11 +28414,11 @@
28414 sqlite3_bind_int(pStmt, 1, nDigits);
28415 rc = sqlite3_step(pStmt);
28416 sqlite3_finalize(pStmt);
28417 if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM);
28418 }
28419 assert(db_int(*pDb, "%s", zHasDupes)==0); /* Consider: remove this */
28420 rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
28421 rc_err_oom_die(rc);
28422 rc = sqlite3_step(pStmt);
28423 if( rc==SQLITE_ROW ){
28424 zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
@@ -29282,11 +29464,15 @@
29464 /* Below, resources must be freed before exit. */
29465 while( (nSkip--)>0 ){
29466 while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
29467 }
29468 import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
29469 if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
29470 && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
29471 " WHERE name=%Q AND type='view'",
29472 zSchema ? zSchema : "main", zTable)
29473 ){
29474 /* Table does not exist. Create it. */
29475 sqlite3 *dbCols = 0;
29476 char *zRenames = 0;
29477 char *zColDefs;
29478 zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
@@ -30354,11 +30540,14 @@
30540 }
30541 close_db(pSrc);
30542 }else
30543 #endif /* !defined(SQLITE_SHELL_FIDDLE) */
30544
30545 if( c=='s' &&
30546 (cli_strncmp(azArg[0], "scanstats", n)==0 ||
30547 cli_strncmp(azArg[0], "scanstatus", n)==0)
30548 ){
30549 if( nArg==2 ){
30550 if( cli_strcmp(azArg[1], "vm")==0 ){
30551 p->scanstatsOn = 3;
30552 }else
30553 if( cli_strcmp(azArg[1], "est")==0 ){
@@ -31409,11 +31598,13 @@
31598 { 0x10000000, 1, "OrderBySubq" },
31599 { 0xffffffff, 0, "All" },
31600 };
31601 unsigned int curOpt;
31602 unsigned int newOpt;
31603 unsigned int m;
31604 int ii;
31605 int nOff;
31606 sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
31607 newOpt = curOpt;
31608 for(ii=2; ii<nArg; ii++){
31609 const char *z = azArg[ii];
31610 int useLabel = 0;
@@ -31450,28 +31641,32 @@
31641 }
31642 }
31643 }
31644 if( curOpt!=newOpt ){
31645 sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
31646 }
31647 for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){
31648 if( m & newOpt ) nOff++;
31649 }
31650 if( nOff<12 ){
31651 sqlite3_fputs("+All", p->out);
31652 for(ii=0; ii<ArraySize(aLabel); ii++){
31653 if( !aLabel[ii].bDsply ) continue;
31654 if( (newOpt & aLabel[ii].mask)!=0 ){
31655 sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel);
31656 }
31657 }
31658 }else{
31659 sqlite3_fputs("-All", p->out);
31660 for(ii=0; ii<ArraySize(aLabel); ii++){
31661 if( !aLabel[ii].bDsply ) continue;
31662 if( (newOpt & aLabel[ii].mask)==0 ){
31663 sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel);
 
 
31664 }
31665 }
31666 }
31667 sqlite3_fputs("\n", p->out);
31668 rc2 = isOk = 3;
31669 break;
31670 }
31671
31672 /* sqlite3_test_control(int, db, int) */
@@ -31928,11 +32123,10 @@
32123 SCAN_TRACKER_REFTYPE pst){
32124 char cin;
32125 char cWait = (char)qss; /* intentional narrowing loss */
32126 if( cWait==0 ){
32127 PlainScan:
 
32128 while( (cin = *zLine++)!=0 ){
32129 if( IsSpace(cin) )
32130 continue;
32131 switch (cin){
32132 case '-':
@@ -31980,11 +32174,10 @@
32174 switch( cWait ){
32175 case '*':
32176 if( *zLine != '/' )
32177 continue;
32178 ++zLine;
 
32179 CONTINUE_PROMPT_AWAITC(pst, 0);
32180 qss = QSS_SETV(qss, 0);
32181 goto PlainScan;
32182 case '`': case '\'': case '"':
32183 if(*zLine==cWait){
@@ -31992,11 +32185,10 @@
32185 ++zLine;
32186 continue;
32187 }
32188 deliberate_fall_through;
32189 case ']':
 
32190 CONTINUE_PROMPT_AWAITC(pst, 0);
32191 qss = QSS_SETV(qss, 0);
32192 goto PlainScan;
32193 default: assert(0);
32194 }
@@ -32180,11 +32372,14 @@
32372 if( doAutoDetectRestore(p, zSql) ) return 1;
32373 return 0;
32374 }
32375
32376 static void echo_group_input(ShellState *p, const char *zDo){
32377 if( ShellHasFlag(p, SHFLG_Echo) ){
32378 sqlite3_fprintf(p->out, "%s\n", zDo);
32379 fflush(p->out);
32380 }
32381 }
32382
32383 #ifdef SQLITE_SHELL_FIDDLE
32384 /*
32385 ** Alternate one_input_line() impl for wasm mode. This is not in the primary
@@ -32640,10 +32835,19 @@
32835 }
32836
32837 static void sayAbnormalExit(void){
32838 if( seenInterrupt ) eputz("Program interrupted.\n");
32839 }
32840
32841 /* Routine to output from vfstrace
32842 */
32843 static int vfstraceOut(const char *z, void *pArg){
32844 ShellState *p = (ShellState*)pArg;
32845 sqlite3_fputs(z, p->out);
32846 fflush(p->out);
32847 return 1;
32848 }
32849
32850 #ifndef SQLITE_SHELL_IS_UTF8
32851 # if (defined(_WIN32) || defined(WIN32)) \
32852 && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
32853 # define SQLITE_SHELL_IS_UTF8 (0)
@@ -32877,12 +33081,10 @@
33081 case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
33082 case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
33083 default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
33084 }
33085 }else if( cli_strcmp(z,"-vfstrace")==0 ){
 
 
33086 bEnableVfstrace = 1;
33087 #ifdef SQLITE_ENABLE_MULTIPLEX
33088 }else if( cli_strcmp(z,"-multiplex")==0 ){
33089 extern int sqlite3_multiplex_initialize(const char*,int);
33090 sqlite3_multiplex_initialize(0, 1);
@@ -32975,10 +33177,13 @@
33177 "%s: Error: no database filename specified\n", Argv0);
33178 return 1;
33179 #endif
33180 }
33181 data.out = stdout;
33182 if( bEnableVfstrace ){
33183 vfstrace_register("trace",0,vfstraceOut, &data, 1);
33184 }
33185 #ifndef SQLITE_SHELL_FIDDLE
33186 sqlite3_appendvfs_init(0,0,0);
33187 #endif
33188
33189 /* Go ahead and open the database file if it already exists. If the
33190
+807 -294
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** 81202d2ab5963fdcf20555b6d0b31cc955ac with changes in files:
21
+** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465465
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466466
** [sqlite_version()] and [sqlite_source_id()].
467467
*/
468468
#define SQLITE_VERSION "3.48.0"
469469
#define SQLITE_VERSION_NUMBER 3048000
470
-#define SQLITE_SOURCE_ID "2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8"
470
+#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
471471
472472
/*
473473
** CAPI3REF: Run-Time Library Version Numbers
474474
** KEYWORDS: sqlite3_version sqlite3_sourceid
475475
**
@@ -4521,15 +4521,26 @@
45214521
**
45224522
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
45234523
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
45244524
** to return an error (error code SQLITE_ERROR) if the statement uses
45254525
** any virtual tables.
4526
+**
4527
+** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
4528
+** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
4529
+** errors from being sent to the error log defined by
4530
+** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
4531
+** compiles to see if some SQL syntax is well-formed, without generating
4532
+** messages on the global error log when it is not. If the test compile
4533
+** fails, the sqlite3_prepare_v3() call returns the same error indications
4534
+** with or without this flag; it just omits the call to [sqlite3_log()] that
4535
+** logs the error.
45264536
** </dl>
45274537
*/
45284538
#define SQLITE_PREPARE_PERSISTENT 0x01
45294539
#define SQLITE_PREPARE_NORMALIZE 0x02
45304540
#define SQLITE_PREPARE_NO_VTAB 0x04
4541
+#define SQLITE_PREPARE_DONT_LOG 0x10
45314542
45324543
/*
45334544
** CAPI3REF: Compiling An SQL Statement
45344545
** KEYWORDS: {SQL statement compiler}
45354546
** METHOD: sqlite3
@@ -13467,17 +13478,32 @@
1346713478
** This is used to access token iToken of phrase hit iIdx within the
1346813479
** current row. If iIdx is less than zero or greater than or equal to the
1346913480
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
1347013481
** output variable (*ppToken) is set to point to a buffer containing the
1347113482
** matching document token, and (*pnToken) to the size of that buffer in
13472
-** bytes. This API is not available if the specified token matches a
13473
-** prefix query term. In that case both output variables are always set
13474
-** to 0.
13483
+** bytes.
1347513484
**
1347613485
** The output text is not a copy of the document text that was tokenized.
1347713486
** It is the output of the tokenizer module. For tokendata=1 tables, this
1347813487
** includes any embedded 0x00 and trailing data.
13488
+**
13489
+** This API may be slow in some cases if the token identified by parameters
13490
+** iIdx and iToken matched a prefix token in the query. In most cases, the
13491
+** first call to this API for each prefix token in the query is forced
13492
+** to scan the portion of the full-text index that matches the prefix
13493
+** token to collect the extra data required by this API. If the prefix
13494
+** token matches a large number of token instances in the document set,
13495
+** this may be a performance problem.
13496
+**
13497
+** If the user knows in advance that a query may use this API for a
13498
+** prefix token, FTS5 may be configured to collect all required data as part
13499
+** of the initial querying of the full-text index, avoiding the second scan
13500
+** entirely. This also causes prefix queries that do not use this API to
13501
+** run more slowly and use more memory. FTS5 may be configured in this way
13502
+** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
13503
+** option, or on a per-query basis using the
13504
+** [fts5_insttoken | fts5_insttoken()] user function.
1347913505
**
1348013506
** This API can be quite slow if used with an FTS5 table created with the
1348113507
** "detail=none" or "detail=column" option.
1348213508
**
1348313509
** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
@@ -17049,11 +17075,11 @@
1704917075
1705017076
/*
1705117077
** Additional non-public SQLITE_PREPARE_* flags
1705217078
*/
1705317079
#define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */
17054
-#define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */
17080
+#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */
1705517081
1705617082
/*
1705717083
** Prototypes for the VDBE interface. See comments on the implementation
1705817084
** for a description of what each of these routines does.
1705917085
*/
@@ -32279,10 +32305,11 @@
3227932305
&& (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
3228032306
){
3228132307
pExpr = pExpr->pLeft;
3228232308
}
3228332309
if( pExpr==0 ) return;
32310
+ if( ExprHasProperty(pExpr, EP_FromDDL) ) return;
3228432311
db->errByteOffset = pExpr->w.iOfst;
3228532312
}
3228632313
3228732314
/*
3228832315
** Enlarge the memory allocation on a StrAccum object so that it is
@@ -33008,11 +33035,11 @@
3300833035
}
3300933036
if( pItem->fg.isCte ){
3301033037
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
3301133038
}
3301233039
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
33013
- sqlite3_str_appendf(&x, " ON");
33040
+ sqlite3_str_appendf(&x, " isOn");
3301433041
}
3301533042
if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
3301633043
if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
3301733044
if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
3301833045
if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
@@ -34092,10 +34119,14 @@
3409234119
**
3409334120
** This routines are given external linkage so that they will always be
3409434121
** accessible to the debugging, and to avoid warnings about unused
3409534122
** functions. But these routines only exist in debugging builds, so they
3409634123
** do not contaminate the interface.
34124
+**
34125
+** See Also:
34126
+**
34127
+** sqlite3ShowWhereTerm() in where.c
3409734128
*/
3409834129
SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
3409934130
SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
3410034131
SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
3410134132
SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
@@ -35668,12 +35699,12 @@
3566835699
int esign = 1; /* sign of exponent */
3566935700
int e = 0; /* exponent */
3567035701
int eValid = 1; /* True exponent is either not used or is well-formed */
3567135702
int nDigit = 0; /* Number of digits processed */
3567235703
int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
35704
+ u64 s2; /* round-tripped significand */
3567335705
double rr[2];
35674
- u64 s2;
3567535706
3567635707
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
3567735708
*pResult = 0.0; /* Default return value, in case of an error */
3567835709
if( length==0 ) return 0;
3567935710
@@ -35772,25 +35803,36 @@
3577235803
3577335804
/* adjust exponent by d, and update sign */
3577435805
e = (e*esign) + d;
3577535806
3577635807
/* Try to adjust the exponent to make it smaller */
35777
- while( e>0 && s<(LARGEST_UINT64/10) ){
35808
+ while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){
3577835809
s *= 10;
3577935810
e--;
3578035811
}
3578135812
while( e<0 && (s%10)==0 ){
3578235813
s /= 10;
3578335814
e++;
3578435815
}
3578535816
3578635817
rr[0] = (double)s;
35787
- s2 = (u64)rr[0];
35788
-#if defined(_MSC_VER) && _MSC_VER<1700
35789
- if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
35818
+ assert( sizeof(s2)==sizeof(rr[0]) );
35819
+#ifdef SQLITE_DEBUG
35820
+ rr[1] = 18446744073709549568.0;
35821
+ memcpy(&s2, &rr[1], sizeof(s2));
35822
+ assert( s2==0x43efffffffffffffLL );
3579035823
#endif
35791
- rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
35824
+ /* Largest double that can be safely converted to u64
35825
+ ** vvvvvvvvvvvvvvvvvvvvvv */
35826
+ if( rr[0]<=18446744073709549568.0 ){
35827
+ s2 = (u64)rr[0];
35828
+ rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
35829
+ }else{
35830
+ rr[1] = 0.0;
35831
+ }
35832
+ assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */
35833
+
3579235834
if( e>0 ){
3579335835
while( e>=100 ){
3579435836
e -= 100;
3579535837
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
3579635838
}
@@ -51771,11 +51813,11 @@
5177151813
*/
5177251814
char *zTmpname = 0; /* For temporary filename, if necessary. */
5177351815
5177451816
int rc = SQLITE_OK; /* Function Return Code */
5177551817
#if !defined(NDEBUG) || SQLITE_OS_WINCE
51776
- int eType = flags&0xFFFFFF00; /* Type of file to open */
51818
+ int eType = flags&0x0FFF00; /* Type of file to open */
5177751819
#endif
5177851820
5177951821
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
5178051822
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
5178151823
int isCreate = (flags & SQLITE_OPEN_CREATE);
@@ -112021,11 +112063,11 @@
112021112063
**
112022112064
** (4) If pSrc is the right operand of a LEFT JOIN, then...
112023112065
** (4a) pExpr must come from an ON clause..
112024112066
** (4b) and specifically the ON clause associated with the LEFT JOIN.
112025112067
**
112026
-** (5) If pSrc is not the right operand of a LEFT JOIN or the left
112068
+** (5) If pSrc is the right operand of a LEFT JOIN or the left
112027112069
** operand of a RIGHT JOIN, then pExpr must be from the WHERE
112028112070
** clause, not an ON clause.
112029112071
**
112030112072
** (6) Either:
112031112073
**
@@ -115555,35 +115597,41 @@
115555115597
**
115556115598
** Additionally, if pExpr is a simple SQL value and the value is the
115557115599
** same as that currently bound to variable pVar, non-zero is returned.
115558115600
** Otherwise, if the values are not the same or if pExpr is not a simple
115559115601
** SQL value, zero is returned.
115602
+**
115603
+** If the SQLITE_EnableQPSG flag is set on the database connection, then
115604
+** this routine always returns false.
115560115605
*/
115561
-static int exprCompareVariable(
115606
+static SQLITE_NOINLINE int exprCompareVariable(
115562115607
const Parse *pParse,
115563115608
const Expr *pVar,
115564115609
const Expr *pExpr
115565115610
){
115566
- int res = 0;
115611
+ int res = 2;
115567115612
int iVar;
115568115613
sqlite3_value *pL, *pR = 0;
115569115614
115615
+ if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){
115616
+ return 0;
115617
+ }
115618
+ if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2;
115570115619
sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
115571115620
if( pR ){
115572115621
iVar = pVar->iColumn;
115573115622
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
115574115623
pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB);
115575115624
if( pL ){
115576115625
if( sqlite3_value_type(pL)==SQLITE_TEXT ){
115577115626
sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */
115578115627
}
115579
- res = 0==sqlite3MemCompare(pL, pR, 0);
115628
+ res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0;
115580115629
}
115581115630
sqlite3ValueFree(pR);
115582115631
sqlite3ValueFree(pL);
115583115632
}
115584
-
115585115633
return res;
115586115634
}
115587115635
115588115636
/*
115589115637
** Do a deep comparison of two expression trees. Return 0 if the two
@@ -115605,16 +115653,14 @@
115605115653
** can be sure the expressions are the same. In the places where
115606115654
** this routine is used, it does not hurt to get an extra 2 - that
115607115655
** just might result in some slightly slower code. But returning
115608115656
** an incorrect 0 or 1 could lead to a malfunction.
115609115657
**
115610
-** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in
115611
-** pParse->pReprepare can be matched against literals in pB. The
115612
-** pParse->pVdbe->expmask bitmask is updated for each variable referenced.
115613
-** If pParse is NULL (the normal case) then any TK_VARIABLE term in
115614
-** Argument pParse should normally be NULL. If it is not NULL and pA or
115615
-** pB causes a return value of 2.
115658
+** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE
115659
+** terms in pA with bindings in pParse->pReprepare can be matched against
115660
+** literals in pB. The pParse->pVdbe->expmask bitmask is updated for
115661
+** each variable referenced.
115616115662
*/
115617115663
SQLITE_PRIVATE int sqlite3ExprCompare(
115618115664
const Parse *pParse,
115619115665
const Expr *pA,
115620115666
const Expr *pB,
@@ -115622,12 +115668,12 @@
115622115668
){
115623115669
u32 combinedFlags;
115624115670
if( pA==0 || pB==0 ){
115625115671
return pB==pA ? 0 : 2;
115626115672
}
115627
- if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){
115628
- return 0;
115673
+ if( pParse && pA->op==TK_VARIABLE ){
115674
+ return exprCompareVariable(pParse, pA, pB);
115629115675
}
115630115676
combinedFlags = pA->flags | pB->flags;
115631115677
if( combinedFlags & EP_IntValue ){
115632115678
if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){
115633115679
return 0;
@@ -115817,23 +115863,75 @@
115817115863
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
115818115864
}
115819115865
}
115820115866
return 0;
115821115867
}
115868
+
115869
+/*
115870
+** Return true if the boolean value of the expression is always either
115871
+** FALSE or NULL.
115872
+*/
115873
+static int sqlite3ExprIsNotTrue(Expr *pExpr){
115874
+ int v;
115875
+ if( pExpr->op==TK_NULL ) return 1;
115876
+ if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1;
115877
+ v = 1;
115878
+ if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1;
115879
+ return 0;
115880
+}
115881
+
115882
+/*
115883
+** Return true if the expression is one of the following:
115884
+**
115885
+** CASE WHEN x THEN y END
115886
+** CASE WHEN x THEN y ELSE NULL END
115887
+** CASE WHEN x THEN y ELSE false END
115888
+** iif(x,y)
115889
+** iif(x,y,NULL)
115890
+** iif(x,y,false)
115891
+*/
115892
+static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){
115893
+ ExprList *pList;
115894
+ if( pExpr->op==TK_FUNCTION ){
115895
+ const char *z = pExpr->u.zToken;
115896
+ FuncDef *pDef;
115897
+ if( (z[0]!='i' && z[0]!='I') ) return 0;
115898
+ if( pExpr->x.pList==0 ) return 0;
115899
+ pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0);
115900
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
115901
+ if( pDef==0 ) return 0;
115902
+#else
115903
+ if( NEVER(pDef==0) ) return 0;
115904
+#endif
115905
+ if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0;
115906
+ if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0;
115907
+ }else if( pExpr->op==TK_CASE ){
115908
+ if( pExpr->pLeft!=0 ) return 0;
115909
+ }else{
115910
+ return 0;
115911
+ }
115912
+ pList = pExpr->x.pList;
115913
+ assert( pList!=0 );
115914
+ if( pList->nExpr==2 ) return 1;
115915
+ if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1;
115916
+ return 0;
115917
+}
115822115918
115823115919
/*
115824115920
** Return true if we can prove the pE2 will always be true if pE1 is
115825115921
** true. Return false if we cannot complete the proof or if pE2 might
115826115922
** be false. Examples:
115827115923
**
115828
-** pE1: x==5 pE2: x==5 Result: true
115829
-** pE1: x>0 pE2: x==5 Result: false
115830
-** pE1: x=21 pE2: x=21 OR y=43 Result: true
115831
-** pE1: x!=123 pE2: x IS NOT NULL Result: true
115832
-** pE1: x!=?1 pE2: x IS NOT NULL Result: true
115833
-** pE1: x IS NULL pE2: x IS NOT NULL Result: false
115834
-** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
115924
+** pE1: x==5 pE2: x==5 Result: true
115925
+** pE1: x>0 pE2: x==5 Result: false
115926
+** pE1: x=21 pE2: x=21 OR y=43 Result: true
115927
+** pE1: x!=123 pE2: x IS NOT NULL Result: true
115928
+** pE1: x!=?1 pE2: x IS NOT NULL Result: true
115929
+** pE1: x IS NULL pE2: x IS NOT NULL Result: false
115930
+** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
115931
+** pE1: iif(x,y) pE2: x Result: true
115932
+** PE1: iif(x,y,0) pE2: x Result: true
115835115933
**
115836115934
** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
115837115935
** Expr.iTable<0 then assume a table number given by iTab.
115838115936
**
115839115937
** If pParse is not NULL, then the values of bound variables in pE1 are
@@ -115863,10 +115961,13 @@
115863115961
if( pE2->op==TK_NOTNULL
115864115962
&& exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
115865115963
){
115866115964
return 1;
115867115965
}
115966
+ if( sqlite3ExprIsIIF(pParse->db, pE1) ){
115967
+ return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab);
115968
+ }
115868115969
return 0;
115869115970
}
115870115971
115871115972
/* This is a helper function to impliesNotNullRow(). In this routine,
115872115973
** set pWalker->eCode to one only if *both* of the input expressions
@@ -132082,11 +132183,14 @@
132082132183
MFUNCTION(degrees, 1, radToDeg, math1Func ),
132083132184
MFUNCTION(pi, 0, 0, piFunc ),
132084132185
#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
132085132186
FUNCTION(sign, 1, 0, 0, signFunc ),
132086132187
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
132188
+ INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ),
132087132189
INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
132190
+ INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ),
132191
+ INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ),
132088132192
};
132089132193
#ifndef SQLITE_OMIT_ALTERTABLE
132090132194
sqlite3AlterFunctions();
132091132195
#endif
132092132196
sqlite3WindowFunctions();
@@ -140730,11 +140834,12 @@
140730140834
pTab = sqliteHashData(k);
140731140835
if( pTab->nCol==0 ){
140732140836
char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
140733140837
if( zSql ){
140734140838
sqlite3_stmt *pDummy = 0;
140735
- (void)sqlite3_prepare(db, zSql, -1, &pDummy, 0);
140839
+ (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG,
140840
+ &pDummy, 0);
140736140841
(void)sqlite3_finalize(pDummy);
140737140842
sqlite3DbFree(db, zSql);
140738140843
}
140739140844
if( db->mallocFailed ){
140740140845
sqlite3ErrorMsg(db->pParse, "out of memory");
@@ -147529,36 +147634,36 @@
147529147634
return pExpr;
147530147635
}
147531147636
if( pSubst->isOuterJoin ){
147532147637
ExprSetProperty(pNew, EP_CanBeNull);
147533147638
}
147639
+ if( pNew->op==TK_TRUEFALSE ){
147640
+ pNew->u.iValue = sqlite3ExprTruthValue(pNew);
147641
+ pNew->op = TK_INTEGER;
147642
+ ExprSetProperty(pNew, EP_IntValue);
147643
+ }
147644
+
147645
+ /* Ensure that the expression now has an implicit collation sequence,
147646
+ ** just as it did when it was a column of a view or sub-query. */
147647
+ {
147648
+ CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew);
147649
+ CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
147650
+ pSubst->pCList->a[iColumn].pExpr
147651
+ );
147652
+ if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){
147653
+ pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew,
147654
+ (pColl ? pColl->zName : "BINARY")
147655
+ );
147656
+ }
147657
+ }
147658
+ ExprClearProperty(pNew, EP_Collate);
147534147659
if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
147535147660
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
147536147661
pExpr->flags & (EP_OuterON|EP_InnerON));
147537147662
}
147538147663
sqlite3ExprDelete(db, pExpr);
147539147664
pExpr = pNew;
147540
- if( pExpr->op==TK_TRUEFALSE ){
147541
- pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
147542
- pExpr->op = TK_INTEGER;
147543
- ExprSetProperty(pExpr, EP_IntValue);
147544
- }
147545
-
147546
- /* Ensure that the expression now has an implicit collation sequence,
147547
- ** just as it did when it was a column of a view or sub-query. */
147548
- {
147549
- CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
147550
- CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
147551
- pSubst->pCList->a[iColumn].pExpr
147552
- );
147553
- if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
147554
- pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
147555
- (pColl ? pColl->zName : "BINARY")
147556
- );
147557
- }
147558
- }
147559
- ExprClearProperty(pExpr, EP_Collate);
147560147665
}
147561147666
}
147562147667
}else{
147563147668
if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
147564147669
pExpr->iTable = pSubst->iNewTable;
@@ -148291,20 +148396,20 @@
148291148396
}
148292148397
148293148398
/* Transfer the FROM clause terms from the subquery into the
148294148399
** outer query.
148295148400
*/
148401
+ iNewParent = pSubSrc->a[0].iCursor;
148296148402
for(i=0; i<nSubSrc; i++){
148297148403
SrcItem *pItem = &pSrc->a[i+iFrom];
148298148404
assert( pItem->fg.isTabFunc==0 );
148299148405
assert( pItem->fg.isSubquery
148300148406
|| pItem->fg.fixedSchema
148301148407
|| pItem->u4.zDatabase==0 );
148302148408
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
148303148409
*pItem = pSubSrc->a[i];
148304148410
pItem->fg.jointype |= ltorj;
148305
- iNewParent = pSubSrc->a[i].iCursor;
148306148411
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
148307148412
}
148308148413
pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
148309148414
pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
148310148415
@@ -148340,10 +148445,11 @@
148340148445
pSub->pOrderBy = 0;
148341148446
}
148342148447
pWhere = pSub->pWhere;
148343148448
pSub->pWhere = 0;
148344148449
if( isOuterJoin>0 ){
148450
+ assert( pSubSrc->nSrc==1 );
148345148451
sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
148346148452
}
148347148453
if( pWhere ){
148348148454
if( pParent->pWhere ){
148349148455
pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
@@ -151439,11 +151545,11 @@
151439151545
sqlite3TreeViewSelect(0, p, 0);
151440151546
}
151441151547
#endif
151442151548
assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
151443151549
}else{
151444
- TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n"));
151550
+ TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n"));
151445151551
}
151446151552
151447151553
/* Convert unused result columns of the subquery into simple NULL
151448151554
** expressions, to avoid unneeded searching and computation.
151449151555
** tag-select-0440
@@ -158930,10 +159036,11 @@
158930159036
if( pOrigLhs ){
158931159037
sqlite3ExprListDelete(db, pOrigLhs);
158932159038
pNew->pLeft->x.pList = pLhs;
158933159039
}
158934159040
pSelect->pEList = pRhs;
159041
+ pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */
158935159042
if( pLhs && pLhs->nExpr==1 ){
158936159043
/* Take care here not to generate a TK_VECTOR containing only a
158937159044
** single value. Since the parser never creates such a vector, some
158938159045
** of the subroutines do not handle this case. */
158939159046
Expr *p = pLhs->a[0].pExpr;
@@ -164002,11 +164109,11 @@
164002164109
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
164003164110
){
164004164111
return 0;
164005164112
}
164006164113
if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
164007
- && ExprHasProperty(pTerm->pExpr, EP_InnerON)
164114
+ && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON))
164008164115
){
164009164116
return 0;
164010164117
}
164011164118
return 1;
164012164119
}
@@ -165495,11 +165602,11 @@
165495165602
return rc;
165496165603
}
165497165604
#endif /* SQLITE_ENABLE_STAT4 */
165498165605
165499165606
165500
-#ifdef WHERETRACE_ENABLED
165607
+#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG)
165501165608
/*
165502165609
** Print the content of a WhereTerm object
165503165610
*/
165504165611
SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
165505165612
if( pTerm==0 ){
@@ -165538,10 +165645,13 @@
165538165645
sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
165539165646
}
165540165647
sqlite3DebugPrintf("\n");
165541165648
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
165542165649
}
165650
+}
165651
+SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){
165652
+ sqlite3WhereTermPrint(pTerm, 0);
165543165653
}
165544165654
#endif
165545165655
165546165656
#ifdef WHERETRACE_ENABLED
165547165657
/*
@@ -166724,11 +166834,10 @@
166724166834
pParse = pWC->pWInfo->pParse;
166725166835
while( pWhere->op==TK_AND ){
166726166836
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
166727166837
pWhere = pWhere->pRight;
166728166838
}
166729
- if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
166730166839
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
166731166840
Expr *pExpr;
166732166841
pExpr = pTerm->pExpr;
166733166842
if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
166734166843
&& ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
@@ -169385,11 +169494,11 @@
169385169494
break;
169386169495
}
169387169496
}
169388169497
if( hasRightJoin
169389169498
&& ExprHasProperty(pTerm->pExpr, EP_InnerON)
169390
- && pTerm->pExpr->w.iJoin==pItem->iCursor
169499
+ && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor)
169391169500
){
169392169501
break; /* restriction (5) */
169393169502
}
169394169503
}
169395169504
if( pTerm<pEnd ) continue;
@@ -173846,10 +173955,17 @@
173846173955
** Then the "b" IdList records the list "a,b,c".
173847173956
*/
173848173957
struct TrigEvent { int a; IdList * b; };
173849173958
173850173959
struct FrameBound { int eType; Expr *pExpr; };
173960
+
173961
+/*
173962
+** Generate a syntax error
173963
+*/
173964
+static void parserSyntaxError(Parse *pParse, Token *p){
173965
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p);
173966
+}
173851173967
173852173968
/*
173853173969
** Disable lookaside memory allocation for objects that might be
173854173970
** shared across database connections.
173855173971
*/
@@ -178214,11 +178330,11 @@
178214178330
** that look like this: #1 #2 ... These terms refer to registers
178215178331
** in the virtual machine. #N is the N-th register. */
178216178332
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
178217178333
assert( t.n>=2 );
178218178334
if( pParse->nested==0 ){
178219
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
178335
+ parserSyntaxError(pParse, &t);
178220178336
yymsp[0].minor.yy454 = 0;
178221178337
}else{
178222178338
yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
178223178339
if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
178224178340
}
@@ -179062,11 +179178,11 @@
179062179178
#define TOKEN yyminor
179063179179
/************ Begin %syntax_error code ****************************************/
179064179180
179065179181
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
179066179182
if( TOKEN.z[0] ){
179067
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
179183
+ parserSyntaxError(pParse, &TOKEN);
179068179184
}else{
179069179185
sqlite3ErrorMsg(pParse, "incomplete input");
179070179186
}
179071179187
/************ End %syntax_error code ******************************************/
179072179188
sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
@@ -180553,11 +180669,13 @@
180553180669
}
180554180670
if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
180555180671
if( pParse->zErrMsg==0 ){
180556180672
pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
180557180673
}
180558
- sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
180674
+ if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){
180675
+ sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
180676
+ }
180559180677
nErr++;
180560180678
}
180561180679
pParse->zTail = zSql;
180562180680
#ifndef SQLITE_OMIT_VIRTUALTABLE
180563180681
sqlite3_free(pParse->apVtabLock);
@@ -185391,10 +185509,11 @@
185391185509
#ifndef SQLITE_OMIT_WINDOWFUNC
185392185510
sqlite3ShowWindow(0);
185393185511
sqlite3ShowWinFunc(0);
185394185512
#endif
185395185513
sqlite3ShowSelect(0);
185514
+ sqlite3ShowWhereTerm(0);
185396185515
}
185397185516
#endif
185398185517
break;
185399185518
}
185400185519
@@ -194537,14 +194656,15 @@
194537194656
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
194538194657
if( rc==SQLITE_OK ){
194539194658
Fts3PhraseToken *pToken;
194540194659
194541194660
p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
194542
- if( !p ) goto no_mem;
194543
-
194544194661
zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
194545
- if( !zTemp ) goto no_mem;
194662
+ if( !zTemp || !p ){
194663
+ rc = SQLITE_NOMEM;
194664
+ goto getnextstring_out;
194665
+ }
194546194666
194547194667
assert( nToken==ii );
194548194668
pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
194549194669
memset(pToken, 0, sizeof(Fts3PhraseToken));
194550194670
@@ -194555,53 +194675,51 @@
194555194675
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
194556194676
pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
194557194677
nToken = ii+1;
194558194678
}
194559194679
}
194560
-
194561
- pModule->xClose(pCursor);
194562
- pCursor = 0;
194563194680
}
194564194681
194565194682
if( rc==SQLITE_DONE ){
194566194683
int jj;
194567194684
char *zBuf = 0;
194568194685
194569194686
p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
194570
- if( !p ) goto no_mem;
194687
+ if( !p ){
194688
+ rc = SQLITE_NOMEM;
194689
+ goto getnextstring_out;
194690
+ }
194571194691
memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
194572194692
p->eType = FTSQUERY_PHRASE;
194573194693
p->pPhrase = (Fts3Phrase *)&p[1];
194574194694
p->pPhrase->iColumn = pParse->iDefaultCol;
194575194695
p->pPhrase->nToken = nToken;
194576194696
194577194697
zBuf = (char *)&p->pPhrase->aToken[nToken];
194698
+ assert( nTemp==0 || zTemp );
194578194699
if( zTemp ){
194579194700
memcpy(zBuf, zTemp, nTemp);
194580
- sqlite3_free(zTemp);
194581
- }else{
194582
- assert( nTemp==0 );
194583194701
}
194584194702
194585194703
for(jj=0; jj<p->pPhrase->nToken; jj++){
194586194704
p->pPhrase->aToken[jj].z = zBuf;
194587194705
zBuf += p->pPhrase->aToken[jj].n;
194588194706
}
194589194707
rc = SQLITE_OK;
194590194708
}
194591194709
194592
- *ppExpr = p;
194593
- return rc;
194594
-no_mem:
194595
-
194710
+ getnextstring_out:
194596194711
if( pCursor ){
194597194712
pModule->xClose(pCursor);
194598194713
}
194599194714
sqlite3_free(zTemp);
194600
- sqlite3_free(p);
194601
- *ppExpr = 0;
194602
- return SQLITE_NOMEM;
194715
+ if( rc!=SQLITE_OK ){
194716
+ sqlite3_free(p);
194717
+ p = 0;
194718
+ }
194719
+ *ppExpr = p;
194720
+ return rc;
194603194721
}
194604194722
194605194723
/*
194606194724
** The output variable *ppExpr is populated with an allocated Fts3Expr
194607194725
** structure, or set to 0 if the end of the input buffer is reached.
@@ -226191,10 +226309,12 @@
226191226309
if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){
226192226310
unsigned char *aPage = sqlite3PagerGetData(pDbPage);
226193226311
memcpy(aPage, pData, szPage);
226194226312
pTab->pgnoTrunc = 0;
226195226313
}
226314
+ }else{
226315
+ pTab->pgnoTrunc = 0;
226196226316
}
226197226317
sqlite3PagerUnref(pDbPage);
226198226318
return rc;
226199226319
226200226320
update_fail:
@@ -233154,17 +233274,32 @@
233154233274
** This is used to access token iToken of phrase hit iIdx within the
233155233275
** current row. If iIdx is less than zero or greater than or equal to the
233156233276
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
233157233277
** output variable (*ppToken) is set to point to a buffer containing the
233158233278
** matching document token, and (*pnToken) to the size of that buffer in
233159
-** bytes. This API is not available if the specified token matches a
233160
-** prefix query term. In that case both output variables are always set
233161
-** to 0.
233279
+** bytes.
233162233280
**
233163233281
** The output text is not a copy of the document text that was tokenized.
233164233282
** It is the output of the tokenizer module. For tokendata=1 tables, this
233165233283
** includes any embedded 0x00 and trailing data.
233284
+**
233285
+** This API may be slow in some cases if the token identified by parameters
233286
+** iIdx and iToken matched a prefix token in the query. In most cases, the
233287
+** first call to this API for each prefix token in the query is forced
233288
+** to scan the portion of the full-text index that matches the prefix
233289
+** token to collect the extra data required by this API. If the prefix
233290
+** token matches a large number of token instances in the document set,
233291
+** this may be a performance problem.
233292
+**
233293
+** If the user knows in advance that a query may use this API for a
233294
+** prefix token, FTS5 may be configured to collect all required data as part
233295
+** of the initial querying of the full-text index, avoiding the second scan
233296
+** entirely. This also causes prefix queries that do not use this API to
233297
+** run more slowly and use more memory. FTS5 may be configured in this way
233298
+** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
233299
+** option, or on a per-query basis using the
233300
+** [fts5_insttoken | fts5_insttoken()] user function.
233166233301
**
233167233302
** This API can be quite slow if used with an FTS5 table created with the
233168233303
** "detail=none" or "detail=column" option.
233169233304
**
233170233305
** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
@@ -233843,11 +233978,12 @@
233843233978
int nUsermerge; /* 'usermerge' setting */
233844233979
int nHashSize; /* Bytes of memory for in-memory hash */
233845233980
char *zRank; /* Name of rank function */
233846233981
char *zRankArgs; /* Arguments to rank function */
233847233982
int bSecureDelete; /* 'secure-delete' */
233848
- int nDeleteMerge; /* 'deletemerge' */
233983
+ int nDeleteMerge; /* 'deletemerge' */
233984
+ int bPrefixInsttoken; /* 'prefix-insttoken' */
233849233985
233850233986
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
233851233987
char **pzErrmsg;
233852233988
233853233989
#ifdef SQLITE_DEBUG
@@ -234100,11 +234236,18 @@
234100234236
static int sqlite3Fts5StructureTest(Fts5Index*, void*);
234101234237
234102234238
/*
234103234239
** Used by xInstToken():
234104234240
*/
234105
-static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*);
234241
+static int sqlite3Fts5IterToken(
234242
+ Fts5IndexIter *pIndexIter,
234243
+ const char *pToken, int nToken,
234244
+ i64 iRowid,
234245
+ int iCol,
234246
+ int iOff,
234247
+ const char **ppOut, int *pnOut
234248
+);
234106234249
234107234250
/*
234108234251
** Insert or remove data to or from the index. Each time a document is
234109234252
** added to or removed from the index, this function is called one or more
234110234253
** times.
@@ -238314,10 +238457,23 @@
238314238457
if( bVal<0 ){
238315238458
*pbBadkey = 1;
238316238459
}else{
238317238460
pConfig->bSecureDelete = (bVal ? 1 : 0);
238318238461
}
238462
+ }
238463
+
238464
+ else if( 0==sqlite3_stricmp(zKey, "insttoken") ){
238465
+ int bVal = -1;
238466
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
238467
+ bVal = sqlite3_value_int(pVal);
238468
+ }
238469
+ if( bVal<0 ){
238470
+ *pbBadkey = 1;
238471
+ }else{
238472
+ pConfig->bPrefixInsttoken = (bVal ? 1 : 0);
238473
+ }
238474
+
238319238475
}else{
238320238476
*pbBadkey = 1;
238321238477
}
238322238478
return rc;
238323238479
}
@@ -241449,11 +241605,11 @@
241449241605
&& memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
241450241606
){
241451241607
int rc = sqlite3Fts5PoslistWriterAppend(
241452241608
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
241453241609
);
241454
- if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){
241610
+ if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){
241455241611
int iCol = p->iOff>>32;
241456241612
int iTokOff = p->iOff & 0x7FFFFFFF;
241457241613
rc = sqlite3Fts5IndexIterWriteTokendata(
241458241614
pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
241459241615
);
@@ -241642,19 +241798,18 @@
241642241798
pPhrase = pExpr->apExprPhrase[iPhrase];
241643241799
if( iToken<0 || iToken>=pPhrase->nTerm ){
241644241800
return SQLITE_RANGE;
241645241801
}
241646241802
pTerm = &pPhrase->aTerm[iToken];
241647
- if( pTerm->bPrefix==0 ){
241648
- if( pExpr->pConfig->bTokendata ){
241649
- rc = sqlite3Fts5IterToken(
241650
- pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut
241651
- );
241652
- }else{
241653
- *ppOut = pTerm->pTerm;
241654
- *pnOut = pTerm->nFullTerm;
241655
- }
241803
+ if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){
241804
+ rc = sqlite3Fts5IterToken(
241805
+ pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm,
241806
+ iRowid, iCol, iOff+iToken, ppOut, pnOut
241807
+ );
241808
+ }else{
241809
+ *ppOut = pTerm->pTerm;
241810
+ *pnOut = pTerm->nFullTerm;
241656241811
}
241657241812
return rc;
241658241813
}
241659241814
241660241815
/*
@@ -248465,10 +248620,387 @@
248465248620
fts5BufferFree(&tmp);
248466248621
memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
248467248622
*p1 = out;
248468248623
}
248469248624
248625
+
248626
+/*
248627
+** Iterate through a range of entries in the FTS index, invoking the xVisit
248628
+** callback for each of them.
248629
+**
248630
+** Parameter pToken points to an nToken buffer containing an FTS index term
248631
+** (i.e. a document term with the preceding 1 byte index identifier -
248632
+** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits
248633
+** all entries for terms that have pToken/nToken as a prefix. If bPrefix
248634
+** is false, then only entries with pToken/nToken as the entire key are
248635
+** visited.
248636
+**
248637
+** If the current table is a tokendata=1 table, then if bPrefix is true then
248638
+** each index term is treated separately. However, if bPrefix is false, then
248639
+** all index terms corresponding to pToken/nToken are collapsed into a single
248640
+** term before the callback is invoked.
248641
+**
248642
+** The callback invoked for each entry visited is specified by paramter xVisit.
248643
+** Each time it is invoked, it is passed a pointer to the Fts5Index object,
248644
+** a copy of the 7th paramter to this function (pCtx) and a pointer to the
248645
+** iterator that indicates the current entry. If the current entry is the
248646
+** first with a new term (i.e. different from that of the previous entry,
248647
+** including the very first term), then the final two parameters are passed
248648
+** a pointer to the term and its size in bytes, respectively. If the current
248649
+** entry is not the first associated with its term, these two parameters
248650
+** are passed 0.
248651
+**
248652
+** If parameter pColset is not NULL, then it is used to filter entries before
248653
+** the callback is invoked.
248654
+*/
248655
+static int fts5VisitEntries(
248656
+ Fts5Index *p, /* Fts5 index object */
248657
+ Fts5Colset *pColset, /* Columns filter to apply, or NULL */
248658
+ u8 *pToken, /* Buffer containing token */
248659
+ int nToken, /* Size of buffer pToken in bytes */
248660
+ int bPrefix, /* True for a prefix scan */
248661
+ void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int),
248662
+ void *pCtx /* Passed as second argument to xVisit() */
248663
+){
248664
+ const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0)
248665
+ | FTS5INDEX_QUERY_SKIPEMPTY
248666
+ | FTS5INDEX_QUERY_NOOUTPUT;
248667
+ Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
248668
+ int bNewTerm = 1;
248669
+ Fts5Structure *pStruct = fts5StructureRead(p);
248670
+
248671
+ fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
248672
+ fts5IterSetOutputCb(&p->rc, p1);
248673
+ for( /* no-op */ ;
248674
+ fts5MultiIterEof(p, p1)==0;
248675
+ fts5MultiIterNext2(p, p1, &bNewTerm)
248676
+ ){
248677
+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
248678
+ int nNew = 0;
248679
+ const u8 *pNew = 0;
248680
+
248681
+ p1->xSetOutputs(p1, pSeg);
248682
+ if( p->rc ) break;
248683
+
248684
+ if( bNewTerm ){
248685
+ nNew = pSeg->term.n;
248686
+ pNew = pSeg->term.p;
248687
+ if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break;
248688
+ }
248689
+
248690
+ xVisit(p, pCtx, p1, pNew, nNew);
248691
+ }
248692
+ fts5MultiIterFree(p1);
248693
+
248694
+ fts5StructureRelease(pStruct);
248695
+ return p->rc;
248696
+}
248697
+
248698
+
248699
+/*
248700
+** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
248701
+** array of these for each row it visits (so all iRowid fields are the same).
248702
+** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an
248703
+** array of these for the entire query (in which case iRowid fields may take
248704
+** a variety of values).
248705
+**
248706
+** Each instance in the array indicates the iterator (and therefore term)
248707
+** associated with position iPos of rowid iRowid. This is used by the
248708
+** xInstToken() API.
248709
+**
248710
+** iRowid:
248711
+** Rowid for the current entry.
248712
+**
248713
+** iPos:
248714
+** Position of current entry within row. In the usual ((iCol<<32)+iOff)
248715
+** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()).
248716
+**
248717
+** iIter:
248718
+** If the Fts5TokenDataIter iterator that the entry is part of is
248719
+** actually an iterator (i.e. with nIter>0, not just a container for
248720
+** Fts5TokenDataMap structures), then this variable is an index into
248721
+** the apIter[] array. The corresponding term is that which the iterator
248722
+** at apIter[iIter] currently points to.
248723
+**
248724
+** Or, if the Fts5TokenDataIter iterator is just a container object
248725
+** (nIter==0), then iIter is an index into the term.p[] buffer where
248726
+** the term is stored.
248727
+**
248728
+** nByte:
248729
+** In the case where iIter is an index into term.p[], this variable
248730
+** is the size of the term in bytes. If iIter is an index into apIter[],
248731
+** this variable is unused.
248732
+*/
248733
+struct Fts5TokenDataMap {
248734
+ i64 iRowid; /* Row this token is located in */
248735
+ i64 iPos; /* Position of token */
248736
+ int iIter; /* Iterator token was read from */
248737
+ int nByte; /* Length of token in bytes (or 0) */
248738
+};
248739
+
248740
+/*
248741
+** An object used to supplement Fts5Iter for tokendata=1 iterators.
248742
+**
248743
+** This object serves two purposes. The first is as a container for an array
248744
+** of Fts5TokenDataMap structures, which are used to find the token required
248745
+** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and
248746
+** aMap[] variables.
248747
+*/
248748
+struct Fts5TokenDataIter {
248749
+ int nMapAlloc; /* Allocated size of aMap[] in entries */
248750
+ int nMap; /* Number of valid entries in aMap[] */
248751
+ Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
248752
+
248753
+ /* The following are used for prefix-queries only. */
248754
+ Fts5Buffer terms;
248755
+
248756
+ /* The following are used for other full-token tokendata queries only. */
248757
+ int nIter;
248758
+ int nIterAlloc;
248759
+ Fts5PoslistReader *aPoslistReader;
248760
+ int *aPoslistToIter;
248761
+ Fts5Iter *apIter[1];
248762
+};
248763
+
248764
+/*
248765
+** The two input arrays - a1[] and a2[] - are in sorted order. This function
248766
+** merges the two arrays together and writes the result to output array
248767
+** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
248768
+**
248769
+** Duplicate entries are copied into the output. So the size of the output
248770
+** array is always (n1+n2) entries.
248771
+*/
248772
+static void fts5TokendataMerge(
248773
+ Fts5TokenDataMap *a1, int n1, /* Input array 1 */
248774
+ Fts5TokenDataMap *a2, int n2, /* Input array 2 */
248775
+ Fts5TokenDataMap *aOut /* Output array */
248776
+){
248777
+ int i1 = 0;
248778
+ int i2 = 0;
248779
+
248780
+ assert( n1>=0 && n2>=0 );
248781
+ while( i1<n1 || i2<n2 ){
248782
+ Fts5TokenDataMap *pOut = &aOut[i1+i2];
248783
+ if( i2>=n2 || (i1<n1 && (
248784
+ a1[i1].iRowid<a2[i2].iRowid
248785
+ || (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos)
248786
+ ))){
248787
+ memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap));
248788
+ i1++;
248789
+ }else{
248790
+ memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap));
248791
+ i2++;
248792
+ }
248793
+ }
248794
+}
248795
+
248796
+
248797
+/*
248798
+** Append a mapping to the token-map belonging to object pT.
248799
+*/
248800
+static void fts5TokendataIterAppendMap(
248801
+ Fts5Index *p,
248802
+ Fts5TokenDataIter *pT,
248803
+ int iIter,
248804
+ int nByte,
248805
+ i64 iRowid,
248806
+ i64 iPos
248807
+){
248808
+ if( p->rc==SQLITE_OK ){
248809
+ if( pT->nMap==pT->nMapAlloc ){
248810
+ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
248811
+ int nAlloc = nNew * sizeof(Fts5TokenDataMap);
248812
+ Fts5TokenDataMap *aNew;
248813
+
248814
+ aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
248815
+ if( aNew==0 ){
248816
+ p->rc = SQLITE_NOMEM;
248817
+ return;
248818
+ }
248819
+
248820
+ pT->aMap = aNew;
248821
+ pT->nMapAlloc = nNew;
248822
+ }
248823
+
248824
+ pT->aMap[pT->nMap].iRowid = iRowid;
248825
+ pT->aMap[pT->nMap].iPos = iPos;
248826
+ pT->aMap[pT->nMap].iIter = iIter;
248827
+ pT->aMap[pT->nMap].nByte = nByte;
248828
+ pT->nMap++;
248829
+ }
248830
+}
248831
+
248832
+/*
248833
+** Sort the contents of the pT->aMap[] array.
248834
+**
248835
+** The sorting algorithm requries a malloc(). If this fails, an error code
248836
+** is left in Fts5Index.rc before returning.
248837
+*/
248838
+static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
248839
+ Fts5TokenDataMap *aTmp = 0;
248840
+ int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
248841
+
248842
+ aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
248843
+ if( aTmp ){
248844
+ Fts5TokenDataMap *a1 = pT->aMap;
248845
+ Fts5TokenDataMap *a2 = aTmp;
248846
+ i64 nHalf;
248847
+
248848
+ for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){
248849
+ int i1;
248850
+ for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){
248851
+ int n1 = MIN(nHalf, pT->nMap-i1);
248852
+ int n2 = MIN(nHalf, pT->nMap-i1-n1);
248853
+ fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]);
248854
+ }
248855
+ SWAPVAL(Fts5TokenDataMap*, a1, a2);
248856
+ }
248857
+
248858
+ if( a1!=pT->aMap ){
248859
+ memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap));
248860
+ }
248861
+ sqlite3_free(aTmp);
248862
+
248863
+#ifdef SQLITE_DEBUG
248864
+ {
248865
+ int ii;
248866
+ for(ii=1; ii<pT->nMap; ii++){
248867
+ Fts5TokenDataMap *p1 = &pT->aMap[ii-1];
248868
+ Fts5TokenDataMap *p2 = &pT->aMap[ii];
248869
+ assert( p1->iRowid<p2->iRowid
248870
+ || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos)
248871
+ );
248872
+ }
248873
+ }
248874
+#endif
248875
+ }
248876
+}
248877
+
248878
+/*
248879
+** Delete an Fts5TokenDataIter structure and its contents.
248880
+*/
248881
+static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
248882
+ if( pSet ){
248883
+ int ii;
248884
+ for(ii=0; ii<pSet->nIter; ii++){
248885
+ fts5MultiIterFree(pSet->apIter[ii]);
248886
+ }
248887
+ fts5BufferFree(&pSet->terms);
248888
+ sqlite3_free(pSet->aPoslistReader);
248889
+ sqlite3_free(pSet->aMap);
248890
+ sqlite3_free(pSet);
248891
+ }
248892
+}
248893
+
248894
+
248895
+/*
248896
+** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata()
248897
+** to pass data to prefixIterSetupTokendataCb().
248898
+*/
248899
+typedef struct TokendataSetupCtx TokendataSetupCtx;
248900
+struct TokendataSetupCtx {
248901
+ Fts5TokenDataIter *pT; /* Object being populated with mappings */
248902
+ int iTermOff; /* Offset of current term in terms.p[] */
248903
+ int nTermByte; /* Size of current term in bytes */
248904
+};
248905
+
248906
+/*
248907
+** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This
248908
+** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each
248909
+** position in the current position-list. It doesn't matter that some of
248910
+** these may be out of order - they will be sorted later.
248911
+*/
248912
+static void prefixIterSetupTokendataCb(
248913
+ Fts5Index *p,
248914
+ void *pCtx,
248915
+ Fts5Iter *p1,
248916
+ const u8 *pNew,
248917
+ int nNew
248918
+){
248919
+ TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx;
248920
+ int iPosOff = 0;
248921
+ i64 iPos = 0;
248922
+
248923
+ if( pNew ){
248924
+ pSetup->nTermByte = nNew-1;
248925
+ pSetup->iTermOff = pSetup->pT->terms.n;
248926
+ fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1);
248927
+ }
248928
+
248929
+ while( 0==sqlite3Fts5PoslistNext64(
248930
+ p1->base.pData, p1->base.nData, &iPosOff, &iPos
248931
+ ) ){
248932
+ fts5TokendataIterAppendMap(p,
248933
+ pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos
248934
+ );
248935
+ }
248936
+}
248937
+
248938
+
248939
+/*
248940
+** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries().
248941
+*/
248942
+typedef struct PrefixSetupCtx PrefixSetupCtx;
248943
+struct PrefixSetupCtx {
248944
+ void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
248945
+ void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
248946
+ i64 iLastRowid;
248947
+ int nMerge;
248948
+ Fts5Buffer *aBuf;
248949
+ int nBuf;
248950
+ Fts5Buffer doclist;
248951
+ TokendataSetupCtx *pTokendata;
248952
+};
248953
+
248954
+/*
248955
+** fts5VisitEntries() callback used by fts5SetupPrefixIter()
248956
+*/
248957
+static void prefixIterSetupCb(
248958
+ Fts5Index *p,
248959
+ void *pCtx,
248960
+ Fts5Iter *p1,
248961
+ const u8 *pNew,
248962
+ int nNew
248963
+){
248964
+ PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx;
248965
+ const int nMerge = pSetup->nMerge;
248966
+
248967
+ if( p1->base.nData>0 ){
248968
+ if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){
248969
+ int i;
248970
+ for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){
248971
+ int i1 = i*nMerge;
248972
+ int iStore;
248973
+ assert( i1+nMerge<=pSetup->nBuf );
248974
+ for(iStore=i1; iStore<i1+nMerge; iStore++){
248975
+ if( pSetup->aBuf[iStore].n==0 ){
248976
+ fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]);
248977
+ fts5BufferZero(&pSetup->doclist);
248978
+ break;
248979
+ }
248980
+ }
248981
+ if( iStore==i1+nMerge ){
248982
+ pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]);
248983
+ for(iStore=i1; iStore<i1+nMerge; iStore++){
248984
+ fts5BufferZero(&pSetup->aBuf[iStore]);
248985
+ }
248986
+ }
248987
+ }
248988
+ pSetup->iLastRowid = 0;
248989
+ }
248990
+
248991
+ pSetup->xAppend(
248992
+ p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist
248993
+ );
248994
+ pSetup->iLastRowid = p1->base.iRowid;
248995
+ }
248996
+
248997
+ if( pSetup->pTokendata ){
248998
+ prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew);
248999
+ }
249000
+}
249001
+
248470249002
static void fts5SetupPrefixIter(
248471249003
Fts5Index *p, /* Index to read from */
248472249004
int bDesc, /* True for "ORDER BY rowid DESC" */
248473249005
int iIdx, /* Index to scan for data */
248474249006
u8 *pToken, /* Buffer containing prefix to match */
@@ -248475,137 +249007,89 @@
248475249007
int nToken, /* Size of buffer pToken in bytes */
248476249008
Fts5Colset *pColset, /* Restrict matches to these columns */
248477249009
Fts5Iter **ppIter /* OUT: New iterator */
248478249010
){
248479249011
Fts5Structure *pStruct;
248480
- Fts5Buffer *aBuf;
248481
- int nBuf = 32;
248482
- int nMerge = 1;
249012
+ PrefixSetupCtx s;
249013
+ TokendataSetupCtx s2;
248483249014
248484
- void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
248485
- void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
249015
+ memset(&s, 0, sizeof(s));
249016
+ memset(&s2, 0, sizeof(s2));
249017
+
249018
+ s.nMerge = 1;
249019
+ s.iLastRowid = 0;
249020
+ s.nBuf = 32;
249021
+ if( iIdx==0
249022
+ && p->pConfig->eDetail==FTS5_DETAIL_FULL
249023
+ && p->pConfig->bPrefixInsttoken
249024
+ ){
249025
+ s.pTokendata = &s2;
249026
+ s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
249027
+ }
249028
+
248486249029
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
248487
- xMerge = fts5MergeRowidLists;
248488
- xAppend = fts5AppendRowid;
249030
+ s.xMerge = fts5MergeRowidLists;
249031
+ s.xAppend = fts5AppendRowid;
248489249032
}else{
248490
- nMerge = FTS5_MERGE_NLIST-1;
248491
- nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
248492
- xMerge = fts5MergePrefixLists;
248493
- xAppend = fts5AppendPoslist;
249033
+ s.nMerge = FTS5_MERGE_NLIST-1;
249034
+ s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
249035
+ s.xMerge = fts5MergePrefixLists;
249036
+ s.xAppend = fts5AppendPoslist;
248494249037
}
248495249038
248496
- aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
249039
+ s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf);
248497249040
pStruct = fts5StructureRead(p);
248498
- assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );
249041
+ assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) );
248499249042
248500249043
if( p->rc==SQLITE_OK ){
248501
- const int flags = FTS5INDEX_QUERY_SCAN
248502
- | FTS5INDEX_QUERY_SKIPEMPTY
248503
- | FTS5INDEX_QUERY_NOOUTPUT;
249044
+ void *pCtx = (void*)&s;
248504249045
int i;
248505
- i64 iLastRowid = 0;
248506
- Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
248507249046
Fts5Data *pData;
248508
- Fts5Buffer doclist;
248509
- int bNewTerm = 1;
248510
-
248511
- memset(&doclist, 0, sizeof(doclist));
248512249047
248513249048
/* If iIdx is non-zero, then it is the number of a prefix-index for
248514249049
** prefixes 1 character longer than the prefix being queried for. That
248515249050
** index contains all the doclists required, except for the one
248516249051
** corresponding to the prefix itself. That one is extracted from the
248517249052
** main term index here. */
248518249053
if( iIdx!=0 ){
248519
- int dummy = 0;
248520
- const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
248521249054
pToken[0] = FTS5_MAIN_PREFIX;
248522
- fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
248523
- fts5IterSetOutputCb(&p->rc, p1);
248524
- for(;
248525
- fts5MultiIterEof(p, p1)==0;
248526
- fts5MultiIterNext2(p, p1, &dummy)
248527
- ){
248528
- Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
248529
- p1->xSetOutputs(p1, pSeg);
248530
- if( p1->base.nData ){
248531
- xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
248532
- iLastRowid = p1->base.iRowid;
248533
- }
248534
- }
248535
- fts5MultiIterFree(p1);
249055
+ fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx);
248536249056
}
248537249057
248538249058
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
248539
- fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
248540
- fts5IterSetOutputCb(&p->rc, p1);
248541
-
248542
- for( /* no-op */ ;
248543
- fts5MultiIterEof(p, p1)==0;
248544
- fts5MultiIterNext2(p, p1, &bNewTerm)
248545
- ){
248546
- Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
248547
- int nTerm = pSeg->term.n;
248548
- const u8 *pTerm = pSeg->term.p;
248549
- p1->xSetOutputs(p1, pSeg);
248550
-
248551
- assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
248552
- if( bNewTerm ){
248553
- if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
248554
- }
248555
-
248556
- if( p1->base.nData==0 ) continue;
248557
- if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
248558
- for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
248559
- int i1 = i*nMerge;
248560
- int iStore;
248561
- assert( i1+nMerge<=nBuf );
248562
- for(iStore=i1; iStore<i1+nMerge; iStore++){
248563
- if( aBuf[iStore].n==0 ){
248564
- fts5BufferSwap(&doclist, &aBuf[iStore]);
248565
- fts5BufferZero(&doclist);
248566
- break;
248567
- }
248568
- }
248569
- if( iStore==i1+nMerge ){
248570
- xMerge(p, &doclist, nMerge, &aBuf[i1]);
248571
- for(iStore=i1; iStore<i1+nMerge; iStore++){
248572
- fts5BufferZero(&aBuf[iStore]);
248573
- }
248574
- }
248575
- }
248576
- iLastRowid = 0;
248577
- }
248578
-
248579
- xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
248580
- iLastRowid = p1->base.iRowid;
248581
- }
248582
-
248583
- assert( (nBuf%nMerge)==0 );
248584
- for(i=0; i<nBuf; i+=nMerge){
249059
+ fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx);
249060
+
249061
+ assert( (s.nBuf%s.nMerge)==0 );
249062
+ for(i=0; i<s.nBuf; i+=s.nMerge){
248585249063
int iFree;
248586249064
if( p->rc==SQLITE_OK ){
248587
- xMerge(p, &doclist, nMerge, &aBuf[i]);
249065
+ s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]);
248588249066
}
248589
- for(iFree=i; iFree<i+nMerge; iFree++){
248590
- fts5BufferFree(&aBuf[iFree]);
249067
+ for(iFree=i; iFree<i+s.nMerge; iFree++){
249068
+ fts5BufferFree(&s.aBuf[iFree]);
248591249069
}
248592249070
}
248593
- fts5MultiIterFree(p1);
248594249071
248595
- pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);
249072
+ pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING);
248596249073
if( pData ){
248597249074
pData->p = (u8*)&pData[1];
248598
- pData->nn = pData->szLeaf = doclist.n;
248599
- if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n);
249075
+ pData->nn = pData->szLeaf = s.doclist.n;
249076
+ if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n);
248600249077
fts5MultiIterNew2(p, pData, bDesc, ppIter);
248601249078
}
248602
- fts5BufferFree(&doclist);
249079
+
249080
+ if( p->rc==SQLITE_OK && s.pTokendata ){
249081
+ fts5TokendataIterSortMap(p, s2.pT);
249082
+ (*ppIter)->pTokenDataIter = s2.pT;
249083
+ s2.pT = 0;
249084
+ }
248603249085
}
248604249086
249087
+ fts5TokendataIterDelete(s2.pT);
249088
+ fts5BufferFree(&s.doclist);
248605249089
fts5StructureRelease(pStruct);
248606
- sqlite3_free(aBuf);
249090
+ sqlite3_free(s.aBuf);
248607249091
}
248608249092
248609249093
248610249094
/*
248611249095
** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
@@ -248855,42 +249339,10 @@
248855249339
static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
248856249340
fts5DataRelease(pSeg->pLeaf);
248857249341
pSeg->pLeaf = 0;
248858249342
}
248859249343
248860
-/*
248861
-** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
248862
-** array of these for each row it visits. Or, for an iterator used by an
248863
-** "ORDER BY rank" query, it accumulates an array of these for the entire
248864
-** query.
248865
-**
248866
-** Each instance in the array indicates the iterator (and therefore term)
248867
-** associated with position iPos of rowid iRowid. This is used by the
248868
-** xInstToken() API.
248869
-*/
248870
-struct Fts5TokenDataMap {
248871
- i64 iRowid; /* Row this token is located in */
248872
- i64 iPos; /* Position of token */
248873
- int iIter; /* Iterator token was read from */
248874
-};
248875
-
248876
-/*
248877
-** An object used to supplement Fts5Iter for tokendata=1 iterators.
248878
-*/
248879
-struct Fts5TokenDataIter {
248880
- int nIter;
248881
- int nIterAlloc;
248882
-
248883
- int nMap;
248884
- int nMapAlloc;
248885
- Fts5TokenDataMap *aMap;
248886
-
248887
- Fts5PoslistReader *aPoslistReader;
248888
- int *aPoslistToIter;
248889
- Fts5Iter *apIter[1];
248890
-};
248891
-
248892249344
/*
248893249345
** This function appends iterator pAppend to Fts5TokenDataIter pIn and
248894249346
** returns the result.
248895249347
*/
248896249348
static Fts5TokenDataIter *fts5AppendTokendataIter(
@@ -248923,58 +249375,10 @@
248923249375
assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
248924249376
248925249377
return pRet;
248926249378
}
248927249379
248928
-/*
248929
-** Delete an Fts5TokenDataIter structure and its contents.
248930
-*/
248931
-static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
248932
- if( pSet ){
248933
- int ii;
248934
- for(ii=0; ii<pSet->nIter; ii++){
248935
- fts5MultiIterFree(pSet->apIter[ii]);
248936
- }
248937
- sqlite3_free(pSet->aPoslistReader);
248938
- sqlite3_free(pSet->aMap);
248939
- sqlite3_free(pSet);
248940
- }
248941
-}
248942
-
248943
-/*
248944
-** Append a mapping to the token-map belonging to object pT.
248945
-*/
248946
-static void fts5TokendataIterAppendMap(
248947
- Fts5Index *p,
248948
- Fts5TokenDataIter *pT,
248949
- int iIter,
248950
- i64 iRowid,
248951
- i64 iPos
248952
-){
248953
- if( p->rc==SQLITE_OK ){
248954
- if( pT->nMap==pT->nMapAlloc ){
248955
- int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
248956
- int nByte = nNew * sizeof(Fts5TokenDataMap);
248957
- Fts5TokenDataMap *aNew;
248958
-
248959
- aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
248960
- if( aNew==0 ){
248961
- p->rc = SQLITE_NOMEM;
248962
- return;
248963
- }
248964
-
248965
- pT->aMap = aNew;
248966
- pT->nMapAlloc = nNew;
248967
- }
248968
-
248969
- pT->aMap[pT->nMap].iRowid = iRowid;
248970
- pT->aMap[pT->nMap].iPos = iPos;
248971
- pT->aMap[pT->nMap].iIter = iIter;
248972
- pT->nMap++;
248973
- }
248974
-}
248975
-
248976249380
/*
248977249381
** The iterator passed as the only argument must be a tokendata=1 iterator
248978249382
** (pIter->pTokenDataIter!=0). This function sets the iterator output
248979249383
** variables (pIter->base.*) according to the contents of the current
248980249384
** row.
@@ -249011,11 +249415,11 @@
249011249415
int eDetail = pIter->pIndex->pConfig->eDetail;
249012249416
pIter->base.bEof = 0;
249013249417
pIter->base.iRowid = iRowid;
249014249418
249015249419
if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
249016
- fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1);
249420
+ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1);
249017249421
}else
249018249422
if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
249019249423
int nReader = 0;
249020249424
int nByte = 0;
249021249425
i64 iPrev = 0;
@@ -249264,10 +249668,11 @@
249264249668
249265249669
if( p->rc==SQLITE_OK ){
249266249670
pRet = fts5MultiIterAlloc(p, 0);
249267249671
}
249268249672
if( pRet ){
249673
+ pRet->nSeg = 0;
249269249674
pRet->pTokenDataIter = pSet;
249270249675
if( pSet ){
249271249676
fts5IterSetOutputsTokendata(pRet);
249272249677
}else{
249273249678
pRet->base.bEof = 1;
@@ -249278,11 +249683,10 @@
249278249683
249279249684
fts5StructureRelease(pStruct);
249280249685
fts5BufferFree(&bSeek);
249281249686
return pRet;
249282249687
}
249283
-
249284249688
249285249689
/*
249286249690
** Open a new iterator to iterate though all rowid that match the
249287249691
** specified token or token prefix.
249288249692
*/
@@ -249304,10 +249708,15 @@
249304249708
int iIdx = 0; /* Index to search */
249305249709
int iPrefixIdx = 0; /* +1 prefix index */
249306249710
int bTokendata = pConfig->bTokendata;
249307249711
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
249308249712
249713
+ /* The NOTOKENDATA flag is set when each token in a tokendata=1 table
249714
+ ** should be treated individually, instead of merging all those with
249715
+ ** a common prefix into a single entry. This is used, for example, by
249716
+ ** queries performed as part of an integrity-check, or by the fts5vocab
249717
+ ** module. */
249309249718
if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
249310249719
bTokendata = 0;
249311249720
}
249312249721
249313249722
/* Figure out which index to search and set iIdx accordingly. If this
@@ -249334,11 +249743,11 @@
249334249743
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
249335249744
}
249336249745
}
249337249746
249338249747
if( bTokendata && iIdx==0 ){
249339
- buf.p[0] = '0';
249748
+ buf.p[0] = FTS5_MAIN_PREFIX;
249340249749
pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
249341249750
}else if( iIdx<=pConfig->nPrefix ){
249342249751
/* Straight index lookup */
249343249752
Fts5Structure *pStruct = fts5StructureRead(p);
249344249753
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
@@ -249347,11 +249756,11 @@
249347249756
pColset, buf.p, nToken+1, -1, 0, &pRet
249348249757
);
249349249758
fts5StructureRelease(pStruct);
249350249759
}
249351249760
}else{
249352
- /* Scan multiple terms in the main index */
249761
+ /* Scan multiple terms in the main index for a prefix query. */
249353249762
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
249354249763
fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
249355249764
if( pRet==0 ){
249356249765
assert( p->rc!=SQLITE_OK );
249357249766
}else{
@@ -249383,11 +249792,12 @@
249383249792
** Move to the next matching rowid.
249384249793
*/
249385249794
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
249386249795
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249387249796
assert( pIter->pIndex->rc==SQLITE_OK );
249388
- if( pIter->pTokenDataIter ){
249797
+ if( pIter->nSeg==0 ){
249798
+ assert( pIter->pTokenDataIter );
249389249799
fts5TokendataIterNext(pIter, 0, 0);
249390249800
}else{
249391249801
fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
249392249802
}
249393249803
return fts5IndexReturn(pIter->pIndex);
@@ -249420,11 +249830,12 @@
249420249830
** definition of "at or after" depends on whether this iterator iterates
249421249831
** in ascending or descending rowid order.
249422249832
*/
249423249833
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
249424249834
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249425
- if( pIter->pTokenDataIter ){
249835
+ if( pIter->nSeg==0 ){
249836
+ assert( pIter->pTokenDataIter );
249426249837
fts5TokendataIterNext(pIter, 1, iMatch);
249427249838
}else{
249428249839
fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
249429249840
}
249430249841
return fts5IndexReturn(pIter->pIndex);
@@ -249438,32 +249849,87 @@
249438249849
const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
249439249850
assert_nc( z || n<=1 );
249440249851
*pn = n-1;
249441249852
return (z ? &z[1] : 0);
249442249853
}
249854
+
249855
+/*
249856
+** pIter is a prefix query. This function populates pIter->pTokenDataIter
249857
+** with an Fts5TokenDataIter object containing mappings for all rows
249858
+** matched by the query.
249859
+*/
249860
+static int fts5SetupPrefixIterTokendata(
249861
+ Fts5Iter *pIter,
249862
+ const char *pToken, /* Token prefix to search for */
249863
+ int nToken /* Size of pToken in bytes */
249864
+){
249865
+ Fts5Index *p = pIter->pIndex;
249866
+ Fts5Buffer token = {0, 0, 0};
249867
+ TokendataSetupCtx ctx;
249868
+
249869
+ memset(&ctx, 0, sizeof(ctx));
249870
+
249871
+ fts5BufferGrow(&p->rc, &token, nToken+1);
249872
+ ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT));
249873
+
249874
+ if( p->rc==SQLITE_OK ){
249875
+
249876
+ /* Fill in the token prefix to search for */
249877
+ token.p[0] = FTS5_MAIN_PREFIX;
249878
+ memcpy(&token.p[1], pToken, nToken);
249879
+ token.n = nToken+1;
249880
+
249881
+ fts5VisitEntries(
249882
+ p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx
249883
+ );
249884
+
249885
+ fts5TokendataIterSortMap(p, ctx.pT);
249886
+ }
249887
+
249888
+ if( p->rc==SQLITE_OK ){
249889
+ pIter->pTokenDataIter = ctx.pT;
249890
+ }else{
249891
+ fts5TokendataIterDelete(ctx.pT);
249892
+ }
249893
+ fts5BufferFree(&token);
249894
+
249895
+ return fts5IndexReturn(p);
249896
+}
249443249897
249444249898
/*
249445249899
** This is used by xInstToken() to access the token at offset iOff, column
249446249900
** iCol of row iRowid. The token is returned via output variables *ppOut
249447249901
** and *pnOut. The iterator passed as the first argument must be a tokendata=1
249448249902
** iterator (pIter->pTokenDataIter!=0).
249903
+**
249904
+** pToken/nToken:
249449249905
*/
249450249906
static int sqlite3Fts5IterToken(
249451249907
Fts5IndexIter *pIndexIter,
249908
+ const char *pToken, int nToken,
249452249909
i64 iRowid,
249453249910
int iCol,
249454249911
int iOff,
249455249912
const char **ppOut, int *pnOut
249456249913
){
249457249914
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249458249915
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
249459
- Fts5TokenDataMap *aMap = pT->aMap;
249460249916
i64 iPos = (((i64)iCol)<<32) + iOff;
249461
-
249917
+ Fts5TokenDataMap *aMap = 0;
249462249918
int i1 = 0;
249463
- int i2 = pT->nMap;
249919
+ int i2 = 0;
249464249920
int iTest = 0;
249921
+
249922
+ assert( pT || (pToken && pIter->nSeg>0) );
249923
+ if( pT==0 ){
249924
+ int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken);
249925
+ if( rc!=SQLITE_OK ) return rc;
249926
+ pT = pIter->pTokenDataIter;
249927
+ }
249928
+
249929
+ i2 = pT->nMap;
249930
+ aMap = pT->aMap;
249465249931
249466249932
while( i2>i1 ){
249467249933
iTest = (i1 + i2) / 2;
249468249934
249469249935
if( aMap[iTest].iRowid<iRowid ){
@@ -249483,13 +249949,19 @@
249483249949
}
249484249950
}
249485249951
}
249486249952
249487249953
if( i2>i1 ){
249488
- Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
249489
- *ppOut = (const char*)pMap->aSeg[0].term.p+1;
249490
- *pnOut = pMap->aSeg[0].term.n-1;
249954
+ if( pIter->nSeg==0 ){
249955
+ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
249956
+ *ppOut = (const char*)pMap->aSeg[0].term.p+1;
249957
+ *pnOut = pMap->aSeg[0].term.n-1;
249958
+ }else{
249959
+ Fts5TokenDataMap *p = &aMap[iTest];
249960
+ *ppOut = (const char*)&pT->terms.p[p->iIter];
249961
+ *pnOut = aMap[iTest].nByte;
249962
+ }
249491249963
}
249492249964
249493249965
return SQLITE_OK;
249494249966
}
249495249967
@@ -249497,11 +249969,13 @@
249497249969
** Clear any existing entries from the token-map associated with the
249498249970
** iterator passed as the only argument.
249499249971
*/
249500249972
static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
249501249973
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249502
- if( pIter && pIter->pTokenDataIter ){
249974
+ if( pIter && pIter->pTokenDataIter
249975
+ && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL)
249976
+ ){
249503249977
pIter->pTokenDataIter->nMap = 0;
249504249978
}
249505249979
}
249506249980
249507249981
/*
@@ -249517,21 +249991,33 @@
249517249991
i64 iRowid, int iCol, int iOff
249518249992
){
249519249993
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249520249994
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
249521249995
Fts5Index *p = pIter->pIndex;
249522
- int ii;
249996
+ i64 iPos = (((i64)iCol)<<32) + iOff;
249523249997
249524249998
assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
249525
- assert( pIter->pTokenDataIter );
249526
-
249527
- for(ii=0; ii<pT->nIter; ii++){
249528
- Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
249529
- if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
249530
- }
249531
- if( ii<pT->nIter ){
249532
- fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff);
249999
+ assert( pIter->pTokenDataIter || pIter->nSeg>0 );
250000
+ if( pIter->nSeg>0 ){
250001
+ /* This is a prefix term iterator. */
250002
+ if( pT==0 ){
250003
+ pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));
250004
+ pIter->pTokenDataIter = pT;
250005
+ }
250006
+ if( pT ){
250007
+ fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
250008
+ fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
250009
+ }
250010
+ }else{
250011
+ int ii;
250012
+ for(ii=0; ii<pT->nIter; ii++){
250013
+ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
250014
+ if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
250015
+ }
250016
+ if( ii<pT->nIter ){
250017
+ fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos);
250018
+ }
249533250019
}
249534250020
return fts5IndexReturn(p);
249535250021
}
249536250022
249537250023
/*
@@ -251432,10 +251918,11 @@
251432251918
** containing a copy of the header from an Fts5Config pointer.
251433251919
*/
251434251920
#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
251435251921
#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
251436251922
251923
+#define FTS5_INSTTOKEN_SUBTYPE 73
251437251924
251438251925
/*
251439251926
** Each auxiliary function registered with the FTS5 module is represented
251440251927
** by an object of the following type. All such objects are stored as part
251441251928
** of the Fts5Global.pAux list.
@@ -252757,10 +253244,11 @@
252757253244
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
252758253245
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
252759253246
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
252760253247
int iCol; /* Column on LHS of MATCH operator */
252761253248
char **pzErrmsg = pConfig->pzErrmsg;
253249
+ int bPrefixInsttoken = pConfig->bPrefixInsttoken;
252762253250
int i;
252763253251
int iIdxStr = 0;
252764253252
Fts5Expr *pExpr = 0;
252765253253
252766253254
assert( pConfig->bLock==0 );
@@ -252792,10 +253280,13 @@
252792253280
int bInternal = 0;
252793253281
252794253282
rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
252795253283
if( rc!=SQLITE_OK ) goto filter_out;
252796253284
if( zText==0 ) zText = "";
253285
+ if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){
253286
+ pConfig->bPrefixInsttoken = 1;
253287
+ }
252797253288
252798253289
iCol = 0;
252799253290
do{
252800253291
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
252801253292
iIdxStr++;
@@ -252932,10 +253423,11 @@
252932253423
}
252933253424
252934253425
filter_out:
252935253426
sqlite3Fts5ExprFree(pExpr);
252936253427
pConfig->pzErrmsg = pzErrmsg;
253428
+ pConfig->bPrefixInsttoken = bPrefixInsttoken;
252937253429
return rc;
252938253430
}
252939253431
252940253432
/*
252941253433
** This is the xEof method of the virtual table. SQLite calls this
@@ -254927,11 +255419,11 @@
254927255419
int nArg, /* Number of args */
254928255420
sqlite3_value **apUnused /* Function arguments */
254929255421
){
254930255422
assert( nArg==0 );
254931255423
UNUSED_PARAM2(nArg, apUnused);
254932
- sqlite3_result_text(pCtx, "fts5: 2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8", -1, SQLITE_TRANSIENT);
255424
+ sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT);
254933255425
}
254934255426
254935255427
/*
254936255428
** Implementation of fts5_locale(LOCALE, TEXT) function.
254937255429
**
@@ -254990,10 +255482,24 @@
254990255482
assert( &pCsr[nText]==&pBlob[nBlob] );
254991255483
254992255484
sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free);
254993255485
}
254994255486
}
255487
+
255488
+/*
255489
+** Implementation of fts5_insttoken() function.
255490
+*/
255491
+static void fts5InsttokenFunc(
255492
+ sqlite3_context *pCtx, /* Function call context */
255493
+ int nArg, /* Number of args */
255494
+ sqlite3_value **apArg /* Function arguments */
255495
+){
255496
+ assert( nArg==1 );
255497
+ (void)nArg;
255498
+ sqlite3_result_value(pCtx, apArg[0]);
255499
+ sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE);
255500
+}
254995255501
254996255502
/*
254997255503
** Return true if zName is the extension on one of the shadow tables used
254998255504
** by this module.
254999255505
*/
@@ -255120,13 +255626,20 @@
255120255626
);
255121255627
}
255122255628
if( rc==SQLITE_OK ){
255123255629
rc = sqlite3_create_function(
255124255630
db, "fts5_locale", 2,
255125
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
255631
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE,
255126255632
p, fts5LocaleFunc, 0, 0
255127255633
);
255634
+ }
255635
+ if( rc==SQLITE_OK ){
255636
+ rc = sqlite3_create_function(
255637
+ db, "fts5_insttoken", 1,
255638
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
255639
+ p, fts5InsttokenFunc, 0, 0
255640
+ );
255128255641
}
255129255642
}
255130255643
255131255644
/* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
255132255645
** fts5_test_mi.c is compiled and linked into the executable. And call
@@ -258048,11 +258561,11 @@
258048258561
int rc = SQLITE_OK;
258049258562
char aBuf[32];
258050258563
char *zOut = aBuf;
258051258564
int ii;
258052258565
const unsigned char *zIn = (const unsigned char*)pText;
258053
- const unsigned char *zEof = &zIn[nText];
258566
+ const unsigned char *zEof = (zIn ? &zIn[nText] : 0);
258054258567
u32 iCode = 0;
258055258568
int aStart[3]; /* Input offset of each character in aBuf[] */
258056258569
258057258570
UNUSED_PARAM(unusedFlags);
258058258571
258059258572
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** 81202d2ab5963fdcf20555b6d0b31cc955ac with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.48.0"
469 #define SQLITE_VERSION_NUMBER 3048000
470 #define SQLITE_SOURCE_ID "2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -4521,15 +4521,26 @@
4521 **
4522 ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
4523 ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
4524 ** to return an error (error code SQLITE_ERROR) if the statement uses
4525 ** any virtual tables.
 
 
 
 
 
 
 
 
 
 
4526 ** </dl>
4527 */
4528 #define SQLITE_PREPARE_PERSISTENT 0x01
4529 #define SQLITE_PREPARE_NORMALIZE 0x02
4530 #define SQLITE_PREPARE_NO_VTAB 0x04
 
4531
4532 /*
4533 ** CAPI3REF: Compiling An SQL Statement
4534 ** KEYWORDS: {SQL statement compiler}
4535 ** METHOD: sqlite3
@@ -13467,17 +13478,32 @@
13467 ** This is used to access token iToken of phrase hit iIdx within the
13468 ** current row. If iIdx is less than zero or greater than or equal to the
13469 ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
13470 ** output variable (*ppToken) is set to point to a buffer containing the
13471 ** matching document token, and (*pnToken) to the size of that buffer in
13472 ** bytes. This API is not available if the specified token matches a
13473 ** prefix query term. In that case both output variables are always set
13474 ** to 0.
13475 **
13476 ** The output text is not a copy of the document text that was tokenized.
13477 ** It is the output of the tokenizer module. For tokendata=1 tables, this
13478 ** includes any embedded 0x00 and trailing data.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13479 **
13480 ** This API can be quite slow if used with an FTS5 table created with the
13481 ** "detail=none" or "detail=column" option.
13482 **
13483 ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
@@ -17049,11 +17075,11 @@
17049
17050 /*
17051 ** Additional non-public SQLITE_PREPARE_* flags
17052 */
17053 #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */
17054 #define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */
17055
17056 /*
17057 ** Prototypes for the VDBE interface. See comments on the implementation
17058 ** for a description of what each of these routines does.
17059 */
@@ -32279,10 +32305,11 @@
32279 && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
32280 ){
32281 pExpr = pExpr->pLeft;
32282 }
32283 if( pExpr==0 ) return;
 
32284 db->errByteOffset = pExpr->w.iOfst;
32285 }
32286
32287 /*
32288 ** Enlarge the memory allocation on a StrAccum object so that it is
@@ -33008,11 +33035,11 @@
33008 }
33009 if( pItem->fg.isCte ){
33010 sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
33011 }
33012 if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
33013 sqlite3_str_appendf(&x, " ON");
33014 }
33015 if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
33016 if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
33017 if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
33018 if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
@@ -34092,10 +34119,14 @@
34092 **
34093 ** This routines are given external linkage so that they will always be
34094 ** accessible to the debugging, and to avoid warnings about unused
34095 ** functions. But these routines only exist in debugging builds, so they
34096 ** do not contaminate the interface.
 
 
 
 
34097 */
34098 SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
34099 SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
34100 SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
34101 SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
@@ -35668,12 +35699,12 @@
35668 int esign = 1; /* sign of exponent */
35669 int e = 0; /* exponent */
35670 int eValid = 1; /* True exponent is either not used or is well-formed */
35671 int nDigit = 0; /* Number of digits processed */
35672 int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
 
35673 double rr[2];
35674 u64 s2;
35675
35676 assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
35677 *pResult = 0.0; /* Default return value, in case of an error */
35678 if( length==0 ) return 0;
35679
@@ -35772,25 +35803,36 @@
35772
35773 /* adjust exponent by d, and update sign */
35774 e = (e*esign) + d;
35775
35776 /* Try to adjust the exponent to make it smaller */
35777 while( e>0 && s<(LARGEST_UINT64/10) ){
35778 s *= 10;
35779 e--;
35780 }
35781 while( e<0 && (s%10)==0 ){
35782 s /= 10;
35783 e++;
35784 }
35785
35786 rr[0] = (double)s;
35787 s2 = (u64)rr[0];
35788 #if defined(_MSC_VER) && _MSC_VER<1700
35789 if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
 
 
35790 #endif
35791 rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
 
 
 
 
 
 
 
 
 
35792 if( e>0 ){
35793 while( e>=100 ){
35794 e -= 100;
35795 dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
35796 }
@@ -51771,11 +51813,11 @@
51771 */
51772 char *zTmpname = 0; /* For temporary filename, if necessary. */
51773
51774 int rc = SQLITE_OK; /* Function Return Code */
51775 #if !defined(NDEBUG) || SQLITE_OS_WINCE
51776 int eType = flags&0xFFFFFF00; /* Type of file to open */
51777 #endif
51778
51779 int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
51780 int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
51781 int isCreate = (flags & SQLITE_OPEN_CREATE);
@@ -112021,11 +112063,11 @@
112021 **
112022 ** (4) If pSrc is the right operand of a LEFT JOIN, then...
112023 ** (4a) pExpr must come from an ON clause..
112024 ** (4b) and specifically the ON clause associated with the LEFT JOIN.
112025 **
112026 ** (5) If pSrc is not the right operand of a LEFT JOIN or the left
112027 ** operand of a RIGHT JOIN, then pExpr must be from the WHERE
112028 ** clause, not an ON clause.
112029 **
112030 ** (6) Either:
112031 **
@@ -115555,35 +115597,41 @@
115555 **
115556 ** Additionally, if pExpr is a simple SQL value and the value is the
115557 ** same as that currently bound to variable pVar, non-zero is returned.
115558 ** Otherwise, if the values are not the same or if pExpr is not a simple
115559 ** SQL value, zero is returned.
 
 
 
115560 */
115561 static int exprCompareVariable(
115562 const Parse *pParse,
115563 const Expr *pVar,
115564 const Expr *pExpr
115565 ){
115566 int res = 0;
115567 int iVar;
115568 sqlite3_value *pL, *pR = 0;
115569
 
 
 
 
115570 sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
115571 if( pR ){
115572 iVar = pVar->iColumn;
115573 sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
115574 pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB);
115575 if( pL ){
115576 if( sqlite3_value_type(pL)==SQLITE_TEXT ){
115577 sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */
115578 }
115579 res = 0==sqlite3MemCompare(pL, pR, 0);
115580 }
115581 sqlite3ValueFree(pR);
115582 sqlite3ValueFree(pL);
115583 }
115584
115585 return res;
115586 }
115587
115588 /*
115589 ** Do a deep comparison of two expression trees. Return 0 if the two
@@ -115605,16 +115653,14 @@
115605 ** can be sure the expressions are the same. In the places where
115606 ** this routine is used, it does not hurt to get an extra 2 - that
115607 ** just might result in some slightly slower code. But returning
115608 ** an incorrect 0 or 1 could lead to a malfunction.
115609 **
115610 ** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in
115611 ** pParse->pReprepare can be matched against literals in pB. The
115612 ** pParse->pVdbe->expmask bitmask is updated for each variable referenced.
115613 ** If pParse is NULL (the normal case) then any TK_VARIABLE term in
115614 ** Argument pParse should normally be NULL. If it is not NULL and pA or
115615 ** pB causes a return value of 2.
115616 */
115617 SQLITE_PRIVATE int sqlite3ExprCompare(
115618 const Parse *pParse,
115619 const Expr *pA,
115620 const Expr *pB,
@@ -115622,12 +115668,12 @@
115622 ){
115623 u32 combinedFlags;
115624 if( pA==0 || pB==0 ){
115625 return pB==pA ? 0 : 2;
115626 }
115627 if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){
115628 return 0;
115629 }
115630 combinedFlags = pA->flags | pB->flags;
115631 if( combinedFlags & EP_IntValue ){
115632 if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){
115633 return 0;
@@ -115817,23 +115863,75 @@
115817 return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
115818 }
115819 }
115820 return 0;
115821 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115822
115823 /*
115824 ** Return true if we can prove the pE2 will always be true if pE1 is
115825 ** true. Return false if we cannot complete the proof or if pE2 might
115826 ** be false. Examples:
115827 **
115828 ** pE1: x==5 pE2: x==5 Result: true
115829 ** pE1: x>0 pE2: x==5 Result: false
115830 ** pE1: x=21 pE2: x=21 OR y=43 Result: true
115831 ** pE1: x!=123 pE2: x IS NOT NULL Result: true
115832 ** pE1: x!=?1 pE2: x IS NOT NULL Result: true
115833 ** pE1: x IS NULL pE2: x IS NOT NULL Result: false
115834 ** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
 
 
115835 **
115836 ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
115837 ** Expr.iTable<0 then assume a table number given by iTab.
115838 **
115839 ** If pParse is not NULL, then the values of bound variables in pE1 are
@@ -115863,10 +115961,13 @@
115863 if( pE2->op==TK_NOTNULL
115864 && exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
115865 ){
115866 return 1;
115867 }
 
 
 
115868 return 0;
115869 }
115870
115871 /* This is a helper function to impliesNotNullRow(). In this routine,
115872 ** set pWalker->eCode to one only if *both* of the input expressions
@@ -132082,11 +132183,14 @@
132082 MFUNCTION(degrees, 1, radToDeg, math1Func ),
132083 MFUNCTION(pi, 0, 0, piFunc ),
132084 #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
132085 FUNCTION(sign, 1, 0, 0, signFunc ),
132086 INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
 
132087 INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
 
 
132088 };
132089 #ifndef SQLITE_OMIT_ALTERTABLE
132090 sqlite3AlterFunctions();
132091 #endif
132092 sqlite3WindowFunctions();
@@ -140730,11 +140834,12 @@
140730 pTab = sqliteHashData(k);
140731 if( pTab->nCol==0 ){
140732 char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
140733 if( zSql ){
140734 sqlite3_stmt *pDummy = 0;
140735 (void)sqlite3_prepare(db, zSql, -1, &pDummy, 0);
 
140736 (void)sqlite3_finalize(pDummy);
140737 sqlite3DbFree(db, zSql);
140738 }
140739 if( db->mallocFailed ){
140740 sqlite3ErrorMsg(db->pParse, "out of memory");
@@ -147529,36 +147634,36 @@
147529 return pExpr;
147530 }
147531 if( pSubst->isOuterJoin ){
147532 ExprSetProperty(pNew, EP_CanBeNull);
147533 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147534 if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
147535 sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
147536 pExpr->flags & (EP_OuterON|EP_InnerON));
147537 }
147538 sqlite3ExprDelete(db, pExpr);
147539 pExpr = pNew;
147540 if( pExpr->op==TK_TRUEFALSE ){
147541 pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
147542 pExpr->op = TK_INTEGER;
147543 ExprSetProperty(pExpr, EP_IntValue);
147544 }
147545
147546 /* Ensure that the expression now has an implicit collation sequence,
147547 ** just as it did when it was a column of a view or sub-query. */
147548 {
147549 CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
147550 CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
147551 pSubst->pCList->a[iColumn].pExpr
147552 );
147553 if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
147554 pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
147555 (pColl ? pColl->zName : "BINARY")
147556 );
147557 }
147558 }
147559 ExprClearProperty(pExpr, EP_Collate);
147560 }
147561 }
147562 }else{
147563 if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
147564 pExpr->iTable = pSubst->iNewTable;
@@ -148291,20 +148396,20 @@
148291 }
148292
148293 /* Transfer the FROM clause terms from the subquery into the
148294 ** outer query.
148295 */
 
148296 for(i=0; i<nSubSrc; i++){
148297 SrcItem *pItem = &pSrc->a[i+iFrom];
148298 assert( pItem->fg.isTabFunc==0 );
148299 assert( pItem->fg.isSubquery
148300 || pItem->fg.fixedSchema
148301 || pItem->u4.zDatabase==0 );
148302 if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
148303 *pItem = pSubSrc->a[i];
148304 pItem->fg.jointype |= ltorj;
148305 iNewParent = pSubSrc->a[i].iCursor;
148306 memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
148307 }
148308 pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
148309 pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
148310
@@ -148340,10 +148445,11 @@
148340 pSub->pOrderBy = 0;
148341 }
148342 pWhere = pSub->pWhere;
148343 pSub->pWhere = 0;
148344 if( isOuterJoin>0 ){
 
148345 sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
148346 }
148347 if( pWhere ){
148348 if( pParent->pWhere ){
148349 pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
@@ -151439,11 +151545,11 @@
151439 sqlite3TreeViewSelect(0, p, 0);
151440 }
151441 #endif
151442 assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
151443 }else{
151444 TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n"));
151445 }
151446
151447 /* Convert unused result columns of the subquery into simple NULL
151448 ** expressions, to avoid unneeded searching and computation.
151449 ** tag-select-0440
@@ -158930,10 +159036,11 @@
158930 if( pOrigLhs ){
158931 sqlite3ExprListDelete(db, pOrigLhs);
158932 pNew->pLeft->x.pList = pLhs;
158933 }
158934 pSelect->pEList = pRhs;
 
158935 if( pLhs && pLhs->nExpr==1 ){
158936 /* Take care here not to generate a TK_VECTOR containing only a
158937 ** single value. Since the parser never creates such a vector, some
158938 ** of the subroutines do not handle this case. */
158939 Expr *p = pLhs->a[0].pExpr;
@@ -164002,11 +164109,11 @@
164002 || pTerm->pExpr->w.iJoin != pSrc->iCursor
164003 ){
164004 return 0;
164005 }
164006 if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
164007 && ExprHasProperty(pTerm->pExpr, EP_InnerON)
164008 ){
164009 return 0;
164010 }
164011 return 1;
164012 }
@@ -165495,11 +165602,11 @@
165495 return rc;
165496 }
165497 #endif /* SQLITE_ENABLE_STAT4 */
165498
165499
165500 #ifdef WHERETRACE_ENABLED
165501 /*
165502 ** Print the content of a WhereTerm object
165503 */
165504 SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
165505 if( pTerm==0 ){
@@ -165538,10 +165645,13 @@
165538 sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
165539 }
165540 sqlite3DebugPrintf("\n");
165541 sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
165542 }
 
 
 
165543 }
165544 #endif
165545
165546 #ifdef WHERETRACE_ENABLED
165547 /*
@@ -166724,11 +166834,10 @@
166724 pParse = pWC->pWInfo->pParse;
166725 while( pWhere->op==TK_AND ){
166726 if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
166727 pWhere = pWhere->pRight;
166728 }
166729 if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
166730 for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
166731 Expr *pExpr;
166732 pExpr = pTerm->pExpr;
166733 if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
166734 && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
@@ -169385,11 +169494,11 @@
169385 break;
169386 }
169387 }
169388 if( hasRightJoin
169389 && ExprHasProperty(pTerm->pExpr, EP_InnerON)
169390 && pTerm->pExpr->w.iJoin==pItem->iCursor
169391 ){
169392 break; /* restriction (5) */
169393 }
169394 }
169395 if( pTerm<pEnd ) continue;
@@ -173846,10 +173955,17 @@
173846 ** Then the "b" IdList records the list "a,b,c".
173847 */
173848 struct TrigEvent { int a; IdList * b; };
173849
173850 struct FrameBound { int eType; Expr *pExpr; };
 
 
 
 
 
 
 
173851
173852 /*
173853 ** Disable lookaside memory allocation for objects that might be
173854 ** shared across database connections.
173855 */
@@ -178214,11 +178330,11 @@
178214 ** that look like this: #1 #2 ... These terms refer to registers
178215 ** in the virtual machine. #N is the N-th register. */
178216 Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
178217 assert( t.n>=2 );
178218 if( pParse->nested==0 ){
178219 sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
178220 yymsp[0].minor.yy454 = 0;
178221 }else{
178222 yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
178223 if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
178224 }
@@ -179062,11 +179178,11 @@
179062 #define TOKEN yyminor
179063 /************ Begin %syntax_error code ****************************************/
179064
179065 UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
179066 if( TOKEN.z[0] ){
179067 sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
179068 }else{
179069 sqlite3ErrorMsg(pParse, "incomplete input");
179070 }
179071 /************ End %syntax_error code ******************************************/
179072 sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
@@ -180553,11 +180669,13 @@
180553 }
180554 if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
180555 if( pParse->zErrMsg==0 ){
180556 pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
180557 }
180558 sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
 
 
180559 nErr++;
180560 }
180561 pParse->zTail = zSql;
180562 #ifndef SQLITE_OMIT_VIRTUALTABLE
180563 sqlite3_free(pParse->apVtabLock);
@@ -185391,10 +185509,11 @@
185391 #ifndef SQLITE_OMIT_WINDOWFUNC
185392 sqlite3ShowWindow(0);
185393 sqlite3ShowWinFunc(0);
185394 #endif
185395 sqlite3ShowSelect(0);
 
185396 }
185397 #endif
185398 break;
185399 }
185400
@@ -194537,14 +194656,15 @@
194537 rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
194538 if( rc==SQLITE_OK ){
194539 Fts3PhraseToken *pToken;
194540
194541 p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
194542 if( !p ) goto no_mem;
194543
194544 zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
194545 if( !zTemp ) goto no_mem;
 
 
 
194546
194547 assert( nToken==ii );
194548 pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
194549 memset(pToken, 0, sizeof(Fts3PhraseToken));
194550
@@ -194555,53 +194675,51 @@
194555 pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
194556 pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
194557 nToken = ii+1;
194558 }
194559 }
194560
194561 pModule->xClose(pCursor);
194562 pCursor = 0;
194563 }
194564
194565 if( rc==SQLITE_DONE ){
194566 int jj;
194567 char *zBuf = 0;
194568
194569 p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
194570 if( !p ) goto no_mem;
 
 
 
194571 memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
194572 p->eType = FTSQUERY_PHRASE;
194573 p->pPhrase = (Fts3Phrase *)&p[1];
194574 p->pPhrase->iColumn = pParse->iDefaultCol;
194575 p->pPhrase->nToken = nToken;
194576
194577 zBuf = (char *)&p->pPhrase->aToken[nToken];
 
194578 if( zTemp ){
194579 memcpy(zBuf, zTemp, nTemp);
194580 sqlite3_free(zTemp);
194581 }else{
194582 assert( nTemp==0 );
194583 }
194584
194585 for(jj=0; jj<p->pPhrase->nToken; jj++){
194586 p->pPhrase->aToken[jj].z = zBuf;
194587 zBuf += p->pPhrase->aToken[jj].n;
194588 }
194589 rc = SQLITE_OK;
194590 }
194591
194592 *ppExpr = p;
194593 return rc;
194594 no_mem:
194595
194596 if( pCursor ){
194597 pModule->xClose(pCursor);
194598 }
194599 sqlite3_free(zTemp);
194600 sqlite3_free(p);
194601 *ppExpr = 0;
194602 return SQLITE_NOMEM;
 
 
 
194603 }
194604
194605 /*
194606 ** The output variable *ppExpr is populated with an allocated Fts3Expr
194607 ** structure, or set to 0 if the end of the input buffer is reached.
@@ -226191,10 +226309,12 @@
226191 if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){
226192 unsigned char *aPage = sqlite3PagerGetData(pDbPage);
226193 memcpy(aPage, pData, szPage);
226194 pTab->pgnoTrunc = 0;
226195 }
 
 
226196 }
226197 sqlite3PagerUnref(pDbPage);
226198 return rc;
226199
226200 update_fail:
@@ -233154,17 +233274,32 @@
233154 ** This is used to access token iToken of phrase hit iIdx within the
233155 ** current row. If iIdx is less than zero or greater than or equal to the
233156 ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
233157 ** output variable (*ppToken) is set to point to a buffer containing the
233158 ** matching document token, and (*pnToken) to the size of that buffer in
233159 ** bytes. This API is not available if the specified token matches a
233160 ** prefix query term. In that case both output variables are always set
233161 ** to 0.
233162 **
233163 ** The output text is not a copy of the document text that was tokenized.
233164 ** It is the output of the tokenizer module. For tokendata=1 tables, this
233165 ** includes any embedded 0x00 and trailing data.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233166 **
233167 ** This API can be quite slow if used with an FTS5 table created with the
233168 ** "detail=none" or "detail=column" option.
233169 **
233170 ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
@@ -233843,11 +233978,12 @@
233843 int nUsermerge; /* 'usermerge' setting */
233844 int nHashSize; /* Bytes of memory for in-memory hash */
233845 char *zRank; /* Name of rank function */
233846 char *zRankArgs; /* Arguments to rank function */
233847 int bSecureDelete; /* 'secure-delete' */
233848 int nDeleteMerge; /* 'deletemerge' */
 
233849
233850 /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
233851 char **pzErrmsg;
233852
233853 #ifdef SQLITE_DEBUG
@@ -234100,11 +234236,18 @@
234100 static int sqlite3Fts5StructureTest(Fts5Index*, void*);
234101
234102 /*
234103 ** Used by xInstToken():
234104 */
234105 static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*);
 
 
 
 
 
 
 
234106
234107 /*
234108 ** Insert or remove data to or from the index. Each time a document is
234109 ** added to or removed from the index, this function is called one or more
234110 ** times.
@@ -238314,10 +238457,23 @@
238314 if( bVal<0 ){
238315 *pbBadkey = 1;
238316 }else{
238317 pConfig->bSecureDelete = (bVal ? 1 : 0);
238318 }
 
 
 
 
 
 
 
 
 
 
 
 
 
238319 }else{
238320 *pbBadkey = 1;
238321 }
238322 return rc;
238323 }
@@ -241449,11 +241605,11 @@
241449 && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
241450 ){
241451 int rc = sqlite3Fts5PoslistWriterAppend(
241452 &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
241453 );
241454 if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){
241455 int iCol = p->iOff>>32;
241456 int iTokOff = p->iOff & 0x7FFFFFFF;
241457 rc = sqlite3Fts5IndexIterWriteTokendata(
241458 pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
241459 );
@@ -241642,19 +241798,18 @@
241642 pPhrase = pExpr->apExprPhrase[iPhrase];
241643 if( iToken<0 || iToken>=pPhrase->nTerm ){
241644 return SQLITE_RANGE;
241645 }
241646 pTerm = &pPhrase->aTerm[iToken];
241647 if( pTerm->bPrefix==0 ){
241648 if( pExpr->pConfig->bTokendata ){
241649 rc = sqlite3Fts5IterToken(
241650 pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut
241651 );
241652 }else{
241653 *ppOut = pTerm->pTerm;
241654 *pnOut = pTerm->nFullTerm;
241655 }
241656 }
241657 return rc;
241658 }
241659
241660 /*
@@ -248465,10 +248620,387 @@
248465 fts5BufferFree(&tmp);
248466 memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
248467 *p1 = out;
248468 }
248469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248470 static void fts5SetupPrefixIter(
248471 Fts5Index *p, /* Index to read from */
248472 int bDesc, /* True for "ORDER BY rowid DESC" */
248473 int iIdx, /* Index to scan for data */
248474 u8 *pToken, /* Buffer containing prefix to match */
@@ -248475,137 +249007,89 @@
248475 int nToken, /* Size of buffer pToken in bytes */
248476 Fts5Colset *pColset, /* Restrict matches to these columns */
248477 Fts5Iter **ppIter /* OUT: New iterator */
248478 ){
248479 Fts5Structure *pStruct;
248480 Fts5Buffer *aBuf;
248481 int nBuf = 32;
248482 int nMerge = 1;
248483
248484 void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
248485 void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
 
 
 
 
 
 
 
 
 
 
 
 
248486 if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
248487 xMerge = fts5MergeRowidLists;
248488 xAppend = fts5AppendRowid;
248489 }else{
248490 nMerge = FTS5_MERGE_NLIST-1;
248491 nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
248492 xMerge = fts5MergePrefixLists;
248493 xAppend = fts5AppendPoslist;
248494 }
248495
248496 aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
248497 pStruct = fts5StructureRead(p);
248498 assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );
248499
248500 if( p->rc==SQLITE_OK ){
248501 const int flags = FTS5INDEX_QUERY_SCAN
248502 | FTS5INDEX_QUERY_SKIPEMPTY
248503 | FTS5INDEX_QUERY_NOOUTPUT;
248504 int i;
248505 i64 iLastRowid = 0;
248506 Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
248507 Fts5Data *pData;
248508 Fts5Buffer doclist;
248509 int bNewTerm = 1;
248510
248511 memset(&doclist, 0, sizeof(doclist));
248512
248513 /* If iIdx is non-zero, then it is the number of a prefix-index for
248514 ** prefixes 1 character longer than the prefix being queried for. That
248515 ** index contains all the doclists required, except for the one
248516 ** corresponding to the prefix itself. That one is extracted from the
248517 ** main term index here. */
248518 if( iIdx!=0 ){
248519 int dummy = 0;
248520 const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
248521 pToken[0] = FTS5_MAIN_PREFIX;
248522 fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
248523 fts5IterSetOutputCb(&p->rc, p1);
248524 for(;
248525 fts5MultiIterEof(p, p1)==0;
248526 fts5MultiIterNext2(p, p1, &dummy)
248527 ){
248528 Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
248529 p1->xSetOutputs(p1, pSeg);
248530 if( p1->base.nData ){
248531 xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
248532 iLastRowid = p1->base.iRowid;
248533 }
248534 }
248535 fts5MultiIterFree(p1);
248536 }
248537
248538 pToken[0] = FTS5_MAIN_PREFIX + iIdx;
248539 fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
248540 fts5IterSetOutputCb(&p->rc, p1);
248541
248542 for( /* no-op */ ;
248543 fts5MultiIterEof(p, p1)==0;
248544 fts5MultiIterNext2(p, p1, &bNewTerm)
248545 ){
248546 Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
248547 int nTerm = pSeg->term.n;
248548 const u8 *pTerm = pSeg->term.p;
248549 p1->xSetOutputs(p1, pSeg);
248550
248551 assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
248552 if( bNewTerm ){
248553 if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
248554 }
248555
248556 if( p1->base.nData==0 ) continue;
248557 if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
248558 for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
248559 int i1 = i*nMerge;
248560 int iStore;
248561 assert( i1+nMerge<=nBuf );
248562 for(iStore=i1; iStore<i1+nMerge; iStore++){
248563 if( aBuf[iStore].n==0 ){
248564 fts5BufferSwap(&doclist, &aBuf[iStore]);
248565 fts5BufferZero(&doclist);
248566 break;
248567 }
248568 }
248569 if( iStore==i1+nMerge ){
248570 xMerge(p, &doclist, nMerge, &aBuf[i1]);
248571 for(iStore=i1; iStore<i1+nMerge; iStore++){
248572 fts5BufferZero(&aBuf[iStore]);
248573 }
248574 }
248575 }
248576 iLastRowid = 0;
248577 }
248578
248579 xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
248580 iLastRowid = p1->base.iRowid;
248581 }
248582
248583 assert( (nBuf%nMerge)==0 );
248584 for(i=0; i<nBuf; i+=nMerge){
248585 int iFree;
248586 if( p->rc==SQLITE_OK ){
248587 xMerge(p, &doclist, nMerge, &aBuf[i]);
248588 }
248589 for(iFree=i; iFree<i+nMerge; iFree++){
248590 fts5BufferFree(&aBuf[iFree]);
248591 }
248592 }
248593 fts5MultiIterFree(p1);
248594
248595 pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);
248596 if( pData ){
248597 pData->p = (u8*)&pData[1];
248598 pData->nn = pData->szLeaf = doclist.n;
248599 if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n);
248600 fts5MultiIterNew2(p, pData, bDesc, ppIter);
248601 }
248602 fts5BufferFree(&doclist);
 
 
 
 
 
248603 }
248604
 
 
248605 fts5StructureRelease(pStruct);
248606 sqlite3_free(aBuf);
248607 }
248608
248609
248610 /*
248611 ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
@@ -248855,42 +249339,10 @@
248855 static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
248856 fts5DataRelease(pSeg->pLeaf);
248857 pSeg->pLeaf = 0;
248858 }
248859
248860 /*
248861 ** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
248862 ** array of these for each row it visits. Or, for an iterator used by an
248863 ** "ORDER BY rank" query, it accumulates an array of these for the entire
248864 ** query.
248865 **
248866 ** Each instance in the array indicates the iterator (and therefore term)
248867 ** associated with position iPos of rowid iRowid. This is used by the
248868 ** xInstToken() API.
248869 */
248870 struct Fts5TokenDataMap {
248871 i64 iRowid; /* Row this token is located in */
248872 i64 iPos; /* Position of token */
248873 int iIter; /* Iterator token was read from */
248874 };
248875
248876 /*
248877 ** An object used to supplement Fts5Iter for tokendata=1 iterators.
248878 */
248879 struct Fts5TokenDataIter {
248880 int nIter;
248881 int nIterAlloc;
248882
248883 int nMap;
248884 int nMapAlloc;
248885 Fts5TokenDataMap *aMap;
248886
248887 Fts5PoslistReader *aPoslistReader;
248888 int *aPoslistToIter;
248889 Fts5Iter *apIter[1];
248890 };
248891
248892 /*
248893 ** This function appends iterator pAppend to Fts5TokenDataIter pIn and
248894 ** returns the result.
248895 */
248896 static Fts5TokenDataIter *fts5AppendTokendataIter(
@@ -248923,58 +249375,10 @@
248923 assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
248924
248925 return pRet;
248926 }
248927
248928 /*
248929 ** Delete an Fts5TokenDataIter structure and its contents.
248930 */
248931 static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
248932 if( pSet ){
248933 int ii;
248934 for(ii=0; ii<pSet->nIter; ii++){
248935 fts5MultiIterFree(pSet->apIter[ii]);
248936 }
248937 sqlite3_free(pSet->aPoslistReader);
248938 sqlite3_free(pSet->aMap);
248939 sqlite3_free(pSet);
248940 }
248941 }
248942
248943 /*
248944 ** Append a mapping to the token-map belonging to object pT.
248945 */
248946 static void fts5TokendataIterAppendMap(
248947 Fts5Index *p,
248948 Fts5TokenDataIter *pT,
248949 int iIter,
248950 i64 iRowid,
248951 i64 iPos
248952 ){
248953 if( p->rc==SQLITE_OK ){
248954 if( pT->nMap==pT->nMapAlloc ){
248955 int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
248956 int nByte = nNew * sizeof(Fts5TokenDataMap);
248957 Fts5TokenDataMap *aNew;
248958
248959 aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
248960 if( aNew==0 ){
248961 p->rc = SQLITE_NOMEM;
248962 return;
248963 }
248964
248965 pT->aMap = aNew;
248966 pT->nMapAlloc = nNew;
248967 }
248968
248969 pT->aMap[pT->nMap].iRowid = iRowid;
248970 pT->aMap[pT->nMap].iPos = iPos;
248971 pT->aMap[pT->nMap].iIter = iIter;
248972 pT->nMap++;
248973 }
248974 }
248975
248976 /*
248977 ** The iterator passed as the only argument must be a tokendata=1 iterator
248978 ** (pIter->pTokenDataIter!=0). This function sets the iterator output
248979 ** variables (pIter->base.*) according to the contents of the current
248980 ** row.
@@ -249011,11 +249415,11 @@
249011 int eDetail = pIter->pIndex->pConfig->eDetail;
249012 pIter->base.bEof = 0;
249013 pIter->base.iRowid = iRowid;
249014
249015 if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
249016 fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1);
249017 }else
249018 if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
249019 int nReader = 0;
249020 int nByte = 0;
249021 i64 iPrev = 0;
@@ -249264,10 +249668,11 @@
249264
249265 if( p->rc==SQLITE_OK ){
249266 pRet = fts5MultiIterAlloc(p, 0);
249267 }
249268 if( pRet ){
 
249269 pRet->pTokenDataIter = pSet;
249270 if( pSet ){
249271 fts5IterSetOutputsTokendata(pRet);
249272 }else{
249273 pRet->base.bEof = 1;
@@ -249278,11 +249683,10 @@
249278
249279 fts5StructureRelease(pStruct);
249280 fts5BufferFree(&bSeek);
249281 return pRet;
249282 }
249283
249284
249285 /*
249286 ** Open a new iterator to iterate though all rowid that match the
249287 ** specified token or token prefix.
249288 */
@@ -249304,10 +249708,15 @@
249304 int iIdx = 0; /* Index to search */
249305 int iPrefixIdx = 0; /* +1 prefix index */
249306 int bTokendata = pConfig->bTokendata;
249307 if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
249308
 
 
 
 
 
249309 if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
249310 bTokendata = 0;
249311 }
249312
249313 /* Figure out which index to search and set iIdx accordingly. If this
@@ -249334,11 +249743,11 @@
249334 if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
249335 }
249336 }
249337
249338 if( bTokendata && iIdx==0 ){
249339 buf.p[0] = '0';
249340 pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
249341 }else if( iIdx<=pConfig->nPrefix ){
249342 /* Straight index lookup */
249343 Fts5Structure *pStruct = fts5StructureRead(p);
249344 buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
@@ -249347,11 +249756,11 @@
249347 pColset, buf.p, nToken+1, -1, 0, &pRet
249348 );
249349 fts5StructureRelease(pStruct);
249350 }
249351 }else{
249352 /* Scan multiple terms in the main index */
249353 int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
249354 fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
249355 if( pRet==0 ){
249356 assert( p->rc!=SQLITE_OK );
249357 }else{
@@ -249383,11 +249792,12 @@
249383 ** Move to the next matching rowid.
249384 */
249385 static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
249386 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249387 assert( pIter->pIndex->rc==SQLITE_OK );
249388 if( pIter->pTokenDataIter ){
 
249389 fts5TokendataIterNext(pIter, 0, 0);
249390 }else{
249391 fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
249392 }
249393 return fts5IndexReturn(pIter->pIndex);
@@ -249420,11 +249830,12 @@
249420 ** definition of "at or after" depends on whether this iterator iterates
249421 ** in ascending or descending rowid order.
249422 */
249423 static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
249424 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249425 if( pIter->pTokenDataIter ){
 
249426 fts5TokendataIterNext(pIter, 1, iMatch);
249427 }else{
249428 fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
249429 }
249430 return fts5IndexReturn(pIter->pIndex);
@@ -249438,32 +249849,87 @@
249438 const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
249439 assert_nc( z || n<=1 );
249440 *pn = n-1;
249441 return (z ? &z[1] : 0);
249442 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249443
249444 /*
249445 ** This is used by xInstToken() to access the token at offset iOff, column
249446 ** iCol of row iRowid. The token is returned via output variables *ppOut
249447 ** and *pnOut. The iterator passed as the first argument must be a tokendata=1
249448 ** iterator (pIter->pTokenDataIter!=0).
 
 
249449 */
249450 static int sqlite3Fts5IterToken(
249451 Fts5IndexIter *pIndexIter,
 
249452 i64 iRowid,
249453 int iCol,
249454 int iOff,
249455 const char **ppOut, int *pnOut
249456 ){
249457 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249458 Fts5TokenDataIter *pT = pIter->pTokenDataIter;
249459 Fts5TokenDataMap *aMap = pT->aMap;
249460 i64 iPos = (((i64)iCol)<<32) + iOff;
249461
249462 int i1 = 0;
249463 int i2 = pT->nMap;
249464 int iTest = 0;
 
 
 
 
 
 
 
 
 
 
249465
249466 while( i2>i1 ){
249467 iTest = (i1 + i2) / 2;
249468
249469 if( aMap[iTest].iRowid<iRowid ){
@@ -249483,13 +249949,19 @@
249483 }
249484 }
249485 }
249486
249487 if( i2>i1 ){
249488 Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
249489 *ppOut = (const char*)pMap->aSeg[0].term.p+1;
249490 *pnOut = pMap->aSeg[0].term.n-1;
 
 
 
 
 
 
249491 }
249492
249493 return SQLITE_OK;
249494 }
249495
@@ -249497,11 +249969,13 @@
249497 ** Clear any existing entries from the token-map associated with the
249498 ** iterator passed as the only argument.
249499 */
249500 static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
249501 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249502 if( pIter && pIter->pTokenDataIter ){
 
 
249503 pIter->pTokenDataIter->nMap = 0;
249504 }
249505 }
249506
249507 /*
@@ -249517,21 +249991,33 @@
249517 i64 iRowid, int iCol, int iOff
249518 ){
249519 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249520 Fts5TokenDataIter *pT = pIter->pTokenDataIter;
249521 Fts5Index *p = pIter->pIndex;
249522 int ii;
249523
249524 assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
249525 assert( pIter->pTokenDataIter );
249526
249527 for(ii=0; ii<pT->nIter; ii++){
249528 Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
249529 if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
249530 }
249531 if( ii<pT->nIter ){
249532 fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff);
 
 
 
 
 
 
 
 
 
 
 
 
249533 }
249534 return fts5IndexReturn(p);
249535 }
249536
249537 /*
@@ -251432,10 +251918,11 @@
251432 ** containing a copy of the header from an Fts5Config pointer.
251433 */
251434 #define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
251435 #define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
251436
 
251437
251438 /*
251439 ** Each auxiliary function registered with the FTS5 module is represented
251440 ** by an object of the following type. All such objects are stored as part
251441 ** of the Fts5Global.pAux list.
@@ -252757,10 +253244,11 @@
252757 sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
252758 sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
252759 sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
252760 int iCol; /* Column on LHS of MATCH operator */
252761 char **pzErrmsg = pConfig->pzErrmsg;
 
252762 int i;
252763 int iIdxStr = 0;
252764 Fts5Expr *pExpr = 0;
252765
252766 assert( pConfig->bLock==0 );
@@ -252792,10 +253280,13 @@
252792 int bInternal = 0;
252793
252794 rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
252795 if( rc!=SQLITE_OK ) goto filter_out;
252796 if( zText==0 ) zText = "";
 
 
 
252797
252798 iCol = 0;
252799 do{
252800 iCol = iCol*10 + (idxStr[iIdxStr]-'0');
252801 iIdxStr++;
@@ -252932,10 +253423,11 @@
252932 }
252933
252934 filter_out:
252935 sqlite3Fts5ExprFree(pExpr);
252936 pConfig->pzErrmsg = pzErrmsg;
 
252937 return rc;
252938 }
252939
252940 /*
252941 ** This is the xEof method of the virtual table. SQLite calls this
@@ -254927,11 +255419,11 @@
254927 int nArg, /* Number of args */
254928 sqlite3_value **apUnused /* Function arguments */
254929 ){
254930 assert( nArg==0 );
254931 UNUSED_PARAM2(nArg, apUnused);
254932 sqlite3_result_text(pCtx, "fts5: 2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8", -1, SQLITE_TRANSIENT);
254933 }
254934
254935 /*
254936 ** Implementation of fts5_locale(LOCALE, TEXT) function.
254937 **
@@ -254990,10 +255482,24 @@
254990 assert( &pCsr[nText]==&pBlob[nBlob] );
254991
254992 sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free);
254993 }
254994 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254995
254996 /*
254997 ** Return true if zName is the extension on one of the shadow tables used
254998 ** by this module.
254999 */
@@ -255120,13 +255626,20 @@
255120 );
255121 }
255122 if( rc==SQLITE_OK ){
255123 rc = sqlite3_create_function(
255124 db, "fts5_locale", 2,
255125 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
255126 p, fts5LocaleFunc, 0, 0
255127 );
 
 
 
 
 
 
 
255128 }
255129 }
255130
255131 /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
255132 ** fts5_test_mi.c is compiled and linked into the executable. And call
@@ -258048,11 +258561,11 @@
258048 int rc = SQLITE_OK;
258049 char aBuf[32];
258050 char *zOut = aBuf;
258051 int ii;
258052 const unsigned char *zIn = (const unsigned char*)pText;
258053 const unsigned char *zEof = &zIn[nText];
258054 u32 iCode = 0;
258055 int aStart[3]; /* Input offset of each character in aBuf[] */
258056
258057 UNUSED_PARAM(unusedFlags);
258058
258059
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.48.0"
469 #define SQLITE_VERSION_NUMBER 3048000
470 #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -4521,15 +4521,26 @@
4521 **
4522 ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
4523 ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
4524 ** to return an error (error code SQLITE_ERROR) if the statement uses
4525 ** any virtual tables.
4526 **
4527 ** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
4528 ** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
4529 ** errors from being sent to the error log defined by
4530 ** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
4531 ** compiles to see if some SQL syntax is well-formed, without generating
4532 ** messages on the global error log when it is not. If the test compile
4533 ** fails, the sqlite3_prepare_v3() call returns the same error indications
4534 ** with or without this flag; it just omits the call to [sqlite3_log()] that
4535 ** logs the error.
4536 ** </dl>
4537 */
4538 #define SQLITE_PREPARE_PERSISTENT 0x01
4539 #define SQLITE_PREPARE_NORMALIZE 0x02
4540 #define SQLITE_PREPARE_NO_VTAB 0x04
4541 #define SQLITE_PREPARE_DONT_LOG 0x10
4542
4543 /*
4544 ** CAPI3REF: Compiling An SQL Statement
4545 ** KEYWORDS: {SQL statement compiler}
4546 ** METHOD: sqlite3
@@ -13467,17 +13478,32 @@
13478 ** This is used to access token iToken of phrase hit iIdx within the
13479 ** current row. If iIdx is less than zero or greater than or equal to the
13480 ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
13481 ** output variable (*ppToken) is set to point to a buffer containing the
13482 ** matching document token, and (*pnToken) to the size of that buffer in
13483 ** bytes.
 
 
13484 **
13485 ** The output text is not a copy of the document text that was tokenized.
13486 ** It is the output of the tokenizer module. For tokendata=1 tables, this
13487 ** includes any embedded 0x00 and trailing data.
13488 **
13489 ** This API may be slow in some cases if the token identified by parameters
13490 ** iIdx and iToken matched a prefix token in the query. In most cases, the
13491 ** first call to this API for each prefix token in the query is forced
13492 ** to scan the portion of the full-text index that matches the prefix
13493 ** token to collect the extra data required by this API. If the prefix
13494 ** token matches a large number of token instances in the document set,
13495 ** this may be a performance problem.
13496 **
13497 ** If the user knows in advance that a query may use this API for a
13498 ** prefix token, FTS5 may be configured to collect all required data as part
13499 ** of the initial querying of the full-text index, avoiding the second scan
13500 ** entirely. This also causes prefix queries that do not use this API to
13501 ** run more slowly and use more memory. FTS5 may be configured in this way
13502 ** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
13503 ** option, or on a per-query basis using the
13504 ** [fts5_insttoken | fts5_insttoken()] user function.
13505 **
13506 ** This API can be quite slow if used with an FTS5 table created with the
13507 ** "detail=none" or "detail=column" option.
13508 **
13509 ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
@@ -17049,11 +17075,11 @@
17075
17076 /*
17077 ** Additional non-public SQLITE_PREPARE_* flags
17078 */
17079 #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */
17080 #define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */
17081
17082 /*
17083 ** Prototypes for the VDBE interface. See comments on the implementation
17084 ** for a description of what each of these routines does.
17085 */
@@ -32279,10 +32305,11 @@
32305 && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
32306 ){
32307 pExpr = pExpr->pLeft;
32308 }
32309 if( pExpr==0 ) return;
32310 if( ExprHasProperty(pExpr, EP_FromDDL) ) return;
32311 db->errByteOffset = pExpr->w.iOfst;
32312 }
32313
32314 /*
32315 ** Enlarge the memory allocation on a StrAccum object so that it is
@@ -33008,11 +33035,11 @@
33035 }
33036 if( pItem->fg.isCte ){
33037 sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
33038 }
33039 if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
33040 sqlite3_str_appendf(&x, " isOn");
33041 }
33042 if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
33043 if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
33044 if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
33045 if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
@@ -34092,10 +34119,14 @@
34119 **
34120 ** This routines are given external linkage so that they will always be
34121 ** accessible to the debugging, and to avoid warnings about unused
34122 ** functions. But these routines only exist in debugging builds, so they
34123 ** do not contaminate the interface.
34124 **
34125 ** See Also:
34126 **
34127 ** sqlite3ShowWhereTerm() in where.c
34128 */
34129 SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
34130 SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
34131 SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
34132 SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
@@ -35668,12 +35699,12 @@
35699 int esign = 1; /* sign of exponent */
35700 int e = 0; /* exponent */
35701 int eValid = 1; /* True exponent is either not used or is well-formed */
35702 int nDigit = 0; /* Number of digits processed */
35703 int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
35704 u64 s2; /* round-tripped significand */
35705 double rr[2];
 
35706
35707 assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
35708 *pResult = 0.0; /* Default return value, in case of an error */
35709 if( length==0 ) return 0;
35710
@@ -35772,25 +35803,36 @@
35803
35804 /* adjust exponent by d, and update sign */
35805 e = (e*esign) + d;
35806
35807 /* Try to adjust the exponent to make it smaller */
35808 while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){
35809 s *= 10;
35810 e--;
35811 }
35812 while( e<0 && (s%10)==0 ){
35813 s /= 10;
35814 e++;
35815 }
35816
35817 rr[0] = (double)s;
35818 assert( sizeof(s2)==sizeof(rr[0]) );
35819 #ifdef SQLITE_DEBUG
35820 rr[1] = 18446744073709549568.0;
35821 memcpy(&s2, &rr[1], sizeof(s2));
35822 assert( s2==0x43efffffffffffffLL );
35823 #endif
35824 /* Largest double that can be safely converted to u64
35825 ** vvvvvvvvvvvvvvvvvvvvvv */
35826 if( rr[0]<=18446744073709549568.0 ){
35827 s2 = (u64)rr[0];
35828 rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
35829 }else{
35830 rr[1] = 0.0;
35831 }
35832 assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */
35833
35834 if( e>0 ){
35835 while( e>=100 ){
35836 e -= 100;
35837 dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
35838 }
@@ -51771,11 +51813,11 @@
51813 */
51814 char *zTmpname = 0; /* For temporary filename, if necessary. */
51815
51816 int rc = SQLITE_OK; /* Function Return Code */
51817 #if !defined(NDEBUG) || SQLITE_OS_WINCE
51818 int eType = flags&0x0FFF00; /* Type of file to open */
51819 #endif
51820
51821 int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
51822 int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
51823 int isCreate = (flags & SQLITE_OPEN_CREATE);
@@ -112021,11 +112063,11 @@
112063 **
112064 ** (4) If pSrc is the right operand of a LEFT JOIN, then...
112065 ** (4a) pExpr must come from an ON clause..
112066 ** (4b) and specifically the ON clause associated with the LEFT JOIN.
112067 **
112068 ** (5) If pSrc is the right operand of a LEFT JOIN or the left
112069 ** operand of a RIGHT JOIN, then pExpr must be from the WHERE
112070 ** clause, not an ON clause.
112071 **
112072 ** (6) Either:
112073 **
@@ -115555,35 +115597,41 @@
115597 **
115598 ** Additionally, if pExpr is a simple SQL value and the value is the
115599 ** same as that currently bound to variable pVar, non-zero is returned.
115600 ** Otherwise, if the values are not the same or if pExpr is not a simple
115601 ** SQL value, zero is returned.
115602 **
115603 ** If the SQLITE_EnableQPSG flag is set on the database connection, then
115604 ** this routine always returns false.
115605 */
115606 static SQLITE_NOINLINE int exprCompareVariable(
115607 const Parse *pParse,
115608 const Expr *pVar,
115609 const Expr *pExpr
115610 ){
115611 int res = 2;
115612 int iVar;
115613 sqlite3_value *pL, *pR = 0;
115614
115615 if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){
115616 return 0;
115617 }
115618 if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2;
115619 sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
115620 if( pR ){
115621 iVar = pVar->iColumn;
115622 sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
115623 pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB);
115624 if( pL ){
115625 if( sqlite3_value_type(pL)==SQLITE_TEXT ){
115626 sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */
115627 }
115628 res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0;
115629 }
115630 sqlite3ValueFree(pR);
115631 sqlite3ValueFree(pL);
115632 }
 
115633 return res;
115634 }
115635
115636 /*
115637 ** Do a deep comparison of two expression trees. Return 0 if the two
@@ -115605,16 +115653,14 @@
115653 ** can be sure the expressions are the same. In the places where
115654 ** this routine is used, it does not hurt to get an extra 2 - that
115655 ** just might result in some slightly slower code. But returning
115656 ** an incorrect 0 or 1 could lead to a malfunction.
115657 **
115658 ** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE
115659 ** terms in pA with bindings in pParse->pReprepare can be matched against
115660 ** literals in pB. The pParse->pVdbe->expmask bitmask is updated for
115661 ** each variable referenced.
 
 
115662 */
115663 SQLITE_PRIVATE int sqlite3ExprCompare(
115664 const Parse *pParse,
115665 const Expr *pA,
115666 const Expr *pB,
@@ -115622,12 +115668,12 @@
115668 ){
115669 u32 combinedFlags;
115670 if( pA==0 || pB==0 ){
115671 return pB==pA ? 0 : 2;
115672 }
115673 if( pParse && pA->op==TK_VARIABLE ){
115674 return exprCompareVariable(pParse, pA, pB);
115675 }
115676 combinedFlags = pA->flags | pB->flags;
115677 if( combinedFlags & EP_IntValue ){
115678 if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){
115679 return 0;
@@ -115817,23 +115863,75 @@
115863 return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
115864 }
115865 }
115866 return 0;
115867 }
115868
115869 /*
115870 ** Return true if the boolean value of the expression is always either
115871 ** FALSE or NULL.
115872 */
115873 static int sqlite3ExprIsNotTrue(Expr *pExpr){
115874 int v;
115875 if( pExpr->op==TK_NULL ) return 1;
115876 if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1;
115877 v = 1;
115878 if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1;
115879 return 0;
115880 }
115881
115882 /*
115883 ** Return true if the expression is one of the following:
115884 **
115885 ** CASE WHEN x THEN y END
115886 ** CASE WHEN x THEN y ELSE NULL END
115887 ** CASE WHEN x THEN y ELSE false END
115888 ** iif(x,y)
115889 ** iif(x,y,NULL)
115890 ** iif(x,y,false)
115891 */
115892 static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){
115893 ExprList *pList;
115894 if( pExpr->op==TK_FUNCTION ){
115895 const char *z = pExpr->u.zToken;
115896 FuncDef *pDef;
115897 if( (z[0]!='i' && z[0]!='I') ) return 0;
115898 if( pExpr->x.pList==0 ) return 0;
115899 pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0);
115900 #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
115901 if( pDef==0 ) return 0;
115902 #else
115903 if( NEVER(pDef==0) ) return 0;
115904 #endif
115905 if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0;
115906 if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0;
115907 }else if( pExpr->op==TK_CASE ){
115908 if( pExpr->pLeft!=0 ) return 0;
115909 }else{
115910 return 0;
115911 }
115912 pList = pExpr->x.pList;
115913 assert( pList!=0 );
115914 if( pList->nExpr==2 ) return 1;
115915 if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1;
115916 return 0;
115917 }
115918
115919 /*
115920 ** Return true if we can prove the pE2 will always be true if pE1 is
115921 ** true. Return false if we cannot complete the proof or if pE2 might
115922 ** be false. Examples:
115923 **
115924 ** pE1: x==5 pE2: x==5 Result: true
115925 ** pE1: x>0 pE2: x==5 Result: false
115926 ** pE1: x=21 pE2: x=21 OR y=43 Result: true
115927 ** pE1: x!=123 pE2: x IS NOT NULL Result: true
115928 ** pE1: x!=?1 pE2: x IS NOT NULL Result: true
115929 ** pE1: x IS NULL pE2: x IS NOT NULL Result: false
115930 ** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
115931 ** pE1: iif(x,y) pE2: x Result: true
115932 ** PE1: iif(x,y,0) pE2: x Result: true
115933 **
115934 ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
115935 ** Expr.iTable<0 then assume a table number given by iTab.
115936 **
115937 ** If pParse is not NULL, then the values of bound variables in pE1 are
@@ -115863,10 +115961,13 @@
115961 if( pE2->op==TK_NOTNULL
115962 && exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
115963 ){
115964 return 1;
115965 }
115966 if( sqlite3ExprIsIIF(pParse->db, pE1) ){
115967 return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab);
115968 }
115969 return 0;
115970 }
115971
115972 /* This is a helper function to impliesNotNullRow(). In this routine,
115973 ** set pWalker->eCode to one only if *both* of the input expressions
@@ -132082,11 +132183,14 @@
132183 MFUNCTION(degrees, 1, radToDeg, math1Func ),
132184 MFUNCTION(pi, 0, 0, piFunc ),
132185 #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
132186 FUNCTION(sign, 1, 0, 0, signFunc ),
132187 INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
132188 INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ),
132189 INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
132190 INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ),
132191 INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ),
132192 };
132193 #ifndef SQLITE_OMIT_ALTERTABLE
132194 sqlite3AlterFunctions();
132195 #endif
132196 sqlite3WindowFunctions();
@@ -140730,11 +140834,12 @@
140834 pTab = sqliteHashData(k);
140835 if( pTab->nCol==0 ){
140836 char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
140837 if( zSql ){
140838 sqlite3_stmt *pDummy = 0;
140839 (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG,
140840 &pDummy, 0);
140841 (void)sqlite3_finalize(pDummy);
140842 sqlite3DbFree(db, zSql);
140843 }
140844 if( db->mallocFailed ){
140845 sqlite3ErrorMsg(db->pParse, "out of memory");
@@ -147529,36 +147634,36 @@
147634 return pExpr;
147635 }
147636 if( pSubst->isOuterJoin ){
147637 ExprSetProperty(pNew, EP_CanBeNull);
147638 }
147639 if( pNew->op==TK_TRUEFALSE ){
147640 pNew->u.iValue = sqlite3ExprTruthValue(pNew);
147641 pNew->op = TK_INTEGER;
147642 ExprSetProperty(pNew, EP_IntValue);
147643 }
147644
147645 /* Ensure that the expression now has an implicit collation sequence,
147646 ** just as it did when it was a column of a view or sub-query. */
147647 {
147648 CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew);
147649 CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
147650 pSubst->pCList->a[iColumn].pExpr
147651 );
147652 if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){
147653 pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew,
147654 (pColl ? pColl->zName : "BINARY")
147655 );
147656 }
147657 }
147658 ExprClearProperty(pNew, EP_Collate);
147659 if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
147660 sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
147661 pExpr->flags & (EP_OuterON|EP_InnerON));
147662 }
147663 sqlite3ExprDelete(db, pExpr);
147664 pExpr = pNew;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147665 }
147666 }
147667 }else{
147668 if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
147669 pExpr->iTable = pSubst->iNewTable;
@@ -148291,20 +148396,20 @@
148396 }
148397
148398 /* Transfer the FROM clause terms from the subquery into the
148399 ** outer query.
148400 */
148401 iNewParent = pSubSrc->a[0].iCursor;
148402 for(i=0; i<nSubSrc; i++){
148403 SrcItem *pItem = &pSrc->a[i+iFrom];
148404 assert( pItem->fg.isTabFunc==0 );
148405 assert( pItem->fg.isSubquery
148406 || pItem->fg.fixedSchema
148407 || pItem->u4.zDatabase==0 );
148408 if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
148409 *pItem = pSubSrc->a[i];
148410 pItem->fg.jointype |= ltorj;
 
148411 memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
148412 }
148413 pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
148414 pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
148415
@@ -148340,10 +148445,11 @@
148445 pSub->pOrderBy = 0;
148446 }
148447 pWhere = pSub->pWhere;
148448 pSub->pWhere = 0;
148449 if( isOuterJoin>0 ){
148450 assert( pSubSrc->nSrc==1 );
148451 sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
148452 }
148453 if( pWhere ){
148454 if( pParent->pWhere ){
148455 pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
@@ -151439,11 +151545,11 @@
151545 sqlite3TreeViewSelect(0, p, 0);
151546 }
151547 #endif
151548 assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
151549 }else{
151550 TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n"));
151551 }
151552
151553 /* Convert unused result columns of the subquery into simple NULL
151554 ** expressions, to avoid unneeded searching and computation.
151555 ** tag-select-0440
@@ -158930,10 +159036,11 @@
159036 if( pOrigLhs ){
159037 sqlite3ExprListDelete(db, pOrigLhs);
159038 pNew->pLeft->x.pList = pLhs;
159039 }
159040 pSelect->pEList = pRhs;
159041 pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */
159042 if( pLhs && pLhs->nExpr==1 ){
159043 /* Take care here not to generate a TK_VECTOR containing only a
159044 ** single value. Since the parser never creates such a vector, some
159045 ** of the subroutines do not handle this case. */
159046 Expr *p = pLhs->a[0].pExpr;
@@ -164002,11 +164109,11 @@
164109 || pTerm->pExpr->w.iJoin != pSrc->iCursor
164110 ){
164111 return 0;
164112 }
164113 if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
164114 && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON))
164115 ){
164116 return 0;
164117 }
164118 return 1;
164119 }
@@ -165495,11 +165602,11 @@
165602 return rc;
165603 }
165604 #endif /* SQLITE_ENABLE_STAT4 */
165605
165606
165607 #if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG)
165608 /*
165609 ** Print the content of a WhereTerm object
165610 */
165611 SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
165612 if( pTerm==0 ){
@@ -165538,10 +165645,13 @@
165645 sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
165646 }
165647 sqlite3DebugPrintf("\n");
165648 sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
165649 }
165650 }
165651 SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){
165652 sqlite3WhereTermPrint(pTerm, 0);
165653 }
165654 #endif
165655
165656 #ifdef WHERETRACE_ENABLED
165657 /*
@@ -166724,11 +166834,10 @@
166834 pParse = pWC->pWInfo->pParse;
166835 while( pWhere->op==TK_AND ){
166836 if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
166837 pWhere = pWhere->pRight;
166838 }
 
166839 for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
166840 Expr *pExpr;
166841 pExpr = pTerm->pExpr;
166842 if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
166843 && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
@@ -169385,11 +169494,11 @@
169494 break;
169495 }
169496 }
169497 if( hasRightJoin
169498 && ExprHasProperty(pTerm->pExpr, EP_InnerON)
169499 && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor)
169500 ){
169501 break; /* restriction (5) */
169502 }
169503 }
169504 if( pTerm<pEnd ) continue;
@@ -173846,10 +173955,17 @@
173955 ** Then the "b" IdList records the list "a,b,c".
173956 */
173957 struct TrigEvent { int a; IdList * b; };
173958
173959 struct FrameBound { int eType; Expr *pExpr; };
173960
173961 /*
173962 ** Generate a syntax error
173963 */
173964 static void parserSyntaxError(Parse *pParse, Token *p){
173965 sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p);
173966 }
173967
173968 /*
173969 ** Disable lookaside memory allocation for objects that might be
173970 ** shared across database connections.
173971 */
@@ -178214,11 +178330,11 @@
178330 ** that look like this: #1 #2 ... These terms refer to registers
178331 ** in the virtual machine. #N is the N-th register. */
178332 Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
178333 assert( t.n>=2 );
178334 if( pParse->nested==0 ){
178335 parserSyntaxError(pParse, &t);
178336 yymsp[0].minor.yy454 = 0;
178337 }else{
178338 yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
178339 if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
178340 }
@@ -179062,11 +179178,11 @@
179178 #define TOKEN yyminor
179179 /************ Begin %syntax_error code ****************************************/
179180
179181 UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
179182 if( TOKEN.z[0] ){
179183 parserSyntaxError(pParse, &TOKEN);
179184 }else{
179185 sqlite3ErrorMsg(pParse, "incomplete input");
179186 }
179187 /************ End %syntax_error code ******************************************/
179188 sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
@@ -180553,11 +180669,13 @@
180669 }
180670 if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
180671 if( pParse->zErrMsg==0 ){
180672 pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
180673 }
180674 if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){
180675 sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
180676 }
180677 nErr++;
180678 }
180679 pParse->zTail = zSql;
180680 #ifndef SQLITE_OMIT_VIRTUALTABLE
180681 sqlite3_free(pParse->apVtabLock);
@@ -185391,10 +185509,11 @@
185509 #ifndef SQLITE_OMIT_WINDOWFUNC
185510 sqlite3ShowWindow(0);
185511 sqlite3ShowWinFunc(0);
185512 #endif
185513 sqlite3ShowSelect(0);
185514 sqlite3ShowWhereTerm(0);
185515 }
185516 #endif
185517 break;
185518 }
185519
@@ -194537,14 +194656,15 @@
194656 rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
194657 if( rc==SQLITE_OK ){
194658 Fts3PhraseToken *pToken;
194659
194660 p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
 
 
194661 zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
194662 if( !zTemp || !p ){
194663 rc = SQLITE_NOMEM;
194664 goto getnextstring_out;
194665 }
194666
194667 assert( nToken==ii );
194668 pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
194669 memset(pToken, 0, sizeof(Fts3PhraseToken));
194670
@@ -194555,53 +194675,51 @@
194675 pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
194676 pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
194677 nToken = ii+1;
194678 }
194679 }
 
 
 
194680 }
194681
194682 if( rc==SQLITE_DONE ){
194683 int jj;
194684 char *zBuf = 0;
194685
194686 p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
194687 if( !p ){
194688 rc = SQLITE_NOMEM;
194689 goto getnextstring_out;
194690 }
194691 memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
194692 p->eType = FTSQUERY_PHRASE;
194693 p->pPhrase = (Fts3Phrase *)&p[1];
194694 p->pPhrase->iColumn = pParse->iDefaultCol;
194695 p->pPhrase->nToken = nToken;
194696
194697 zBuf = (char *)&p->pPhrase->aToken[nToken];
194698 assert( nTemp==0 || zTemp );
194699 if( zTemp ){
194700 memcpy(zBuf, zTemp, nTemp);
 
 
 
194701 }
194702
194703 for(jj=0; jj<p->pPhrase->nToken; jj++){
194704 p->pPhrase->aToken[jj].z = zBuf;
194705 zBuf += p->pPhrase->aToken[jj].n;
194706 }
194707 rc = SQLITE_OK;
194708 }
194709
194710 getnextstring_out:
 
 
 
194711 if( pCursor ){
194712 pModule->xClose(pCursor);
194713 }
194714 sqlite3_free(zTemp);
194715 if( rc!=SQLITE_OK ){
194716 sqlite3_free(p);
194717 p = 0;
194718 }
194719 *ppExpr = p;
194720 return rc;
194721 }
194722
194723 /*
194724 ** The output variable *ppExpr is populated with an allocated Fts3Expr
194725 ** structure, or set to 0 if the end of the input buffer is reached.
@@ -226191,10 +226309,12 @@
226309 if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){
226310 unsigned char *aPage = sqlite3PagerGetData(pDbPage);
226311 memcpy(aPage, pData, szPage);
226312 pTab->pgnoTrunc = 0;
226313 }
226314 }else{
226315 pTab->pgnoTrunc = 0;
226316 }
226317 sqlite3PagerUnref(pDbPage);
226318 return rc;
226319
226320 update_fail:
@@ -233154,17 +233274,32 @@
233274 ** This is used to access token iToken of phrase hit iIdx within the
233275 ** current row. If iIdx is less than zero or greater than or equal to the
233276 ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
233277 ** output variable (*ppToken) is set to point to a buffer containing the
233278 ** matching document token, and (*pnToken) to the size of that buffer in
233279 ** bytes.
 
 
233280 **
233281 ** The output text is not a copy of the document text that was tokenized.
233282 ** It is the output of the tokenizer module. For tokendata=1 tables, this
233283 ** includes any embedded 0x00 and trailing data.
233284 **
233285 ** This API may be slow in some cases if the token identified by parameters
233286 ** iIdx and iToken matched a prefix token in the query. In most cases, the
233287 ** first call to this API for each prefix token in the query is forced
233288 ** to scan the portion of the full-text index that matches the prefix
233289 ** token to collect the extra data required by this API. If the prefix
233290 ** token matches a large number of token instances in the document set,
233291 ** this may be a performance problem.
233292 **
233293 ** If the user knows in advance that a query may use this API for a
233294 ** prefix token, FTS5 may be configured to collect all required data as part
233295 ** of the initial querying of the full-text index, avoiding the second scan
233296 ** entirely. This also causes prefix queries that do not use this API to
233297 ** run more slowly and use more memory. FTS5 may be configured in this way
233298 ** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
233299 ** option, or on a per-query basis using the
233300 ** [fts5_insttoken | fts5_insttoken()] user function.
233301 **
233302 ** This API can be quite slow if used with an FTS5 table created with the
233303 ** "detail=none" or "detail=column" option.
233304 **
233305 ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
@@ -233843,11 +233978,12 @@
233978 int nUsermerge; /* 'usermerge' setting */
233979 int nHashSize; /* Bytes of memory for in-memory hash */
233980 char *zRank; /* Name of rank function */
233981 char *zRankArgs; /* Arguments to rank function */
233982 int bSecureDelete; /* 'secure-delete' */
233983 int nDeleteMerge; /* 'deletemerge' */
233984 int bPrefixInsttoken; /* 'prefix-insttoken' */
233985
233986 /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
233987 char **pzErrmsg;
233988
233989 #ifdef SQLITE_DEBUG
@@ -234100,11 +234236,18 @@
234236 static int sqlite3Fts5StructureTest(Fts5Index*, void*);
234237
234238 /*
234239 ** Used by xInstToken():
234240 */
234241 static int sqlite3Fts5IterToken(
234242 Fts5IndexIter *pIndexIter,
234243 const char *pToken, int nToken,
234244 i64 iRowid,
234245 int iCol,
234246 int iOff,
234247 const char **ppOut, int *pnOut
234248 );
234249
234250 /*
234251 ** Insert or remove data to or from the index. Each time a document is
234252 ** added to or removed from the index, this function is called one or more
234253 ** times.
@@ -238314,10 +238457,23 @@
238457 if( bVal<0 ){
238458 *pbBadkey = 1;
238459 }else{
238460 pConfig->bSecureDelete = (bVal ? 1 : 0);
238461 }
238462 }
238463
238464 else if( 0==sqlite3_stricmp(zKey, "insttoken") ){
238465 int bVal = -1;
238466 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
238467 bVal = sqlite3_value_int(pVal);
238468 }
238469 if( bVal<0 ){
238470 *pbBadkey = 1;
238471 }else{
238472 pConfig->bPrefixInsttoken = (bVal ? 1 : 0);
238473 }
238474
238475 }else{
238476 *pbBadkey = 1;
238477 }
238478 return rc;
238479 }
@@ -241449,11 +241605,11 @@
241605 && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
241606 ){
241607 int rc = sqlite3Fts5PoslistWriterAppend(
241608 &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
241609 );
241610 if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){
241611 int iCol = p->iOff>>32;
241612 int iTokOff = p->iOff & 0x7FFFFFFF;
241613 rc = sqlite3Fts5IndexIterWriteTokendata(
241614 pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
241615 );
@@ -241642,19 +241798,18 @@
241798 pPhrase = pExpr->apExprPhrase[iPhrase];
241799 if( iToken<0 || iToken>=pPhrase->nTerm ){
241800 return SQLITE_RANGE;
241801 }
241802 pTerm = &pPhrase->aTerm[iToken];
241803 if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){
241804 rc = sqlite3Fts5IterToken(
241805 pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm,
241806 iRowid, iCol, iOff+iToken, ppOut, pnOut
241807 );
241808 }else{
241809 *ppOut = pTerm->pTerm;
241810 *pnOut = pTerm->nFullTerm;
 
241811 }
241812 return rc;
241813 }
241814
241815 /*
@@ -248465,10 +248620,387 @@
248620 fts5BufferFree(&tmp);
248621 memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
248622 *p1 = out;
248623 }
248624
248625
248626 /*
248627 ** Iterate through a range of entries in the FTS index, invoking the xVisit
248628 ** callback for each of them.
248629 **
248630 ** Parameter pToken points to an nToken buffer containing an FTS index term
248631 ** (i.e. a document term with the preceding 1 byte index identifier -
248632 ** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits
248633 ** all entries for terms that have pToken/nToken as a prefix. If bPrefix
248634 ** is false, then only entries with pToken/nToken as the entire key are
248635 ** visited.
248636 **
248637 ** If the current table is a tokendata=1 table, then if bPrefix is true then
248638 ** each index term is treated separately. However, if bPrefix is false, then
248639 ** all index terms corresponding to pToken/nToken are collapsed into a single
248640 ** term before the callback is invoked.
248641 **
248642 ** The callback invoked for each entry visited is specified by paramter xVisit.
248643 ** Each time it is invoked, it is passed a pointer to the Fts5Index object,
248644 ** a copy of the 7th paramter to this function (pCtx) and a pointer to the
248645 ** iterator that indicates the current entry. If the current entry is the
248646 ** first with a new term (i.e. different from that of the previous entry,
248647 ** including the very first term), then the final two parameters are passed
248648 ** a pointer to the term and its size in bytes, respectively. If the current
248649 ** entry is not the first associated with its term, these two parameters
248650 ** are passed 0.
248651 **
248652 ** If parameter pColset is not NULL, then it is used to filter entries before
248653 ** the callback is invoked.
248654 */
248655 static int fts5VisitEntries(
248656 Fts5Index *p, /* Fts5 index object */
248657 Fts5Colset *pColset, /* Columns filter to apply, or NULL */
248658 u8 *pToken, /* Buffer containing token */
248659 int nToken, /* Size of buffer pToken in bytes */
248660 int bPrefix, /* True for a prefix scan */
248661 void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int),
248662 void *pCtx /* Passed as second argument to xVisit() */
248663 ){
248664 const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0)
248665 | FTS5INDEX_QUERY_SKIPEMPTY
248666 | FTS5INDEX_QUERY_NOOUTPUT;
248667 Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
248668 int bNewTerm = 1;
248669 Fts5Structure *pStruct = fts5StructureRead(p);
248670
248671 fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
248672 fts5IterSetOutputCb(&p->rc, p1);
248673 for( /* no-op */ ;
248674 fts5MultiIterEof(p, p1)==0;
248675 fts5MultiIterNext2(p, p1, &bNewTerm)
248676 ){
248677 Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
248678 int nNew = 0;
248679 const u8 *pNew = 0;
248680
248681 p1->xSetOutputs(p1, pSeg);
248682 if( p->rc ) break;
248683
248684 if( bNewTerm ){
248685 nNew = pSeg->term.n;
248686 pNew = pSeg->term.p;
248687 if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break;
248688 }
248689
248690 xVisit(p, pCtx, p1, pNew, nNew);
248691 }
248692 fts5MultiIterFree(p1);
248693
248694 fts5StructureRelease(pStruct);
248695 return p->rc;
248696 }
248697
248698
248699 /*
248700 ** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
248701 ** array of these for each row it visits (so all iRowid fields are the same).
248702 ** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an
248703 ** array of these for the entire query (in which case iRowid fields may take
248704 ** a variety of values).
248705 **
248706 ** Each instance in the array indicates the iterator (and therefore term)
248707 ** associated with position iPos of rowid iRowid. This is used by the
248708 ** xInstToken() API.
248709 **
248710 ** iRowid:
248711 ** Rowid for the current entry.
248712 **
248713 ** iPos:
248714 ** Position of current entry within row. In the usual ((iCol<<32)+iOff)
248715 ** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()).
248716 **
248717 ** iIter:
248718 ** If the Fts5TokenDataIter iterator that the entry is part of is
248719 ** actually an iterator (i.e. with nIter>0, not just a container for
248720 ** Fts5TokenDataMap structures), then this variable is an index into
248721 ** the apIter[] array. The corresponding term is that which the iterator
248722 ** at apIter[iIter] currently points to.
248723 **
248724 ** Or, if the Fts5TokenDataIter iterator is just a container object
248725 ** (nIter==0), then iIter is an index into the term.p[] buffer where
248726 ** the term is stored.
248727 **
248728 ** nByte:
248729 ** In the case where iIter is an index into term.p[], this variable
248730 ** is the size of the term in bytes. If iIter is an index into apIter[],
248731 ** this variable is unused.
248732 */
248733 struct Fts5TokenDataMap {
248734 i64 iRowid; /* Row this token is located in */
248735 i64 iPos; /* Position of token */
248736 int iIter; /* Iterator token was read from */
248737 int nByte; /* Length of token in bytes (or 0) */
248738 };
248739
248740 /*
248741 ** An object used to supplement Fts5Iter for tokendata=1 iterators.
248742 **
248743 ** This object serves two purposes. The first is as a container for an array
248744 ** of Fts5TokenDataMap structures, which are used to find the token required
248745 ** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and
248746 ** aMap[] variables.
248747 */
248748 struct Fts5TokenDataIter {
248749 int nMapAlloc; /* Allocated size of aMap[] in entries */
248750 int nMap; /* Number of valid entries in aMap[] */
248751 Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
248752
248753 /* The following are used for prefix-queries only. */
248754 Fts5Buffer terms;
248755
248756 /* The following are used for other full-token tokendata queries only. */
248757 int nIter;
248758 int nIterAlloc;
248759 Fts5PoslistReader *aPoslistReader;
248760 int *aPoslistToIter;
248761 Fts5Iter *apIter[1];
248762 };
248763
248764 /*
248765 ** The two input arrays - a1[] and a2[] - are in sorted order. This function
248766 ** merges the two arrays together and writes the result to output array
248767 ** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
248768 **
248769 ** Duplicate entries are copied into the output. So the size of the output
248770 ** array is always (n1+n2) entries.
248771 */
248772 static void fts5TokendataMerge(
248773 Fts5TokenDataMap *a1, int n1, /* Input array 1 */
248774 Fts5TokenDataMap *a2, int n2, /* Input array 2 */
248775 Fts5TokenDataMap *aOut /* Output array */
248776 ){
248777 int i1 = 0;
248778 int i2 = 0;
248779
248780 assert( n1>=0 && n2>=0 );
248781 while( i1<n1 || i2<n2 ){
248782 Fts5TokenDataMap *pOut = &aOut[i1+i2];
248783 if( i2>=n2 || (i1<n1 && (
248784 a1[i1].iRowid<a2[i2].iRowid
248785 || (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos)
248786 ))){
248787 memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap));
248788 i1++;
248789 }else{
248790 memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap));
248791 i2++;
248792 }
248793 }
248794 }
248795
248796
248797 /*
248798 ** Append a mapping to the token-map belonging to object pT.
248799 */
248800 static void fts5TokendataIterAppendMap(
248801 Fts5Index *p,
248802 Fts5TokenDataIter *pT,
248803 int iIter,
248804 int nByte,
248805 i64 iRowid,
248806 i64 iPos
248807 ){
248808 if( p->rc==SQLITE_OK ){
248809 if( pT->nMap==pT->nMapAlloc ){
248810 int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
248811 int nAlloc = nNew * sizeof(Fts5TokenDataMap);
248812 Fts5TokenDataMap *aNew;
248813
248814 aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
248815 if( aNew==0 ){
248816 p->rc = SQLITE_NOMEM;
248817 return;
248818 }
248819
248820 pT->aMap = aNew;
248821 pT->nMapAlloc = nNew;
248822 }
248823
248824 pT->aMap[pT->nMap].iRowid = iRowid;
248825 pT->aMap[pT->nMap].iPos = iPos;
248826 pT->aMap[pT->nMap].iIter = iIter;
248827 pT->aMap[pT->nMap].nByte = nByte;
248828 pT->nMap++;
248829 }
248830 }
248831
248832 /*
248833 ** Sort the contents of the pT->aMap[] array.
248834 **
248835 ** The sorting algorithm requries a malloc(). If this fails, an error code
248836 ** is left in Fts5Index.rc before returning.
248837 */
248838 static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
248839 Fts5TokenDataMap *aTmp = 0;
248840 int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
248841
248842 aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
248843 if( aTmp ){
248844 Fts5TokenDataMap *a1 = pT->aMap;
248845 Fts5TokenDataMap *a2 = aTmp;
248846 i64 nHalf;
248847
248848 for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){
248849 int i1;
248850 for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){
248851 int n1 = MIN(nHalf, pT->nMap-i1);
248852 int n2 = MIN(nHalf, pT->nMap-i1-n1);
248853 fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]);
248854 }
248855 SWAPVAL(Fts5TokenDataMap*, a1, a2);
248856 }
248857
248858 if( a1!=pT->aMap ){
248859 memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap));
248860 }
248861 sqlite3_free(aTmp);
248862
248863 #ifdef SQLITE_DEBUG
248864 {
248865 int ii;
248866 for(ii=1; ii<pT->nMap; ii++){
248867 Fts5TokenDataMap *p1 = &pT->aMap[ii-1];
248868 Fts5TokenDataMap *p2 = &pT->aMap[ii];
248869 assert( p1->iRowid<p2->iRowid
248870 || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos)
248871 );
248872 }
248873 }
248874 #endif
248875 }
248876 }
248877
248878 /*
248879 ** Delete an Fts5TokenDataIter structure and its contents.
248880 */
248881 static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
248882 if( pSet ){
248883 int ii;
248884 for(ii=0; ii<pSet->nIter; ii++){
248885 fts5MultiIterFree(pSet->apIter[ii]);
248886 }
248887 fts5BufferFree(&pSet->terms);
248888 sqlite3_free(pSet->aPoslistReader);
248889 sqlite3_free(pSet->aMap);
248890 sqlite3_free(pSet);
248891 }
248892 }
248893
248894
248895 /*
248896 ** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata()
248897 ** to pass data to prefixIterSetupTokendataCb().
248898 */
248899 typedef struct TokendataSetupCtx TokendataSetupCtx;
248900 struct TokendataSetupCtx {
248901 Fts5TokenDataIter *pT; /* Object being populated with mappings */
248902 int iTermOff; /* Offset of current term in terms.p[] */
248903 int nTermByte; /* Size of current term in bytes */
248904 };
248905
248906 /*
248907 ** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This
248908 ** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each
248909 ** position in the current position-list. It doesn't matter that some of
248910 ** these may be out of order - they will be sorted later.
248911 */
248912 static void prefixIterSetupTokendataCb(
248913 Fts5Index *p,
248914 void *pCtx,
248915 Fts5Iter *p1,
248916 const u8 *pNew,
248917 int nNew
248918 ){
248919 TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx;
248920 int iPosOff = 0;
248921 i64 iPos = 0;
248922
248923 if( pNew ){
248924 pSetup->nTermByte = nNew-1;
248925 pSetup->iTermOff = pSetup->pT->terms.n;
248926 fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1);
248927 }
248928
248929 while( 0==sqlite3Fts5PoslistNext64(
248930 p1->base.pData, p1->base.nData, &iPosOff, &iPos
248931 ) ){
248932 fts5TokendataIterAppendMap(p,
248933 pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos
248934 );
248935 }
248936 }
248937
248938
248939 /*
248940 ** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries().
248941 */
248942 typedef struct PrefixSetupCtx PrefixSetupCtx;
248943 struct PrefixSetupCtx {
248944 void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
248945 void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
248946 i64 iLastRowid;
248947 int nMerge;
248948 Fts5Buffer *aBuf;
248949 int nBuf;
248950 Fts5Buffer doclist;
248951 TokendataSetupCtx *pTokendata;
248952 };
248953
248954 /*
248955 ** fts5VisitEntries() callback used by fts5SetupPrefixIter()
248956 */
248957 static void prefixIterSetupCb(
248958 Fts5Index *p,
248959 void *pCtx,
248960 Fts5Iter *p1,
248961 const u8 *pNew,
248962 int nNew
248963 ){
248964 PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx;
248965 const int nMerge = pSetup->nMerge;
248966
248967 if( p1->base.nData>0 ){
248968 if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){
248969 int i;
248970 for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){
248971 int i1 = i*nMerge;
248972 int iStore;
248973 assert( i1+nMerge<=pSetup->nBuf );
248974 for(iStore=i1; iStore<i1+nMerge; iStore++){
248975 if( pSetup->aBuf[iStore].n==0 ){
248976 fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]);
248977 fts5BufferZero(&pSetup->doclist);
248978 break;
248979 }
248980 }
248981 if( iStore==i1+nMerge ){
248982 pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]);
248983 for(iStore=i1; iStore<i1+nMerge; iStore++){
248984 fts5BufferZero(&pSetup->aBuf[iStore]);
248985 }
248986 }
248987 }
248988 pSetup->iLastRowid = 0;
248989 }
248990
248991 pSetup->xAppend(
248992 p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist
248993 );
248994 pSetup->iLastRowid = p1->base.iRowid;
248995 }
248996
248997 if( pSetup->pTokendata ){
248998 prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew);
248999 }
249000 }
249001
249002 static void fts5SetupPrefixIter(
249003 Fts5Index *p, /* Index to read from */
249004 int bDesc, /* True for "ORDER BY rowid DESC" */
249005 int iIdx, /* Index to scan for data */
249006 u8 *pToken, /* Buffer containing prefix to match */
@@ -248475,137 +249007,89 @@
249007 int nToken, /* Size of buffer pToken in bytes */
249008 Fts5Colset *pColset, /* Restrict matches to these columns */
249009 Fts5Iter **ppIter /* OUT: New iterator */
249010 ){
249011 Fts5Structure *pStruct;
249012 PrefixSetupCtx s;
249013 TokendataSetupCtx s2;
 
249014
249015 memset(&s, 0, sizeof(s));
249016 memset(&s2, 0, sizeof(s2));
249017
249018 s.nMerge = 1;
249019 s.iLastRowid = 0;
249020 s.nBuf = 32;
249021 if( iIdx==0
249022 && p->pConfig->eDetail==FTS5_DETAIL_FULL
249023 && p->pConfig->bPrefixInsttoken
249024 ){
249025 s.pTokendata = &s2;
249026 s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
249027 }
249028
249029 if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
249030 s.xMerge = fts5MergeRowidLists;
249031 s.xAppend = fts5AppendRowid;
249032 }else{
249033 s.nMerge = FTS5_MERGE_NLIST-1;
249034 s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
249035 s.xMerge = fts5MergePrefixLists;
249036 s.xAppend = fts5AppendPoslist;
249037 }
249038
249039 s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf);
249040 pStruct = fts5StructureRead(p);
249041 assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) );
249042
249043 if( p->rc==SQLITE_OK ){
249044 void *pCtx = (void*)&s;
 
 
249045 int i;
 
 
249046 Fts5Data *pData;
 
 
 
 
249047
249048 /* If iIdx is non-zero, then it is the number of a prefix-index for
249049 ** prefixes 1 character longer than the prefix being queried for. That
249050 ** index contains all the doclists required, except for the one
249051 ** corresponding to the prefix itself. That one is extracted from the
249052 ** main term index here. */
249053 if( iIdx!=0 ){
 
 
249054 pToken[0] = FTS5_MAIN_PREFIX;
249055 fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx);
 
 
 
 
 
 
 
 
 
 
 
 
 
249056 }
249057
249058 pToken[0] = FTS5_MAIN_PREFIX + iIdx;
249059 fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx);
249060
249061 assert( (s.nBuf%s.nMerge)==0 );
249062 for(i=0; i<s.nBuf; i+=s.nMerge){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249063 int iFree;
249064 if( p->rc==SQLITE_OK ){
249065 s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]);
249066 }
249067 for(iFree=i; iFree<i+s.nMerge; iFree++){
249068 fts5BufferFree(&s.aBuf[iFree]);
249069 }
249070 }
 
249071
249072 pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING);
249073 if( pData ){
249074 pData->p = (u8*)&pData[1];
249075 pData->nn = pData->szLeaf = s.doclist.n;
249076 if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n);
249077 fts5MultiIterNew2(p, pData, bDesc, ppIter);
249078 }
249079
249080 if( p->rc==SQLITE_OK && s.pTokendata ){
249081 fts5TokendataIterSortMap(p, s2.pT);
249082 (*ppIter)->pTokenDataIter = s2.pT;
249083 s2.pT = 0;
249084 }
249085 }
249086
249087 fts5TokendataIterDelete(s2.pT);
249088 fts5BufferFree(&s.doclist);
249089 fts5StructureRelease(pStruct);
249090 sqlite3_free(s.aBuf);
249091 }
249092
249093
249094 /*
249095 ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
@@ -248855,42 +249339,10 @@
249339 static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
249340 fts5DataRelease(pSeg->pLeaf);
249341 pSeg->pLeaf = 0;
249342 }
249343
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249344 /*
249345 ** This function appends iterator pAppend to Fts5TokenDataIter pIn and
249346 ** returns the result.
249347 */
249348 static Fts5TokenDataIter *fts5AppendTokendataIter(
@@ -248923,58 +249375,10 @@
249375 assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
249376
249377 return pRet;
249378 }
249379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249380 /*
249381 ** The iterator passed as the only argument must be a tokendata=1 iterator
249382 ** (pIter->pTokenDataIter!=0). This function sets the iterator output
249383 ** variables (pIter->base.*) according to the contents of the current
249384 ** row.
@@ -249011,11 +249415,11 @@
249415 int eDetail = pIter->pIndex->pConfig->eDetail;
249416 pIter->base.bEof = 0;
249417 pIter->base.iRowid = iRowid;
249418
249419 if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
249420 fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1);
249421 }else
249422 if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
249423 int nReader = 0;
249424 int nByte = 0;
249425 i64 iPrev = 0;
@@ -249264,10 +249668,11 @@
249668
249669 if( p->rc==SQLITE_OK ){
249670 pRet = fts5MultiIterAlloc(p, 0);
249671 }
249672 if( pRet ){
249673 pRet->nSeg = 0;
249674 pRet->pTokenDataIter = pSet;
249675 if( pSet ){
249676 fts5IterSetOutputsTokendata(pRet);
249677 }else{
249678 pRet->base.bEof = 1;
@@ -249278,11 +249683,10 @@
249683
249684 fts5StructureRelease(pStruct);
249685 fts5BufferFree(&bSeek);
249686 return pRet;
249687 }
 
249688
249689 /*
249690 ** Open a new iterator to iterate though all rowid that match the
249691 ** specified token or token prefix.
249692 */
@@ -249304,10 +249708,15 @@
249708 int iIdx = 0; /* Index to search */
249709 int iPrefixIdx = 0; /* +1 prefix index */
249710 int bTokendata = pConfig->bTokendata;
249711 if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
249712
249713 /* The NOTOKENDATA flag is set when each token in a tokendata=1 table
249714 ** should be treated individually, instead of merging all those with
249715 ** a common prefix into a single entry. This is used, for example, by
249716 ** queries performed as part of an integrity-check, or by the fts5vocab
249717 ** module. */
249718 if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
249719 bTokendata = 0;
249720 }
249721
249722 /* Figure out which index to search and set iIdx accordingly. If this
@@ -249334,11 +249743,11 @@
249743 if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
249744 }
249745 }
249746
249747 if( bTokendata && iIdx==0 ){
249748 buf.p[0] = FTS5_MAIN_PREFIX;
249749 pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
249750 }else if( iIdx<=pConfig->nPrefix ){
249751 /* Straight index lookup */
249752 Fts5Structure *pStruct = fts5StructureRead(p);
249753 buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
@@ -249347,11 +249756,11 @@
249756 pColset, buf.p, nToken+1, -1, 0, &pRet
249757 );
249758 fts5StructureRelease(pStruct);
249759 }
249760 }else{
249761 /* Scan multiple terms in the main index for a prefix query. */
249762 int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
249763 fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
249764 if( pRet==0 ){
249765 assert( p->rc!=SQLITE_OK );
249766 }else{
@@ -249383,11 +249792,12 @@
249792 ** Move to the next matching rowid.
249793 */
249794 static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
249795 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249796 assert( pIter->pIndex->rc==SQLITE_OK );
249797 if( pIter->nSeg==0 ){
249798 assert( pIter->pTokenDataIter );
249799 fts5TokendataIterNext(pIter, 0, 0);
249800 }else{
249801 fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
249802 }
249803 return fts5IndexReturn(pIter->pIndex);
@@ -249420,11 +249830,12 @@
249830 ** definition of "at or after" depends on whether this iterator iterates
249831 ** in ascending or descending rowid order.
249832 */
249833 static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
249834 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249835 if( pIter->nSeg==0 ){
249836 assert( pIter->pTokenDataIter );
249837 fts5TokendataIterNext(pIter, 1, iMatch);
249838 }else{
249839 fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
249840 }
249841 return fts5IndexReturn(pIter->pIndex);
@@ -249438,32 +249849,87 @@
249849 const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
249850 assert_nc( z || n<=1 );
249851 *pn = n-1;
249852 return (z ? &z[1] : 0);
249853 }
249854
249855 /*
249856 ** pIter is a prefix query. This function populates pIter->pTokenDataIter
249857 ** with an Fts5TokenDataIter object containing mappings for all rows
249858 ** matched by the query.
249859 */
249860 static int fts5SetupPrefixIterTokendata(
249861 Fts5Iter *pIter,
249862 const char *pToken, /* Token prefix to search for */
249863 int nToken /* Size of pToken in bytes */
249864 ){
249865 Fts5Index *p = pIter->pIndex;
249866 Fts5Buffer token = {0, 0, 0};
249867 TokendataSetupCtx ctx;
249868
249869 memset(&ctx, 0, sizeof(ctx));
249870
249871 fts5BufferGrow(&p->rc, &token, nToken+1);
249872 ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT));
249873
249874 if( p->rc==SQLITE_OK ){
249875
249876 /* Fill in the token prefix to search for */
249877 token.p[0] = FTS5_MAIN_PREFIX;
249878 memcpy(&token.p[1], pToken, nToken);
249879 token.n = nToken+1;
249880
249881 fts5VisitEntries(
249882 p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx
249883 );
249884
249885 fts5TokendataIterSortMap(p, ctx.pT);
249886 }
249887
249888 if( p->rc==SQLITE_OK ){
249889 pIter->pTokenDataIter = ctx.pT;
249890 }else{
249891 fts5TokendataIterDelete(ctx.pT);
249892 }
249893 fts5BufferFree(&token);
249894
249895 return fts5IndexReturn(p);
249896 }
249897
249898 /*
249899 ** This is used by xInstToken() to access the token at offset iOff, column
249900 ** iCol of row iRowid. The token is returned via output variables *ppOut
249901 ** and *pnOut. The iterator passed as the first argument must be a tokendata=1
249902 ** iterator (pIter->pTokenDataIter!=0).
249903 **
249904 ** pToken/nToken:
249905 */
249906 static int sqlite3Fts5IterToken(
249907 Fts5IndexIter *pIndexIter,
249908 const char *pToken, int nToken,
249909 i64 iRowid,
249910 int iCol,
249911 int iOff,
249912 const char **ppOut, int *pnOut
249913 ){
249914 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249915 Fts5TokenDataIter *pT = pIter->pTokenDataIter;
 
249916 i64 iPos = (((i64)iCol)<<32) + iOff;
249917 Fts5TokenDataMap *aMap = 0;
249918 int i1 = 0;
249919 int i2 = 0;
249920 int iTest = 0;
249921
249922 assert( pT || (pToken && pIter->nSeg>0) );
249923 if( pT==0 ){
249924 int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken);
249925 if( rc!=SQLITE_OK ) return rc;
249926 pT = pIter->pTokenDataIter;
249927 }
249928
249929 i2 = pT->nMap;
249930 aMap = pT->aMap;
249931
249932 while( i2>i1 ){
249933 iTest = (i1 + i2) / 2;
249934
249935 if( aMap[iTest].iRowid<iRowid ){
@@ -249483,13 +249949,19 @@
249949 }
249950 }
249951 }
249952
249953 if( i2>i1 ){
249954 if( pIter->nSeg==0 ){
249955 Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
249956 *ppOut = (const char*)pMap->aSeg[0].term.p+1;
249957 *pnOut = pMap->aSeg[0].term.n-1;
249958 }else{
249959 Fts5TokenDataMap *p = &aMap[iTest];
249960 *ppOut = (const char*)&pT->terms.p[p->iIter];
249961 *pnOut = aMap[iTest].nByte;
249962 }
249963 }
249964
249965 return SQLITE_OK;
249966 }
249967
@@ -249497,11 +249969,13 @@
249969 ** Clear any existing entries from the token-map associated with the
249970 ** iterator passed as the only argument.
249971 */
249972 static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
249973 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249974 if( pIter && pIter->pTokenDataIter
249975 && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL)
249976 ){
249977 pIter->pTokenDataIter->nMap = 0;
249978 }
249979 }
249980
249981 /*
@@ -249517,21 +249991,33 @@
249991 i64 iRowid, int iCol, int iOff
249992 ){
249993 Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
249994 Fts5TokenDataIter *pT = pIter->pTokenDataIter;
249995 Fts5Index *p = pIter->pIndex;
249996 i64 iPos = (((i64)iCol)<<32) + iOff;
249997
249998 assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
249999 assert( pIter->pTokenDataIter || pIter->nSeg>0 );
250000 if( pIter->nSeg>0 ){
250001 /* This is a prefix term iterator. */
250002 if( pT==0 ){
250003 pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));
250004 pIter->pTokenDataIter = pT;
250005 }
250006 if( pT ){
250007 fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
250008 fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
250009 }
250010 }else{
250011 int ii;
250012 for(ii=0; ii<pT->nIter; ii++){
250013 Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
250014 if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
250015 }
250016 if( ii<pT->nIter ){
250017 fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos);
250018 }
250019 }
250020 return fts5IndexReturn(p);
250021 }
250022
250023 /*
@@ -251432,10 +251918,11 @@
251918 ** containing a copy of the header from an Fts5Config pointer.
251919 */
251920 #define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
251921 #define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
251922
251923 #define FTS5_INSTTOKEN_SUBTYPE 73
251924
251925 /*
251926 ** Each auxiliary function registered with the FTS5 module is represented
251927 ** by an object of the following type. All such objects are stored as part
251928 ** of the Fts5Global.pAux list.
@@ -252757,10 +253244,11 @@
253244 sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
253245 sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
253246 sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
253247 int iCol; /* Column on LHS of MATCH operator */
253248 char **pzErrmsg = pConfig->pzErrmsg;
253249 int bPrefixInsttoken = pConfig->bPrefixInsttoken;
253250 int i;
253251 int iIdxStr = 0;
253252 Fts5Expr *pExpr = 0;
253253
253254 assert( pConfig->bLock==0 );
@@ -252792,10 +253280,13 @@
253280 int bInternal = 0;
253281
253282 rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
253283 if( rc!=SQLITE_OK ) goto filter_out;
253284 if( zText==0 ) zText = "";
253285 if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){
253286 pConfig->bPrefixInsttoken = 1;
253287 }
253288
253289 iCol = 0;
253290 do{
253291 iCol = iCol*10 + (idxStr[iIdxStr]-'0');
253292 iIdxStr++;
@@ -252932,10 +253423,11 @@
253423 }
253424
253425 filter_out:
253426 sqlite3Fts5ExprFree(pExpr);
253427 pConfig->pzErrmsg = pzErrmsg;
253428 pConfig->bPrefixInsttoken = bPrefixInsttoken;
253429 return rc;
253430 }
253431
253432 /*
253433 ** This is the xEof method of the virtual table. SQLite calls this
@@ -254927,11 +255419,11 @@
255419 int nArg, /* Number of args */
255420 sqlite3_value **apUnused /* Function arguments */
255421 ){
255422 assert( nArg==0 );
255423 UNUSED_PARAM2(nArg, apUnused);
255424 sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT);
255425 }
255426
255427 /*
255428 ** Implementation of fts5_locale(LOCALE, TEXT) function.
255429 **
@@ -254990,10 +255482,24 @@
255482 assert( &pCsr[nText]==&pBlob[nBlob] );
255483
255484 sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free);
255485 }
255486 }
255487
255488 /*
255489 ** Implementation of fts5_insttoken() function.
255490 */
255491 static void fts5InsttokenFunc(
255492 sqlite3_context *pCtx, /* Function call context */
255493 int nArg, /* Number of args */
255494 sqlite3_value **apArg /* Function arguments */
255495 ){
255496 assert( nArg==1 );
255497 (void)nArg;
255498 sqlite3_result_value(pCtx, apArg[0]);
255499 sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE);
255500 }
255501
255502 /*
255503 ** Return true if zName is the extension on one of the shadow tables used
255504 ** by this module.
255505 */
@@ -255120,13 +255626,20 @@
255626 );
255627 }
255628 if( rc==SQLITE_OK ){
255629 rc = sqlite3_create_function(
255630 db, "fts5_locale", 2,
255631 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE,
255632 p, fts5LocaleFunc, 0, 0
255633 );
255634 }
255635 if( rc==SQLITE_OK ){
255636 rc = sqlite3_create_function(
255637 db, "fts5_insttoken", 1,
255638 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
255639 p, fts5InsttokenFunc, 0, 0
255640 );
255641 }
255642 }
255643
255644 /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
255645 ** fts5_test_mi.c is compiled and linked into the executable. And call
@@ -258048,11 +258561,11 @@
258561 int rc = SQLITE_OK;
258562 char aBuf[32];
258563 char *zOut = aBuf;
258564 int ii;
258565 const unsigned char *zIn = (const unsigned char*)pText;
258566 const unsigned char *zEof = (zIn ? &zIn[nText] : 0);
258567 u32 iCode = 0;
258568 int aStart[3]; /* Input offset of each character in aBuf[] */
258569
258570 UNUSED_PARAM(unusedFlags);
258571
258572
+30 -4
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.48.0"
150150
#define SQLITE_VERSION_NUMBER 3048000
151
-#define SQLITE_SOURCE_ID "2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8"
151
+#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
@@ -4202,15 +4202,26 @@
42024202
**
42034203
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
42044204
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
42054205
** to return an error (error code SQLITE_ERROR) if the statement uses
42064206
** any virtual tables.
4207
+**
4208
+** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
4209
+** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
4210
+** errors from being sent to the error log defined by
4211
+** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
4212
+** compiles to see if some SQL syntax is well-formed, without generating
4213
+** messages on the global error log when it is not. If the test compile
4214
+** fails, the sqlite3_prepare_v3() call returns the same error indications
4215
+** with or without this flag; it just omits the call to [sqlite3_log()] that
4216
+** logs the error.
42074217
** </dl>
42084218
*/
42094219
#define SQLITE_PREPARE_PERSISTENT 0x01
42104220
#define SQLITE_PREPARE_NORMALIZE 0x02
42114221
#define SQLITE_PREPARE_NO_VTAB 0x04
4222
+#define SQLITE_PREPARE_DONT_LOG 0x10
42124223
42134224
/*
42144225
** CAPI3REF: Compiling An SQL Statement
42154226
** KEYWORDS: {SQL statement compiler}
42164227
** METHOD: sqlite3
@@ -13148,17 +13159,32 @@
1314813159
** This is used to access token iToken of phrase hit iIdx within the
1314913160
** current row. If iIdx is less than zero or greater than or equal to the
1315013161
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
1315113162
** output variable (*ppToken) is set to point to a buffer containing the
1315213163
** matching document token, and (*pnToken) to the size of that buffer in
13153
-** bytes. This API is not available if the specified token matches a
13154
-** prefix query term. In that case both output variables are always set
13155
-** to 0.
13164
+** bytes.
1315613165
**
1315713166
** The output text is not a copy of the document text that was tokenized.
1315813167
** It is the output of the tokenizer module. For tokendata=1 tables, this
1315913168
** includes any embedded 0x00 and trailing data.
13169
+**
13170
+** This API may be slow in some cases if the token identified by parameters
13171
+** iIdx and iToken matched a prefix token in the query. In most cases, the
13172
+** first call to this API for each prefix token in the query is forced
13173
+** to scan the portion of the full-text index that matches the prefix
13174
+** token to collect the extra data required by this API. If the prefix
13175
+** token matches a large number of token instances in the document set,
13176
+** this may be a performance problem.
13177
+**
13178
+** If the user knows in advance that a query may use this API for a
13179
+** prefix token, FTS5 may be configured to collect all required data as part
13180
+** of the initial querying of the full-text index, avoiding the second scan
13181
+** entirely. This also causes prefix queries that do not use this API to
13182
+** run more slowly and use more memory. FTS5 may be configured in this way
13183
+** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
13184
+** option, or on a per-query basis using the
13185
+** [fts5_insttoken | fts5_insttoken()] user function.
1316013186
**
1316113187
** This API can be quite slow if used with an FTS5 table created with the
1316213188
** "detail=none" or "detail=column" option.
1316313189
**
1316413190
** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
1316513191
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.48.0"
150 #define SQLITE_VERSION_NUMBER 3048000
151 #define SQLITE_SOURCE_ID "2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -4202,15 +4202,26 @@
4202 **
4203 ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
4204 ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
4205 ** to return an error (error code SQLITE_ERROR) if the statement uses
4206 ** any virtual tables.
 
 
 
 
 
 
 
 
 
 
4207 ** </dl>
4208 */
4209 #define SQLITE_PREPARE_PERSISTENT 0x01
4210 #define SQLITE_PREPARE_NORMALIZE 0x02
4211 #define SQLITE_PREPARE_NO_VTAB 0x04
 
4212
4213 /*
4214 ** CAPI3REF: Compiling An SQL Statement
4215 ** KEYWORDS: {SQL statement compiler}
4216 ** METHOD: sqlite3
@@ -13148,17 +13159,32 @@
13148 ** This is used to access token iToken of phrase hit iIdx within the
13149 ** current row. If iIdx is less than zero or greater than or equal to the
13150 ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
13151 ** output variable (*ppToken) is set to point to a buffer containing the
13152 ** matching document token, and (*pnToken) to the size of that buffer in
13153 ** bytes. This API is not available if the specified token matches a
13154 ** prefix query term. In that case both output variables are always set
13155 ** to 0.
13156 **
13157 ** The output text is not a copy of the document text that was tokenized.
13158 ** It is the output of the tokenizer module. For tokendata=1 tables, this
13159 ** includes any embedded 0x00 and trailing data.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13160 **
13161 ** This API can be quite slow if used with an FTS5 table created with the
13162 ** "detail=none" or "detail=column" option.
13163 **
13164 ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
13165
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.48.0"
150 #define SQLITE_VERSION_NUMBER 3048000
151 #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -4202,15 +4202,26 @@
4202 **
4203 ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
4204 ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
4205 ** to return an error (error code SQLITE_ERROR) if the statement uses
4206 ** any virtual tables.
4207 **
4208 ** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
4209 ** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
4210 ** errors from being sent to the error log defined by
4211 ** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
4212 ** compiles to see if some SQL syntax is well-formed, without generating
4213 ** messages on the global error log when it is not. If the test compile
4214 ** fails, the sqlite3_prepare_v3() call returns the same error indications
4215 ** with or without this flag; it just omits the call to [sqlite3_log()] that
4216 ** logs the error.
4217 ** </dl>
4218 */
4219 #define SQLITE_PREPARE_PERSISTENT 0x01
4220 #define SQLITE_PREPARE_NORMALIZE 0x02
4221 #define SQLITE_PREPARE_NO_VTAB 0x04
4222 #define SQLITE_PREPARE_DONT_LOG 0x10
4223
4224 /*
4225 ** CAPI3REF: Compiling An SQL Statement
4226 ** KEYWORDS: {SQL statement compiler}
4227 ** METHOD: sqlite3
@@ -13148,17 +13159,32 @@
13159 ** This is used to access token iToken of phrase hit iIdx within the
13160 ** current row. If iIdx is less than zero or greater than or equal to the
13161 ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
13162 ** output variable (*ppToken) is set to point to a buffer containing the
13163 ** matching document token, and (*pnToken) to the size of that buffer in
13164 ** bytes.
 
 
13165 **
13166 ** The output text is not a copy of the document text that was tokenized.
13167 ** It is the output of the tokenizer module. For tokendata=1 tables, this
13168 ** includes any embedded 0x00 and trailing data.
13169 **
13170 ** This API may be slow in some cases if the token identified by parameters
13171 ** iIdx and iToken matched a prefix token in the query. In most cases, the
13172 ** first call to this API for each prefix token in the query is forced
13173 ** to scan the portion of the full-text index that matches the prefix
13174 ** token to collect the extra data required by this API. If the prefix
13175 ** token matches a large number of token instances in the document set,
13176 ** this may be a performance problem.
13177 **
13178 ** If the user knows in advance that a query may use this API for a
13179 ** prefix token, FTS5 may be configured to collect all required data as part
13180 ** of the initial querying of the full-text index, avoiding the second scan
13181 ** entirely. This also causes prefix queries that do not use this API to
13182 ** run more slowly and use more memory. FTS5 may be configured in this way
13183 ** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
13184 ** option, or on a per-query basis using the
13185 ** [fts5_insttoken | fts5_insttoken()] user function.
13186 **
13187 ** This API can be quite slow if used with an FTS5 table created with the
13188 ** "detail=none" or "detail=column" option.
13189 **
13190 ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
13191
--- skins/darkmode/css.txt
+++ skins/darkmode/css.txt
@@ -98,14 +98,17 @@
9898
}
9999
.fileage tr:hover,
100100
div.filetreeline:hover {
101101
background-color: #333;
102102
}
103
+div.file-change-line button {
104
+ background-color: #484848
105
+}
103106
.button,
104107
button {
105108
color: #aaa;
106
- background-color: #444;
109
+ background-color: #484848;
107110
border-radius: 5px;
108111
border: 0
109112
}
110113
.button:hover,
111114
button:hover {
112115
--- skins/darkmode/css.txt
+++ skins/darkmode/css.txt
@@ -98,14 +98,17 @@
98 }
99 .fileage tr:hover,
100 div.filetreeline:hover {
101 background-color: #333;
102 }
 
 
 
103 .button,
104 button {
105 color: #aaa;
106 background-color: #444;
107 border-radius: 5px;
108 border: 0
109 }
110 .button:hover,
111 button:hover {
112
--- skins/darkmode/css.txt
+++ skins/darkmode/css.txt
@@ -98,14 +98,17 @@
98 }
99 .fileage tr:hover,
100 div.filetreeline:hover {
101 background-color: #333;
102 }
103 div.file-change-line button {
104 background-color: #484848
105 }
106 .button,
107 button {
108 color: #aaa;
109 background-color: #484848;
110 border-radius: 5px;
111 border: 0
112 }
113 .button:hover,
114 button:hover {
115
+2 -3
--- src/add.c
+++ src/add.c
@@ -353,11 +353,11 @@
353353
** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore
354354
** option does not appear on the command line then the "ignore-glob" setting
355355
** is used. If the --clean option does not appear on the command line then
356356
** the "clean-glob" setting is used.
357357
**
358
-** If files are attempted to be added explicitly on the command line which
358
+** When attempting to explicitly add files on the commandline, and if those
359359
** match "ignore-glob", a confirmation is asked first. This can be prevented
360360
** using the -f|--force option.
361361
**
362362
** The --case-sensitive option determines whether or not filenames should
363363
** be treated case sensitive or not. If the option is not given, the default
@@ -753,12 +753,11 @@
753753
**
754754
** * All files in the repository but missing from the check-out (that is,
755755
** all files that show as MISSING with the "status" command) are
756756
** removed as if by the "[[rm]]" command.
757757
**
758
-** The command does not "[[commit]]". You must run the "[[commit]]" separately
759
-** as a separate step.
758
+** Note that this command does not "commit", as that is a separate step.
760759
**
761760
** Files and directories whose names begin with "." are ignored unless
762761
** the --dotfiles option is used.
763762
**
764763
** The --ignore option overrides the "ignore-glob" setting, as do the
765764
--- src/add.c
+++ src/add.c
@@ -353,11 +353,11 @@
353 ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore
354 ** option does not appear on the command line then the "ignore-glob" setting
355 ** is used. If the --clean option does not appear on the command line then
356 ** the "clean-glob" setting is used.
357 **
358 ** If files are attempted to be added explicitly on the command line which
359 ** match "ignore-glob", a confirmation is asked first. This can be prevented
360 ** using the -f|--force option.
361 **
362 ** The --case-sensitive option determines whether or not filenames should
363 ** be treated case sensitive or not. If the option is not given, the default
@@ -753,12 +753,11 @@
753 **
754 ** * All files in the repository but missing from the check-out (that is,
755 ** all files that show as MISSING with the "status" command) are
756 ** removed as if by the "[[rm]]" command.
757 **
758 ** The command does not "[[commit]]". You must run the "[[commit]]" separately
759 ** as a separate step.
760 **
761 ** Files and directories whose names begin with "." are ignored unless
762 ** the --dotfiles option is used.
763 **
764 ** The --ignore option overrides the "ignore-glob" setting, as do the
765
--- src/add.c
+++ src/add.c
@@ -353,11 +353,11 @@
353 ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore
354 ** option does not appear on the command line then the "ignore-glob" setting
355 ** is used. If the --clean option does not appear on the command line then
356 ** the "clean-glob" setting is used.
357 **
358 ** When attempting to explicitly add files on the commandline, and if those
359 ** match "ignore-glob", a confirmation is asked first. This can be prevented
360 ** using the -f|--force option.
361 **
362 ** The --case-sensitive option determines whether or not filenames should
363 ** be treated case sensitive or not. If the option is not given, the default
@@ -753,12 +753,11 @@
753 **
754 ** * All files in the repository but missing from the check-out (that is,
755 ** all files that show as MISSING with the "status" command) are
756 ** removed as if by the "[[rm]]" command.
757 **
758 ** Note that this command does not "commit", as that is a separate step.
 
759 **
760 ** Files and directories whose names begin with "." are ignored unless
761 ** the --dotfiles option is used.
762 **
763 ** The --ignore option overrides the "ignore-glob" setting, as do the
764
+12 -4
--- src/bisect.c
+++ src/bisect.c
@@ -543,14 +543,16 @@
543543
**
544544
** > fossil bisect vlist|ls|status ?-a|--all?
545545
**
546546
** List the versions in between the inner-most "bad" and "good".
547547
**
548
-** > fossil bisect ui
548
+** > fossil bisect ui ?HOST@USER:PATH?
549549
**
550550
** Like "fossil ui" except start on a timeline that shows only the
551
-** check-ins that are part of the current bisect.
551
+** check-ins that are part of the current bisect. If the optional
552
+** fourth term is added, then information is shown for the bisect that
553
+** occurred in the PATH directory by USER on remote machine HOST.
552554
**
553555
** > fossil bisect undo
554556
**
555557
** Undo the most recent "good", "bad", or "skip" command.
556558
*/
@@ -719,17 +721,23 @@
719721
}
720722
}else if( strncmp(zCmd, "reset", n)==0 ){
721723
bisect_reset();
722724
}else if( strcmp(zCmd, "ui")==0 ){
723725
char *newArgv[8];
726
+ verify_all_options();
724727
newArgv[0] = g.argv[0];
725728
newArgv[1] = "ui";
726729
newArgv[2] = "--page";
727730
newArgv[3] = "timeline?bisect";
728
- newArgv[4] = 0;
731
+ if( g.argc==4 ){
732
+ newArgv[4] = g.argv[3];
733
+ g.argc = 5;
734
+ }else{
735
+ g.argc = 4;
736
+ }
737
+ newArgv[g.argc] = 0;
729738
g.argv = newArgv;
730
- g.argc = 4;
731739
cmd_webserver();
732740
}else if( strncmp(zCmd, "vlist", n)==0
733741
|| strncmp(zCmd, "ls", n)==0
734742
|| strncmp(zCmd, "status", n)==0
735743
){
736744
--- src/bisect.c
+++ src/bisect.c
@@ -543,14 +543,16 @@
543 **
544 ** > fossil bisect vlist|ls|status ?-a|--all?
545 **
546 ** List the versions in between the inner-most "bad" and "good".
547 **
548 ** > fossil bisect ui
549 **
550 ** Like "fossil ui" except start on a timeline that shows only the
551 ** check-ins that are part of the current bisect.
 
 
552 **
553 ** > fossil bisect undo
554 **
555 ** Undo the most recent "good", "bad", or "skip" command.
556 */
@@ -719,17 +721,23 @@
719 }
720 }else if( strncmp(zCmd, "reset", n)==0 ){
721 bisect_reset();
722 }else if( strcmp(zCmd, "ui")==0 ){
723 char *newArgv[8];
 
724 newArgv[0] = g.argv[0];
725 newArgv[1] = "ui";
726 newArgv[2] = "--page";
727 newArgv[3] = "timeline?bisect";
728 newArgv[4] = 0;
 
 
 
 
 
 
729 g.argv = newArgv;
730 g.argc = 4;
731 cmd_webserver();
732 }else if( strncmp(zCmd, "vlist", n)==0
733 || strncmp(zCmd, "ls", n)==0
734 || strncmp(zCmd, "status", n)==0
735 ){
736
--- src/bisect.c
+++ src/bisect.c
@@ -543,14 +543,16 @@
543 **
544 ** > fossil bisect vlist|ls|status ?-a|--all?
545 **
546 ** List the versions in between the inner-most "bad" and "good".
547 **
548 ** > fossil bisect ui ?HOST@USER:PATH?
549 **
550 ** Like "fossil ui" except start on a timeline that shows only the
551 ** check-ins that are part of the current bisect. If the optional
552 ** fourth term is added, then information is shown for the bisect that
553 ** occurred in the PATH directory by USER on remote machine HOST.
554 **
555 ** > fossil bisect undo
556 **
557 ** Undo the most recent "good", "bad", or "skip" command.
558 */
@@ -719,17 +721,23 @@
721 }
722 }else if( strncmp(zCmd, "reset", n)==0 ){
723 bisect_reset();
724 }else if( strcmp(zCmd, "ui")==0 ){
725 char *newArgv[8];
726 verify_all_options();
727 newArgv[0] = g.argv[0];
728 newArgv[1] = "ui";
729 newArgv[2] = "--page";
730 newArgv[3] = "timeline?bisect";
731 if( g.argc==4 ){
732 newArgv[4] = g.argv[3];
733 g.argc = 5;
734 }else{
735 g.argc = 4;
736 }
737 newArgv[g.argc] = 0;
738 g.argv = newArgv;
 
739 cmd_webserver();
740 }else if( strncmp(zCmd, "vlist", n)==0
741 || strncmp(zCmd, "ls", n)==0
742 || strncmp(zCmd, "status", n)==0
743 ){
744
+2 -2
--- src/branch.c
+++ src/branch.c
@@ -640,12 +640,12 @@
640640
** -M|--unmerged List branches not merged into the current branch
641641
** -p List only private branches
642642
** -r Reverse the sort order
643643
** -t Show recently changed branches first
644644
** --self List only branches where you participate
645
-** --username USER List only branches where USER participate
646
-** --users N List up to N users partipiating
645
+** --username USER List only branches where USER participates
646
+** --users N List up to N users participating
647647
**
648648
** The current branch is marked with an asterisk. Private branches are
649649
** marked with a hash sign.
650650
**
651651
** If GLOB is given, show only branches matching the pattern.
652652
--- src/branch.c
+++ src/branch.c
@@ -640,12 +640,12 @@
640 ** -M|--unmerged List branches not merged into the current branch
641 ** -p List only private branches
642 ** -r Reverse the sort order
643 ** -t Show recently changed branches first
644 ** --self List only branches where you participate
645 ** --username USER List only branches where USER participate
646 ** --users N List up to N users partipiating
647 **
648 ** The current branch is marked with an asterisk. Private branches are
649 ** marked with a hash sign.
650 **
651 ** If GLOB is given, show only branches matching the pattern.
652
--- src/branch.c
+++ src/branch.c
@@ -640,12 +640,12 @@
640 ** -M|--unmerged List branches not merged into the current branch
641 ** -p List only private branches
642 ** -r Reverse the sort order
643 ** -t Show recently changed branches first
644 ** --self List only branches where you participate
645 ** --username USER List only branches where USER participates
646 ** --users N List up to N users participating
647 **
648 ** The current branch is marked with an asterisk. Private branches are
649 ** marked with a hash sign.
650 **
651 ** If GLOB is given, show only branches matching the pattern.
652
+2 -2
--- src/checkin.c
+++ src/checkin.c
@@ -1563,17 +1563,17 @@
15631563
break;
15641564
}
15651565
diffFiles[i].nName = strlen(diffFiles[i].zName);
15661566
diffFiles[i].nUsed = 0;
15671567
}
1568
- diff_against_disk(0, &DCfg, diffFiles, &prompt);
1568
+ diff_version_to_checkout(0, &DCfg, diffFiles, &prompt);
15691569
for( i=0; diffFiles[i].zName; ++i ){
15701570
fossil_free(diffFiles[i].zName);
15711571
}
15721572
fossil_free(diffFiles);
15731573
}else{
1574
- diff_against_disk(0, &DCfg, 0, &prompt);
1574
+ diff_version_to_checkout(0, &DCfg, 0, &prompt);
15751575
}
15761576
}
15771577
prompt_for_user_comment(pComment, &prompt);
15781578
blob_reset(&prompt);
15791579
}
15801580
--- src/checkin.c
+++ src/checkin.c
@@ -1563,17 +1563,17 @@
1563 break;
1564 }
1565 diffFiles[i].nName = strlen(diffFiles[i].zName);
1566 diffFiles[i].nUsed = 0;
1567 }
1568 diff_against_disk(0, &DCfg, diffFiles, &prompt);
1569 for( i=0; diffFiles[i].zName; ++i ){
1570 fossil_free(diffFiles[i].zName);
1571 }
1572 fossil_free(diffFiles);
1573 }else{
1574 diff_against_disk(0, &DCfg, 0, &prompt);
1575 }
1576 }
1577 prompt_for_user_comment(pComment, &prompt);
1578 blob_reset(&prompt);
1579 }
1580
--- src/checkin.c
+++ src/checkin.c
@@ -1563,17 +1563,17 @@
1563 break;
1564 }
1565 diffFiles[i].nName = strlen(diffFiles[i].zName);
1566 diffFiles[i].nUsed = 0;
1567 }
1568 diff_version_to_checkout(0, &DCfg, diffFiles, &prompt);
1569 for( i=0; diffFiles[i].zName; ++i ){
1570 fossil_free(diffFiles[i].zName);
1571 }
1572 fossil_free(diffFiles);
1573 }else{
1574 diff_version_to_checkout(0, &DCfg, 0, &prompt);
1575 }
1576 }
1577 prompt_for_user_comment(pComment, &prompt);
1578 blob_reset(&prompt);
1579 }
1580
+10 -4
--- src/default.css
+++ src/default.css
@@ -748,10 +748,20 @@
748748
border-bottom: 3px solid gold;
749749
}
750750
body.tkt div.content ol.tkt-changes > li:target > ol {
751751
border-left: 1px solid gold;
752752
}
753
+body.cpage-ckout .file-change-line,
754
+body.cpage-info .file-change-line,
755
+body.cpage-vdiff .file-change-line {
756
+ margin-top: 16px;
757
+ margin-bottom: 16px;
758
+ margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
759
+ display: flex;
760
+ flex-direction: row;
761
+ justify-content: space-between;
762
+}
753763
754764
span.modpending {
755765
color: #b03800;
756766
font-style: italic;
757767
}
@@ -1818,14 +1828,10 @@
18181828
}
18191829
body.fossil-dark-style .settings-icon {
18201830
filter: invert(100%);
18211831
}
18221832
1823
-input[type="checkbox"].diff-toggle {
1824
- float: right;
1825
-}
1826
-
18271833
body.branch .brlist > table > tbody > tr:hover:not(.selected),
18281834
body.branch .brlist > table > tbody > tr.selected {
18291835
background-color: #ffc;
18301836
}
18311837
body.branch .brlist > table > tbody td:first-child > input {
18321838
--- src/default.css
+++ src/default.css
@@ -748,10 +748,20 @@
748 border-bottom: 3px solid gold;
749 }
750 body.tkt div.content ol.tkt-changes > li:target > ol {
751 border-left: 1px solid gold;
752 }
 
 
 
 
 
 
 
 
 
 
753
754 span.modpending {
755 color: #b03800;
756 font-style: italic;
757 }
@@ -1818,14 +1828,10 @@
1818 }
1819 body.fossil-dark-style .settings-icon {
1820 filter: invert(100%);
1821 }
1822
1823 input[type="checkbox"].diff-toggle {
1824 float: right;
1825 }
1826
1827 body.branch .brlist > table > tbody > tr:hover:not(.selected),
1828 body.branch .brlist > table > tbody > tr.selected {
1829 background-color: #ffc;
1830 }
1831 body.branch .brlist > table > tbody td:first-child > input {
1832
--- src/default.css
+++ src/default.css
@@ -748,10 +748,20 @@
748 border-bottom: 3px solid gold;
749 }
750 body.tkt div.content ol.tkt-changes > li:target > ol {
751 border-left: 1px solid gold;
752 }
753 body.cpage-ckout .file-change-line,
754 body.cpage-info .file-change-line,
755 body.cpage-vdiff .file-change-line {
756 margin-top: 16px;
757 margin-bottom: 16px;
758 margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
759 display: flex;
760 flex-direction: row;
761 justify-content: space-between;
762 }
763
764 span.modpending {
765 color: #b03800;
766 font-style: italic;
767 }
@@ -1818,14 +1828,10 @@
1828 }
1829 body.fossil-dark-style .settings-icon {
1830 filter: invert(100%);
1831 }
1832
 
 
 
 
1833 body.branch .brlist > table > tbody > tr:hover:not(.selected),
1834 body.branch .brlist > table > tbody > tr.selected {
1835 background-color: #ffc;
1836 }
1837 body.branch .brlist > table > tbody td:first-child > input {
1838
+9
--- src/diff.c
+++ src/diff.c
@@ -3227,20 +3227,29 @@
32273227
** Initialize the DiffConfig object using command-line options.
32283228
**
32293229
** Process diff-related command-line options and return an appropriate
32303230
** "diffFlags" integer.
32313231
**
3232
+** -b|--browser Show the diff output in a web-browser
32323233
** --brief Show filenames only DIFF_BRIEF
3234
+** --by Shorthand for "--browser -y"
32333235
** -c|--context N N lines of context. nContext
3236
+** --dark Use dark mode for Tcl/Tk and HTML output
32343237
** --html Format for HTML DIFF_HTML
3238
+** -i|--internal Use built-in diff, not an external tool
32353239
** --invert Invert the diff DIFF_INVERT
3240
+** --json Output formatted as JSON
32363241
** -n|--linenum Show line numbers DIFF_LINENO
3242
+** -N|--new-file Alias for --verbose
32373243
** --noopt Disable optimization DIFF_NOOPT
32383244
** --numstat Show change counts DIFF_NUMSTAT
32393245
** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
3246
+** --tcl Tcl-formatted output used internally by --tk
32403247
** --unified Unified diff. ~DIFF_SIDEBYSIDE
3248
+** -v|--verbose Show complete text of added or deleted files
32413249
** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
3250
+** --webpage Format output as a stand-alone HTML webpage
32423251
** -W|--width N N character lines. wColumn
32433252
** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
32443253
** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
32453254
*/
32463255
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
32473256
--- src/diff.c
+++ src/diff.c
@@ -3227,20 +3227,29 @@
3227 ** Initialize the DiffConfig object using command-line options.
3228 **
3229 ** Process diff-related command-line options and return an appropriate
3230 ** "diffFlags" integer.
3231 **
 
3232 ** --brief Show filenames only DIFF_BRIEF
 
3233 ** -c|--context N N lines of context. nContext
 
3234 ** --html Format for HTML DIFF_HTML
 
3235 ** --invert Invert the diff DIFF_INVERT
 
3236 ** -n|--linenum Show line numbers DIFF_LINENO
 
3237 ** --noopt Disable optimization DIFF_NOOPT
3238 ** --numstat Show change counts DIFF_NUMSTAT
3239 ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
 
3240 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
 
3241 ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
 
3242 ** -W|--width N N character lines. wColumn
3243 ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
3244 ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
3245 */
3246 void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
3247
--- src/diff.c
+++ src/diff.c
@@ -3227,20 +3227,29 @@
3227 ** Initialize the DiffConfig object using command-line options.
3228 **
3229 ** Process diff-related command-line options and return an appropriate
3230 ** "diffFlags" integer.
3231 **
3232 ** -b|--browser Show the diff output in a web-browser
3233 ** --brief Show filenames only DIFF_BRIEF
3234 ** --by Shorthand for "--browser -y"
3235 ** -c|--context N N lines of context. nContext
3236 ** --dark Use dark mode for Tcl/Tk and HTML output
3237 ** --html Format for HTML DIFF_HTML
3238 ** -i|--internal Use built-in diff, not an external tool
3239 ** --invert Invert the diff DIFF_INVERT
3240 ** --json Output formatted as JSON
3241 ** -n|--linenum Show line numbers DIFF_LINENO
3242 ** -N|--new-file Alias for --verbose
3243 ** --noopt Disable optimization DIFF_NOOPT
3244 ** --numstat Show change counts DIFF_NUMSTAT
3245 ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
3246 ** --tcl Tcl-formatted output used internally by --tk
3247 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
3248 ** -v|--verbose Show complete text of added or deleted files
3249 ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
3250 ** --webpage Format output as a stand-alone HTML webpage
3251 ** -W|--width N N character lines. wColumn
3252 ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
3253 ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
3254 */
3255 void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
3256
+5 -1
--- src/diff.tcl
+++ src/diff.tcl
@@ -110,11 +110,15 @@
110110
111111
set fromIndex [lsearch -glob $fossilcmd *-from]
112112
set toIndex [lsearch -glob $fossilcmd *-to]
113113
set branchIndex [lsearch -glob $fossilcmd *-branch]
114114
set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115
- set fA {base check-in}
115
+ if {[string match *?--external-baseline* $fossilcmd]} {
116
+ set fA {external baseline}
117
+ } else {
118
+ set fA {base check-in}
119
+ }
116120
set fB {current check-out}
117121
if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
118122
if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
119123
if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
120124
if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
121125
--- src/diff.tcl
+++ src/diff.tcl
@@ -110,11 +110,15 @@
110
111 set fromIndex [lsearch -glob $fossilcmd *-from]
112 set toIndex [lsearch -glob $fossilcmd *-to]
113 set branchIndex [lsearch -glob $fossilcmd *-branch]
114 set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115 set fA {base check-in}
 
 
 
 
116 set fB {current check-out}
117 if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
118 if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
119 if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
120 if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
121
--- src/diff.tcl
+++ src/diff.tcl
@@ -110,11 +110,15 @@
110
111 set fromIndex [lsearch -glob $fossilcmd *-from]
112 set toIndex [lsearch -glob $fossilcmd *-to]
113 set branchIndex [lsearch -glob $fossilcmd *-branch]
114 set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115 if {[string match *?--external-baseline* $fossilcmd]} {
116 set fA {external baseline}
117 } else {
118 set fA {base check-in}
119 }
120 set fB {current check-out}
121 if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
122 if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
123 if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
124 if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
125
+96 -20
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -249,19 +249,19 @@
249249
@ margin: 0 0 0 0;
250250
@ line-height: inherit;
251251
@ font-size: inherit;
252252
@ }
253253
@ td.diffln {
254
-@ width: 1px;
254
+@ width: fit-content;
255255
@ text-align: right;
256256
@ padding: 0 1em 0 0;
257257
@ }
258258
@ td.difflne {
259259
@ padding-bottom: 0.4em;
260260
@ }
261261
@ td.diffsep {
262
-@ width: 1px;
262
+@ width: fit-content;
263263
@ padding: 0 0.3em 0 1em;
264264
@ line-height: inherit;
265265
@ font-size: inherit;
266266
@ }
267267
@ td.diffsep pre {
@@ -379,19 +379,19 @@
379379
@ margin: 0 0 0 0;
380380
@ line-height: inherit;
381381
@ font-size: inherit;
382382
@ }
383383
@ td.diffln {
384
-@ width: 1px;
384
+@ width: fit-content;
385385
@ text-align: right;
386386
@ padding: 0 1em 0 0;
387387
@ }
388388
@ td.difflne {
389389
@ padding-bottom: 0.4em;
390390
@ }
391391
@ td.diffsep {
392
-@ width: 1px;
392
+@ width: fit-content;
393393
@ padding: 0 0.3em 0 1em;
394394
@ line-height: inherit;
395395
@ font-size: inherit;
396396
@ }
397397
@ td.diffsep pre {
@@ -784,26 +784,27 @@
784784
blob_reset(&file);
785785
return rc;
786786
}
787787
788788
/*
789
-** Run a diff between the version zFrom and files on disk. zFrom might
790
-** be NULL which means to simply show the difference between the edited
791
-** files on disk and the check-out on which they are based.
789
+** Run a diff between the version zFrom and files on disk in the current
790
+** working checkout. zFrom might be NULL which means to simply show the
791
+** difference between the edited files on disk and the check-out on which
792
+** they are based.
792793
**
793794
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
794795
** command zDiffCmd to do the diffing.
795796
**
796797
** When using an external diff program, zBinGlob contains the GLOB patterns
797798
** for file names to treat as binary. If fIncludeBinary is zero, these files
798799
** will be skipped in addition to files that may contain binary content.
799800
*/
800
-void diff_against_disk(
801
+void diff_version_to_checkout(
801802
const char *zFrom, /* Version to difference from */
802803
DiffConfig *pCfg, /* Flags controlling diff output */
803804
FileDirList *pFileDir, /* Which files to diff */
804
- Blob *pOut /* Blob to output diff instead of stdout */
805
+ Blob *pOut /* Blob to output diff instead of stdout */
805806
){
806807
int vid;
807808
Blob sql;
808809
Stmt q;
809810
int asNewFile; /* Treat non-existant files as empty files */
@@ -928,20 +929,20 @@
928929
db_finalize(&q);
929930
db_end_transaction(1); /* ROLLBACK */
930931
}
931932
932933
/*
933
-** Run a diff between the undo buffer and files on disk.
934
+** Run a diff from the undo buffer to files on disk.
934935
**
935936
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
936937
** command zDiffCmd to do the diffing.
937938
**
938939
** When using an external diff program, zBinGlob contains the GLOB patterns
939940
** for file names to treat as binary. If fIncludeBinary is zero, these files
940941
** will be skipped in addition to files that may contain binary content.
941942
*/
942
-static void diff_against_undo(
943
+static void diff_undo_to_checkout(
943944
DiffConfig *pCfg, /* Flags controlling diff output */
944945
FileDirList *pFileDir /* List of files and directories to diff */
945946
){
946947
Stmt q;
947948
Blob content;
@@ -1088,10 +1089,67 @@
10881089
}
10891090
}
10901091
manifest_destroy(pFrom);
10911092
manifest_destroy(pTo);
10921093
}
1094
+
1095
+/*
1096
+** Compute the difference from an external tree of files to the current
1097
+** working checkout with its edits.
1098
+**
1099
+** To put it another way: Every managed file in the current working
1100
+** checkout is compared to the file with same name under zExternBase. The
1101
+** zExternBase files are on the left and the files in the current working
1102
+** directory are on the right.
1103
+*/
1104
+void diff_externbase_to_checkout(
1105
+ const char *zExternBase, /* Remote tree to use as the baseline */
1106
+ DiffConfig *pCfg, /* Diff settings */
1107
+ FileDirList *pFileDir /* Only look at these files */
1108
+){
1109
+ int vid;
1110
+ Stmt q;
1111
+
1112
+ vid = db_lget_int("checkout",0);
1113
+ if( file_isdir(zExternBase, ExtFILE)!=1 ){
1114
+ fossil_fatal("\"%s\" is not a directory", zExternBase);
1115
+ }
1116
+ db_prepare(&q,
1117
+ "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1118
+ vid
1119
+ );
1120
+ while( db_step(&q)==SQLITE_ROW ){
1121
+ const char *zFile; /* Name of file in the repository */
1122
+ char *zLhs; /* Full name of left-hand side file */
1123
+ char *zRhs; /* Full name of right-hand side file */
1124
+ Blob rhs; /* Full text of RHS */
1125
+ Blob lhs; /* Full text of LHS */
1126
+
1127
+ zFile = db_column_text(&q,0);
1128
+ if( !file_dir_match(pFileDir, zFile) ) continue;
1129
+ zLhs = mprintf("%s/%s", zExternBase, zFile);
1130
+ zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1131
+ if( file_size(zLhs, ExtFILE)<0 ){
1132
+ blob_zero(&lhs);
1133
+ }else{
1134
+ blob_read_from_file(&lhs, zLhs, ExtFILE);
1135
+ }
1136
+ blob_read_from_file(&rhs, zRhs, ExtFILE);
1137
+ if( blob_size(&lhs)!=blob_size(&rhs)
1138
+ || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1139
+ ){
1140
+ diff_print_index(zFile, pCfg, 0);
1141
+ diff_file_mem(&lhs, &rhs, zFile, pCfg);
1142
+ }
1143
+ blob_reset(&lhs);
1144
+ blob_reset(&rhs);
1145
+ fossil_free(zLhs);
1146
+ fossil_free(zRhs);
1147
+ }
1148
+ db_finalize(&q);
1149
+}
1150
+
10931151
10941152
/*
10951153
** Return the name of the external diff command, or return NULL if
10961154
** no external diff command is defined.
10971155
*/
@@ -1224,10 +1282,14 @@
12241282
** option specifies the check-in from which the second version of the file
12251283
** or files is taken. If there is no "--to" option then the (possibly edited)
12261284
** files in the current check-out are used. The "--checkin VERSION" option
12271285
** shows the changes made by check-in VERSION relative to its primary parent.
12281286
** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1287
+**
1288
+** With the "--from VERSION" option, if VERSION is actually a directory name
1289
+** (not a tag or check-in hash) then the files under that directory are used
1290
+** as the baseline for the diff.
12291291
**
12301292
** The "-i" command-line option forces the use of Fossil's own internal
12311293
** diff logic rather than any external diff program that might be configured
12321294
** using the "setting" command. If no external diff program is configured,
12331295
** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1256,11 +1318,13 @@
12561318
** with negative N meaning show all content
12571319
** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
12581320
** --diff-binary BOOL Include binary files with external commands
12591321
** --exec-abs-paths Force absolute path names on external commands
12601322
** --exec-rel-paths Force relative path names on external commands
1261
-** -r|--from VERSION Select VERSION as source for the diff
1323
+** -r|--from VERSION Use VERSION as the baseline for the diff, or
1324
+** if VERSION is a directory name, use files in
1325
+** that directory as the baseline.
12621326
** -w|--ignore-all-space Ignore white space when comparing lines
12631327
** -i|--internal Use internal diff logic
12641328
** --invert Invert the diff
12651329
** --json Output formatted as JSON
12661330
** -n|--linenum Show line numbers
@@ -1270,11 +1334,11 @@
12701334
** --strip-trailing-cr Strip trailing CR
12711335
** --tcl Tcl-formatted output used internally by --tk
12721336
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
12731337
** --tk Launch a Tcl/Tk GUI for display
12741338
** --to VERSION Select VERSION as target for the diff
1275
-** --undo Diff against the "undo" buffer
1339
+** --undo Use the undo buffer as the baseline
12761340
** --unified Unified diff
12771341
** -v|--verbose Output complete text of added or deleted files
12781342
** -h|--versions Show compared versions in the diff header
12791343
** --webpage Format output as a stand-alone HTML webpage
12801344
** -W|--width N Width of lines in side-by-side diff
@@ -1287,10 +1351,11 @@
12871351
const char *zCheckin; /* Check-in version number */
12881352
const char *zBranch; /* Branch to diff */
12891353
int againstUndo = 0; /* Diff against files in the undo buffer */
12901354
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
12911355
DiffConfig DCfg; /* Diff configuration object */
1356
+ int bFromIsDir = 0; /* True if zFrom is a directory name */
12921357
12931358
if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
12941359
diff_tk("diff", 2);
12951360
return;
12961361
}
@@ -1298,11 +1363,11 @@
12981363
zFrom = find_option("from", "r", 1);
12991364
zTo = find_option("to", 0, 1);
13001365
zCheckin = find_option("checkin", "ci", 1);
13011366
zBranch = find_option("branch", 0, 1);
13021367
againstUndo = find_option("undo",0,0)!=0;
1303
- if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1368
+ if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
13041369
fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
13051370
" or --branch");
13061371
}
13071372
if( zBranch ){
13081373
if( zTo || zFrom || zCheckin ){
@@ -1309,14 +1374,13 @@
13091374
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
13101375
}
13111376
zTo = zBranch;
13121377
zFrom = mprintf("root:%s", zBranch);
13131378
}
1314
- if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1379
+ if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
13151380
fossil_fatal("cannot use --checkin together with --from or --to");
13161381
}
1317
- g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
13181382
if( 0==zCheckin ){
13191383
if( zTo==0 || againstUndo ){
13201384
db_must_be_within_tree();
13211385
}else if( zFrom==0 ){
13221386
fossil_fatal("must use --from if --to is present");
@@ -1324,13 +1388,23 @@
13241388
db_find_and_open_repository(0, 0);
13251389
}
13261390
}else{
13271391
db_find_and_open_repository(0, 0);
13281392
}
1329
- diff_options(&DCfg, isGDiff, 0);
13301393
determine_exec_relative_option(1);
1394
+ if( zFrom!=file_tail(zFrom)
1395
+ && file_isdir(zFrom, ExtFILE)==1
1396
+ && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1397
+ ){
1398
+ bFromIsDir = 1;
1399
+ if( zTo ){
1400
+ fossil_fatal("cannot use --to together with \"--from PATH\"");
1401
+ }
1402
+ }
1403
+ diff_options(&DCfg, isGDiff, 0);
13311404
verify_all_options();
1405
+ g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
13321406
if( g.argc>=3 ){
13331407
int i;
13341408
Blob fname;
13351409
pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) );
13361410
memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1));
@@ -1357,18 +1431,20 @@
13571431
if( zFrom==0 ){
13581432
fossil_fatal("check-in %s has no parent", zTo);
13591433
}
13601434
}
13611435
diff_begin(&DCfg);
1362
- if( againstUndo ){
1436
+ if( bFromIsDir ){
1437
+ diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1438
+ }else if( againstUndo ){
13631439
if( db_lget_int("undo_available",0)==0 ){
13641440
fossil_print("No undo or redo is available\n");
13651441
return;
13661442
}
1367
- diff_against_undo(&DCfg, pFileDir);
1443
+ diff_undo_to_checkout(&DCfg, pFileDir);
13681444
}else if( zTo==0 ){
1369
- diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1445
+ diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
13701446
}else{
13711447
diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
13721448
}
13731449
if( pFileDir ){
13741450
int i;
13751451
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -249,19 +249,19 @@
249 @ margin: 0 0 0 0;
250 @ line-height: inherit;
251 @ font-size: inherit;
252 @ }
253 @ td.diffln {
254 @ width: 1px;
255 @ text-align: right;
256 @ padding: 0 1em 0 0;
257 @ }
258 @ td.difflne {
259 @ padding-bottom: 0.4em;
260 @ }
261 @ td.diffsep {
262 @ width: 1px;
263 @ padding: 0 0.3em 0 1em;
264 @ line-height: inherit;
265 @ font-size: inherit;
266 @ }
267 @ td.diffsep pre {
@@ -379,19 +379,19 @@
379 @ margin: 0 0 0 0;
380 @ line-height: inherit;
381 @ font-size: inherit;
382 @ }
383 @ td.diffln {
384 @ width: 1px;
385 @ text-align: right;
386 @ padding: 0 1em 0 0;
387 @ }
388 @ td.difflne {
389 @ padding-bottom: 0.4em;
390 @ }
391 @ td.diffsep {
392 @ width: 1px;
393 @ padding: 0 0.3em 0 1em;
394 @ line-height: inherit;
395 @ font-size: inherit;
396 @ }
397 @ td.diffsep pre {
@@ -784,26 +784,27 @@
784 blob_reset(&file);
785 return rc;
786 }
787
788 /*
789 ** Run a diff between the version zFrom and files on disk. zFrom might
790 ** be NULL which means to simply show the difference between the edited
791 ** files on disk and the check-out on which they are based.
 
792 **
793 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
794 ** command zDiffCmd to do the diffing.
795 **
796 ** When using an external diff program, zBinGlob contains the GLOB patterns
797 ** for file names to treat as binary. If fIncludeBinary is zero, these files
798 ** will be skipped in addition to files that may contain binary content.
799 */
800 void diff_against_disk(
801 const char *zFrom, /* Version to difference from */
802 DiffConfig *pCfg, /* Flags controlling diff output */
803 FileDirList *pFileDir, /* Which files to diff */
804 Blob *pOut /* Blob to output diff instead of stdout */
805 ){
806 int vid;
807 Blob sql;
808 Stmt q;
809 int asNewFile; /* Treat non-existant files as empty files */
@@ -928,20 +929,20 @@
928 db_finalize(&q);
929 db_end_transaction(1); /* ROLLBACK */
930 }
931
932 /*
933 ** Run a diff between the undo buffer and files on disk.
934 **
935 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
936 ** command zDiffCmd to do the diffing.
937 **
938 ** When using an external diff program, zBinGlob contains the GLOB patterns
939 ** for file names to treat as binary. If fIncludeBinary is zero, these files
940 ** will be skipped in addition to files that may contain binary content.
941 */
942 static void diff_against_undo(
943 DiffConfig *pCfg, /* Flags controlling diff output */
944 FileDirList *pFileDir /* List of files and directories to diff */
945 ){
946 Stmt q;
947 Blob content;
@@ -1088,10 +1089,67 @@
1088 }
1089 }
1090 manifest_destroy(pFrom);
1091 manifest_destroy(pTo);
1092 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1093
1094 /*
1095 ** Return the name of the external diff command, or return NULL if
1096 ** no external diff command is defined.
1097 */
@@ -1224,10 +1282,14 @@
1224 ** option specifies the check-in from which the second version of the file
1225 ** or files is taken. If there is no "--to" option then the (possibly edited)
1226 ** files in the current check-out are used. The "--checkin VERSION" option
1227 ** shows the changes made by check-in VERSION relative to its primary parent.
1228 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
 
 
 
 
1229 **
1230 ** The "-i" command-line option forces the use of Fossil's own internal
1231 ** diff logic rather than any external diff program that might be configured
1232 ** using the "setting" command. If no external diff program is configured,
1233 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1256,11 +1318,13 @@
1256 ** with negative N meaning show all content
1257 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1258 ** --diff-binary BOOL Include binary files with external commands
1259 ** --exec-abs-paths Force absolute path names on external commands
1260 ** --exec-rel-paths Force relative path names on external commands
1261 ** -r|--from VERSION Select VERSION as source for the diff
 
 
1262 ** -w|--ignore-all-space Ignore white space when comparing lines
1263 ** -i|--internal Use internal diff logic
1264 ** --invert Invert the diff
1265 ** --json Output formatted as JSON
1266 ** -n|--linenum Show line numbers
@@ -1270,11 +1334,11 @@
1270 ** --strip-trailing-cr Strip trailing CR
1271 ** --tcl Tcl-formatted output used internally by --tk
1272 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1273 ** --tk Launch a Tcl/Tk GUI for display
1274 ** --to VERSION Select VERSION as target for the diff
1275 ** --undo Diff against the "undo" buffer
1276 ** --unified Unified diff
1277 ** -v|--verbose Output complete text of added or deleted files
1278 ** -h|--versions Show compared versions in the diff header
1279 ** --webpage Format output as a stand-alone HTML webpage
1280 ** -W|--width N Width of lines in side-by-side diff
@@ -1287,10 +1351,11 @@
1287 const char *zCheckin; /* Check-in version number */
1288 const char *zBranch; /* Branch to diff */
1289 int againstUndo = 0; /* Diff against files in the undo buffer */
1290 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1291 DiffConfig DCfg; /* Diff configuration object */
 
1292
1293 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1294 diff_tk("diff", 2);
1295 return;
1296 }
@@ -1298,11 +1363,11 @@
1298 zFrom = find_option("from", "r", 1);
1299 zTo = find_option("to", 0, 1);
1300 zCheckin = find_option("checkin", "ci", 1);
1301 zBranch = find_option("branch", 0, 1);
1302 againstUndo = find_option("undo",0,0)!=0;
1303 if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1304 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1305 " or --branch");
1306 }
1307 if( zBranch ){
1308 if( zTo || zFrom || zCheckin ){
@@ -1309,14 +1374,13 @@
1309 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1310 }
1311 zTo = zBranch;
1312 zFrom = mprintf("root:%s", zBranch);
1313 }
1314 if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1315 fossil_fatal("cannot use --checkin together with --from or --to");
1316 }
1317 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1318 if( 0==zCheckin ){
1319 if( zTo==0 || againstUndo ){
1320 db_must_be_within_tree();
1321 }else if( zFrom==0 ){
1322 fossil_fatal("must use --from if --to is present");
@@ -1324,13 +1388,23 @@
1324 db_find_and_open_repository(0, 0);
1325 }
1326 }else{
1327 db_find_and_open_repository(0, 0);
1328 }
1329 diff_options(&DCfg, isGDiff, 0);
1330 determine_exec_relative_option(1);
 
 
 
 
 
 
 
 
 
 
1331 verify_all_options();
 
1332 if( g.argc>=3 ){
1333 int i;
1334 Blob fname;
1335 pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) );
1336 memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1));
@@ -1357,18 +1431,20 @@
1357 if( zFrom==0 ){
1358 fossil_fatal("check-in %s has no parent", zTo);
1359 }
1360 }
1361 diff_begin(&DCfg);
1362 if( againstUndo ){
 
 
1363 if( db_lget_int("undo_available",0)==0 ){
1364 fossil_print("No undo or redo is available\n");
1365 return;
1366 }
1367 diff_against_undo(&DCfg, pFileDir);
1368 }else if( zTo==0 ){
1369 diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1370 }else{
1371 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1372 }
1373 if( pFileDir ){
1374 int i;
1375
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -249,19 +249,19 @@
249 @ margin: 0 0 0 0;
250 @ line-height: inherit;
251 @ font-size: inherit;
252 @ }
253 @ td.diffln {
254 @ width: fit-content;
255 @ text-align: right;
256 @ padding: 0 1em 0 0;
257 @ }
258 @ td.difflne {
259 @ padding-bottom: 0.4em;
260 @ }
261 @ td.diffsep {
262 @ width: fit-content;
263 @ padding: 0 0.3em 0 1em;
264 @ line-height: inherit;
265 @ font-size: inherit;
266 @ }
267 @ td.diffsep pre {
@@ -379,19 +379,19 @@
379 @ margin: 0 0 0 0;
380 @ line-height: inherit;
381 @ font-size: inherit;
382 @ }
383 @ td.diffln {
384 @ width: fit-content;
385 @ text-align: right;
386 @ padding: 0 1em 0 0;
387 @ }
388 @ td.difflne {
389 @ padding-bottom: 0.4em;
390 @ }
391 @ td.diffsep {
392 @ width: fit-content;
393 @ padding: 0 0.3em 0 1em;
394 @ line-height: inherit;
395 @ font-size: inherit;
396 @ }
397 @ td.diffsep pre {
@@ -784,26 +784,27 @@
784 blob_reset(&file);
785 return rc;
786 }
787
788 /*
789 ** Run a diff between the version zFrom and files on disk in the current
790 ** working checkout. zFrom might be NULL which means to simply show the
791 ** difference between the edited files on disk and the check-out on which
792 ** they are based.
793 **
794 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
795 ** command zDiffCmd to do the diffing.
796 **
797 ** When using an external diff program, zBinGlob contains the GLOB patterns
798 ** for file names to treat as binary. If fIncludeBinary is zero, these files
799 ** will be skipped in addition to files that may contain binary content.
800 */
801 void diff_version_to_checkout(
802 const char *zFrom, /* Version to difference from */
803 DiffConfig *pCfg, /* Flags controlling diff output */
804 FileDirList *pFileDir, /* Which files to diff */
805 Blob *pOut /* Blob to output diff instead of stdout */
806 ){
807 int vid;
808 Blob sql;
809 Stmt q;
810 int asNewFile; /* Treat non-existant files as empty files */
@@ -928,20 +929,20 @@
929 db_finalize(&q);
930 db_end_transaction(1); /* ROLLBACK */
931 }
932
933 /*
934 ** Run a diff from the undo buffer to files on disk.
935 **
936 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
937 ** command zDiffCmd to do the diffing.
938 **
939 ** When using an external diff program, zBinGlob contains the GLOB patterns
940 ** for file names to treat as binary. If fIncludeBinary is zero, these files
941 ** will be skipped in addition to files that may contain binary content.
942 */
943 static void diff_undo_to_checkout(
944 DiffConfig *pCfg, /* Flags controlling diff output */
945 FileDirList *pFileDir /* List of files and directories to diff */
946 ){
947 Stmt q;
948 Blob content;
@@ -1088,10 +1089,67 @@
1089 }
1090 }
1091 manifest_destroy(pFrom);
1092 manifest_destroy(pTo);
1093 }
1094
1095 /*
1096 ** Compute the difference from an external tree of files to the current
1097 ** working checkout with its edits.
1098 **
1099 ** To put it another way: Every managed file in the current working
1100 ** checkout is compared to the file with same name under zExternBase. The
1101 ** zExternBase files are on the left and the files in the current working
1102 ** directory are on the right.
1103 */
1104 void diff_externbase_to_checkout(
1105 const char *zExternBase, /* Remote tree to use as the baseline */
1106 DiffConfig *pCfg, /* Diff settings */
1107 FileDirList *pFileDir /* Only look at these files */
1108 ){
1109 int vid;
1110 Stmt q;
1111
1112 vid = db_lget_int("checkout",0);
1113 if( file_isdir(zExternBase, ExtFILE)!=1 ){
1114 fossil_fatal("\"%s\" is not a directory", zExternBase);
1115 }
1116 db_prepare(&q,
1117 "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1118 vid
1119 );
1120 while( db_step(&q)==SQLITE_ROW ){
1121 const char *zFile; /* Name of file in the repository */
1122 char *zLhs; /* Full name of left-hand side file */
1123 char *zRhs; /* Full name of right-hand side file */
1124 Blob rhs; /* Full text of RHS */
1125 Blob lhs; /* Full text of LHS */
1126
1127 zFile = db_column_text(&q,0);
1128 if( !file_dir_match(pFileDir, zFile) ) continue;
1129 zLhs = mprintf("%s/%s", zExternBase, zFile);
1130 zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1131 if( file_size(zLhs, ExtFILE)<0 ){
1132 blob_zero(&lhs);
1133 }else{
1134 blob_read_from_file(&lhs, zLhs, ExtFILE);
1135 }
1136 blob_read_from_file(&rhs, zRhs, ExtFILE);
1137 if( blob_size(&lhs)!=blob_size(&rhs)
1138 || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1139 ){
1140 diff_print_index(zFile, pCfg, 0);
1141 diff_file_mem(&lhs, &rhs, zFile, pCfg);
1142 }
1143 blob_reset(&lhs);
1144 blob_reset(&rhs);
1145 fossil_free(zLhs);
1146 fossil_free(zRhs);
1147 }
1148 db_finalize(&q);
1149 }
1150
1151
1152 /*
1153 ** Return the name of the external diff command, or return NULL if
1154 ** no external diff command is defined.
1155 */
@@ -1224,10 +1282,14 @@
1282 ** option specifies the check-in from which the second version of the file
1283 ** or files is taken. If there is no "--to" option then the (possibly edited)
1284 ** files in the current check-out are used. The "--checkin VERSION" option
1285 ** shows the changes made by check-in VERSION relative to its primary parent.
1286 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1287 **
1288 ** With the "--from VERSION" option, if VERSION is actually a directory name
1289 ** (not a tag or check-in hash) then the files under that directory are used
1290 ** as the baseline for the diff.
1291 **
1292 ** The "-i" command-line option forces the use of Fossil's own internal
1293 ** diff logic rather than any external diff program that might be configured
1294 ** using the "setting" command. If no external diff program is configured,
1295 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1256,11 +1318,13 @@
1318 ** with negative N meaning show all content
1319 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1320 ** --diff-binary BOOL Include binary files with external commands
1321 ** --exec-abs-paths Force absolute path names on external commands
1322 ** --exec-rel-paths Force relative path names on external commands
1323 ** -r|--from VERSION Use VERSION as the baseline for the diff, or
1324 ** if VERSION is a directory name, use files in
1325 ** that directory as the baseline.
1326 ** -w|--ignore-all-space Ignore white space when comparing lines
1327 ** -i|--internal Use internal diff logic
1328 ** --invert Invert the diff
1329 ** --json Output formatted as JSON
1330 ** -n|--linenum Show line numbers
@@ -1270,11 +1334,11 @@
1334 ** --strip-trailing-cr Strip trailing CR
1335 ** --tcl Tcl-formatted output used internally by --tk
1336 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1337 ** --tk Launch a Tcl/Tk GUI for display
1338 ** --to VERSION Select VERSION as target for the diff
1339 ** --undo Use the undo buffer as the baseline
1340 ** --unified Unified diff
1341 ** -v|--verbose Output complete text of added or deleted files
1342 ** -h|--versions Show compared versions in the diff header
1343 ** --webpage Format output as a stand-alone HTML webpage
1344 ** -W|--width N Width of lines in side-by-side diff
@@ -1287,10 +1351,11 @@
1351 const char *zCheckin; /* Check-in version number */
1352 const char *zBranch; /* Branch to diff */
1353 int againstUndo = 0; /* Diff against files in the undo buffer */
1354 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1355 DiffConfig DCfg; /* Diff configuration object */
1356 int bFromIsDir = 0; /* True if zFrom is a directory name */
1357
1358 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1359 diff_tk("diff", 2);
1360 return;
1361 }
@@ -1298,11 +1363,11 @@
1363 zFrom = find_option("from", "r", 1);
1364 zTo = find_option("to", 0, 1);
1365 zCheckin = find_option("checkin", "ci", 1);
1366 zBranch = find_option("branch", 0, 1);
1367 againstUndo = find_option("undo",0,0)!=0;
1368 if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1369 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1370 " or --branch");
1371 }
1372 if( zBranch ){
1373 if( zTo || zFrom || zCheckin ){
@@ -1309,14 +1374,13 @@
1374 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1375 }
1376 zTo = zBranch;
1377 zFrom = mprintf("root:%s", zBranch);
1378 }
1379 if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
1380 fossil_fatal("cannot use --checkin together with --from or --to");
1381 }
 
1382 if( 0==zCheckin ){
1383 if( zTo==0 || againstUndo ){
1384 db_must_be_within_tree();
1385 }else if( zFrom==0 ){
1386 fossil_fatal("must use --from if --to is present");
@@ -1324,13 +1388,23 @@
1388 db_find_and_open_repository(0, 0);
1389 }
1390 }else{
1391 db_find_and_open_repository(0, 0);
1392 }
 
1393 determine_exec_relative_option(1);
1394 if( zFrom!=file_tail(zFrom)
1395 && file_isdir(zFrom, ExtFILE)==1
1396 && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1397 ){
1398 bFromIsDir = 1;
1399 if( zTo ){
1400 fossil_fatal("cannot use --to together with \"--from PATH\"");
1401 }
1402 }
1403 diff_options(&DCfg, isGDiff, 0);
1404 verify_all_options();
1405 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1406 if( g.argc>=3 ){
1407 int i;
1408 Blob fname;
1409 pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) );
1410 memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1));
@@ -1357,18 +1431,20 @@
1431 if( zFrom==0 ){
1432 fossil_fatal("check-in %s has no parent", zTo);
1433 }
1434 }
1435 diff_begin(&DCfg);
1436 if( bFromIsDir ){
1437 diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1438 }else if( againstUndo ){
1439 if( db_lget_int("undo_available",0)==0 ){
1440 fossil_print("No undo or redo is available\n");
1441 return;
1442 }
1443 diff_undo_to_checkout(&DCfg, pFileDir);
1444 }else if( zTo==0 ){
1445 diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
1446 }else{
1447 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1448 }
1449 if( pFileDir ){
1450 int i;
1451
+4
--- src/file.c
+++ src/file.c
@@ -2973,19 +2973,23 @@
29732973
** Returns 1 if the given directory contains a file named .fslckout, 2
29742974
** if it contains a file named _FOSSIL_, else returns 0.
29752975
*/
29762976
int dir_has_ckout_db(const char *zDir){
29772977
int rc = 0;
2978
+ i64 sz;
29782979
char * zCkoutDb = mprintf("%//.fslckout", zDir);
29792980
if(file_isfile(zCkoutDb, ExtFILE)){
29802981
rc = 1;
29812982
}else{
29822983
fossil_free(zCkoutDb);
29832984
zCkoutDb = mprintf("%//_FOSSIL_", zDir);
29842985
if(file_isfile(zCkoutDb, ExtFILE)){
29852986
rc = 2;
29862987
}
2988
+ }
2989
+ if( rc && ((sz = file_size(zCkoutDb, ExtFILE))<1024 || (sz%512)!=0) ){
2990
+ rc = 0;
29872991
}
29882992
fossil_free(zCkoutDb);
29892993
return rc;
29902994
}
29912995
29922996
--- src/file.c
+++ src/file.c
@@ -2973,19 +2973,23 @@
2973 ** Returns 1 if the given directory contains a file named .fslckout, 2
2974 ** if it contains a file named _FOSSIL_, else returns 0.
2975 */
2976 int dir_has_ckout_db(const char *zDir){
2977 int rc = 0;
 
2978 char * zCkoutDb = mprintf("%//.fslckout", zDir);
2979 if(file_isfile(zCkoutDb, ExtFILE)){
2980 rc = 1;
2981 }else{
2982 fossil_free(zCkoutDb);
2983 zCkoutDb = mprintf("%//_FOSSIL_", zDir);
2984 if(file_isfile(zCkoutDb, ExtFILE)){
2985 rc = 2;
2986 }
 
 
 
2987 }
2988 fossil_free(zCkoutDb);
2989 return rc;
2990 }
2991
2992
--- src/file.c
+++ src/file.c
@@ -2973,19 +2973,23 @@
2973 ** Returns 1 if the given directory contains a file named .fslckout, 2
2974 ** if it contains a file named _FOSSIL_, else returns 0.
2975 */
2976 int dir_has_ckout_db(const char *zDir){
2977 int rc = 0;
2978 i64 sz;
2979 char * zCkoutDb = mprintf("%//.fslckout", zDir);
2980 if(file_isfile(zCkoutDb, ExtFILE)){
2981 rc = 1;
2982 }else{
2983 fossil_free(zCkoutDb);
2984 zCkoutDb = mprintf("%//_FOSSIL_", zDir);
2985 if(file_isfile(zCkoutDb, ExtFILE)){
2986 rc = 2;
2987 }
2988 }
2989 if( rc && ((sz = file_size(zCkoutDb, ExtFILE))<1024 || (sz%512)!=0) ){
2990 rc = 0;
2991 }
2992 fossil_free(zCkoutDb);
2993 return rc;
2994 }
2995
2996
+80 -26
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -1,28 +1,93 @@
11
/**
22
diff-related JS APIs for fossil.
33
*/
44
"use strict";
5
+/* Locate the UI element (if any) into which we can inject some diff-related
6
+ UI controls. */
7
+window.fossil.onPageLoad(function(){
8
+ const potentialParents = window.fossil.page.diffControlContainers = [
9
+ /* CSS selectors for possible parents for injected diff-related UI
10
+ controls. */
11
+ /* Put the most likely pages at the end, as array.pop() is more
12
+ efficient than array.shift() (see loop below). */
13
+ /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
14
+ /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
15
+ /* /fdiff */ 'body.fdiff form div.submenu',
16
+ /* /vdiff */ 'body.vdiff form div.submenu',
17
+ /* /info, /vinfo, /ckout */ 'body.vinfo div.sectionmenu.info-changes-menu'
18
+ ];
19
+ window.fossil.page.diffControlContainer = undefined;
20
+ while( potentialParents.length ){
21
+ if( (window.fossil.page.diffControlContainer
22
+ = document.querySelector(potentialParents.pop())) ){
23
+ break;
24
+ }
25
+ }
26
+});
27
+
528
window.fossil.onPageLoad(function(){
629
/**
730
Adds toggle checkboxes to each file entry in the diff views for
831
/info and similar pages.
932
*/
33
+ if( !window.fossil.page.diffControlContainer ){
34
+ return;
35
+ }
1036
const D = window.fossil.dom;
11
- const isFdiff = !!document.querySelector('body.fdiff');
37
+ const allToggles = [/*collection of all diff-toggle checkboxes*/];
38
+ let checkedCount =
39
+ 0 /* When showing more than one diff, keep track of how many
40
+ "show/hide" checkboxes are are checked so we can update the
41
+ "show/hide all" label dynamically. */;
42
+ let btnAll /* UI control to show/hide all diffs */;
43
+ /* Install a diff-toggle button for the given diff table element. */
1244
const addToggle = function(diffElem){
1345
const sib = diffElem.previousElementSibling,
14
- btn = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
46
+ ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
1547
if(!sib) return;
16
- if(isFdiff) sib.parentElement.insertBefore(
17
- D.append(D.div(),btn),sib.nextElementSibling);
18
- else D.append(sib,btn);
19
- btn.addEventListener('click', function(){
20
- diffElem.classList.toggle('hidden');
48
+ const lblToggle = D.label();
49
+ D.append(lblToggle, ckbox, D.text(" show/hide "));
50
+ const wrapper = D.append(D.span(), lblToggle);
51
+ allToggles.push(ckbox);
52
+ ++checkedCount;
53
+ D.append(sib, D.append(wrapper, lblToggle));
54
+ ckbox.addEventListener('change', function(){
55
+ diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
56
+ if(btnAll){
57
+ checkedCount += (this.checked ? 1 : -1);
58
+ btnAll.innerText = (checkedCount < allToggles.length)
59
+ ? "Show diffs" : "Hide diffs";
60
+ }
61
+ }, false);
62
+ };
63
+ if( !document.querySelector('body.fdiff') ){
64
+ /* Don't show the diff toggle button for /fdiff because it only
65
+ has a single file to show (and also a different DOM layout). */
66
+ document.querySelectorAll('table.diff').forEach(addToggle);
67
+ }
68
+ /**
69
+ Set up a "toggle all diffs" button which toggles all of the
70
+ above-installed checkboxes, but only if more than one diff is
71
+ rendered.
72
+ */
73
+ const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
74
+ if(icm) {
75
+ btnAll = D.addClass(D.a("#", "Hide diffs"), "button");
76
+ D.append( icm, btnAll );
77
+ btnAll.addEventListener('click', function(ev){
78
+ ev.preventDefault();
79
+ ev.stopPropagation();
80
+ const show = checkedCount < allToggles.length;
81
+ for( const ckbox of allToggles ){
82
+ /* Toggle all entries to match this new state. We use click()
83
+ instead of ckbox.checked=... so that the on-change event handler
84
+ fires. */
85
+ if(ckbox.checked!==show) ckbox.click();
86
+ }
2187
}, false);
22
- };
23
- document.querySelectorAll('table.diff').forEach(addToggle);
88
+ }
2489
});
2590
2691
window.fossil.onPageLoad(function(){
2792
const F = window.fossil, D = F.dom;
2893
const Diff = F.diff = {
@@ -635,26 +700,15 @@
635700
/* Look for a parent element to hold the sbs-sync-scroll toggle
636701
checkbox. This differs per page. If we don't find one, simply
637702
elide that toggle and use whatever preference the user last
638703
specified (defaulting to on). */
639704
let cbSync /* scroll-sync checkbox */;
640
- let eToggleParent /* element to put the sync-scroll checkbox in */;
641
- const potentialParents = [ /* possible parents for the checkbox */
642
- /* Put the most likely pages at the end, as array.pop() is more
643
- efficient than array.shift() (see loop below). */
644
- /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
645
- /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
646
- /* /fdiff */ 'body.fdiff form div.submenu',
647
- /* /vdiff */ 'body.vdiff form div.submenu',
648
- /* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu'
649
- ];
650
- while( potentialParents.length ){
651
- if( (eToggleParent = document.querySelector(potentialParents.pop())) ){
652
- break;
653
- }
654
- }
655
- const keySbsScroll = 'sync-diff-scroll' /* F.storage key */;
705
+ let eToggleParent = /* element to put the sync-scroll checkbox in */
706
+ document.querySelector('table.diff.splitdiff')
707
+ ? window.fossil.page.diffControlContainer
708
+ : undefined;
709
+ const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */;
656710
if( eToggleParent ){
657711
/* Add a checkbox to toggle sbs scroll sync. Remember that in
658712
order to be UI-consistent in the /vdiff page we have to ensure
659713
that the checkbox is to the LEFT of of its label. We store the
660714
sync-scroll preference in F.storage (not a cookie) so that it
@@ -661,11 +715,11 @@
661715
persists across page loads and different apps. */
662716
cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
663717
D.append(eToggleParent, D.append(
664718
D.addClass(D.create('span'), 'input-with-label'),
665719
D.append(D.create('label'),
666
- cbSync, "Sync side-by-side scrolling")
720
+ cbSync, "Scroll Sync")
667721
));
668722
cbSync.addEventListener('change', function(e){
669723
F.storage.set(keySbsScroll, e.target.checked);
670724
});
671725
}
672726
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -1,28 +1,93 @@
1 /**
2 diff-related JS APIs for fossil.
3 */
4 "use strict";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5 window.fossil.onPageLoad(function(){
6 /**
7 Adds toggle checkboxes to each file entry in the diff views for
8 /info and similar pages.
9 */
 
 
 
10 const D = window.fossil.dom;
11 const isFdiff = !!document.querySelector('body.fdiff');
 
 
 
 
 
 
12 const addToggle = function(diffElem){
13 const sib = diffElem.previousElementSibling,
14 btn = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
15 if(!sib) return;
16 if(isFdiff) sib.parentElement.insertBefore(
17 D.append(D.div(),btn),sib.nextElementSibling);
18 else D.append(sib,btn);
19 btn.addEventListener('click', function(){
20 diffElem.classList.toggle('hidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21 }, false);
22 };
23 document.querySelectorAll('table.diff').forEach(addToggle);
24 });
25
26 window.fossil.onPageLoad(function(){
27 const F = window.fossil, D = F.dom;
28 const Diff = F.diff = {
@@ -635,26 +700,15 @@
635 /* Look for a parent element to hold the sbs-sync-scroll toggle
636 checkbox. This differs per page. If we don't find one, simply
637 elide that toggle and use whatever preference the user last
638 specified (defaulting to on). */
639 let cbSync /* scroll-sync checkbox */;
640 let eToggleParent /* element to put the sync-scroll checkbox in */;
641 const potentialParents = [ /* possible parents for the checkbox */
642 /* Put the most likely pages at the end, as array.pop() is more
643 efficient than array.shift() (see loop below). */
644 /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
645 /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
646 /* /fdiff */ 'body.fdiff form div.submenu',
647 /* /vdiff */ 'body.vdiff form div.submenu',
648 /* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu'
649 ];
650 while( potentialParents.length ){
651 if( (eToggleParent = document.querySelector(potentialParents.pop())) ){
652 break;
653 }
654 }
655 const keySbsScroll = 'sync-diff-scroll' /* F.storage key */;
656 if( eToggleParent ){
657 /* Add a checkbox to toggle sbs scroll sync. Remember that in
658 order to be UI-consistent in the /vdiff page we have to ensure
659 that the checkbox is to the LEFT of of its label. We store the
660 sync-scroll preference in F.storage (not a cookie) so that it
@@ -661,11 +715,11 @@
661 persists across page loads and different apps. */
662 cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
663 D.append(eToggleParent, D.append(
664 D.addClass(D.create('span'), 'input-with-label'),
665 D.append(D.create('label'),
666 cbSync, "Sync side-by-side scrolling")
667 ));
668 cbSync.addEventListener('change', function(e){
669 F.storage.set(keySbsScroll, e.target.checked);
670 });
671 }
672
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -1,28 +1,93 @@
1 /**
2 diff-related JS APIs for fossil.
3 */
4 "use strict";
5 /* Locate the UI element (if any) into which we can inject some diff-related
6 UI controls. */
7 window.fossil.onPageLoad(function(){
8 const potentialParents = window.fossil.page.diffControlContainers = [
9 /* CSS selectors for possible parents for injected diff-related UI
10 controls. */
11 /* Put the most likely pages at the end, as array.pop() is more
12 efficient than array.shift() (see loop below). */
13 /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
14 /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
15 /* /fdiff */ 'body.fdiff form div.submenu',
16 /* /vdiff */ 'body.vdiff form div.submenu',
17 /* /info, /vinfo, /ckout */ 'body.vinfo div.sectionmenu.info-changes-menu'
18 ];
19 window.fossil.page.diffControlContainer = undefined;
20 while( potentialParents.length ){
21 if( (window.fossil.page.diffControlContainer
22 = document.querySelector(potentialParents.pop())) ){
23 break;
24 }
25 }
26 });
27
28 window.fossil.onPageLoad(function(){
29 /**
30 Adds toggle checkboxes to each file entry in the diff views for
31 /info and similar pages.
32 */
33 if( !window.fossil.page.diffControlContainer ){
34 return;
35 }
36 const D = window.fossil.dom;
37 const allToggles = [/*collection of all diff-toggle checkboxes*/];
38 let checkedCount =
39 0 /* When showing more than one diff, keep track of how many
40 "show/hide" checkboxes are are checked so we can update the
41 "show/hide all" label dynamically. */;
42 let btnAll /* UI control to show/hide all diffs */;
43 /* Install a diff-toggle button for the given diff table element. */
44 const addToggle = function(diffElem){
45 const sib = diffElem.previousElementSibling,
46 ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
47 if(!sib) return;
48 const lblToggle = D.label();
49 D.append(lblToggle, ckbox, D.text(" show/hide "));
50 const wrapper = D.append(D.span(), lblToggle);
51 allToggles.push(ckbox);
52 ++checkedCount;
53 D.append(sib, D.append(wrapper, lblToggle));
54 ckbox.addEventListener('change', function(){
55 diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
56 if(btnAll){
57 checkedCount += (this.checked ? 1 : -1);
58 btnAll.innerText = (checkedCount < allToggles.length)
59 ? "Show diffs" : "Hide diffs";
60 }
61 }, false);
62 };
63 if( !document.querySelector('body.fdiff') ){
64 /* Don't show the diff toggle button for /fdiff because it only
65 has a single file to show (and also a different DOM layout). */
66 document.querySelectorAll('table.diff').forEach(addToggle);
67 }
68 /**
69 Set up a "toggle all diffs" button which toggles all of the
70 above-installed checkboxes, but only if more than one diff is
71 rendered.
72 */
73 const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
74 if(icm) {
75 btnAll = D.addClass(D.a("#", "Hide diffs"), "button");
76 D.append( icm, btnAll );
77 btnAll.addEventListener('click', function(ev){
78 ev.preventDefault();
79 ev.stopPropagation();
80 const show = checkedCount < allToggles.length;
81 for( const ckbox of allToggles ){
82 /* Toggle all entries to match this new state. We use click()
83 instead of ckbox.checked=... so that the on-change event handler
84 fires. */
85 if(ckbox.checked!==show) ckbox.click();
86 }
87 }, false);
88 }
 
89 });
90
91 window.fossil.onPageLoad(function(){
92 const F = window.fossil, D = F.dom;
93 const Diff = F.diff = {
@@ -635,26 +700,15 @@
700 /* Look for a parent element to hold the sbs-sync-scroll toggle
701 checkbox. This differs per page. If we don't find one, simply
702 elide that toggle and use whatever preference the user last
703 specified (defaulting to on). */
704 let cbSync /* scroll-sync checkbox */;
705 let eToggleParent = /* element to put the sync-scroll checkbox in */
706 document.querySelector('table.diff.splitdiff')
707 ? window.fossil.page.diffControlContainer
708 : undefined;
709 const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */;
 
 
 
 
 
 
 
 
 
 
 
710 if( eToggleParent ){
711 /* Add a checkbox to toggle sbs scroll sync. Remember that in
712 order to be UI-consistent in the /vdiff page we have to ensure
713 that the checkbox is to the LEFT of of its label. We store the
714 sync-scroll preference in F.storage (not a cookie) so that it
@@ -661,11 +715,11 @@
715 persists across page loads and different apps. */
716 cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
717 D.append(eToggleParent, D.append(
718 D.addClass(D.create('span'), 'input-with-label'),
719 D.append(D.create('label'),
720 cbSync, "Scroll Sync")
721 ));
722 cbSync.addEventListener('change', function(e){
723 F.storage.set(keySbsScroll, e.target.checked);
724 });
725 }
726
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -87,11 +87,13 @@
8787
const rc = document.createElement('label');
8888
if(forElem){
8989
if(forElem instanceof HTMLElement){
9090
forElem = this.attr(forElem, 'id');
9191
}
92
- dom.attr(rc, 'for', forElem);
92
+ if(forElem){
93
+ dom.attr(rc, 'for', forElem);
94
+ }
9395
}
9496
if(text) this.append(rc, text);
9597
return rc;
9698
};
9799
/**
98100
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -87,11 +87,13 @@
87 const rc = document.createElement('label');
88 if(forElem){
89 if(forElem instanceof HTMLElement){
90 forElem = this.attr(forElem, 'id');
91 }
92 dom.attr(rc, 'for', forElem);
 
 
93 }
94 if(text) this.append(rc, text);
95 return rc;
96 };
97 /**
98
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -87,11 +87,13 @@
87 const rc = document.createElement('label');
88 if(forElem){
89 if(forElem instanceof HTMLElement){
90 forElem = this.attr(forElem, 'id');
91 }
92 if(forElem){
93 dom.attr(rc, 'for', forElem);
94 }
95 }
96 if(text) this.append(rc, text);
97 return rc;
98 };
99 /**
100
+328 -35
--- src/info.c
+++ src/info.c
@@ -372,11 +372,13 @@
372372
const char *zNew, /* blob.uuid after change. NULL for deletes */
373373
const char *zOldName, /* Prior name. NULL if no name change. */
374374
DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
375375
int mperm /* executable or symlink permission for zNew */
376376
){
377
- @ <p>
377
+ @ <div class='file-change-line'><span>
378
+ /* Maintenance reminder: the extra level of SPAN is for
379
+ ** arranging new elements via JS. */
378380
if( !g.perm.Hyperlink ){
379381
if( zNew==0 ){
380382
@ Deleted %h(zName).
381383
}else if( zOld==0 ){
382384
@ Added %h(zName).
@@ -391,10 +393,11 @@
391393
@ %h(zName) became a regular file.
392394
}
393395
}else{
394396
@ Changes to %h(zName).
395397
}
398
+ @ </span></div>
396399
if( pCfg ){
397400
append_diff(zOld, zNew, pCfg);
398401
}
399402
}else{
400403
if( zOld && zNew ){
@@ -438,18 +441,20 @@
438441
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
439442
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
440443
}
441444
if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
442445
if( pCfg ){
446
+ @ </span></div>
443447
append_diff(zOld, zNew, pCfg);
444
- }else{
445
- @ &nbsp;&nbsp;
448
+ }else{
446449
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
450
+ @ </span></div>
447451
}
452
+ }else{
453
+ @ </span></div>
448454
}
449455
}
450
- @ </p>
451456
}
452457
453458
/*
454459
** Generate javascript to enhance HTML diffs.
455460
*/
@@ -602,10 +607,278 @@
602607
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
603608
0, 0, 0, rid, 0, 0);
604609
db_finalize(&q);
605610
style_finish_page();
606611
}
612
+
613
+/*
614
+** Render a web-page diff of the changes in the working check-out
615
+*/
616
+static void ckout_normal_diff(int vid){
617
+ int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
618
+ DiffConfig DCfg,*pCfg; /* Diff details */
619
+ const char *zW; /* The "w" query parameter */
620
+ int nChng; /* Number of changes */
621
+ Stmt q;
622
+
623
+ diffType = preferred_diff_type();
624
+ pCfg = construct_diff_flags(diffType, &DCfg);
625
+ nChng = db_int(0, "SELECT count(*) FROM vfile"
626
+ " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
627
+ if( nChng==0 ){
628
+ @ <p>No uncommitted changes</p>
629
+ style_finish_page();
630
+ return;
631
+ }
632
+ db_prepare(&q,
633
+ /* 0 1 2 3 4 5 6 */
634
+ "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
635
+ " FROM vfile LEFT JOIN blob USING(rid)"
636
+ " WHERE vid=%d"
637
+ " AND (deleted OR chnged OR rid==0)"
638
+ " ORDER BY pathname /*scan*/",
639
+ vid
640
+ );
641
+ if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
642
+ DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
643
+ }else{
644
+ DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
645
+ }
646
+ @ <div class="sectionmenu info-changes-menu">
647
+ zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
648
+ if( diffType!=1 ){
649
+ @ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
650
+ }
651
+ if( diffType!=2 ){
652
+ @ %z(chref("button","%R?diff=2%s",zW))Side-by-Side&nbsp;Diff</a>
653
+ }
654
+ if( diffType!=0 ){
655
+ if( *zW ){
656
+ @ %z(chref("button","%R?diff=%d",diffType))\
657
+ @ Show&nbsp;Whitespace&nbsp;Changes</a>
658
+ }else{
659
+ @ %z(chref("button","%R?diff=%d&w",diffType))Ignore&nbsp;Whitespace</a>
660
+ }
661
+ }
662
+ @ </div>
663
+ while( db_step(&q)==SQLITE_ROW ){
664
+ const char *zTreename = db_column_text(&q,0);
665
+ int isDeleted = db_column_int(&q, 1);
666
+ int isChnged = db_column_int(&q,2);
667
+ int isNew = db_column_int(&q,3);
668
+ int srcid = db_column_int(&q, 4);
669
+ int isLink = db_column_int(&q, 5);
670
+ const char *zUuid = db_column_text(&q, 6);
671
+ int showDiff = 1;
672
+
673
+ DCfg.diffFlags &= (~DIFF_FILE_MASK);
674
+ @ <div class='file-change-line'><span>
675
+ if( isDeleted ){
676
+ @ DELETED %h(zTreename)
677
+ DCfg.diffFlags |= DIFF_FILE_DELETED;
678
+ showDiff = 0;
679
+ }else if( file_access(zTreename, F_OK) ){
680
+ @ MISSING %h(zTreename)
681
+ showDiff = 0;
682
+ }else if( isNew ){
683
+ @ ADDED %h(zTreename)
684
+ DCfg.diffFlags |= DIFF_FILE_ADDED;
685
+ srcid = 0;
686
+ showDiff = 0;
687
+ }else if( isChnged==3 ){
688
+ @ ADDED_BY_MERGE %h(zTreename)
689
+ DCfg.diffFlags |= DIFF_FILE_ADDED;
690
+ srcid = 0;
691
+ showDiff = 0;
692
+ }else if( isChnged==5 ){
693
+ @ ADDED_BY_INTEGRATE %h(zTreename)
694
+ DCfg.diffFlags |= DIFF_FILE_ADDED;
695
+ srcid = 0;
696
+ showDiff = 0;
697
+ }else{
698
+ @ CHANGED %h(zTreename)
699
+ }
700
+ @ </span></div>
701
+ if( showDiff && pCfg ){
702
+ Blob old, new;
703
+ if( !isLink != !file_islink(zTreename) ){
704
+ @ %s(DIFF_CANNOT_COMPUTE_SYMLINK)
705
+ continue;
706
+ }
707
+ if( srcid>0 ){
708
+ content_get(srcid, &old);
709
+ pCfg->zLeftHash = zUuid;
710
+ }else{
711
+ blob_zero(&old);
712
+ pCfg->zLeftHash = 0;
713
+ }
714
+ blob_read_from_file(&new, zTreename, ExtFILE);
715
+ text_diff(&old, &new, cgi_output_blob(), pCfg);
716
+ blob_reset(&old);
717
+ blob_reset(&new);
718
+ }
719
+ }
720
+ db_finalize(&q);
721
+ append_diff_javascript(diffType);
722
+}
723
+
724
+/*
725
+** Render a web-page diff of the changes in the working check-out to
726
+** an external reference.
727
+*/
728
+static void ckout_external_base_diff(int vid, const char *zExBase){
729
+ int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
730
+ DiffConfig DCfg,*pCfg; /* Diff details */
731
+ const char *zW; /* The "w" query parameter */
732
+ Stmt q;
733
+
734
+ diffType = preferred_diff_type();
735
+ pCfg = construct_diff_flags(diffType, &DCfg);
736
+ db_prepare(&q,
737
+ "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
738
+ );
739
+ if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
740
+ DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
741
+ }else{
742
+ DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
743
+ }
744
+ @ <div class="sectionmenu info-changes-menu">
745
+ zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
746
+ if( diffType!=1 ){
747
+ @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
748
+ @ Unified&nbsp;Diff</a>
749
+ }
750
+ if( diffType!=2 ){
751
+ @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\
752
+ @ Side-by-Side&nbsp;Diff</a>
753
+ }
754
+ if( diffType!=0 ){
755
+ if( *zW ){
756
+ @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\
757
+ @ Show&nbsp;Whitespace&nbsp;Changes</a>
758
+ }else{
759
+ @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\
760
+ @ Ignore&nbsp;Whitespace</a>
761
+ }
762
+ }
763
+ @ </div>
764
+ while( db_step(&q)==SQLITE_ROW ){
765
+ const char *zFile; /* Name of file in the repository */
766
+ char *zLhs; /* Full name of left-hand side file */
767
+ char *zRhs; /* Full name of right-hand side file */
768
+ Blob rhs; /* Full text of RHS */
769
+ Blob lhs; /* Full text of LHS */
770
+
771
+ zFile = db_column_text(&q,0);
772
+ zLhs = mprintf("%s/%s", zExBase, zFile);
773
+ zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
774
+ if( file_size(zLhs, ExtFILE)<0 ){
775
+ @ <div class='file-change-line'><span>
776
+ @ Missing from external baseline: %h(zFile)
777
+ @ </span></div>
778
+ }else{
779
+ blob_read_from_file(&lhs, zLhs, ExtFILE);
780
+ blob_read_from_file(&rhs, zRhs, ExtFILE);
781
+ if( blob_size(&lhs)!=blob_size(&rhs)
782
+ || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
783
+ ){
784
+ @ <div class='file-change-line'><span>
785
+ @ Changes to %h(zFile)
786
+ @ </span></div>
787
+ if( pCfg ){
788
+ char *zFullFN;
789
+ char *zHexFN;
790
+ int nFullFN;
791
+ zFullFN = file_canonical_name_dup(zLhs);
792
+ nFullFN = (int)strlen(zFullFN);
793
+ zHexFN = fossil_malloc( nFullFN*2 + 5 );
794
+ zHexFN[0] = 'x';
795
+ encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
796
+ zHexFN[1+nFullFN*2] = 0;
797
+ fossil_free(zFullFN);
798
+ pCfg->zLeftHash = zHexFN;
799
+ text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
800
+ pCfg->zLeftHash = 0;
801
+ fossil_free(zHexFN);
802
+ }
803
+ }
804
+ blob_reset(&lhs);
805
+ blob_reset(&rhs);
806
+ }
807
+ fossil_free(zLhs);
808
+ fossil_free(zRhs);
809
+ }
810
+ db_finalize(&q);
811
+ append_diff_javascript(diffType);
812
+}
813
+
814
+/*
815
+** WEBPAGE: ckout
816
+**
817
+** Show information about the current checkout. This page only functions
818
+** if the web server is run on a loopback interface (in other words, was
819
+** started using "fossil ui" or similar) from within an open check-out.
820
+**
821
+** If the "exbase=PATH" query parameter is provided, then the diff shown
822
+** uses the files in PATH as the baseline. This is the same as using
823
+** the "--from PATH" argument to the "fossil diff" command-line. In fact,
824
+** when using "fossil ui --from PATH", the --from argument becomes the value
825
+** of the exbase query parameter for the start page.
826
+**
827
+** Other query parameters related to diffs are also accepted.
828
+*/
829
+void ckout_page(void){
830
+ int vid;
831
+ const char *zHome; /* Home directory */
832
+ int nHome;
833
+ const char *zExBase;
834
+ char *zHostname;
835
+ char *zCwd;
836
+
837
+ if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
838
+ cgi_redirectf("%R/home");
839
+ return;
840
+ }
841
+ file_chdir(g.zLocalRoot, 0);
842
+ vid = db_lget_int("checkout", 0);
843
+ db_unprotect(PROTECT_ALL);
844
+ vfile_check_signature(vid, CKSIG_ENOTFILE);
845
+ db_protect_pop();
846
+ style_set_current_feature("vinfo");
847
+ zHostname = fossil_hostname();
848
+ zCwd = file_getcwd(0,0);
849
+ zHome = fossil_getenv("HOME");
850
+ if( zHome ){
851
+ nHome = (int)strlen(zHome);
852
+ if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
853
+ zCwd = mprintf("~%s", zCwd+nHome);
854
+ }
855
+ }else{
856
+ nHome = 0;
857
+ }
858
+ if( zHostname ){
859
+ style_header("Checkout Status: %h on %h", zCwd, zHostname);
860
+ }else{
861
+ style_header("Checkout Status: %h", zCwd);
862
+ }
863
+ render_checkin_context(vid, 0, 0, 0);
864
+ @ <hr>
865
+ zExBase = P("exbase");
866
+ if( zExBase && zExBase[0] ){
867
+ char *zCBase = file_canonical_name_dup(zExBase);
868
+ if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
869
+ @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
870
+ }else{
871
+ @ <p>Using external baseline: %h(zCBase)</p>
872
+ }
873
+ ckout_external_base_diff(vid, zCBase);
874
+ fossil_free(zCBase);
875
+ }else{
876
+ ckout_normal_diff(vid);
877
+ }
878
+ style_finish_page();
879
+}
607880
608881
/*
609882
** WEBPAGE: vinfo
610883
** WEBPAGE: ci
611884
** URL: /ci/ARTIFACTID
@@ -628,11 +901,10 @@
628901
const char *zParent; /* Hash of the parent check-in (if any) */
629902
const char *zRe; /* regex parameter */
630903
ReCompiled *pRe = 0; /* regex */
631904
const char *zW; /* URL param for ignoring whitespace */
632905
const char *zPage = "vinfo"; /* Page that shows diffs */
633
- const char *zPageHide = "ci"; /* Page that hides diffs */
634906
const char *zBrName; /* Branch name */
635907
DiffConfig DCfg,*pCfg; /* Type of diff */
636908
637909
login_check_credentials();
638910
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -898,21 +1170,17 @@
8981170
@ <div class="sectionmenu info-changes-menu">
8991171
/* ^^^ .info-changes-menu is used by diff scroll sync */
9001172
pCfg = construct_diff_flags(diffType, &DCfg);
9011173
DCfg.pRe = pRe;
9021174
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
903
- if( diffType!=0 ){
904
- @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
905
- @ Hide&nbsp;Diffs</a>
906
- }
9071175
if( diffType!=1 ){
9081176
@ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
909
- @ Unified&nbsp;Diffs</a>
1177
+ @ Unified&nbsp;Diff</a>
9101178
}
9111179
if( diffType!=2 ){
9121180
@ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
913
- @ Side-by-Side&nbsp;Diffs</a>
1181
+ @ Side-by-Side&nbsp;Diff</a>
9141182
}
9151183
if( diffType!=0 ){
9161184
if( *zW ){
9171185
@ %z(chref("button","%R/%s/%T",zPage,zName))
9181186
@ Show&nbsp;Whitespace&nbsp;Changes</a>
@@ -1268,13 +1536,10 @@
12681536
cgi_check_for_malice();
12691537
style_set_current_feature("vdiff");
12701538
if( zBranch==0 ){
12711539
style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
12721540
}
1273
- if( diffType!=0 ){
1274
- style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob);
1275
- }
12761541
if( diffType!=2 ){
12771542
style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
12781543
&qpGlob);
12791544
}
12801545
if( diffType!=1 ) {
@@ -1933,10 +2198,16 @@
19332198
** WEBPAGE: jchunk hidden
19342199
** URL: /jchunk/HASH?from=N&to=M
19352200
**
19362201
** Return lines of text from a file as a JSON array - one entry in the
19372202
** array for each line of text.
2203
+**
2204
+** The HASH is normally a sha1 or sha3 hash that identifies an artifact
2205
+** in the BLOB table of the database. However, if HASH starts with an "x"
2206
+** and is followed by valid hexadecimal, and if we are running in a
2207
+** "fossil ui" situation (locally and with privilege), then decode the hex
2208
+** into a filename and read the file content from that name.
19382209
**
19392210
** **Warning:** This is an internal-use-only interface that is subject to
19402211
** change at any moment. External application should not use this interface
19412212
** since the application will break when this interface changes, and this
19422213
** interface will undoubtedly change.
@@ -1948,10 +2219,11 @@
19482219
** ajax_route_error().
19492220
*/
19502221
void jchunk_page(void){
19512222
int rid = 0;
19522223
const char *zName = PD("name", "");
2224
+ int nName = (int)(strlen(zName)&0x7fffffff);
19532225
int iFrom = atoi(PD("from","0"));
19542226
int iTo = atoi(PD("to","0"));
19552227
int ln;
19562228
int go = 1;
19572229
const char *zSep;
@@ -1968,36 +2240,57 @@
19682240
cgi_check_for_malice();
19692241
if( !g.perm.Read ){
19702242
ajax_route_error(403, "Access requires Read permissions.");
19712243
return;
19722244
}
1973
-#if 1
1974
- /* Re-enable this block once this code is integrated somewhere into
1975
- the UI. */
1976
- rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
1977
- if( rid==0 ){
1978
- ajax_route_error(404, "Unknown artifact: %h", zName);
1979
- return;
1980
- }
1981
-#else
1982
- /* This impl is only to simplify "manual" testing via the JS
1983
- console. */
1984
- rid = symbolic_name_to_rid(zName, "*");
1985
- if( rid==0 ){
1986
- ajax_route_error(404, "Unknown artifact: %h", zName);
1987
- return;
1988
- }else if( rid<0 ){
1989
- ajax_route_error(418, "Ambiguous artifact name: %h", zName);
1990
- return;
1991
- }
1992
-#endif
19932245
if( iFrom<1 || iTo<iFrom ){
19942246
ajax_route_error(500, "Invalid line range from=%d, to=%d.",
19952247
iFrom, iTo);
19962248
return;
19972249
}
1998
- content_get(rid, &content);
2250
+ if( zName[0]=='x'
2251
+ && ((nName-1)&1)==0
2252
+ && validate16(&zName[1],nName-1)
2253
+ && g.perm.Admin
2254
+ && db_open_local(0)
2255
+ && cgi_is_loopback(g.zIpAddr)
2256
+ ){
2257
+ /* Treat the HASH as a hex-encoded filename */
2258
+ int n = (nName-1)/2;
2259
+ char *zFN = fossil_malloc(n+1);
2260
+ decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
2261
+ zFN[n] = 0;
2262
+ if( file_size(zFN, ExtFILE)<0 ){
2263
+ blob_zero(&content);
2264
+ }else{
2265
+ blob_read_from_file(&content, zFN, ExtFILE);
2266
+ }
2267
+ fossil_free(zFN);
2268
+ }else{
2269
+ /* Treat the HASH as an artifact hash matching BLOB.UUID */
2270
+#if 1
2271
+ /* Re-enable this block once this code is integrated somewhere into
2272
+ the UI. */
2273
+ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2274
+ if( rid==0 ){
2275
+ ajax_route_error(404, "Unknown artifact: %h", zName);
2276
+ return;
2277
+ }
2278
+#else
2279
+ /* This impl is only to simplify "manual" testing via the JS
2280
+ console. */
2281
+ rid = symbolic_name_to_rid(zName, "*");
2282
+ if( rid==0 ){
2283
+ ajax_route_error(404, "Unknown artifact: %h", zName);
2284
+ return;
2285
+ }else if( rid<0 ){
2286
+ ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2287
+ return;
2288
+ }
2289
+#endif
2290
+ content_get(rid, &content);
2291
+ }
19992292
g.isConst = 1;
20002293
cgi_set_content_type("application/json");
20012294
ln = 0;
20022295
while( go && ln<iFrom ){
20032296
go = blob_line(&content, &line);
20042297
--- src/info.c
+++ src/info.c
@@ -372,11 +372,13 @@
372 const char *zNew, /* blob.uuid after change. NULL for deletes */
373 const char *zOldName, /* Prior name. NULL if no name change. */
374 DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
375 int mperm /* executable or symlink permission for zNew */
376 ){
377 @ <p>
 
 
378 if( !g.perm.Hyperlink ){
379 if( zNew==0 ){
380 @ Deleted %h(zName).
381 }else if( zOld==0 ){
382 @ Added %h(zName).
@@ -391,10 +393,11 @@
391 @ %h(zName) became a regular file.
392 }
393 }else{
394 @ Changes to %h(zName).
395 }
 
396 if( pCfg ){
397 append_diff(zOld, zNew, pCfg);
398 }
399 }else{
400 if( zOld && zNew ){
@@ -438,18 +441,20 @@
438 @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
439 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
440 }
441 if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
442 if( pCfg ){
 
443 append_diff(zOld, zNew, pCfg);
444 }else{
445 @ &nbsp;&nbsp;
446 @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
 
447 }
 
 
448 }
449 }
450 @ </p>
451 }
452
453 /*
454 ** Generate javascript to enhance HTML diffs.
455 */
@@ -602,10 +607,278 @@
602 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
603 0, 0, 0, rid, 0, 0);
604 db_finalize(&q);
605 style_finish_page();
606 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
608 /*
609 ** WEBPAGE: vinfo
610 ** WEBPAGE: ci
611 ** URL: /ci/ARTIFACTID
@@ -628,11 +901,10 @@
628 const char *zParent; /* Hash of the parent check-in (if any) */
629 const char *zRe; /* regex parameter */
630 ReCompiled *pRe = 0; /* regex */
631 const char *zW; /* URL param for ignoring whitespace */
632 const char *zPage = "vinfo"; /* Page that shows diffs */
633 const char *zPageHide = "ci"; /* Page that hides diffs */
634 const char *zBrName; /* Branch name */
635 DiffConfig DCfg,*pCfg; /* Type of diff */
636
637 login_check_credentials();
638 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -898,21 +1170,17 @@
898 @ <div class="sectionmenu info-changes-menu">
899 /* ^^^ .info-changes-menu is used by diff scroll sync */
900 pCfg = construct_diff_flags(diffType, &DCfg);
901 DCfg.pRe = pRe;
902 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
903 if( diffType!=0 ){
904 @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
905 @ Hide&nbsp;Diffs</a>
906 }
907 if( diffType!=1 ){
908 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
909 @ Unified&nbsp;Diffs</a>
910 }
911 if( diffType!=2 ){
912 @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
913 @ Side-by-Side&nbsp;Diffs</a>
914 }
915 if( diffType!=0 ){
916 if( *zW ){
917 @ %z(chref("button","%R/%s/%T",zPage,zName))
918 @ Show&nbsp;Whitespace&nbsp;Changes</a>
@@ -1268,13 +1536,10 @@
1268 cgi_check_for_malice();
1269 style_set_current_feature("vdiff");
1270 if( zBranch==0 ){
1271 style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
1272 }
1273 if( diffType!=0 ){
1274 style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob);
1275 }
1276 if( diffType!=2 ){
1277 style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
1278 &qpGlob);
1279 }
1280 if( diffType!=1 ) {
@@ -1933,10 +2198,16 @@
1933 ** WEBPAGE: jchunk hidden
1934 ** URL: /jchunk/HASH?from=N&to=M
1935 **
1936 ** Return lines of text from a file as a JSON array - one entry in the
1937 ** array for each line of text.
 
 
 
 
 
 
1938 **
1939 ** **Warning:** This is an internal-use-only interface that is subject to
1940 ** change at any moment. External application should not use this interface
1941 ** since the application will break when this interface changes, and this
1942 ** interface will undoubtedly change.
@@ -1948,10 +2219,11 @@
1948 ** ajax_route_error().
1949 */
1950 void jchunk_page(void){
1951 int rid = 0;
1952 const char *zName = PD("name", "");
 
1953 int iFrom = atoi(PD("from","0"));
1954 int iTo = atoi(PD("to","0"));
1955 int ln;
1956 int go = 1;
1957 const char *zSep;
@@ -1968,36 +2240,57 @@
1968 cgi_check_for_malice();
1969 if( !g.perm.Read ){
1970 ajax_route_error(403, "Access requires Read permissions.");
1971 return;
1972 }
1973 #if 1
1974 /* Re-enable this block once this code is integrated somewhere into
1975 the UI. */
1976 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
1977 if( rid==0 ){
1978 ajax_route_error(404, "Unknown artifact: %h", zName);
1979 return;
1980 }
1981 #else
1982 /* This impl is only to simplify "manual" testing via the JS
1983 console. */
1984 rid = symbolic_name_to_rid(zName, "*");
1985 if( rid==0 ){
1986 ajax_route_error(404, "Unknown artifact: %h", zName);
1987 return;
1988 }else if( rid<0 ){
1989 ajax_route_error(418, "Ambiguous artifact name: %h", zName);
1990 return;
1991 }
1992 #endif
1993 if( iFrom<1 || iTo<iFrom ){
1994 ajax_route_error(500, "Invalid line range from=%d, to=%d.",
1995 iFrom, iTo);
1996 return;
1997 }
1998 content_get(rid, &content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1999 g.isConst = 1;
2000 cgi_set_content_type("application/json");
2001 ln = 0;
2002 while( go && ln<iFrom ){
2003 go = blob_line(&content, &line);
2004
--- src/info.c
+++ src/info.c
@@ -372,11 +372,13 @@
372 const char *zNew, /* blob.uuid after change. NULL for deletes */
373 const char *zOldName, /* Prior name. NULL if no name change. */
374 DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
375 int mperm /* executable or symlink permission for zNew */
376 ){
377 @ <div class='file-change-line'><span>
378 /* Maintenance reminder: the extra level of SPAN is for
379 ** arranging new elements via JS. */
380 if( !g.perm.Hyperlink ){
381 if( zNew==0 ){
382 @ Deleted %h(zName).
383 }else if( zOld==0 ){
384 @ Added %h(zName).
@@ -391,10 +393,11 @@
393 @ %h(zName) became a regular file.
394 }
395 }else{
396 @ Changes to %h(zName).
397 }
398 @ </span></div>
399 if( pCfg ){
400 append_diff(zOld, zNew, pCfg);
401 }
402 }else{
403 if( zOld && zNew ){
@@ -438,18 +441,20 @@
441 @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
442 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
443 }
444 if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
445 if( pCfg ){
446 @ </span></div>
447 append_diff(zOld, zNew, pCfg);
448 }else{
 
449 @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
450 @ </span></div>
451 }
452 }else{
453 @ </span></div>
454 }
455 }
 
456 }
457
458 /*
459 ** Generate javascript to enhance HTML diffs.
460 */
@@ -602,10 +607,278 @@
607 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
608 0, 0, 0, rid, 0, 0);
609 db_finalize(&q);
610 style_finish_page();
611 }
612
613 /*
614 ** Render a web-page diff of the changes in the working check-out
615 */
616 static void ckout_normal_diff(int vid){
617 int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
618 DiffConfig DCfg,*pCfg; /* Diff details */
619 const char *zW; /* The "w" query parameter */
620 int nChng; /* Number of changes */
621 Stmt q;
622
623 diffType = preferred_diff_type();
624 pCfg = construct_diff_flags(diffType, &DCfg);
625 nChng = db_int(0, "SELECT count(*) FROM vfile"
626 " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
627 if( nChng==0 ){
628 @ <p>No uncommitted changes</p>
629 style_finish_page();
630 return;
631 }
632 db_prepare(&q,
633 /* 0 1 2 3 4 5 6 */
634 "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
635 " FROM vfile LEFT JOIN blob USING(rid)"
636 " WHERE vid=%d"
637 " AND (deleted OR chnged OR rid==0)"
638 " ORDER BY pathname /*scan*/",
639 vid
640 );
641 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
642 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
643 }else{
644 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
645 }
646 @ <div class="sectionmenu info-changes-menu">
647 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
648 if( diffType!=1 ){
649 @ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
650 }
651 if( diffType!=2 ){
652 @ %z(chref("button","%R?diff=2%s",zW))Side-by-Side&nbsp;Diff</a>
653 }
654 if( diffType!=0 ){
655 if( *zW ){
656 @ %z(chref("button","%R?diff=%d",diffType))\
657 @ Show&nbsp;Whitespace&nbsp;Changes</a>
658 }else{
659 @ %z(chref("button","%R?diff=%d&w",diffType))Ignore&nbsp;Whitespace</a>
660 }
661 }
662 @ </div>
663 while( db_step(&q)==SQLITE_ROW ){
664 const char *zTreename = db_column_text(&q,0);
665 int isDeleted = db_column_int(&q, 1);
666 int isChnged = db_column_int(&q,2);
667 int isNew = db_column_int(&q,3);
668 int srcid = db_column_int(&q, 4);
669 int isLink = db_column_int(&q, 5);
670 const char *zUuid = db_column_text(&q, 6);
671 int showDiff = 1;
672
673 DCfg.diffFlags &= (~DIFF_FILE_MASK);
674 @ <div class='file-change-line'><span>
675 if( isDeleted ){
676 @ DELETED %h(zTreename)
677 DCfg.diffFlags |= DIFF_FILE_DELETED;
678 showDiff = 0;
679 }else if( file_access(zTreename, F_OK) ){
680 @ MISSING %h(zTreename)
681 showDiff = 0;
682 }else if( isNew ){
683 @ ADDED %h(zTreename)
684 DCfg.diffFlags |= DIFF_FILE_ADDED;
685 srcid = 0;
686 showDiff = 0;
687 }else if( isChnged==3 ){
688 @ ADDED_BY_MERGE %h(zTreename)
689 DCfg.diffFlags |= DIFF_FILE_ADDED;
690 srcid = 0;
691 showDiff = 0;
692 }else if( isChnged==5 ){
693 @ ADDED_BY_INTEGRATE %h(zTreename)
694 DCfg.diffFlags |= DIFF_FILE_ADDED;
695 srcid = 0;
696 showDiff = 0;
697 }else{
698 @ CHANGED %h(zTreename)
699 }
700 @ </span></div>
701 if( showDiff && pCfg ){
702 Blob old, new;
703 if( !isLink != !file_islink(zTreename) ){
704 @ %s(DIFF_CANNOT_COMPUTE_SYMLINK)
705 continue;
706 }
707 if( srcid>0 ){
708 content_get(srcid, &old);
709 pCfg->zLeftHash = zUuid;
710 }else{
711 blob_zero(&old);
712 pCfg->zLeftHash = 0;
713 }
714 blob_read_from_file(&new, zTreename, ExtFILE);
715 text_diff(&old, &new, cgi_output_blob(), pCfg);
716 blob_reset(&old);
717 blob_reset(&new);
718 }
719 }
720 db_finalize(&q);
721 append_diff_javascript(diffType);
722 }
723
724 /*
725 ** Render a web-page diff of the changes in the working check-out to
726 ** an external reference.
727 */
728 static void ckout_external_base_diff(int vid, const char *zExBase){
729 int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
730 DiffConfig DCfg,*pCfg; /* Diff details */
731 const char *zW; /* The "w" query parameter */
732 Stmt q;
733
734 diffType = preferred_diff_type();
735 pCfg = construct_diff_flags(diffType, &DCfg);
736 db_prepare(&q,
737 "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
738 );
739 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
740 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
741 }else{
742 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
743 }
744 @ <div class="sectionmenu info-changes-menu">
745 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
746 if( diffType!=1 ){
747 @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
748 @ Unified&nbsp;Diff</a>
749 }
750 if( diffType!=2 ){
751 @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\
752 @ Side-by-Side&nbsp;Diff</a>
753 }
754 if( diffType!=0 ){
755 if( *zW ){
756 @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\
757 @ Show&nbsp;Whitespace&nbsp;Changes</a>
758 }else{
759 @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\
760 @ Ignore&nbsp;Whitespace</a>
761 }
762 }
763 @ </div>
764 while( db_step(&q)==SQLITE_ROW ){
765 const char *zFile; /* Name of file in the repository */
766 char *zLhs; /* Full name of left-hand side file */
767 char *zRhs; /* Full name of right-hand side file */
768 Blob rhs; /* Full text of RHS */
769 Blob lhs; /* Full text of LHS */
770
771 zFile = db_column_text(&q,0);
772 zLhs = mprintf("%s/%s", zExBase, zFile);
773 zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
774 if( file_size(zLhs, ExtFILE)<0 ){
775 @ <div class='file-change-line'><span>
776 @ Missing from external baseline: %h(zFile)
777 @ </span></div>
778 }else{
779 blob_read_from_file(&lhs, zLhs, ExtFILE);
780 blob_read_from_file(&rhs, zRhs, ExtFILE);
781 if( blob_size(&lhs)!=blob_size(&rhs)
782 || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
783 ){
784 @ <div class='file-change-line'><span>
785 @ Changes to %h(zFile)
786 @ </span></div>
787 if( pCfg ){
788 char *zFullFN;
789 char *zHexFN;
790 int nFullFN;
791 zFullFN = file_canonical_name_dup(zLhs);
792 nFullFN = (int)strlen(zFullFN);
793 zHexFN = fossil_malloc( nFullFN*2 + 5 );
794 zHexFN[0] = 'x';
795 encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
796 zHexFN[1+nFullFN*2] = 0;
797 fossil_free(zFullFN);
798 pCfg->zLeftHash = zHexFN;
799 text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
800 pCfg->zLeftHash = 0;
801 fossil_free(zHexFN);
802 }
803 }
804 blob_reset(&lhs);
805 blob_reset(&rhs);
806 }
807 fossil_free(zLhs);
808 fossil_free(zRhs);
809 }
810 db_finalize(&q);
811 append_diff_javascript(diffType);
812 }
813
814 /*
815 ** WEBPAGE: ckout
816 **
817 ** Show information about the current checkout. This page only functions
818 ** if the web server is run on a loopback interface (in other words, was
819 ** started using "fossil ui" or similar) from within an open check-out.
820 **
821 ** If the "exbase=PATH" query parameter is provided, then the diff shown
822 ** uses the files in PATH as the baseline. This is the same as using
823 ** the "--from PATH" argument to the "fossil diff" command-line. In fact,
824 ** when using "fossil ui --from PATH", the --from argument becomes the value
825 ** of the exbase query parameter for the start page.
826 **
827 ** Other query parameters related to diffs are also accepted.
828 */
829 void ckout_page(void){
830 int vid;
831 const char *zHome; /* Home directory */
832 int nHome;
833 const char *zExBase;
834 char *zHostname;
835 char *zCwd;
836
837 if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
838 cgi_redirectf("%R/home");
839 return;
840 }
841 file_chdir(g.zLocalRoot, 0);
842 vid = db_lget_int("checkout", 0);
843 db_unprotect(PROTECT_ALL);
844 vfile_check_signature(vid, CKSIG_ENOTFILE);
845 db_protect_pop();
846 style_set_current_feature("vinfo");
847 zHostname = fossil_hostname();
848 zCwd = file_getcwd(0,0);
849 zHome = fossil_getenv("HOME");
850 if( zHome ){
851 nHome = (int)strlen(zHome);
852 if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
853 zCwd = mprintf("~%s", zCwd+nHome);
854 }
855 }else{
856 nHome = 0;
857 }
858 if( zHostname ){
859 style_header("Checkout Status: %h on %h", zCwd, zHostname);
860 }else{
861 style_header("Checkout Status: %h", zCwd);
862 }
863 render_checkin_context(vid, 0, 0, 0);
864 @ <hr>
865 zExBase = P("exbase");
866 if( zExBase && zExBase[0] ){
867 char *zCBase = file_canonical_name_dup(zExBase);
868 if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
869 @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
870 }else{
871 @ <p>Using external baseline: %h(zCBase)</p>
872 }
873 ckout_external_base_diff(vid, zCBase);
874 fossil_free(zCBase);
875 }else{
876 ckout_normal_diff(vid);
877 }
878 style_finish_page();
879 }
880
881 /*
882 ** WEBPAGE: vinfo
883 ** WEBPAGE: ci
884 ** URL: /ci/ARTIFACTID
@@ -628,11 +901,10 @@
901 const char *zParent; /* Hash of the parent check-in (if any) */
902 const char *zRe; /* regex parameter */
903 ReCompiled *pRe = 0; /* regex */
904 const char *zW; /* URL param for ignoring whitespace */
905 const char *zPage = "vinfo"; /* Page that shows diffs */
 
906 const char *zBrName; /* Branch name */
907 DiffConfig DCfg,*pCfg; /* Type of diff */
908
909 login_check_credentials();
910 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -898,21 +1170,17 @@
1170 @ <div class="sectionmenu info-changes-menu">
1171 /* ^^^ .info-changes-menu is used by diff scroll sync */
1172 pCfg = construct_diff_flags(diffType, &DCfg);
1173 DCfg.pRe = pRe;
1174 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
 
 
 
 
1175 if( diffType!=1 ){
1176 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1177 @ Unified&nbsp;Diff</a>
1178 }
1179 if( diffType!=2 ){
1180 @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
1181 @ Side-by-Side&nbsp;Diff</a>
1182 }
1183 if( diffType!=0 ){
1184 if( *zW ){
1185 @ %z(chref("button","%R/%s/%T",zPage,zName))
1186 @ Show&nbsp;Whitespace&nbsp;Changes</a>
@@ -1268,13 +1536,10 @@
1536 cgi_check_for_malice();
1537 style_set_current_feature("vdiff");
1538 if( zBranch==0 ){
1539 style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
1540 }
 
 
 
1541 if( diffType!=2 ){
1542 style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
1543 &qpGlob);
1544 }
1545 if( diffType!=1 ) {
@@ -1933,10 +2198,16 @@
2198 ** WEBPAGE: jchunk hidden
2199 ** URL: /jchunk/HASH?from=N&to=M
2200 **
2201 ** Return lines of text from a file as a JSON array - one entry in the
2202 ** array for each line of text.
2203 **
2204 ** The HASH is normally a sha1 or sha3 hash that identifies an artifact
2205 ** in the BLOB table of the database. However, if HASH starts with an "x"
2206 ** and is followed by valid hexadecimal, and if we are running in a
2207 ** "fossil ui" situation (locally and with privilege), then decode the hex
2208 ** into a filename and read the file content from that name.
2209 **
2210 ** **Warning:** This is an internal-use-only interface that is subject to
2211 ** change at any moment. External application should not use this interface
2212 ** since the application will break when this interface changes, and this
2213 ** interface will undoubtedly change.
@@ -1948,10 +2219,11 @@
2219 ** ajax_route_error().
2220 */
2221 void jchunk_page(void){
2222 int rid = 0;
2223 const char *zName = PD("name", "");
2224 int nName = (int)(strlen(zName)&0x7fffffff);
2225 int iFrom = atoi(PD("from","0"));
2226 int iTo = atoi(PD("to","0"));
2227 int ln;
2228 int go = 1;
2229 const char *zSep;
@@ -1968,36 +2240,57 @@
2240 cgi_check_for_malice();
2241 if( !g.perm.Read ){
2242 ajax_route_error(403, "Access requires Read permissions.");
2243 return;
2244 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2245 if( iFrom<1 || iTo<iFrom ){
2246 ajax_route_error(500, "Invalid line range from=%d, to=%d.",
2247 iFrom, iTo);
2248 return;
2249 }
2250 if( zName[0]=='x'
2251 && ((nName-1)&1)==0
2252 && validate16(&zName[1],nName-1)
2253 && g.perm.Admin
2254 && db_open_local(0)
2255 && cgi_is_loopback(g.zIpAddr)
2256 ){
2257 /* Treat the HASH as a hex-encoded filename */
2258 int n = (nName-1)/2;
2259 char *zFN = fossil_malloc(n+1);
2260 decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
2261 zFN[n] = 0;
2262 if( file_size(zFN, ExtFILE)<0 ){
2263 blob_zero(&content);
2264 }else{
2265 blob_read_from_file(&content, zFN, ExtFILE);
2266 }
2267 fossil_free(zFN);
2268 }else{
2269 /* Treat the HASH as an artifact hash matching BLOB.UUID */
2270 #if 1
2271 /* Re-enable this block once this code is integrated somewhere into
2272 the UI. */
2273 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2274 if( rid==0 ){
2275 ajax_route_error(404, "Unknown artifact: %h", zName);
2276 return;
2277 }
2278 #else
2279 /* This impl is only to simplify "manual" testing via the JS
2280 console. */
2281 rid = symbolic_name_to_rid(zName, "*");
2282 if( rid==0 ){
2283 ajax_route_error(404, "Unknown artifact: %h", zName);
2284 return;
2285 }else if( rid<0 ){
2286 ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2287 return;
2288 }
2289 #endif
2290 content_get(rid, &content);
2291 }
2292 g.isConst = 1;
2293 cgi_set_content_type("application/json");
2294 ln = 0;
2295 while( go && ln<iFrom ){
2296 go = blob_line(&content, &line);
2297
+23 -10
--- src/main.c
+++ src/main.c
@@ -2040,20 +2040,27 @@
20402040
*/
20412041
set_base_url(0);
20422042
if( fossil_redirect_to_https_if_needed(2) ) return;
20432043
if( zPathInfo==0 || zPathInfo[0]==0
20442044
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
2045
- /* Second special case: If the PATH_INFO is blank, issue a redirect to
2046
- ** the home page identified by the "index-page" setting in the repository
2047
- ** CONFIG table, to "/index" if there no "index-page" setting. */
2045
+ /* Second special case: If the PATH_INFO is blank, issue a redirect:
2046
+ ** (1) to "/ckout" if g.useLocalauth and g.localOpen are both set.
2047
+ ** (2) to the home page identified by the "index-page" setting
2048
+ ** in the repository CONFIG table
2049
+ ** (3) to "/index" if there no "index-page" setting in CONFIG
2050
+ */
20482051
#ifdef FOSSIL_ENABLE_JSON
20492052
if(g.json.isJsonMode){
20502053
json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
20512054
fossil_exit(0);
20522055
}
20532056
#endif
2054
- fossil_redirect_home() /*does not return*/;
2057
+ if( g.useLocalauth && g.localOpen ){
2058
+ cgi_redirectf("%R/ckout");
2059
+ }else{
2060
+ fossil_redirect_home() /*does not return*/;
2061
+ }
20552062
}else{
20562063
zPath = mprintf("%s", zPathInfo);
20572064
}
20582065
20592066
/* Make g.zPath point to the first element of the path. Make
@@ -3171,10 +3178,11 @@
31713178
** --errorlog FILE Append HTTP error messages to FILE
31723179
** --extroot DIR Document root for the /ext extension mechanism
31733180
** --files GLOBLIST Comma-separated list of glob patterns for static files
31743181
** --fossilcmd PATH The pathname of the "fossil" executable on the remote
31753182
** system when REPOSITORY is remote.
3183
+** --from PATH Use PATH as the diff baseline for the /ckout page
31763184
** --localauth Enable automatic login for requests from localhost
31773185
** --localhost Listen on 127.0.0.1 only (always true for "ui")
31783186
** --https Indicates that the input is coming through a reverse
31793187
** proxy that has already translated HTTPS into HTTP.
31803188
** --jsmode MODE Determine how JavaScript is delivered with pages.
@@ -3243,10 +3251,11 @@
32433251
const char *zInitPage = 0; /* Start on this page. --page option */
32443252
int findServerArg = 2; /* argv index for find_server_repository() */
32453253
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
32463254
const char *zJsMode; /* The --jsmode parameter */
32473255
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3256
+ const char *zFrom; /* Value for --from */
32483257
32493258
32503259
#if USE_SEE
32513260
db_setup_for_saved_encryption_key();
32523261
#endif
@@ -3279,13 +3288,21 @@
32793288
g.useLocalauth = find_option("localauth", 0, 0)!=0;
32803289
Th_InitTraceLog();
32813290
zPort = find_option("port", "P", 1);
32823291
isUiCmd = g.argv[1][0]=='u';
32833292
if( isUiCmd ){
3293
+ zFrom = find_option("from", 0, 1);
3294
+ if( zFrom && zFrom==file_tail(zFrom) ){
3295
+ fossil_fatal("the argument to --from must be a pathname for"
3296
+ " the \"ui\" command");
3297
+ }
32843298
zInitPage = find_option("page", "p", 1);
32853299
if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
32863300
zFossilCmd = find_option("fossilcmd", 0, 1);
3301
+ if( zFrom && zInitPage==0 ){
3302
+ zInitPage = mprintf("ckout?exbase=%T", zFrom);
3303
+ }
32873304
}
32883305
zNotFound = find_option("notfound", 0, 1);
32893306
allowRepoList = find_option("repolist",0,0)!=0;
32903307
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
32913308
zAltBase = find_option("baseurl", 0, 1);
@@ -3356,11 +3373,11 @@
33563373
const char * zDir = g.argv[2];
33573374
if(dir_has_ckout_db(zDir)){
33583375
if(0!=file_chdir(zDir, 0)){
33593376
fossil_fatal("Cannot chdir to %s", zDir);
33603377
}
3361
- findServerArg = 99;
3378
+ findServerArg = g.argc;
33623379
fCreate = 0;
33633380
g.argv[2] = 0;
33643381
--g.argc;
33653382
}
33663383
}
@@ -3384,15 +3401,11 @@
33843401
}
33853402
if( !zRemote ){
33863403
find_server_repository(findServerArg, fCreate);
33873404
}
33883405
if( zInitPage==0 ){
3389
- if( isUiCmd && g.localOpen ){
3390
- zInitPage = "timeline?c=current";
3391
- }else{
3392
- zInitPage = "";
3393
- }
3406
+ zInitPage = "";
33943407
}
33953408
if( zPort ){
33963409
if( strchr(zPort,':') ){
33973410
int i;
33983411
for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
33993412
--- src/main.c
+++ src/main.c
@@ -2040,20 +2040,27 @@
2040 */
2041 set_base_url(0);
2042 if( fossil_redirect_to_https_if_needed(2) ) return;
2043 if( zPathInfo==0 || zPathInfo[0]==0
2044 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
2045 /* Second special case: If the PATH_INFO is blank, issue a redirect to
2046 ** the home page identified by the "index-page" setting in the repository
2047 ** CONFIG table, to "/index" if there no "index-page" setting. */
 
 
 
2048 #ifdef FOSSIL_ENABLE_JSON
2049 if(g.json.isJsonMode){
2050 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
2051 fossil_exit(0);
2052 }
2053 #endif
2054 fossil_redirect_home() /*does not return*/;
 
 
 
 
2055 }else{
2056 zPath = mprintf("%s", zPathInfo);
2057 }
2058
2059 /* Make g.zPath point to the first element of the path. Make
@@ -3171,10 +3178,11 @@
3171 ** --errorlog FILE Append HTTP error messages to FILE
3172 ** --extroot DIR Document root for the /ext extension mechanism
3173 ** --files GLOBLIST Comma-separated list of glob patterns for static files
3174 ** --fossilcmd PATH The pathname of the "fossil" executable on the remote
3175 ** system when REPOSITORY is remote.
 
3176 ** --localauth Enable automatic login for requests from localhost
3177 ** --localhost Listen on 127.0.0.1 only (always true for "ui")
3178 ** --https Indicates that the input is coming through a reverse
3179 ** proxy that has already translated HTTPS into HTTP.
3180 ** --jsmode MODE Determine how JavaScript is delivered with pages.
@@ -3243,10 +3251,11 @@
3243 const char *zInitPage = 0; /* Start on this page. --page option */
3244 int findServerArg = 2; /* argv index for find_server_repository() */
3245 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3246 const char *zJsMode; /* The --jsmode parameter */
3247 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
 
3248
3249
3250 #if USE_SEE
3251 db_setup_for_saved_encryption_key();
3252 #endif
@@ -3279,13 +3288,21 @@
3279 g.useLocalauth = find_option("localauth", 0, 0)!=0;
3280 Th_InitTraceLog();
3281 zPort = find_option("port", "P", 1);
3282 isUiCmd = g.argv[1][0]=='u';
3283 if( isUiCmd ){
 
 
 
 
 
3284 zInitPage = find_option("page", "p", 1);
3285 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3286 zFossilCmd = find_option("fossilcmd", 0, 1);
 
 
 
3287 }
3288 zNotFound = find_option("notfound", 0, 1);
3289 allowRepoList = find_option("repolist",0,0)!=0;
3290 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3291 zAltBase = find_option("baseurl", 0, 1);
@@ -3356,11 +3373,11 @@
3356 const char * zDir = g.argv[2];
3357 if(dir_has_ckout_db(zDir)){
3358 if(0!=file_chdir(zDir, 0)){
3359 fossil_fatal("Cannot chdir to %s", zDir);
3360 }
3361 findServerArg = 99;
3362 fCreate = 0;
3363 g.argv[2] = 0;
3364 --g.argc;
3365 }
3366 }
@@ -3384,15 +3401,11 @@
3384 }
3385 if( !zRemote ){
3386 find_server_repository(findServerArg, fCreate);
3387 }
3388 if( zInitPage==0 ){
3389 if( isUiCmd && g.localOpen ){
3390 zInitPage = "timeline?c=current";
3391 }else{
3392 zInitPage = "";
3393 }
3394 }
3395 if( zPort ){
3396 if( strchr(zPort,':') ){
3397 int i;
3398 for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
3399
--- src/main.c
+++ src/main.c
@@ -2040,20 +2040,27 @@
2040 */
2041 set_base_url(0);
2042 if( fossil_redirect_to_https_if_needed(2) ) return;
2043 if( zPathInfo==0 || zPathInfo[0]==0
2044 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
2045 /* Second special case: If the PATH_INFO is blank, issue a redirect:
2046 ** (1) to "/ckout" if g.useLocalauth and g.localOpen are both set.
2047 ** (2) to the home page identified by the "index-page" setting
2048 ** in the repository CONFIG table
2049 ** (3) to "/index" if there no "index-page" setting in CONFIG
2050 */
2051 #ifdef FOSSIL_ENABLE_JSON
2052 if(g.json.isJsonMode){
2053 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
2054 fossil_exit(0);
2055 }
2056 #endif
2057 if( g.useLocalauth && g.localOpen ){
2058 cgi_redirectf("%R/ckout");
2059 }else{
2060 fossil_redirect_home() /*does not return*/;
2061 }
2062 }else{
2063 zPath = mprintf("%s", zPathInfo);
2064 }
2065
2066 /* Make g.zPath point to the first element of the path. Make
@@ -3171,10 +3178,11 @@
3178 ** --errorlog FILE Append HTTP error messages to FILE
3179 ** --extroot DIR Document root for the /ext extension mechanism
3180 ** --files GLOBLIST Comma-separated list of glob patterns for static files
3181 ** --fossilcmd PATH The pathname of the "fossil" executable on the remote
3182 ** system when REPOSITORY is remote.
3183 ** --from PATH Use PATH as the diff baseline for the /ckout page
3184 ** --localauth Enable automatic login for requests from localhost
3185 ** --localhost Listen on 127.0.0.1 only (always true for "ui")
3186 ** --https Indicates that the input is coming through a reverse
3187 ** proxy that has already translated HTTPS into HTTP.
3188 ** --jsmode MODE Determine how JavaScript is delivered with pages.
@@ -3243,10 +3251,11 @@
3251 const char *zInitPage = 0; /* Start on this page. --page option */
3252 int findServerArg = 2; /* argv index for find_server_repository() */
3253 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3254 const char *zJsMode; /* The --jsmode parameter */
3255 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3256 const char *zFrom; /* Value for --from */
3257
3258
3259 #if USE_SEE
3260 db_setup_for_saved_encryption_key();
3261 #endif
@@ -3279,13 +3288,21 @@
3288 g.useLocalauth = find_option("localauth", 0, 0)!=0;
3289 Th_InitTraceLog();
3290 zPort = find_option("port", "P", 1);
3291 isUiCmd = g.argv[1][0]=='u';
3292 if( isUiCmd ){
3293 zFrom = find_option("from", 0, 1);
3294 if( zFrom && zFrom==file_tail(zFrom) ){
3295 fossil_fatal("the argument to --from must be a pathname for"
3296 " the \"ui\" command");
3297 }
3298 zInitPage = find_option("page", "p", 1);
3299 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3300 zFossilCmd = find_option("fossilcmd", 0, 1);
3301 if( zFrom && zInitPage==0 ){
3302 zInitPage = mprintf("ckout?exbase=%T", zFrom);
3303 }
3304 }
3305 zNotFound = find_option("notfound", 0, 1);
3306 allowRepoList = find_option("repolist",0,0)!=0;
3307 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3308 zAltBase = find_option("baseurl", 0, 1);
@@ -3356,11 +3373,11 @@
3373 const char * zDir = g.argv[2];
3374 if(dir_has_ckout_db(zDir)){
3375 if(0!=file_chdir(zDir, 0)){
3376 fossil_fatal("Cannot chdir to %s", zDir);
3377 }
3378 findServerArg = g.argc;
3379 fCreate = 0;
3380 g.argv[2] = 0;
3381 --g.argc;
3382 }
3383 }
@@ -3384,15 +3401,11 @@
3401 }
3402 if( !zRemote ){
3403 find_server_repository(findServerArg, fCreate);
3404 }
3405 if( zInitPage==0 ){
3406 zInitPage = "";
 
 
 
 
3407 }
3408 if( zPort ){
3409 if( strchr(zPort,':') ){
3410 int i;
3411 for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
3412
+16 -7
--- src/merge.c
+++ src/merge.c
@@ -51,11 +51,14 @@
5151
/* No files named on the command-line. Use every file mentioned
5252
** in the MERGESTAT table to generate the file list. */
5353
Stmt q;
5454
int cnt = 0;
5555
db_prepare(&q,
56
- "SELECT coalesce(fnr,fn), op FROM mergestat %s ORDER BY 1",
56
+ "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
57
+ "('MERGE',1),('ADDED',2),('UPDATE',2))"
58
+ "SELECT coalesce(fnr,fn), op FROM mergestat JOIN priority USING(op)"
59
+ " %s ORDER BY pri, 1",
5760
bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/
5861
);
5962
while( db_step(&q)==SQLITE_ROW ){
6063
blob_appendf(&script," %s ", db_column_text(&q,1));
6164
blob_append_tcl_literal(&script, db_column_text(&q,0),
@@ -199,11 +202,11 @@
199202
" WHERE pathname=%Q AND octet_length(content)=%d",
200203
zFN, sz
201204
);
202205
blob_zero(&v1);
203206
if( db_step(&q2)==SQLITE_ROW ){
204
- db_column_blob(&q, 0, &v1);
207
+ db_column_blob(&q2, 0, &v1);
205208
}else{
206209
mb.zV1 = "(local content missing)";
207210
}
208211
db_finalize(&q2);
209212
}else{
@@ -306,15 +309,18 @@
306309
zWhere = "";
307310
}else{
308311
zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
309312
}
310313
db_prepare(&q,
314
+ "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
315
+ "('MERGE',1),('ADDED',2),('UPDATE',2))"
316
+
311317
/* 0 1 2 */
312318
"SELECT op, coalesce(fnr,fn), msg"
313
- " FROM mergestat"
319
+ " FROM mergestat JOIN priority USING(op)"
314320
" %s"
315
- " ORDER BY coalesce(fnr,fn)",
321
+ " ORDER BY pri, coalesce(fnr,fn)",
316322
zWhere /*safe-for-%s*/
317323
);
318324
while( db_step(&q)==SQLITE_ROW ){
319325
const char *zOp = db_column_text(&q, 0);
320326
const char *zName = db_column_text(&q, 1);
@@ -327,21 +333,24 @@
327333
cnt++;
328334
}
329335
db_finalize(&q);
330336
if( !bAll && cnt==0 ){
331337
fossil_print(
332
- "No interesting change in this merge. Use --all to see everything.\n"
338
+ "No interesting changes in this merge. Use --all to see everything.\n"
333339
);
334340
}
335341
}
336342
337343
/*
338344
** Erase all information about prior merges. Do this, for example, after
339345
** a commit.
340346
*/
341347
void merge_info_forget(void){
342
- db_multi_exec("DROP TABLE IF EXISTS localdb.mergestat");
348
+ db_multi_exec(
349
+ "DROP TABLE IF EXISTS localdb.mergestat;"
350
+ "DELETE FROM localdb.vvar WHERE name glob 'mergestat-*';"
351
+ );
343352
}
344353
345354
346355
/*
347356
** Initialize the MERGESTAT table.
@@ -355,12 +364,12 @@
355364
**
356365
** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
357366
** added by merge.
358367
*/
359368
void merge_info_init(void){
369
+ merge_info_forget();
360370
db_multi_exec(
361
- "DROP TABLE IF EXISTS localdb.mergestat;\n"
362371
"CREATE TABLE localdb.mergestat(\n"
363372
" op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
364373
" fnp TEXT, -- Name of the pivot file (P)\n"
365374
" ridp INT, -- RID for the pivot file\n"
366375
" fn TEXT, -- Name of origin file (V)\n"
367376
--- src/merge.c
+++ src/merge.c
@@ -51,11 +51,14 @@
51 /* No files named on the command-line. Use every file mentioned
52 ** in the MERGESTAT table to generate the file list. */
53 Stmt q;
54 int cnt = 0;
55 db_prepare(&q,
56 "SELECT coalesce(fnr,fn), op FROM mergestat %s ORDER BY 1",
 
 
 
57 bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/
58 );
59 while( db_step(&q)==SQLITE_ROW ){
60 blob_appendf(&script," %s ", db_column_text(&q,1));
61 blob_append_tcl_literal(&script, db_column_text(&q,0),
@@ -199,11 +202,11 @@
199 " WHERE pathname=%Q AND octet_length(content)=%d",
200 zFN, sz
201 );
202 blob_zero(&v1);
203 if( db_step(&q2)==SQLITE_ROW ){
204 db_column_blob(&q, 0, &v1);
205 }else{
206 mb.zV1 = "(local content missing)";
207 }
208 db_finalize(&q2);
209 }else{
@@ -306,15 +309,18 @@
306 zWhere = "";
307 }else{
308 zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
309 }
310 db_prepare(&q,
 
 
 
311 /* 0 1 2 */
312 "SELECT op, coalesce(fnr,fn), msg"
313 " FROM mergestat"
314 " %s"
315 " ORDER BY coalesce(fnr,fn)",
316 zWhere /*safe-for-%s*/
317 );
318 while( db_step(&q)==SQLITE_ROW ){
319 const char *zOp = db_column_text(&q, 0);
320 const char *zName = db_column_text(&q, 1);
@@ -327,21 +333,24 @@
327 cnt++;
328 }
329 db_finalize(&q);
330 if( !bAll && cnt==0 ){
331 fossil_print(
332 "No interesting change in this merge. Use --all to see everything.\n"
333 );
334 }
335 }
336
337 /*
338 ** Erase all information about prior merges. Do this, for example, after
339 ** a commit.
340 */
341 void merge_info_forget(void){
342 db_multi_exec("DROP TABLE IF EXISTS localdb.mergestat");
 
 
 
343 }
344
345
346 /*
347 ** Initialize the MERGESTAT table.
@@ -355,12 +364,12 @@
355 **
356 ** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
357 ** added by merge.
358 */
359 void merge_info_init(void){
 
360 db_multi_exec(
361 "DROP TABLE IF EXISTS localdb.mergestat;\n"
362 "CREATE TABLE localdb.mergestat(\n"
363 " op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
364 " fnp TEXT, -- Name of the pivot file (P)\n"
365 " ridp INT, -- RID for the pivot file\n"
366 " fn TEXT, -- Name of origin file (V)\n"
367
--- src/merge.c
+++ src/merge.c
@@ -51,11 +51,14 @@
51 /* No files named on the command-line. Use every file mentioned
52 ** in the MERGESTAT table to generate the file list. */
53 Stmt q;
54 int cnt = 0;
55 db_prepare(&q,
56 "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
57 "('MERGE',1),('ADDED',2),('UPDATE',2))"
58 "SELECT coalesce(fnr,fn), op FROM mergestat JOIN priority USING(op)"
59 " %s ORDER BY pri, 1",
60 bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/
61 );
62 while( db_step(&q)==SQLITE_ROW ){
63 blob_appendf(&script," %s ", db_column_text(&q,1));
64 blob_append_tcl_literal(&script, db_column_text(&q,0),
@@ -199,11 +202,11 @@
202 " WHERE pathname=%Q AND octet_length(content)=%d",
203 zFN, sz
204 );
205 blob_zero(&v1);
206 if( db_step(&q2)==SQLITE_ROW ){
207 db_column_blob(&q2, 0, &v1);
208 }else{
209 mb.zV1 = "(local content missing)";
210 }
211 db_finalize(&q2);
212 }else{
@@ -306,15 +309,18 @@
309 zWhere = "";
310 }else{
311 zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
312 }
313 db_prepare(&q,
314 "WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
315 "('MERGE',1),('ADDED',2),('UPDATE',2))"
316
317 /* 0 1 2 */
318 "SELECT op, coalesce(fnr,fn), msg"
319 " FROM mergestat JOIN priority USING(op)"
320 " %s"
321 " ORDER BY pri, coalesce(fnr,fn)",
322 zWhere /*safe-for-%s*/
323 );
324 while( db_step(&q)==SQLITE_ROW ){
325 const char *zOp = db_column_text(&q, 0);
326 const char *zName = db_column_text(&q, 1);
@@ -327,21 +333,24 @@
333 cnt++;
334 }
335 db_finalize(&q);
336 if( !bAll && cnt==0 ){
337 fossil_print(
338 "No interesting changes in this merge. Use --all to see everything.\n"
339 );
340 }
341 }
342
343 /*
344 ** Erase all information about prior merges. Do this, for example, after
345 ** a commit.
346 */
347 void merge_info_forget(void){
348 db_multi_exec(
349 "DROP TABLE IF EXISTS localdb.mergestat;"
350 "DELETE FROM localdb.vvar WHERE name glob 'mergestat-*';"
351 );
352 }
353
354
355 /*
356 ** Initialize the MERGESTAT table.
@@ -355,12 +364,12 @@
364 **
365 ** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
366 ** added by merge.
367 */
368 void merge_info_init(void){
369 merge_info_forget();
370 db_multi_exec(
 
371 "CREATE TABLE localdb.mergestat(\n"
372 " op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
373 " fnp TEXT, -- Name of the pivot file (P)\n"
374 " ridp INT, -- RID for the pivot file\n"
375 " fn TEXT, -- Name of origin file (V)\n"
376
+14 -4
--- src/merge.tcl
+++ src/merge.tcl
@@ -223,12 +223,11 @@
223223
set mx $lnA
224224
if {$lnB>$mx} {set mx $lnB}
225225
if {$lnC>$mx} {set mx $lnC}
226226
if {$lnD>$mx} {set mx $lnD}
227227
global lnWidth
228
- set lnWidth [string length [format %d $mx]]
229
- if {$::tcl_platform(platform)=="windows"} {incr lnWidth}
228
+ set lnWidth [string length [format +%d $mx]]
230229
.lnA config -width $lnWidth
231230
.lnB config -width $lnWidth
232231
.lnC config -width $lnWidth
233232
.lnD config -width $lnWidth
234233
grid columnconfig . {0 2 4 6} -minsize $lnWidth
@@ -442,13 +441,13 @@
442441
%W selection clear 0 end
443442
%W selection set @%x,%y
444443
}
445444
}
446445
447
-foreach {side syncCol} {A .txtB B .txtA C .txtC D .txtD} {
446
+foreach {side syncCol} {A .txtA B .txtB C .txtC D .txtD} {
448447
set ln .ln$side
449
- text $ln
448
+ text $ln -width 6
450449
$ln tag config - -justify right
451450
452451
set txt .txt$side
453452
text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
454453
-xscroll ".sbx$side set"
@@ -460,10 +459,21 @@
460459
$txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
461460
-justify center
462461
$txt tag config err -foreground $CFG(ERR_FG)
463462
}
464463
text .mkr
464
+
465
+set mxwidth [lindex [wm maxsize .] 0]
466
+while {$CFG(WIDTH)>=40} {
467
+ set wanted [expr {([winfo reqwidth .lnA]+[winfo reqwidth .txtA])*4+30}]
468
+ if {$wanted<=$mxwidth} break
469
+ incr CFG(WIDTH) -10
470
+ .txtA config -width $CFG(WIDTH)
471
+ .txtB config -width $CFG(WIDTH)
472
+ .txtC config -width $CFG(WIDTH)
473
+ .txtD config -width $CFG(WIDTH)
474
+}
465475
466476
foreach c [cols] {
467477
set keyPrefix [string toupper [colType $c]]_COL_
468478
if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
469479
$c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
470480
--- src/merge.tcl
+++ src/merge.tcl
@@ -223,12 +223,11 @@
223 set mx $lnA
224 if {$lnB>$mx} {set mx $lnB}
225 if {$lnC>$mx} {set mx $lnC}
226 if {$lnD>$mx} {set mx $lnD}
227 global lnWidth
228 set lnWidth [string length [format %d $mx]]
229 if {$::tcl_platform(platform)=="windows"} {incr lnWidth}
230 .lnA config -width $lnWidth
231 .lnB config -width $lnWidth
232 .lnC config -width $lnWidth
233 .lnD config -width $lnWidth
234 grid columnconfig . {0 2 4 6} -minsize $lnWidth
@@ -442,13 +441,13 @@
442 %W selection clear 0 end
443 %W selection set @%x,%y
444 }
445 }
446
447 foreach {side syncCol} {A .txtB B .txtA C .txtC D .txtD} {
448 set ln .ln$side
449 text $ln
450 $ln tag config - -justify right
451
452 set txt .txt$side
453 text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
454 -xscroll ".sbx$side set"
@@ -460,10 +459,21 @@
460 $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
461 -justify center
462 $txt tag config err -foreground $CFG(ERR_FG)
463 }
464 text .mkr
 
 
 
 
 
 
 
 
 
 
 
465
466 foreach c [cols] {
467 set keyPrefix [string toupper [colType $c]]_COL_
468 if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
469 $c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
470
--- src/merge.tcl
+++ src/merge.tcl
@@ -223,12 +223,11 @@
223 set mx $lnA
224 if {$lnB>$mx} {set mx $lnB}
225 if {$lnC>$mx} {set mx $lnC}
226 if {$lnD>$mx} {set mx $lnD}
227 global lnWidth
228 set lnWidth [string length [format +%d $mx]]
 
229 .lnA config -width $lnWidth
230 .lnB config -width $lnWidth
231 .lnC config -width $lnWidth
232 .lnD config -width $lnWidth
233 grid columnconfig . {0 2 4 6} -minsize $lnWidth
@@ -442,13 +441,13 @@
441 %W selection clear 0 end
442 %W selection set @%x,%y
443 }
444 }
445
446 foreach {side syncCol} {A .txtA B .txtB C .txtC D .txtD} {
447 set ln .ln$side
448 text $ln -width 6
449 $ln tag config - -justify right
450
451 set txt .txt$side
452 text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
453 -xscroll ".sbx$side set"
@@ -460,10 +459,21 @@
459 $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
460 -justify center
461 $txt tag config err -foreground $CFG(ERR_FG)
462 }
463 text .mkr
464
465 set mxwidth [lindex [wm maxsize .] 0]
466 while {$CFG(WIDTH)>=40} {
467 set wanted [expr {([winfo reqwidth .lnA]+[winfo reqwidth .txtA])*4+30}]
468 if {$wanted<=$mxwidth} break
469 incr CFG(WIDTH) -10
470 .txtA config -width $CFG(WIDTH)
471 .txtB config -width $CFG(WIDTH)
472 .txtC config -width $CFG(WIDTH)
473 .txtD config -width $CFG(WIDTH)
474 }
475
476 foreach c [cols] {
477 set keyPrefix [string toupper [colType $c]]_COL_
478 if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
479 $c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
480
+1 -1
--- src/merge3.c
+++ src/merge3.c
@@ -392,11 +392,11 @@
392392
blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot;
393393
blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
394394
}
395395
static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
396396
blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
397
- blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nV;
397
+ blob_copy_lines(0, p->pV1, nV); p->lnV1 += nV;
398398
blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV;
399399
}
400400
static void txtConflict(
401401
MergeBuilder *p,
402402
unsigned int nPivot,
403403
--- src/merge3.c
+++ src/merge3.c
@@ -392,11 +392,11 @@
392 blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot;
393 blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
394 }
395 static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
396 blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
397 blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nV;
398 blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV;
399 }
400 static void txtConflict(
401 MergeBuilder *p,
402 unsigned int nPivot,
403
--- src/merge3.c
+++ src/merge3.c
@@ -392,11 +392,11 @@
392 blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot;
393 blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
394 }
395 static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
396 blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
397 blob_copy_lines(0, p->pV1, nV); p->lnV1 += nV;
398 blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV;
399 }
400 static void txtConflict(
401 MergeBuilder *p,
402 unsigned int nPivot,
403
+9 -3
--- src/rebuild.c
+++ src/rebuild.c
@@ -390,25 +390,31 @@
390390
}
391391
manifest_disable_event_triggers();
392392
rebuild_update_schema();
393393
blob_init(&sql, 0, 0);
394394
db_unprotect(PROTECT_ALL);
395
+#ifndef SQLITE_PREPARE_DONT_LOG
396
+ g.dbIgnoreErrors++;
397
+#endif
395398
db_prepare(&q,
396
- "SELECT name FROM sqlite_schema /*scan*/"
397
- " WHERE type='table'"
399
+ "SELECT name FROM pragma_table_list /*scan*/"
400
+ " WHERE schema='repository' AND type IN ('table','virtual')"
398401
" AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
399402
"'config','shun','private','reportfmt',"
400403
"'concealed','accesslog','modreq',"
401404
"'purgeevent','purgeitem','unversioned',"
402405
"'subscriber','pending_alert','chat')"
403406
" AND name NOT GLOB 'sqlite_*'"
404
- " AND name NOT GLOB 'fx_*'"
407
+ " AND name NOT GLOB 'fx_*';"
405408
);
406409
while( db_step(&q)==SQLITE_ROW ){
407410
blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
408411
}
409412
db_finalize(&q);
413
+#ifndef SQLITE_PREPARE_DONT_LOG
414
+ g.dbIgnoreErrors--;
415
+#endif
410416
db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/);
411417
blob_reset(&sql);
412418
db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/);
413419
ticket_create_table(0);
414420
shun_artifacts();
415421
--- src/rebuild.c
+++ src/rebuild.c
@@ -390,25 +390,31 @@
390 }
391 manifest_disable_event_triggers();
392 rebuild_update_schema();
393 blob_init(&sql, 0, 0);
394 db_unprotect(PROTECT_ALL);
 
 
 
395 db_prepare(&q,
396 "SELECT name FROM sqlite_schema /*scan*/"
397 " WHERE type='table'"
398 " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
399 "'config','shun','private','reportfmt',"
400 "'concealed','accesslog','modreq',"
401 "'purgeevent','purgeitem','unversioned',"
402 "'subscriber','pending_alert','chat')"
403 " AND name NOT GLOB 'sqlite_*'"
404 " AND name NOT GLOB 'fx_*'"
405 );
406 while( db_step(&q)==SQLITE_ROW ){
407 blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
408 }
409 db_finalize(&q);
 
 
 
410 db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/);
411 blob_reset(&sql);
412 db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/);
413 ticket_create_table(0);
414 shun_artifacts();
415
--- src/rebuild.c
+++ src/rebuild.c
@@ -390,25 +390,31 @@
390 }
391 manifest_disable_event_triggers();
392 rebuild_update_schema();
393 blob_init(&sql, 0, 0);
394 db_unprotect(PROTECT_ALL);
395 #ifndef SQLITE_PREPARE_DONT_LOG
396 g.dbIgnoreErrors++;
397 #endif
398 db_prepare(&q,
399 "SELECT name FROM pragma_table_list /*scan*/"
400 " WHERE schema='repository' AND type IN ('table','virtual')"
401 " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
402 "'config','shun','private','reportfmt',"
403 "'concealed','accesslog','modreq',"
404 "'purgeevent','purgeitem','unversioned',"
405 "'subscriber','pending_alert','chat')"
406 " AND name NOT GLOB 'sqlite_*'"
407 " AND name NOT GLOB 'fx_*';"
408 );
409 while( db_step(&q)==SQLITE_ROW ){
410 blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
411 }
412 db_finalize(&q);
413 #ifndef SQLITE_PREPARE_DONT_LOG
414 g.dbIgnoreErrors--;
415 #endif
416 db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/);
417 blob_reset(&sql);
418 db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/);
419 ticket_create_table(0);
420 shun_artifacts();
421
+7 -7
--- src/schema.c
+++ src/schema.c
@@ -28,11 +28,11 @@
2828
@ -- ~/.fossil file and that stores information about the users setup.
2929
@ --
3030
@ CREATE TABLE global_config(
3131
@ name TEXT PRIMARY KEY,
3232
@ value TEXT
33
-@ );
33
+@ ) WITHOUT ROWID;
3434
@
3535
@ -- Identifier for this file type.
3636
@ -- The integer is the same as 'FSLG'.
3737
@ PRAGMA application_id=252006675;
3838
;
@@ -138,11 +138,11 @@
138138
@ CREATE TABLE config(
139139
@ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry
140140
@ value CLOB, -- Content of the named parameter
141141
@ mtime DATE, -- last modified. seconds since 1970
142142
@ CHECK( typeof(name)='text' AND length(name)>=1 )
143
-@ );
143
+@ ) WITHOUT ROWID;
144144
@
145145
@ -- Artifacts that should not be processed are identified in the
146146
@ -- "shun" table. Artifacts that are control-file forgeries or
147147
@ -- spam or artifacts whose contents violate administrative policy
148148
@ -- can be shunned in order to prevent them from contaminating
@@ -151,14 +151,14 @@
151151
@ -- Shunned artifacts do not exist in the blob table. Hence they
152152
@ -- have not artifact ID (rid) and we thus must store their full
153153
@ -- UUID.
154154
@ --
155155
@ CREATE TABLE shun(
156
-@ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form
156
+@ uuid TEXT PRIMARY KEY,-- UUID of artifact to be shunned. Canonical form
157157
@ mtime DATE, -- When added. seconds since 1970
158158
@ scom TEXT -- Optional text explaining why the shun occurred
159
-@ );
159
+@ ) WITHOUT ROWID;
160160
@
161161
@ -- Artifacts that should not be pushed are stored in the "private"
162162
@ -- table. Private artifacts are omitted from the "unclustered" and
163163
@ -- "unsent" tables.
164164
@ --
@@ -193,11 +193,11 @@
193193
@ --
194194
@ CREATE TABLE concealed(
195195
@ hash TEXT PRIMARY KEY, -- The SHA1 hash of content
196196
@ mtime DATE, -- Time created. Seconds since 1970
197197
@ content TEXT -- Content intended to be concealed
198
-@ );
198
+@ ) WITHOUT ROWID;
199199
@
200200
@ -- The application ID helps the unix "file" command to identify the
201201
@ -- database as a fossil repository.
202202
@ PRAGMA application_id=252006673;
203203
;
@@ -528,11 +528,11 @@
528528
** The schema for the local FOSSIL database file found at the root
529529
** of every check-out. This database contains the complete state of
530530
** the check-out. See also the addendum in zLocalSchemaVmerge[].
531531
*/
532532
const char zLocalSchema[] =
533
-@ -- The VVAR table holds miscellanous information about the local database
533
+@ -- The VVAR table holds miscellanous information about the local checkout
534534
@ -- in the form of name-value pairs. This is similar to the VAR table
535535
@ -- table in the repository except that this table holds information that
536536
@ -- is specific to the local check-out.
537537
@ --
538538
@ -- Important Variables:
@@ -542,11 +542,11 @@
542542
@ --
543543
@ CREATE TABLE vvar(
544544
@ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry
545545
@ value CLOB, -- Content of the named parameter
546546
@ CHECK( typeof(name)='text' AND length(name)>=1 )
547
-@ );
547
+@ ) WITHOUT ROWID;
548548
@
549549
@ -- Each entry in the vfile table represents a single file in the
550550
@ -- current check-out.
551551
@ --
552552
@ -- The file.rid field is 0 for files or folders that have been
553553
--- src/schema.c
+++ src/schema.c
@@ -28,11 +28,11 @@
28 @ -- ~/.fossil file and that stores information about the users setup.
29 @ --
30 @ CREATE TABLE global_config(
31 @ name TEXT PRIMARY KEY,
32 @ value TEXT
33 @ );
34 @
35 @ -- Identifier for this file type.
36 @ -- The integer is the same as 'FSLG'.
37 @ PRAGMA application_id=252006675;
38 ;
@@ -138,11 +138,11 @@
138 @ CREATE TABLE config(
139 @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry
140 @ value CLOB, -- Content of the named parameter
141 @ mtime DATE, -- last modified. seconds since 1970
142 @ CHECK( typeof(name)='text' AND length(name)>=1 )
143 @ );
144 @
145 @ -- Artifacts that should not be processed are identified in the
146 @ -- "shun" table. Artifacts that are control-file forgeries or
147 @ -- spam or artifacts whose contents violate administrative policy
148 @ -- can be shunned in order to prevent them from contaminating
@@ -151,14 +151,14 @@
151 @ -- Shunned artifacts do not exist in the blob table. Hence they
152 @ -- have not artifact ID (rid) and we thus must store their full
153 @ -- UUID.
154 @ --
155 @ CREATE TABLE shun(
156 @ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form
157 @ mtime DATE, -- When added. seconds since 1970
158 @ scom TEXT -- Optional text explaining why the shun occurred
159 @ );
160 @
161 @ -- Artifacts that should not be pushed are stored in the "private"
162 @ -- table. Private artifacts are omitted from the "unclustered" and
163 @ -- "unsent" tables.
164 @ --
@@ -193,11 +193,11 @@
193 @ --
194 @ CREATE TABLE concealed(
195 @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content
196 @ mtime DATE, -- Time created. Seconds since 1970
197 @ content TEXT -- Content intended to be concealed
198 @ );
199 @
200 @ -- The application ID helps the unix "file" command to identify the
201 @ -- database as a fossil repository.
202 @ PRAGMA application_id=252006673;
203 ;
@@ -528,11 +528,11 @@
528 ** The schema for the local FOSSIL database file found at the root
529 ** of every check-out. This database contains the complete state of
530 ** the check-out. See also the addendum in zLocalSchemaVmerge[].
531 */
532 const char zLocalSchema[] =
533 @ -- The VVAR table holds miscellanous information about the local database
534 @ -- in the form of name-value pairs. This is similar to the VAR table
535 @ -- table in the repository except that this table holds information that
536 @ -- is specific to the local check-out.
537 @ --
538 @ -- Important Variables:
@@ -542,11 +542,11 @@
542 @ --
543 @ CREATE TABLE vvar(
544 @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry
545 @ value CLOB, -- Content of the named parameter
546 @ CHECK( typeof(name)='text' AND length(name)>=1 )
547 @ );
548 @
549 @ -- Each entry in the vfile table represents a single file in the
550 @ -- current check-out.
551 @ --
552 @ -- The file.rid field is 0 for files or folders that have been
553
--- src/schema.c
+++ src/schema.c
@@ -28,11 +28,11 @@
28 @ -- ~/.fossil file and that stores information about the users setup.
29 @ --
30 @ CREATE TABLE global_config(
31 @ name TEXT PRIMARY KEY,
32 @ value TEXT
33 @ ) WITHOUT ROWID;
34 @
35 @ -- Identifier for this file type.
36 @ -- The integer is the same as 'FSLG'.
37 @ PRAGMA application_id=252006675;
38 ;
@@ -138,11 +138,11 @@
138 @ CREATE TABLE config(
139 @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry
140 @ value CLOB, -- Content of the named parameter
141 @ mtime DATE, -- last modified. seconds since 1970
142 @ CHECK( typeof(name)='text' AND length(name)>=1 )
143 @ ) WITHOUT ROWID;
144 @
145 @ -- Artifacts that should not be processed are identified in the
146 @ -- "shun" table. Artifacts that are control-file forgeries or
147 @ -- spam or artifacts whose contents violate administrative policy
148 @ -- can be shunned in order to prevent them from contaminating
@@ -151,14 +151,14 @@
151 @ -- Shunned artifacts do not exist in the blob table. Hence they
152 @ -- have not artifact ID (rid) and we thus must store their full
153 @ -- UUID.
154 @ --
155 @ CREATE TABLE shun(
156 @ uuid TEXT PRIMARY KEY,-- UUID of artifact to be shunned. Canonical form
157 @ mtime DATE, -- When added. seconds since 1970
158 @ scom TEXT -- Optional text explaining why the shun occurred
159 @ ) WITHOUT ROWID;
160 @
161 @ -- Artifacts that should not be pushed are stored in the "private"
162 @ -- table. Private artifacts are omitted from the "unclustered" and
163 @ -- "unsent" tables.
164 @ --
@@ -193,11 +193,11 @@
193 @ --
194 @ CREATE TABLE concealed(
195 @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content
196 @ mtime DATE, -- Time created. Seconds since 1970
197 @ content TEXT -- Content intended to be concealed
198 @ ) WITHOUT ROWID;
199 @
200 @ -- The application ID helps the unix "file" command to identify the
201 @ -- database as a fossil repository.
202 @ PRAGMA application_id=252006673;
203 ;
@@ -528,11 +528,11 @@
528 ** The schema for the local FOSSIL database file found at the root
529 ** of every check-out. This database contains the complete state of
530 ** the check-out. See also the addendum in zLocalSchemaVmerge[].
531 */
532 const char zLocalSchema[] =
533 @ -- The VVAR table holds miscellanous information about the local checkout
534 @ -- in the form of name-value pairs. This is similar to the VAR table
535 @ -- table in the repository except that this table holds information that
536 @ -- is specific to the local check-out.
537 @ --
538 @ -- Important Variables:
@@ -542,11 +542,11 @@
542 @ --
543 @ CREATE TABLE vvar(
544 @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry
545 @ value CLOB, -- Content of the named parameter
546 @ CHECK( typeof(name)='text' AND length(name)>=1 )
547 @ ) WITHOUT ROWID;
548 @
549 @ -- Each entry in the vfile table represents a single file in the
550 @ -- current check-out.
551 @ --
552 @ -- The file.rid field is 0 for files or folders that have been
553
+3 -39
--- src/sitemap.c
+++ src/sitemap.c
@@ -56,22 +56,10 @@
5656
int i;
5757
int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
5858
int e = atoi(PD("e","0"));
5959
const char *zExtra;
6060
61
-#if 0 /* Removed 2021-01-26 */
62
- const struct {
63
- const char *zTitle;
64
- const char *zProperty;
65
- } aExtra[] = {
66
- { "Documentation", "sitemap-docidx" },
67
- { "Download", "sitemap-download" },
68
- { "License", "sitemap-license" },
69
- { "Contact", "sitemap-contact" },
70
- };
71
-#endif
72
-
7361
login_check_credentials();
7462
if( P("popup")!=0 ){
7563
/* The "popup" query parameter
7664
** then disable anti-robot defenses */
7765
isPopup = 1;
@@ -87,26 +75,10 @@
8775
@ <ul id="sitemap" class="columns" style="column-width:20em">
8876
if( (e&1)==0 ){
8977
@ <li>%z(href("%R/home"))Home Page</a>
9078
}
9179
92
-#if 0 /* Removed 2021-01-26 */
93
- for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){
94
- char *z = db_get(aExtra[i].zProperty,0);
95
- if( z==0 || z[0]==0 ) continue;
96
- if( !inSublist ){
97
- @ <ul>
98
- inSublist = 1;
99
- }
100
- if( z[0]=='/' ){
101
- @ <li>%z(href("%R%s",z))%s(aExtra[i].zTitle)</a></li>
102
- }else{
103
- @ <li>%z(href("%s",z))%s(aExtra[i].zTitle)</a></li>
104
- }
105
- }
106
-#endif
107
-
10880
zExtra = db_get("sitemap-extra",0);
10981
if( zExtra && (e&2)==0 ){
11082
int rc;
11183
char **azExtra = 0;
11284
int *anExtra;
@@ -139,26 +111,18 @@
139111
}
140112
Th_Free(g.interp, azExtra);
141113
}
142114
if( (e&1)!=0 ) goto end_of_sitemap;
143115
144
-#if 0 /* Removed on 2021-02-11. Make a sitemap-extra entry if you */
145
- /* really want this */
146
- if( srchFlags & SRCH_DOC ){
147
- if( !inSublist ){
148
- @ <ul>
149
- inSublist = 1;
150
- }
151
- @ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
152
- }
153
-#endif
154
-
155116
if( inSublist ){
156117
@ </ul>
157118
inSublist = 0;
158119
}
159120
@ </li>
121
+ if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){
122
+ @ <li>%z(href("%R/ckout"))Checkout Status</a></li>
123
+ }
160124
if( g.perm.Read ){
161125
const char *zEditGlob = db_get("fileedit-glob","");
162126
@ <li>%z(href("%R/tree"))File Browser</a>
163127
@ <ul>
164128
@ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
165129
--- src/sitemap.c
+++ src/sitemap.c
@@ -56,22 +56,10 @@
56 int i;
57 int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
58 int e = atoi(PD("e","0"));
59 const char *zExtra;
60
61 #if 0 /* Removed 2021-01-26 */
62 const struct {
63 const char *zTitle;
64 const char *zProperty;
65 } aExtra[] = {
66 { "Documentation", "sitemap-docidx" },
67 { "Download", "sitemap-download" },
68 { "License", "sitemap-license" },
69 { "Contact", "sitemap-contact" },
70 };
71 #endif
72
73 login_check_credentials();
74 if( P("popup")!=0 ){
75 /* The "popup" query parameter
76 ** then disable anti-robot defenses */
77 isPopup = 1;
@@ -87,26 +75,10 @@
87 @ <ul id="sitemap" class="columns" style="column-width:20em">
88 if( (e&1)==0 ){
89 @ <li>%z(href("%R/home"))Home Page</a>
90 }
91
92 #if 0 /* Removed 2021-01-26 */
93 for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){
94 char *z = db_get(aExtra[i].zProperty,0);
95 if( z==0 || z[0]==0 ) continue;
96 if( !inSublist ){
97 @ <ul>
98 inSublist = 1;
99 }
100 if( z[0]=='/' ){
101 @ <li>%z(href("%R%s",z))%s(aExtra[i].zTitle)</a></li>
102 }else{
103 @ <li>%z(href("%s",z))%s(aExtra[i].zTitle)</a></li>
104 }
105 }
106 #endif
107
108 zExtra = db_get("sitemap-extra",0);
109 if( zExtra && (e&2)==0 ){
110 int rc;
111 char **azExtra = 0;
112 int *anExtra;
@@ -139,26 +111,18 @@
139 }
140 Th_Free(g.interp, azExtra);
141 }
142 if( (e&1)!=0 ) goto end_of_sitemap;
143
144 #if 0 /* Removed on 2021-02-11. Make a sitemap-extra entry if you */
145 /* really want this */
146 if( srchFlags & SRCH_DOC ){
147 if( !inSublist ){
148 @ <ul>
149 inSublist = 1;
150 }
151 @ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
152 }
153 #endif
154
155 if( inSublist ){
156 @ </ul>
157 inSublist = 0;
158 }
159 @ </li>
 
 
 
160 if( g.perm.Read ){
161 const char *zEditGlob = db_get("fileedit-glob","");
162 @ <li>%z(href("%R/tree"))File Browser</a>
163 @ <ul>
164 @ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
165
--- src/sitemap.c
+++ src/sitemap.c
@@ -56,22 +56,10 @@
56 int i;
57 int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
58 int e = atoi(PD("e","0"));
59 const char *zExtra;
60
 
 
 
 
 
 
 
 
 
 
 
 
61 login_check_credentials();
62 if( P("popup")!=0 ){
63 /* The "popup" query parameter
64 ** then disable anti-robot defenses */
65 isPopup = 1;
@@ -87,26 +75,10 @@
75 @ <ul id="sitemap" class="columns" style="column-width:20em">
76 if( (e&1)==0 ){
77 @ <li>%z(href("%R/home"))Home Page</a>
78 }
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80 zExtra = db_get("sitemap-extra",0);
81 if( zExtra && (e&2)==0 ){
82 int rc;
83 char **azExtra = 0;
84 int *anExtra;
@@ -139,26 +111,18 @@
111 }
112 Th_Free(g.interp, azExtra);
113 }
114 if( (e&1)!=0 ) goto end_of_sitemap;
115
 
 
 
 
 
 
 
 
 
 
 
116 if( inSublist ){
117 @ </ul>
118 inSublist = 0;
119 }
120 @ </li>
121 if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){
122 @ <li>%z(href("%R/ckout"))Checkout Status</a></li>
123 }
124 if( g.perm.Read ){
125 const char *zEditGlob = db_get("fileedit-glob","");
126 @ <li>%z(href("%R/tree"))File Browser</a>
127 @ <ul>
128 @ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
129
+43
--- src/update.c
+++ src/update.c
@@ -407,10 +407,11 @@
407407
" WHERE id=:idt"
408408
);
409409
assert( g.zLocalRoot!=0 );
410410
assert( strlen(g.zLocalRoot)>0 );
411411
assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
412
+ merge_info_init();
412413
while( db_step(&q)==SQLITE_ROW ){
413414
const char *zName = db_column_text(&q, 0); /* The filename from root */
414415
int idv = db_column_int(&q, 1); /* VFILE entry for current */
415416
int ridv = db_column_int(&q, 2); /* RecordID for current */
416417
int idt = db_column_int(&q, 3); /* VFILE entry for target */
@@ -422,13 +423,18 @@
422423
int islinkt = db_column_int(&q, 9); /* Is target file is a link */
423424
int deleted = db_column_int(&q, 10); /* Marked for deletion */
424425
char *zFullPath; /* Full pathname of the file */
425426
char *zFullNewPath; /* Full pathname of dest */
426427
char nameChng; /* True if the name changed */
428
+ const char *zOp = 0; /* Type of change. */
429
+ i64 sz = 0; /* Size of the file */
430
+ int nc = 0; /* Number of conflicts */
431
+ const char *zErrMsg = 0; /* Error message */
427432
428433
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
429434
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
435
+ sz = file_size(zFullNewPath, ExtFILE);
430436
nameChng = fossil_strcmp(zName, zNewName);
431437
nUpdate++;
432438
if( deleted ){
433439
db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
434440
}
@@ -436,10 +442,13 @@
436442
/* Conflict. This file has been added to the current check-out
437443
** but also exists in the target check-out. Use the current version.
438444
*/
439445
fossil_print("CONFLICT %s\n", zName);
440446
nConflict++;
447
+ zOp = "CONFLICT";
448
+ nc = 1;
449
+ zErrMsg = "duplicate file";
441450
}else if( idt>0 && idv==0 ){
442451
/* File added in the target. */
443452
if( file_isfile_or_link(zFullPath) ){
444453
/* Name of backup file with Original content */
445454
char *zOrig = file_newname(zFullPath, "original", 1);
@@ -448,10 +457,13 @@
448457
fossil_free(zOrig);
449458
fossil_print("ADD %s - overwrites an unmanaged file", zName);
450459
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
451460
fossil_print("\n");
452461
nOverwrite++;
462
+ nc = 1;
463
+ zOp = "CONFLICT";
464
+ zErrMsg = "new file overwrites unmanaged file";
453465
}else{
454466
fossil_print("ADD %s\n", zName);
455467
}
456468
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
457469
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
@@ -462,16 +474,18 @@
462474
}else{
463475
fossil_print("UPDATE %s\n", zName);
464476
}
465477
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
466478
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
479
+ zOp = "UPDATE";
467480
}else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
468481
/* The file missing from the local check-out. Restore it to the
469482
** version that appears in the target. */
470483
fossil_print("UPDATE %s\n", zName);
471484
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
472485
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
486
+ zOp = "UPDATE";
473487
}else if( idt==0 && idv>0 ){
474488
if( ridv==0 ){
475489
/* Added in current check-out. Continue to hold the file as
476490
** as an addition */
477491
db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
@@ -478,10 +492,13 @@
478492
}else if( chnged ){
479493
/* Edited locally but deleted from the target. Do not track the
480494
** file but keep the edited version around. */
481495
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
482496
zName);
497
+ zOp = "CONFLICT";
498
+ zErrMsg = "edited locally but deleted by update";
499
+ nc = 1;
483500
nConflict++;
484501
}else{
485502
fossil_print("REMOVE %s\n", zName);
486503
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
487504
if( !dryRunFlag ){
@@ -500,17 +517,19 @@
500517
}
501518
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
502519
/* Merge the changes in the current tree into the target version */
503520
Blob r, t, v;
504521
int rc;
522
+
505523
if( nameChng ){
506524
fossil_print("MERGE %s -> %s\n", zName, zNewName);
507525
}else{
508526
fossil_print("MERGE %s\n", zName);
509527
}
510528
if( islinkv || islinkt ){
511529
fossil_print("***** Cannot merge symlink %s\n", zNewName);
530
+ zOp = "CONFLICT";
512531
nConflict++;
513532
}else{
514533
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
515534
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
516535
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
@@ -521,12 +540,17 @@
521540
if( !dryRunFlag ){
522541
blob_write_to_file(&r, zFullNewPath);
523542
file_setexe(zFullNewPath, isexe);
524543
}
525544
if( rc>0 ){
545
+ nc = rc;
546
+ zOp = "CONFLICT";
547
+ zErrMsg = "merge conflicts";
526548
fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
527549
nConflict++;
550
+ }else{
551
+ zOp = "MERGE";
528552
}
529553
}else{
530554
if( !dryRunFlag ){
531555
if( !keepMergeFlag ){
532556
/* Name of backup file with Original content */
@@ -543,10 +567,13 @@
543567
if( !dryRunFlag ){
544568
fossil_print(", original copy backed up locally");
545569
}
546570
fossil_print("\n");
547571
nConflict++;
572
+ zOp = "ERROR";
573
+ zErrMsg = "cannot merge binary file";
574
+ nc = 1;
548575
}
549576
}
550577
if( nameChng && !dryRunFlag ) file_delete(zFullPath);
551578
blob_reset(&v);
552579
blob_reset(&t);
@@ -560,10 +587,26 @@
560587
db_bind_int(&mtimeXfer, ":idt", idt);
561588
db_step(&mtimeXfer);
562589
db_reset(&mtimeXfer);
563590
if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
564591
}
592
+ }
593
+ if( zOp!=0 ){
594
+ db_multi_exec(
595
+ "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
596
+ "VALUES(%Q,%Q,%d,%Q,NULL,%lld,%Q,%d,%Q,%d,%Q)",
597
+ /* op */ zOp,
598
+ /* fnp */ zName,
599
+ /* ridp */ ridv,
600
+ /* fn */ zNewName,
601
+ /* sz */ sz,
602
+ /* fnm */ zName,
603
+ /* ridm */ ridt,
604
+ /* fnr */ zNewName,
605
+ /* nc */ nc,
606
+ /* msg */ zErrMsg
607
+ );
565608
}
566609
free(zFullPath);
567610
free(zFullNewPath);
568611
}
569612
db_finalize(&q);
570613
--- src/update.c
+++ src/update.c
@@ -407,10 +407,11 @@
407 " WHERE id=:idt"
408 );
409 assert( g.zLocalRoot!=0 );
410 assert( strlen(g.zLocalRoot)>0 );
411 assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
 
412 while( db_step(&q)==SQLITE_ROW ){
413 const char *zName = db_column_text(&q, 0); /* The filename from root */
414 int idv = db_column_int(&q, 1); /* VFILE entry for current */
415 int ridv = db_column_int(&q, 2); /* RecordID for current */
416 int idt = db_column_int(&q, 3); /* VFILE entry for target */
@@ -422,13 +423,18 @@
422 int islinkt = db_column_int(&q, 9); /* Is target file is a link */
423 int deleted = db_column_int(&q, 10); /* Marked for deletion */
424 char *zFullPath; /* Full pathname of the file */
425 char *zFullNewPath; /* Full pathname of dest */
426 char nameChng; /* True if the name changed */
 
 
 
 
427
428 zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
429 zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
 
430 nameChng = fossil_strcmp(zName, zNewName);
431 nUpdate++;
432 if( deleted ){
433 db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
434 }
@@ -436,10 +442,13 @@
436 /* Conflict. This file has been added to the current check-out
437 ** but also exists in the target check-out. Use the current version.
438 */
439 fossil_print("CONFLICT %s\n", zName);
440 nConflict++;
 
 
 
441 }else if( idt>0 && idv==0 ){
442 /* File added in the target. */
443 if( file_isfile_or_link(zFullPath) ){
444 /* Name of backup file with Original content */
445 char *zOrig = file_newname(zFullPath, "original", 1);
@@ -448,10 +457,13 @@
448 fossil_free(zOrig);
449 fossil_print("ADD %s - overwrites an unmanaged file", zName);
450 if( !dryRunFlag ) fossil_print(", original copy backed up locally");
451 fossil_print("\n");
452 nOverwrite++;
 
 
 
453 }else{
454 fossil_print("ADD %s\n", zName);
455 }
456 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
457 if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
@@ -462,16 +474,18 @@
462 }else{
463 fossil_print("UPDATE %s\n", zName);
464 }
465 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
466 if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
 
467 }else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
468 /* The file missing from the local check-out. Restore it to the
469 ** version that appears in the target. */
470 fossil_print("UPDATE %s\n", zName);
471 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
472 if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
 
473 }else if( idt==0 && idv>0 ){
474 if( ridv==0 ){
475 /* Added in current check-out. Continue to hold the file as
476 ** as an addition */
477 db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
@@ -478,10 +492,13 @@
478 }else if( chnged ){
479 /* Edited locally but deleted from the target. Do not track the
480 ** file but keep the edited version around. */
481 fossil_print("CONFLICT %s - edited locally but deleted by update\n",
482 zName);
 
 
 
483 nConflict++;
484 }else{
485 fossil_print("REMOVE %s\n", zName);
486 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
487 if( !dryRunFlag ){
@@ -500,17 +517,19 @@
500 }
501 }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
502 /* Merge the changes in the current tree into the target version */
503 Blob r, t, v;
504 int rc;
 
505 if( nameChng ){
506 fossil_print("MERGE %s -> %s\n", zName, zNewName);
507 }else{
508 fossil_print("MERGE %s\n", zName);
509 }
510 if( islinkv || islinkt ){
511 fossil_print("***** Cannot merge symlink %s\n", zNewName);
 
512 nConflict++;
513 }else{
514 unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
515 if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
516 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
@@ -521,12 +540,17 @@
521 if( !dryRunFlag ){
522 blob_write_to_file(&r, zFullNewPath);
523 file_setexe(zFullNewPath, isexe);
524 }
525 if( rc>0 ){
 
 
 
526 fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
527 nConflict++;
 
 
528 }
529 }else{
530 if( !dryRunFlag ){
531 if( !keepMergeFlag ){
532 /* Name of backup file with Original content */
@@ -543,10 +567,13 @@
543 if( !dryRunFlag ){
544 fossil_print(", original copy backed up locally");
545 }
546 fossil_print("\n");
547 nConflict++;
 
 
 
548 }
549 }
550 if( nameChng && !dryRunFlag ) file_delete(zFullPath);
551 blob_reset(&v);
552 blob_reset(&t);
@@ -560,10 +587,26 @@
560 db_bind_int(&mtimeXfer, ":idt", idt);
561 db_step(&mtimeXfer);
562 db_reset(&mtimeXfer);
563 if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
564 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565 }
566 free(zFullPath);
567 free(zFullNewPath);
568 }
569 db_finalize(&q);
570
--- src/update.c
+++ src/update.c
@@ -407,10 +407,11 @@
407 " WHERE id=:idt"
408 );
409 assert( g.zLocalRoot!=0 );
410 assert( strlen(g.zLocalRoot)>0 );
411 assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
412 merge_info_init();
413 while( db_step(&q)==SQLITE_ROW ){
414 const char *zName = db_column_text(&q, 0); /* The filename from root */
415 int idv = db_column_int(&q, 1); /* VFILE entry for current */
416 int ridv = db_column_int(&q, 2); /* RecordID for current */
417 int idt = db_column_int(&q, 3); /* VFILE entry for target */
@@ -422,13 +423,18 @@
423 int islinkt = db_column_int(&q, 9); /* Is target file is a link */
424 int deleted = db_column_int(&q, 10); /* Marked for deletion */
425 char *zFullPath; /* Full pathname of the file */
426 char *zFullNewPath; /* Full pathname of dest */
427 char nameChng; /* True if the name changed */
428 const char *zOp = 0; /* Type of change. */
429 i64 sz = 0; /* Size of the file */
430 int nc = 0; /* Number of conflicts */
431 const char *zErrMsg = 0; /* Error message */
432
433 zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
434 zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
435 sz = file_size(zFullNewPath, ExtFILE);
436 nameChng = fossil_strcmp(zName, zNewName);
437 nUpdate++;
438 if( deleted ){
439 db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
440 }
@@ -436,10 +442,13 @@
442 /* Conflict. This file has been added to the current check-out
443 ** but also exists in the target check-out. Use the current version.
444 */
445 fossil_print("CONFLICT %s\n", zName);
446 nConflict++;
447 zOp = "CONFLICT";
448 nc = 1;
449 zErrMsg = "duplicate file";
450 }else if( idt>0 && idv==0 ){
451 /* File added in the target. */
452 if( file_isfile_or_link(zFullPath) ){
453 /* Name of backup file with Original content */
454 char *zOrig = file_newname(zFullPath, "original", 1);
@@ -448,10 +457,13 @@
457 fossil_free(zOrig);
458 fossil_print("ADD %s - overwrites an unmanaged file", zName);
459 if( !dryRunFlag ) fossil_print(", original copy backed up locally");
460 fossil_print("\n");
461 nOverwrite++;
462 nc = 1;
463 zOp = "CONFLICT";
464 zErrMsg = "new file overwrites unmanaged file";
465 }else{
466 fossil_print("ADD %s\n", zName);
467 }
468 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
469 if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
@@ -462,16 +474,18 @@
474 }else{
475 fossil_print("UPDATE %s\n", zName);
476 }
477 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
478 if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
479 zOp = "UPDATE";
480 }else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
481 /* The file missing from the local check-out. Restore it to the
482 ** version that appears in the target. */
483 fossil_print("UPDATE %s\n", zName);
484 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
485 if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
486 zOp = "UPDATE";
487 }else if( idt==0 && idv>0 ){
488 if( ridv==0 ){
489 /* Added in current check-out. Continue to hold the file as
490 ** as an addition */
491 db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
@@ -478,10 +492,13 @@
492 }else if( chnged ){
493 /* Edited locally but deleted from the target. Do not track the
494 ** file but keep the edited version around. */
495 fossil_print("CONFLICT %s - edited locally but deleted by update\n",
496 zName);
497 zOp = "CONFLICT";
498 zErrMsg = "edited locally but deleted by update";
499 nc = 1;
500 nConflict++;
501 }else{
502 fossil_print("REMOVE %s\n", zName);
503 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
504 if( !dryRunFlag ){
@@ -500,17 +517,19 @@
517 }
518 }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
519 /* Merge the changes in the current tree into the target version */
520 Blob r, t, v;
521 int rc;
522
523 if( nameChng ){
524 fossil_print("MERGE %s -> %s\n", zName, zNewName);
525 }else{
526 fossil_print("MERGE %s\n", zName);
527 }
528 if( islinkv || islinkt ){
529 fossil_print("***** Cannot merge symlink %s\n", zNewName);
530 zOp = "CONFLICT";
531 nConflict++;
532 }else{
533 unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
534 if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
535 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
@@ -521,12 +540,17 @@
540 if( !dryRunFlag ){
541 blob_write_to_file(&r, zFullNewPath);
542 file_setexe(zFullNewPath, isexe);
543 }
544 if( rc>0 ){
545 nc = rc;
546 zOp = "CONFLICT";
547 zErrMsg = "merge conflicts";
548 fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
549 nConflict++;
550 }else{
551 zOp = "MERGE";
552 }
553 }else{
554 if( !dryRunFlag ){
555 if( !keepMergeFlag ){
556 /* Name of backup file with Original content */
@@ -543,10 +567,13 @@
567 if( !dryRunFlag ){
568 fossil_print(", original copy backed up locally");
569 }
570 fossil_print("\n");
571 nConflict++;
572 zOp = "ERROR";
573 zErrMsg = "cannot merge binary file";
574 nc = 1;
575 }
576 }
577 if( nameChng && !dryRunFlag ) file_delete(zFullPath);
578 blob_reset(&v);
579 blob_reset(&t);
@@ -560,10 +587,26 @@
587 db_bind_int(&mtimeXfer, ":idt", idt);
588 db_step(&mtimeXfer);
589 db_reset(&mtimeXfer);
590 if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
591 }
592 }
593 if( zOp!=0 ){
594 db_multi_exec(
595 "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
596 "VALUES(%Q,%Q,%d,%Q,NULL,%lld,%Q,%d,%Q,%d,%Q)",
597 /* op */ zOp,
598 /* fnp */ zName,
599 /* ridp */ ridv,
600 /* fn */ zNewName,
601 /* sz */ sz,
602 /* fnm */ zName,
603 /* ridm */ ridt,
604 /* fnr */ zNewName,
605 /* nc */ nc,
606 /* msg */ zErrMsg
607 );
608 }
609 free(zFullPath);
610 free(zFullNewPath);
611 }
612 db_finalize(&q);
613
+16 -1
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,10 +1,25 @@
11
<title>Change Log</title>
22
33
<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
44
5
- * <i>(pending)</i>
5
+ * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that
6
+ it optionally accepts a directory name as its argument, and uses files
7
+ under that directory as the baseline for the diff.
8
+ * Added the [/help?cmd=/ckout|/ckout web page] to provide information
9
+ about pending changes in a working check-out
10
+ * The [/help?cmd=ui|fossil ui] command defaults to using the
11
+ [/help?cmd=/ckout|/ckout page] as its start page. Or, if the
12
+ "--from PATH" option is present, the default start page becomes
13
+ "/ckout?exbase=PATH".
14
+ * Added the [/help?cmd=merge-info|fossil merge-info] command and especially
15
+ the --tk option to that command, to provide analysis of the most recent
16
+ merge or update operation.
17
+ * Issue a warning if a user tries to commit on a check-in where the
18
+ branch has been changed.
19
+ * When a merge conflict occurs, a new section is added to the conflict
20
+ text that shows Fossil's suggested resolution to the conflict.
621
722
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
823
924
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
1025
that have non-ASCII filenames
1126
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,10 +1,25 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
4
5 * <i>(pending)</i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
7 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
8
9 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
10 that have non-ASCII filenames
11
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,10 +1,25 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
4
5 * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that
6 it optionally accepts a directory name as its argument, and uses files
7 under that directory as the baseline for the diff.
8 * Added the [/help?cmd=/ckout|/ckout web page] to provide information
9 about pending changes in a working check-out
10 * The [/help?cmd=ui|fossil ui] command defaults to using the
11 [/help?cmd=/ckout|/ckout page] as its start page. Or, if the
12 "--from PATH" option is present, the default start page becomes
13 "/ckout?exbase=PATH".
14 * Added the [/help?cmd=merge-info|fossil merge-info] command and especially
15 the --tk option to that command, to provide analysis of the most recent
16 merge or update operation.
17 * Issue a warning if a user tries to commit on a check-in where the
18 branch has been changed.
19 * When a merge conflict occurs, a new section is added to the conflict
20 text that shows Fossil's suggested resolution to the conflict.
21
22 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
23
24 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
25 that have non-ASCII filenames
26

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button