Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Update with all changes from trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | clone-resume |
| Files: | files | file ages | folders |
| SHA3-256: |
c454059a18bd2c3564b6ec7f42b317e5 |
| User & Date: | andybradford 2024-02-03 16:55:40.013 |
Context
|
2024-02-06
| ||
| 14:19 | Bring in fixes for SSH from trunk. check-in: c55d1b7b26 user: andybradford tags: clone-resume | |
|
2024-02-03
| ||
| 16:55 | Update with all changes from trunk. check-in: c454059a18 user: andybradford tags: clone-resume | |
|
2024-02-02
| ||
| 22:22 | Fix overlong lines and remove trailing whitespace from non-external C files. check-in: 275da70f8c user: danield tags: trunk | |
|
2023-12-17
| ||
| 05:49 | When resuming a clone, delete the previous setup user so it can be reset. This also handles the case where a new --admin-user is passed in. check-in: b86d4da5a2 user: andybradford tags: clone-resume | |
Changes
Changes to Dockerfile.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 | ## --------------------------------------------------------------------- ## RUN! ## --------------------------------------------------------------------- ENV PATH "/bin" EXPOSE 8080/tcp USER fossil | | | > | 79 80 81 82 83 84 85 86 87 88 89 90 91 |
## ---------------------------------------------------------------------
## RUN!
## ---------------------------------------------------------------------
ENV PATH "/bin"
EXPOSE 8080/tcp
USER fossil
ENTRYPOINT [ "fossil", "server" ]
CMD [ \
"--create", \
"--jsmode", "bundled", \
"--user", "admin", \
"museum/repo.fossil" ]
|
Changes to auto.def.
| ︙ | ︙ | |||
453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
}
}
}
if {$found} {
define FOSSIL_ENABLE_SSL
define-append EXTRA_CFLAGS $cflags
define-append EXTRA_LDFLAGS $ldflags
if {[info exists ssllibs]} {
define-append LIBS $ssllibs
} else {
define-append LIBS -lssl -lcrypto
}
if {[info exists ::zlib_lib]} {
define-append LIBS $::zlib_lib
| > > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
}
}
}
if {$found} {
define FOSSIL_ENABLE_SSL
define-append EXTRA_CFLAGS $cflags
define-append EXTRA_LDFLAGS $ldflags
define-append CFLAGS $cflags
define-append LDFLAGS $ldflags
if {[info exists ssllibs]} {
define-append LIBS $ssllibs
} else {
define-append LIBS -lssl -lcrypto
}
if {[info exists ::zlib_lib]} {
define-append LIBS $::zlib_lib
|
| ︙ | ︙ | |||
653 654 655 656 657 658 659 660 661 662 663 664 665 666 |
}
set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
if {!$tclprivatestubs} {
define-append LIBS $libs
}
define-append EXTRA_CFLAGS $cflags
if {[info exists zlibpath] && $zlibpath eq "tree"} {
#
# NOTE: When using zlib in the source tree, prevent Tcl from
# pulling in the system one.
#
set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \
$tclconfig(TCL_LD_FLAGS)]
| > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
}
set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
if {!$tclprivatestubs} {
define-append LIBS $libs
}
define-append EXTRA_CFLAGS $cflags
define-append CFLAGS $cflags
if {[info exists zlibpath] && $zlibpath eq "tree"} {
#
# NOTE: When using zlib in the source tree, prevent Tcl from
# pulling in the system one.
#
set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \
$tclconfig(TCL_LD_FLAGS)]
|
| ︙ | ︙ |
Changes to extsrc/pikchr.c.
| ︙ | ︙ | |||
5106 5107 5108 5109 5110 5111 5112 | int iErrCol; /* Column of the error token on its line */ int iStart; /* Start position of the error context */ int iEnd; /* End position of the error context */ int iLineno; /* Line number of the error */ int iFirstLineno; /* Line number of start of error context */ int i; /* Loop counter */ int iBump = 0; /* Bump the location of the error cursor */ | | | 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 |
int iErrCol; /* Column of the error token on its line */
int iStart; /* Start position of the error context */
int iEnd; /* End position of the error context */
int iLineno; /* Line number of the error */
int iFirstLineno; /* Line number of start of error context */
int i; /* Loop counter */
int iBump = 0; /* Bump the location of the error cursor */
char zLineno[24]; /* Buffer in which to generate line numbers */
iErrPt = (int)(pErr->z - p->sIn.z);
if( iErrPt>=(int)p->sIn.n ){
iErrPt = p->sIn.n-1;
iBump = 1;
}else{
while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){
|
| ︙ | ︙ |
Changes to extsrc/shell.c.
| ︙ | ︙ | |||
292 293 294 295 296 297 298 | ** This code may change in tandem with other project code as needed. ** ** When this .h file and its companion .c are directly incorporated into ** a source conglomeration (such as shell.c), the preprocessor symbol ** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O ** translation for Windows is effected for the build. */ | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | ** This code may change in tandem with other project code as needed. ** ** When this .h file and its companion .c are directly incorporated into ** a source conglomeration (such as shell.c), the preprocessor symbol ** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O ** translation for Windows is effected for the build. */ #define HAVE_CONSOLE_IO_H 1 #ifndef SQLITE_INTERNAL_LINKAGE # define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ # include <stdio.h> #else # define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ #endif |
| ︙ | ︙ | |||
430 431 432 433 434 435 436 | ** accepted char or character sequence is limited by nAccept. ** ** Returns the number of accepted char values. */ #ifdef CONSIO_SPUTB SQLITE_INTERNAL_LINKAGE int fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); | < > | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | ** accepted char or character sequence is limited by nAccept. ** ** Returns the number of accepted char values. */ #ifdef CONSIO_SPUTB SQLITE_INTERNAL_LINKAGE int fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated output. */ #endif SQLITE_INTERNAL_LINKAGE int oPutbUtf8(const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated error. */ #ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept); #endif |
| ︙ | ︙ | |||
571 572 573 574 575 576 577 | #ifndef SHELL_NO_SYSINC # include <stdarg.h> # include <string.h> # include <stdlib.h> # include <limits.h> # include <assert.h> | < > > > | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | #ifndef SHELL_NO_SYSINC # include <stdarg.h> # include <string.h> # include <stdlib.h> # include <limits.h> # include <assert.h> /* # include "sqlite3.h" */ #endif #ifndef HAVE_CONSOLE_IO_H # include "console_io.h" #endif #ifndef SQLITE_CIO_NO_TRANSLATE # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT # ifndef SHELL_NO_SYSINC # include <io.h> # include <fcntl.h> |
| ︙ | ︙ | |||
1096 1097 1098 1099 1100 1101 1102 |
}
}
return z;
}
#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/
#ifndef SQLITE_CIO_NO_TRANSLATE
| < | | | | | | | 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 |
}
}
return z;
}
#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/
#ifndef SQLITE_CIO_NO_TRANSLATE
# ifdef CONSIO_SPUTB
SQLITE_INTERNAL_LINKAGE int
fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){
assert(pfO!=0);
# if CIO_WIN_WC_XLATE
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
if( pstReachesConsole(ppst) ){
int rv;
maybeSetupAsConsole(ppst, 1);
rv = conZstrEmit(ppst, cBuf, nAccept);
if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
return rv;
}else {
# endif
return (int)fwrite(cBuf, 1, nAccept, pfO);
# if CIO_WIN_WC_XLATE
}
# endif
}
# endif
SQLITE_INTERNAL_LINKAGE int
oPutbUtf8(const char *cBuf, int nAccept){
FILE *pfOut;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
|
| ︙ | ︙ | |||
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 | #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ #undef SHELL_INVALID_FILE_PTR /************************* End ../ext/consio/console_io.c ********************/ #ifndef SQLITE_SHELL_FIDDLE /* From here onward, fgets() is redirected to the console_io library. */ # define fgets(b,n,f) fGetsUtf8(b,n,f) /* * Define macros for emitting output text in various ways: * sputz(s, z) => emit 0-terminated string z to given stream s * sputf(s, f, ...) => emit varargs per format f to given stream s * oputz(z) => emit 0-terminated string z to default stream | > | 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ #undef SHELL_INVALID_FILE_PTR /************************* End ../ext/consio/console_io.c ********************/ #ifndef SQLITE_SHELL_FIDDLE /* From here onward, fgets() is redirected to the console_io library. */ # define fgets(b,n,f) fGetsUtf8(b,n,f) /* * Define macros for emitting output text in various ways: * sputz(s, z) => emit 0-terminated string z to given stream s * sputf(s, f, ...) => emit varargs per format f to given stream s * oputz(z) => emit 0-terminated string z to default stream |
| ︙ | ︙ | |||
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 | # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 # define eputz(z) ePutsUtf8(z) # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) #else /* For Fiddle, all console handling and emit redirection is omitted. */ # define sputz(fp,z) fputs(z,fp) # define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) # define oputz(z) fputs(z,stdout) # define oputf(fmt, ...) printf(fmt,__VA_ARGS__) # define eputz(z) fputs(z,stderr) | > | 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 | # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 # define eputz(z) ePutsUtf8(z) # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) #else /* For Fiddle, all console handling and emit redirection is omitted. */ # define sputz(fp,z) fputs(z,fp) # define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) # define oputz(z) fputs(z,stdout) # define oputf(fmt, ...) printf(fmt,__VA_ARGS__) # define eputz(z) fputs(z,stderr) |
| ︙ | ︙ | |||
1335 1336 1337 1338 1339 1340 1341 |
** Print the timing results.
*/
static void endTimer(void){
if( enableTimer ){
sqlite3_int64 iEnd = timeOfDay();
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
| | | 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 |
** Print the timing results.
*/
static void endTimer(void){
if( enableTimer ){
sqlite3_int64 iEnd = timeOfDay();
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
sputf(stdout, "Run Time: real %.3f user %f sys %f\n",
(iEnd - iBegin)*0.001,
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
}
}
#define BEGIN_TIMER beginTimer()
|
| ︙ | ︙ | |||
1414 1415 1416 1417 1418 1419 1420 |
** Print the timing results.
*/
static void endTimer(void){
if( enableTimer && getProcessTimesAddr){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
| | | 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 |
** Print the timing results.
*/
static void endTimer(void){
if( enableTimer && getProcessTimesAddr){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
sputf(stdout, "Run Time: real %.3f user %f sys %f\n",
(ftWallEnd - ftWallBegin)*0.001,
timeDiff(&ftUserBegin, &ftUserEnd),
timeDiff(&ftKernelBegin, &ftKernelEnd));
}
}
#define BEGIN_TIMER beginTimer()
|
| ︙ | ︙ | |||
1711 1712 1713 1714 1715 1716 1717 |
/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.
** Otherwise return 0.
*/
static FILE * openChrSource(const char *zFile){
#if defined(_WIN32) || defined(WIN32)
| | | | 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 |
/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.
** Otherwise return 0.
*/
static FILE * openChrSource(const char *zFile){
#if defined(_WIN32) || defined(WIN32)
struct __stat64 x = {0};
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
/* On Windows, open first, then check the stream nature. This order
** is necessary because _stat() and sibs, when checking a named pipe,
** effectively break the pipe as its supplier sees it. */
FILE *rv = fopen(zFile, "rb");
if( rv==0 ) return 0;
if( _fstat64(_fileno(rv), &x) != 0
|| !STAT_CHR_SRC(x.st_mode)){
fclose(rv);
rv = 0;
}
return rv;
#else
struct stat x = {0};
|
| ︙ | ︙ | |||
14823 14824 14825 14826 14827 14828 14829 14830 14831 14832 14833 14834 14835 14836 |
iOff += nPointer;
/* Load the "byte of payload including overflow" field */
if( bNextPage || iOff>pCsr->nPage ){
bNextPage = 1;
}else{
iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload);
}
/* If this is a leaf intkey cell, load the rowid */
if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){
iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey);
}
| > | 14826 14827 14828 14829 14830 14831 14832 14833 14834 14835 14836 14837 14838 14839 14840 |
iOff += nPointer;
/* Load the "byte of payload including overflow" field */
if( bNextPage || iOff>pCsr->nPage ){
bNextPage = 1;
}else{
iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload);
if( nPayload>0x7fffff00 ) nPayload &= 0x3fff;
}
/* If this is a leaf intkey cell, load the rowid */
if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){
iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey);
}
|
| ︙ | ︙ | |||
18139 18140 18141 18142 18143 18144 18145 18146 18147 18148 18149 18150 18151 18152 | u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ | > | 18143 18144 18145 18146 18147 18148 18149 18150 18151 18152 18153 18154 18155 18156 18157 | u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ u8 eRestoreState; /* See comments above doAutoDetectRestore() */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ |
| ︙ | ︙ | |||
22166 22167 22168 22169 22170 22171 22172 |
case SHELL_OPEN_UNSPEC:
case SHELL_OPEN_NORMAL: {
sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
break;
}
}
| < > | 22171 22172 22173 22174 22175 22176 22177 22178 22179 22180 22181 22182 22183 22184 22185 22186 22187 22188 22189 22190 22191 22192 22193 22194 22195 22196 22197 22198 22199 22200 22201 |
case SHELL_OPEN_UNSPEC:
case SHELL_OPEN_NORMAL: {
sqlite3_open_v2(zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
break;
}
}
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
eputf("Error: unable to open database \"%s\": %s\n",
zDbFilename, sqlite3_errmsg(p->db));
if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
exit(1);
}
sqlite3_close(p->db);
sqlite3_open(":memory:", &p->db);
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
eputz("Also: unable to open substitute in-memory database.\n");
exit(1);
}else{
eputf("Notice: using substitute in-memory database instead of \"%s\"\n",
zDbFilename);
}
}
globalDb = p->db;
sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0);
/* Reflect the use or absence of --unsafe-testing invocation. */
{
int testmode_on = ShellHasFlag(p,SHFLG_TestingMode);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0);
|
| ︙ | ︙ | |||
23578 23579 23580 23581 23582 23583 23584 |
usage:
eputf("Usage %s sub-command ?switches...?\n", azArg[0]);
eputz("Where sub-commands are:\n");
eputz(" fkey-indexes\n");
return SQLITE_ERROR;
}
| < < < < < | | 23583 23584 23585 23586 23587 23588 23589 23590 23591 23592 23593 23594 23595 23596 23597 23598 23599 23600 23601 23602 23603 23604 23605 23606 23607 23608 23609 23610 23611 23612 23613 23614 23615 23616 |
usage:
eputf("Usage %s sub-command ?switches...?\n", azArg[0]);
eputz("Where sub-commands are:\n");
eputz(" fkey-indexes\n");
return SQLITE_ERROR;
}
static void shellPrepare(
sqlite3 *db,
int *pRc,
const char *zSql,
sqlite3_stmt **ppStmt
){
*ppStmt = 0;
if( *pRc==SQLITE_OK ){
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db));
*pRc = rc;
}
}
}
/*
** Create a prepared statement using printf-style arguments for the SQL.
*/
static void shellPreparePrintf(
sqlite3 *db,
int *pRc,
sqlite3_stmt **ppStmt,
const char *zFmt,
...
){
*ppStmt = 0;
|
| ︙ | ︙ | |||
23625 23626 23627 23628 23629 23630 23631 |
}else{
shellPrepare(db, pRc, z, ppStmt);
sqlite3_free(z);
}
}
}
| > | < < < < | > | 23625 23626 23627 23628 23629 23630 23631 23632 23633 23634 23635 23636 23637 23638 23639 23640 23641 23642 23643 23644 23645 23646 23647 23648 23649 23650 23651 23652 23653 23654 23655 23656 23657 23658 |
}else{
shellPrepare(db, pRc, z, ppStmt);
sqlite3_free(z);
}
}
}
/*
** Finalize the prepared statement created using shellPreparePrintf().
*/
static void shellFinalize(
int *pRc,
sqlite3_stmt *pStmt
){
if( pStmt ){
sqlite3 *db = sqlite3_db_handle(pStmt);
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
eputf("SQL error: %s\n", sqlite3_errmsg(db));
}
*pRc = rc;
}
}
}
#if !defined SQLITE_OMIT_VIRTUALTABLE
/* Reset the prepared statement created using shellPreparePrintf().
**
** This routine is could be marked "static". But it is not always used,
** depending on compile-time options. By omitting the "static", we avoid
** nuisance compiler warnings about "defined but not used".
*/
void shellReset(
|
| ︙ | ︙ | |||
24712 24713 24714 24715 24716 24717 24718 24719 24720 24721 24722 24723 24724 24725 |
}
sqlite3_finalize(pStmt);
sqlite3_close(*pDb);
*pDb = 0;
return zColsSpec;
}
}
/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > | 24710 24711 24712 24713 24714 24715 24716 24717 24718 24719 24720 24721 24722 24723 24724 24725 24726 24727 24728 24729 24730 24731 24732 24733 24734 24735 24736 24737 24738 24739 24740 24741 24742 24743 24744 24745 24746 24747 |
}
sqlite3_finalize(pStmt);
sqlite3_close(*pDb);
*pDb = 0;
return zColsSpec;
}
}
/*
** Check if the sqlite_schema table contains one or more virtual tables. If
** parameter zLike is not NULL, then it is an SQL expression that the
** sqlite_schema row must also match. If one or more such rows are found,
** print the following warning to the output:
**
** WARNING: Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled
*/
static int outputDumpWarning(ShellState *p, const char *zLike){
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
shellPreparePrintf(p->db, &rc, &pStmt,
"SELECT 1 FROM sqlite_schema o WHERE "
"sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true"
);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
oputz("/* WARNING: "
"Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n"
);
}
shellFinalize(&rc, pStmt);
return rc;
}
/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
|
| ︙ | ︙ | |||
25175 25176 25177 25178 25179 25180 25181 25182 25183 25184 25185 25186 25187 25188 |
zLike = zExpr;
}
}
}
open_db(p, 0);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
oputz("PRAGMA foreign_keys=OFF;\n");
oputz("BEGIN TRANSACTION;\n");
}
| > | 25197 25198 25199 25200 25201 25202 25203 25204 25205 25206 25207 25208 25209 25210 25211 |
zLike = zExpr;
}
}
}
open_db(p, 0);
outputDumpWarning(p, zLike);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
oputz("PRAGMA foreign_keys=OFF;\n");
oputz("BEGIN TRANSACTION;\n");
}
|
| ︙ | ︙ | |||
27603 27604 27605 27606 27607 27608 27609 27610 27611 27612 27613 27614 27615 27616 |
/*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
/*{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/
{"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" },
{"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
{"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" },
{"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" },
{"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" },
{"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" },
#ifdef YYCOVERAGE
{"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" },
#endif
{"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " },
| > | 27626 27627 27628 27629 27630 27631 27632 27633 27634 27635 27636 27637 27638 27639 27640 |
/*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
/*{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/
{"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" },
{"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
{"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" },
{"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" },
{"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" },
{"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" },
{"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" },
#ifdef YYCOVERAGE
{"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" },
#endif
{"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " },
|
| ︙ | ︙ | |||
27821 27822 27823 27824 27825 27826 27827 27828 27829 27830 27831 27832 27833 27834 |
case SQLITE_TESTCTRL_SORTER_MMAP:
if( nArg==3 ){
int opt = (unsigned int)integerValue(azArg[2]);
rc2 = sqlite3_test_control(testctrl, p->db, opt);
isOk = 3;
}
break;
}
}
if( isOk==0 && iCtrl>=0 ){
oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
oputf("%d\n", rc2);
| > > > > > > > > > > | 27845 27846 27847 27848 27849 27850 27851 27852 27853 27854 27855 27856 27857 27858 27859 27860 27861 27862 27863 27864 27865 27866 27867 27868 |
case SQLITE_TESTCTRL_SORTER_MMAP:
if( nArg==3 ){
int opt = (unsigned int)integerValue(azArg[2]);
rc2 = sqlite3_test_control(testctrl, p->db, opt);
isOk = 3;
}
break;
case SQLITE_TESTCTRL_JSON_SELFCHECK:
if( nArg==2 ){
rc2 = -1;
isOk = 1;
}else{
rc2 = booleanValue(azArg[2]);
isOk = 3;
}
sqlite3_test_control(testctrl, &rc2);
break;
}
}
if( isOk==0 && iCtrl>=0 ){
oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
oputf("%d\n", rc2);
|
| ︙ | ︙ | |||
28226 28227 28228 28229 28230 28231 28232 28233 28234 28235 28236 28237 28238 28239 |
if( zSql==0 ) return 1;
zSql[nSql] = ';';
zSql[nSql+1] = 0;
rc = sqlite3_complete(zSql);
zSql[nSql] = 0;
return rc;
}
/*
** Run a single line of SQL. Return the number of errors.
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
int rc;
char *zErrMsg = 0;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 28260 28261 28262 28263 28264 28265 28266 28267 28268 28269 28270 28271 28272 28273 28274 28275 28276 28277 28278 28279 28280 28281 28282 28283 28284 28285 28286 28287 28288 28289 28290 28291 28292 28293 28294 28295 28296 28297 28298 28299 28300 28301 28302 28303 28304 28305 28306 28307 28308 28309 28310 28311 28312 28313 28314 28315 28316 28317 28318 28319 28320 28321 28322 28323 28324 28325 28326 28327 28328 28329 28330 28331 28332 28333 28334 28335 28336 28337 28338 28339 28340 28341 28342 28343 28344 28345 28346 28347 28348 28349 28350 28351 28352 28353 28354 28355 |
if( zSql==0 ) return 1;
zSql[nSql] = ';';
zSql[nSql+1] = 0;
rc = sqlite3_complete(zSql);
zSql[nSql] = 0;
return rc;
}
/*
** This function is called after processing each line of SQL in the
** runOneSqlLine() function. Its purpose is to detect scenarios where
** defensive mode should be automatically turned off. Specifically, when
**
** 1. The first line of input is "PRAGMA foreign_keys=OFF;",
** 2. The second line of input is "BEGIN TRANSACTION;",
** 3. The database is empty, and
** 4. The shell is not running in --safe mode.
**
** The implementation uses the ShellState.eRestoreState to maintain state:
**
** 0: Have not seen any SQL.
** 1: Have seen "PRAGMA foreign_keys=OFF;".
** 2-6: Currently running .dump transaction. If the "2" bit is set,
** disable DEFENSIVE when done. If "4" is set, disable DQS_DDL.
** 7: Nothing left to do. This function becomes a no-op.
*/
static int doAutoDetectRestore(ShellState *p, const char *zSql){
int rc = SQLITE_OK;
if( p->eRestoreState<7 ){
switch( p->eRestoreState ){
case 0: {
const char *zExpect = "PRAGMA foreign_keys=OFF;";
assert( strlen(zExpect)==24 );
if( p->bSafeMode==0 && memcmp(zSql, zExpect, 25)==0 ){
p->eRestoreState = 1;
}else{
p->eRestoreState = 7;
}
break;
};
case 1: {
int bIsDump = 0;
const char *zExpect = "BEGIN TRANSACTION;";
assert( strlen(zExpect)==18 );
if( memcmp(zSql, zExpect, 19)==0 ){
/* Now check if the database is empty. */
const char *zQuery = "SELECT 1 FROM sqlite_schema LIMIT 1";
sqlite3_stmt *pStmt = 0;
bIsDump = 1;
shellPrepare(p->db, &rc, zQuery, &pStmt);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
bIsDump = 0;
}
shellFinalize(&rc, pStmt);
}
if( bIsDump && rc==SQLITE_OK ){
int bDefense = 0;
int bDqsDdl = 0;
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDefense);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, -1, &bDqsDdl);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 1, 0);
p->eRestoreState = (bDefense ? 2 : 0) + (bDqsDdl ? 4 : 0);
}else{
p->eRestoreState = 7;
}
break;
}
default: {
if( sqlite3_get_autocommit(p->db) ){
if( (p->eRestoreState & 2) ){
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
}
if( (p->eRestoreState & 4) ){
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 0, 0);
}
p->eRestoreState = 7;
}
break;
}
}
}
return rc;
}
/*
** Run a single line of SQL. Return the number of errors.
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
int rc;
char *zErrMsg = 0;
|
| ︙ | ︙ | |||
28274 28275 28276 28277 28278 28279 28280 28281 28282 28283 28284 28285 28286 28287 |
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
char zLineBuf[2000];
sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
"changes: %lld total_changes: %lld",
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
oputf("%s\n", zLineBuf);
}
return 0;
}
static void echo_group_input(ShellState *p, const char *zDo){
if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo);
}
| > > | 28390 28391 28392 28393 28394 28395 28396 28397 28398 28399 28400 28401 28402 28403 28404 28405 |
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
char zLineBuf[2000];
sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
"changes: %lld total_changes: %lld",
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
oputf("%s\n", zLineBuf);
}
if( doAutoDetectRestore(p, zSql) ) return 1;
return 0;
}
static void echo_group_input(ShellState *p, const char *zDo){
if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo);
}
|
| ︙ | ︙ | |||
28707 28708 28709 28710 28711 28712 28713 |
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
SetConsoleTextAttribute(out,
FOREGROUND_RED|FOREGROUND_INTENSITY
);
#endif
| | | | 28825 28826 28827 28828 28829 28830 28831 28832 28833 28834 28835 28836 28837 28838 28839 28840 28841 28842 28843 28844 28845 28846 |
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
SetConsoleTextAttribute(out,
FOREGROUND_RED|FOREGROUND_INTENSITY
);
#endif
sputz(stdout, zText);
#if !SQLITE_OS_WINRT
SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
#endif
}
#else
static void printBold(const char *zText){
sputf(stdout, "\033[1m%s\033[0m", zText);
}
#endif
/*
** Get the argument to an --option. Throw an error and die if no argument
** is available.
*/
|
| ︙ | ︙ | |||
28908 28909 28910 28911 28912 28913 28914 |
|| cli_strcmp(z,"-newline")==0
|| cli_strcmp(z,"-cmd")==0
){
(void)cmdline_option_value(argc, argv, ++i);
}else if( cli_strcmp(z,"-init")==0 ){
zInitFile = cmdline_option_value(argc, argv, ++i);
}else if( cli_strcmp(z,"-interactive")==0 ){
| < < < < | 29026 29027 29028 29029 29030 29031 29032 29033 29034 29035 29036 29037 29038 29039 |
|| cli_strcmp(z,"-newline")==0
|| cli_strcmp(z,"-cmd")==0
){
(void)cmdline_option_value(argc, argv, ++i);
}else if( cli_strcmp(z,"-init")==0 ){
zInitFile = cmdline_option_value(argc, argv, ++i);
}else if( cli_strcmp(z,"-interactive")==0 ){
}else if( cli_strcmp(z,"-batch")==0 ){
/* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass.
*/
stdin_is_interactive = 0;
}else if( cli_strcmp(z,"-utf8")==0 ){
|
| ︙ | ︙ | |||
29181 29182 29183 29184 29185 29186 29187 |
** prior to sending the SQL into SQLite. Useful for injecting
** crazy bytes in the middle of SQL statements for testing and debugging.
*/
ShellSetFlag(&data, SHFLG_Backslash);
}else if( cli_strcmp(z,"-bail")==0 ){
/* No-op. The bail_on_error flag should already be set. */
}else if( cli_strcmp(z,"-version")==0 ){
| | | | > > > | 29295 29296 29297 29298 29299 29300 29301 29302 29303 29304 29305 29306 29307 29308 29309 29310 29311 29312 29313 29314 29315 29316 |
** prior to sending the SQL into SQLite. Useful for injecting
** crazy bytes in the middle of SQL statements for testing and debugging.
*/
ShellSetFlag(&data, SHFLG_Backslash);
}else if( cli_strcmp(z,"-bail")==0 ){
/* No-op. The bail_on_error flag should already be set. */
}else if( cli_strcmp(z,"-version")==0 ){
sputf(stdout, "%s %s (%d-bit)\n",
sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
return 0;
}else if( cli_strcmp(z,"-interactive")==0 ){
/* Need to check for interactive override here to so that it can
** affect console setup (for Windows only) and testing thereof.
*/
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-utf8")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-no-utf8")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-heap")==0 ){
|
| ︙ | ︙ | |||
29314 29315 29316 29317 29318 29319 29320 |
char *zHistory;
int nHistory;
#if CIO_WIN_WC_XLATE
# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "")
#else
# define SHELL_CIO_CHAR_SET ""
#endif
| | | | | 29431 29432 29433 29434 29435 29436 29437 29438 29439 29440 29441 29442 29443 29444 29445 29446 29447 29448 29449 29450 29451 |
char *zHistory;
int nHistory;
#if CIO_WIN_WC_XLATE
# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "")
#else
# define SHELL_CIO_CHAR_SET ""
#endif
sputf(stdout, "SQLite version %s %.19s%s\n" /*extra-version-info*/
"Enter \".help\" for usage hints.\n",
sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET);
if( warnInmemoryDb ){
sputz(stdout, "Connected to a ");
printBold("transient in-memory database");
sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
" persistent database.\n");
}
zHistory = getenv("SQLITE_HISTORY");
if( zHistory ){
zHistory = strdup(zHistory);
}else if( (zHome = find_home_dir(0))!=0 ){
nHistory = strlen30(zHome) + 20;
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.c.
1 2 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.45.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have ** the "sqlite3.h" header file at hand, you will find a copy embedded within ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in ** e876e51a0ed5c5b3126f52e532044363a014. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif /************** Begin file sqliteInt.h ***************************************/ |
| ︙ | ︙ | |||
455 456 457 458 459 460 461 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.45.1" #define SQLITE_VERSION_NUMBER 3045001 #define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
4263 4264 4265 4266 4267 4268 4269 | ** <li> sqlite3_extended_errcode() ** <li> sqlite3_errmsg() ** <li> sqlite3_errmsg16() ** <li> sqlite3_error_offset() ** </ul> ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language | | > | | > | 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 | ** <li> sqlite3_extended_errcode() ** <li> sqlite3_errmsg() ** <li> sqlite3_errmsg16() ** <li> sqlite3_error_offset() ** </ul> ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively, ** or NULL if no error message is available. ** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** ** ^The sqlite3_errstr(E) interface returns the English-language text ** that describes the [result code] E, as UTF-8, or NULL if E is not an ** result code for which a text error message is available. ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** ** ^If the most recent error references a specific token in the input ** SQL, the sqlite3_error_offset() interface returns the byte offset ** of the start of that token. ^The byte offset returned by ** sqlite3_error_offset() assumes that the input SQL is UTF8. |
| ︙ | ︙ | |||
8346 8347 8348 8349 8350 8351 8352 | ** In such cases, the ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() | | | | > > | 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 | ** In such cases, the ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() ** will always return SQLITE_BUSY. In most cases the SQLite core only uses ** sqlite3_mutex_try() as an optimization, so this is acceptable ** behavior. The exceptions are unix builds that set the ** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working ** sqlite3_mutex_try() is required.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), |
| ︙ | ︙ | |||
8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 | > | 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 |
| ︙ | ︙ | |||
13120 13121 13122 13123 13124 13125 13126 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: | > > > | | > > | | | > | | | | 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. ** ** Otherwise, this function attempts to retrieve the text of column iCol of ** the current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values ** of (*pz) and (*pn) are undefined. ** ** xPhraseCount: ** Returns the number of phrases in the current query expression. ** ** xPhraseSize: ** If parameter iCol is less than zero, or greater than or equal to the ** number of phrases in the current query, as returned by xPhraseCount, ** 0 is returned. Otherwise, this function returns the number of tokens in ** phrase iPhrase of the query. Phrases are numbered starting from zero. ** ** xInstCount: ** Set *pnInst to the total number of occurrences of all phrases within ** the query within the current row. Return SQLITE_OK if successful, or ** an error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. If the FTS5 table is created ** with either "detail=none" or "detail=column" and "content=" option ** (i.e. if it is a contentless table), then this API always returns 0. ** ** xInst: ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value ** output by xInstCount(). If iIdx is less than zero or greater than ** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. ** ** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the ** first token of the phrase. SQLITE_OK is returned if successful, or an ** error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xRowid: ** Returns the rowid of the current row. ** |
| ︙ | ︙ | |||
13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. | > > > > | 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If parameter iPhrase is less than zero, or greater than or equal to ** the number of phrases in the query, as returned by xPhraseCount(), ** this function returns SQLITE_RANGE. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. |
| ︙ | ︙ | |||
13293 13294 13295 13296 13297 13298 13299 13300 13301 |
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
*/
struct Fts5ExtensionApi {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 |
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
**
** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
** This is used to access token iToken of phrase iPhrase of the current
** query. Before returning, output parameter *ppToken is set to point
** to a buffer containing the requested token, and *pnToken to the
** size of this buffer in bytes.
**
** If iPhrase or iToken are less than zero, or if iPhrase is greater than
** or equal to the number of phrases in the query as reported by
** xPhraseCount(), or if iToken is equal to or greater than the number of
** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
are both zeroed.
**
** The output text is not a copy of the query text that specified the
** token. It is the output of the tokenizer module. For tokendata=1
** tables, this includes any embedded 0x00 and trailing data.
**
** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
** This is used to access token iToken of phrase hit iIdx within the
** current row. If iIdx is less than zero or greater than or equal to the
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
** output variable (*ppToken) is set to point to a buffer containing the
** matching document token, and (*pnToken) to the size of that buffer in
** bytes. This API is not available if the specified token matches a
** prefix query term. In that case both output variables are always set
** to 0.
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
int (*xColumnCount)(Fts5Context*);
int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
|
| ︙ | ︙ | |||
13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 | void *(*xGetAuxdata)(Fts5Context*, int bClear); int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* | > > > > > > > | 13378 13379 13380 13381 13382 13383 13384 13385 13386 13387 13388 13389 13390 13391 13392 13393 13394 13395 13396 13397 13398 |
void *(*xGetAuxdata)(Fts5Context*, int bClear);
int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
/* Below this point are iVersion>=3 only */
int (*xQueryToken)(Fts5Context*,
int iPhrase, int iToken,
const char **ppToken, int *pnToken
);
int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
|
| ︙ | ︙ | |||
13816 13817 13818 13819 13820 13821 13822 | ** Maximum number of pages in one database file. ** ** This is really just the default value for the max_page_count pragma. ** This value can be lowered (or raised) at run-time using that the ** max_page_count macro. */ #ifndef SQLITE_MAX_PAGE_COUNT | | | 13871 13872 13873 13874 13875 13876 13877 13878 13879 13880 13881 13882 13883 13884 13885 | ** Maximum number of pages in one database file. ** ** This is really just the default value for the max_page_count pragma. ** This value can be lowered (or raised) at run-time using that the ** max_page_count macro. */ #ifndef SQLITE_MAX_PAGE_COUNT # define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */ #endif /* ** Maximum length (in bytes) of the pattern in a LIKE or GLOB ** operator. */ #ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH |
| ︙ | ︙ | |||
13954 13955 13956 13957 13958 13959 13960 13961 13962 13963 13964 13965 13966 13967 | ** SEH support if the -DSQLITE_OMIT_SEH option is given. */ #if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) # define SQLITE_USE_SEH 1 #else # undef SQLITE_USE_SEH #endif /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never ** threadsafe. 1 means the library is serialized which is the highest ** level of threadsafety. 2 means the library is multithreaded - multiple ** threads can use SQLite as long as no two threads try to use the same | > > > > > > > > > > > > > | 14009 14010 14011 14012 14013 14014 14015 14016 14017 14018 14019 14020 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 | ** SEH support if the -DSQLITE_OMIT_SEH option is given. */ #if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) # define SQLITE_USE_SEH 1 #else # undef SQLITE_USE_SEH #endif /* ** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly ** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0 */ #if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1 /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */ # undef SQLITE_DIRECT_OVERFLOW_READ #else /* In all other cases, enable */ # define SQLITE_DIRECT_OVERFLOW_READ 1 #endif /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never ** threadsafe. 1 means the library is serialized which is the highest ** level of threadsafety. 2 means the library is multithreaded - multiple ** threads can use SQLite as long as no two threads try to use the same |
| ︙ | ︙ | |||
15837 15838 15839 15840 15841 15842 15843 | SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int); SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); | | | 15905 15906 15907 15908 15909 15910 15911 15912 15913 15914 15915 15916 15917 15918 15919 | SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int); SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*); SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); |
| ︙ | ︙ | |||
16424 16425 16426 16427 16428 16429 16430 16431 16432 16433 16434 16435 16436 16437 | #define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ #define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ #define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 #define P5_ConstraintUnique 2 #define P5_ConstraintCheck 3 #define P5_ConstraintFK 4 | > | 16492 16493 16494 16495 16496 16497 16498 16499 16500 16501 16502 16503 16504 16505 16506 | #define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ #define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ #define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ #define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 #define P5_ConstraintUnique 2 #define P5_ConstraintCheck 3 #define P5_ConstraintFK 4 |
| ︙ | ︙ | |||
16646 16647 16648 16649 16650 16651 16652 | #define OP_VCheck 174 #define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */ #define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */ #define OP_VRename 177 #define OP_Pagecount 178 #define OP_MaxPgcnt 179 #define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */ | > > | | | | | | | | 16715 16716 16717 16718 16719 16720 16721 16722 16723 16724 16725 16726 16727 16728 16729 16730 16731 16732 16733 16734 16735 16736 16737 | #define OP_VCheck 174 #define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */ #define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */ #define OP_VRename 177 #define OP_Pagecount 178 #define OP_MaxPgcnt 179 #define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */ #define OP_GetSubtype 181 /* synopsis: r[P2] = r[P1].subtype */ #define OP_SetSubtype 182 /* synopsis: r[P2].subtype = r[P1] */ #define OP_FilterAdd 183 /* synopsis: filter(P1) += key(P3@P4) */ #define OP_Trace 184 #define OP_CursorHint 185 #define OP_ReleaseReg 186 /* synopsis: release r[P1@P2] mask P3 */ #define OP_Noop 187 #define OP_Explain 188 #define OP_Abortable 189 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c ** are encoded into bitvectors as follows: */ #define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */ #define OPFLG_IN1 0x02 /* in1: P1 is an input */ |
| ︙ | ︙ | |||
16688 16689 16690 16691 16692 16693 16694 | /* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ /* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\ /* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ /* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ /* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ /* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\ | | | | 16759 16760 16761 16762 16763 16764 16765 16766 16767 16768 16769 16770 16771 16772 16773 16774 | /* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ /* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\ /* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ /* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ /* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ /* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\ /* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\ /* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} /* The resolve3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum ** JUMP opcode the better, so the mkopcodeh.tcl script that ** generated this include file strives to group all JUMP opcodes ** together near the beginning of the list. */ |
| ︙ | ︙ | |||
17950 17951 17952 17953 17954 17955 17956 |
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
| | | | 18021 18022 18023 18024 18025 18026 18027 18028 18029 18030 18031 18032 18033 18034 18035 18036 18037 18038 18039 |
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
{nArg, SQLITE_FUNC_BUILTIN|\
SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
#define TEST_FUNC(zName, nArg, iArg, mFlags) \
{nArg, SQLITE_FUNC_BUILTIN|\
SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \
|
| ︙ | ︙ | |||
18589 18590 18591 18592 18593 18594 18595 18596 18597 18598 18599 18600 18601 18602 |
unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
unsigned bUnordered:1; /* Use this index for == or IN queries only */
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
unsigned isResized:1; /* True if resizeIndexObject() has been called */
unsigned isCovering:1; /* True if this is a covering index */
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
unsigned bHasExpr:1; /* Index contains an expression, either a literal
** expression, or a reference to a VIRTUAL column */
#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
| > | 18660 18661 18662 18663 18664 18665 18666 18667 18668 18669 18670 18671 18672 18673 18674 |
unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
unsigned bUnordered:1; /* Use this index for == or IN queries only */
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
unsigned isResized:1; /* True if resizeIndexObject() has been called */
unsigned isCovering:1; /* True if this is a covering index */
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
unsigned bHasExpr:1; /* Index contains an expression, either a literal
** expression, or a reference to a VIRTUAL column */
#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
|
| ︙ | ︙ | |||
18702 18703 18704 18705 18706 18707 18708 18709 18710 18711 18712 18713 18714 18715 |
Expr *pFExpr; /* Expression encoding the function */
FuncDef *pFunc; /* The aggregate function implementation */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
int iDistAddr; /* Address of OP_OpenEphemeral */
int iOBTab; /* Ephemeral table to implement ORDER BY */
u8 bOBPayload; /* iOBTab has payload columns separate from key */
u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
#ifdef SQLITE_DEBUG
Select *pSelect; /* SELECT statement that this AggInfo supports */
#endif
};
| > | 18774 18775 18776 18777 18778 18779 18780 18781 18782 18783 18784 18785 18786 18787 18788 |
Expr *pFExpr; /* Expression encoding the function */
FuncDef *pFunc; /* The aggregate function implementation */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
int iDistAddr; /* Address of OP_OpenEphemeral */
int iOBTab; /* Ephemeral table to implement ORDER BY */
u8 bOBPayload; /* iOBTab has payload columns separate from key */
u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */
u8 bUseSubtype; /* Transfer subtype info through sorter */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
#ifdef SQLITE_DEBUG
Select *pSelect; /* SELECT statement that this AggInfo supports */
#endif
};
|
| ︙ | ︙ | |||
19235 19236 19237 19238 19239 19240 19241 19242 19243 19244 19245 19246 19247 19248 |
Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */
int iBaseReg; /* For TK_REGISTER when parsing RETURNING */
} uNC;
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
int nNcErr; /* Number of errors encountered while resolving names */
int ncFlags; /* Zero or more NC_* flags defined below */
Select *pWinSelect; /* SELECT statement for any window functions */
};
/*
** Allowed values for the NameContext, ncFlags field.
**
** Value constraints (all checked via assert()):
| > | 19308 19309 19310 19311 19312 19313 19314 19315 19316 19317 19318 19319 19320 19321 19322 |
Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */
int iBaseReg; /* For TK_REGISTER when parsing RETURNING */
} uNC;
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
int nNcErr; /* Number of errors encountered while resolving names */
int ncFlags; /* Zero or more NC_* flags defined below */
u32 nNestedSelect; /* Number of nested selects using this NC */
Select *pWinSelect; /* SELECT statement for any window functions */
};
/*
** Allowed values for the NameContext, ncFlags field.
**
** Value constraints (all checked via assert()):
|
| ︙ | ︙ | |||
19951 19952 19953 19954 19955 19956 19957 19958 19959 19960 19961 19962 19963 19964 |
** when the reference count reaches zero.
**
** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than
** sqlite3_free()
**
** 3. Make a (read-only) copy of a read-only RCStr string using
** sqlite3RCStrRef().
*/
struct RCStr {
u64 nRCRef; /* Number of references */
/* Total structure size should be a multiple of 8 bytes for alignment */
};
/*
| > > > | 20025 20026 20027 20028 20029 20030 20031 20032 20033 20034 20035 20036 20037 20038 20039 20040 20041 |
** when the reference count reaches zero.
**
** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than
** sqlite3_free()
**
** 3. Make a (read-only) copy of a read-only RCStr string using
** sqlite3RCStrRef().
**
** "String" is in the name, but an RCStr object can also be used to hold
** binary data.
*/
struct RCStr {
u64 nRCRef; /* Number of references */
/* Total structure size should be a multiple of 8 bytes for alignment */
};
/*
|
| ︙ | ︙ | |||
20009 20010 20011 20012 20013 20014 20015 20016 20017 20018 20019 20020 20021 20022 | u8 bCoreMutex; /* True to enable core mutexing */ u8 bFullMutex; /* True to enable full mutexing */ u8 bOpenUri; /* True to interpret filenames as URIs */ u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ u8 bUseLongDouble; /* Make use of long double */ int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ int nStmtSpill; /* Stmt-journal spill-to-disk threshold */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ | > > > | 20086 20087 20088 20089 20090 20091 20092 20093 20094 20095 20096 20097 20098 20099 20100 20101 20102 | u8 bCoreMutex; /* True to enable core mutexing */ u8 bFullMutex; /* True to enable full mutexing */ u8 bOpenUri; /* True to interpret filenames as URIs */ u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ u8 bUseLongDouble; /* Make use of long double */ #ifdef SQLITE_DEBUG u8 bJsonSelfcheck; /* Double-check JSON parsing */ #endif int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ int nStmtSpill; /* Stmt-journal spill-to-disk threshold */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ |
| ︙ | ︙ | |||
20635 20636 20637 20638 20639 20640 20641 20642 20643 20644 20645 20646 20647 20648 20649 20650 20651 20652 20653 20654 20655 20656 20657 | SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*); SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*); SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int); SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32); SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); #ifndef SQLITE_OMIT_VIRTUALTABLE | > > | 20715 20716 20717 20718 20719 20720 20721 20722 20723 20724 20725 20726 20727 20728 20729 20730 20731 20732 20733 20734 20735 20736 20737 20738 20739 | SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*); SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*); SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*); SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int); SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*); SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32); SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); #ifndef SQLITE_OMIT_VIRTUALTABLE |
| ︙ | ︙ | |||
20734 20735 20736 20737 20738 20739 20740 20741 20742 20743 20744 20745 20746 20747 | #if SQLITE_MAX_ATTACHED>30 SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask); #endif SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int); SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int); SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*); SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse); SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse); #else # define sqlite3AutoincrementBegin(X) # define sqlite3AutoincrementEnd(X) | > | 20816 20817 20818 20819 20820 20821 20822 20823 20824 20825 20826 20827 20828 20829 20830 | #if SQLITE_MAX_ATTACHED>30 SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask); #endif SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int); SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int); SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*); SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*); SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse); SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse); #else # define sqlite3AutoincrementBegin(X) # define sqlite3AutoincrementEnd(X) |
| ︙ | ︙ | |||
20770 20771 20772 20773 20774 20775 20776 20777 20778 20779 20780 20781 20782 20783 |
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
#endif
SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*);
| > | 20853 20854 20855 20856 20857 20858 20859 20860 20861 20862 20863 20864 20865 20866 20867 |
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
#endif
SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*);
|
| ︙ | ︙ | |||
20996 20997 20998 20999 21000 21001 21002 21003 21004 21005 21006 21007 21008 21009 | SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); SQLITE_PRIVATE int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); #endif SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); SQLITE_PRIVATE LogEst sqlite3LogEst(u64); SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst); SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double); SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst); SQLITE_PRIVATE VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int); SQLITE_PRIVATE const char *sqlite3VListNumToName(VList*,int); SQLITE_PRIVATE int sqlite3VListNameToNum(VList*,const char*,int); | > | 21080 21081 21082 21083 21084 21085 21086 21087 21088 21089 21090 21091 21092 21093 21094 | SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); SQLITE_PRIVATE int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); #endif SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); SQLITE_PRIVATE LogEst sqlite3LogEst(u64); SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst); SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double); SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst); SQLITE_PRIVATE VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int); SQLITE_PRIVATE const char *sqlite3VListNumToName(VList*,int); SQLITE_PRIVATE int sqlite3VListNameToNum(VList*,const char*,int); |
| ︙ | ︙ | |||
21342 21343 21344 21345 21346 21347 21348 21349 21350 21351 21352 21353 21354 21355 | SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); #endif #ifndef SQLITE_OMIT_CTE SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); #else # define sqlite3CteNew(P,T,E,S) ((void*)0) # define sqlite3CteDelete(D,C) # define sqlite3CteWithAdd(P,W,C) ((void*)0) # define sqlite3WithDelete(x,y) # define sqlite3WithPush(x,y,z) ((void*)0) | > | 21427 21428 21429 21430 21431 21432 21433 21434 21435 21436 21437 21438 21439 21440 21441 | SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); #endif #ifndef SQLITE_OMIT_CTE SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3*,void*); SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); #else # define sqlite3CteNew(P,T,E,S) ((void*)0) # define sqlite3CteDelete(D,C) # define sqlite3CteWithAdd(P,W,C) ((void*)0) # define sqlite3WithDelete(x,y) # define sqlite3WithPush(x,y,z) ((void*)0) |
| ︙ | ︙ | |||
22719 22720 22721 22722 22723 22724 22725 22726 22727 22728 22729 22730 22731 22732 |
1, /* bCoreMutex */
SQLITE_THREADSAFE==1, /* bFullMutex */
SQLITE_USE_URI, /* bOpenUri */
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0, /* bSmallMalloc */
1, /* bExtraSchemaChecks */
sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
SQLITE_STMTJRNL_SPILL, /* nStmtSpill */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
| > > > | 22805 22806 22807 22808 22809 22810 22811 22812 22813 22814 22815 22816 22817 22818 22819 22820 22821 |
1, /* bCoreMutex */
SQLITE_THREADSAFE==1, /* bFullMutex */
SQLITE_USE_URI, /* bOpenUri */
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0, /* bSmallMalloc */
1, /* bExtraSchemaChecks */
sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
#ifdef SQLITE_DEBUG
0, /* bJsonSelfcheck */
#endif
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
SQLITE_STMTJRNL_SPILL, /* nStmtSpill */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
|
| ︙ | ︙ | |||
23971 23972 23973 23974 23975 23976 23977 |
case SQLITE_DBSTATUS_CACHE_SPILL:
op = SQLITE_DBSTATUS_CACHE_WRITE+1;
/* no break */ deliberate_fall_through
case SQLITE_DBSTATUS_CACHE_HIT:
case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i;
| | | | 24060 24061 24062 24063 24064 24065 24066 24067 24068 24069 24070 24071 24072 24073 24074 24075 24076 24077 24078 24079 24080 24081 24082 24083 24084 24085 24086 24087 |
case SQLITE_DBSTATUS_CACHE_SPILL:
op = SQLITE_DBSTATUS_CACHE_WRITE+1;
/* no break */ deliberate_fall_through
case SQLITE_DBSTATUS_CACHE_HIT:
case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i;
u64 nRet = 0;
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt ){
Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
}
}
*pHighwater = 0; /* IMP: R-42420-56072 */
/* IMP: R-54100-20147 */
/* IMP: R-29431-39229 */
*pCurrent = (int)nRet & 0x7fffffff;
break;
}
/* Set *pCurrent to non-zero if there are unresolved deferred foreign
** key constraints. Set *pCurrent to zero if all foreign key constraints
** have been satisfied. The *pHighwater is always set to zero.
*/
|
| ︙ | ︙ | |||
25053 25054 25055 25056 25057 25058 25059 25060 25061 25062 25063 25064 25065 25066 |
for(i=1; i<argc; i++){
z = sqlite3_value_text(argv[i]);
n = sqlite3_value_bytes(argv[i]);
if( z==0 || parseModifier(context, (char*)z, n, p, i) ) return 1;
}
computeJD(p);
if( p->isError || !validJulianDay(p->iJD) ) return 1;
return 0;
}
/*
** The following routines implement the various date and time functions
** of SQLite.
| > > > > > > | 25142 25143 25144 25145 25146 25147 25148 25149 25150 25151 25152 25153 25154 25155 25156 25157 25158 25159 25160 25161 |
for(i=1; i<argc; i++){
z = sqlite3_value_text(argv[i]);
n = sqlite3_value_bytes(argv[i]);
if( z==0 || parseModifier(context, (char*)z, n, p, i) ) return 1;
}
computeJD(p);
if( p->isError || !validJulianDay(p->iJD) ) return 1;
if( argc==1 && p->validYMD && p->D>28 ){
/* Make sure a YYYY-MM-DD is normalized.
** Example: 2023-02-31 -> 2023-03-03 */
assert( p->validJD );
p->validYMD = 0;
}
return 0;
}
/*
** The following routines implement the various date and time functions
** of SQLite.
|
| ︙ | ︙ | |||
32081 32082 32083 32084 32085 32086 32087 | va_start(ap,zFormat); sqlite3_str_vappendf(p, zFormat, ap); va_end(ap); } /***************************************************************************** | | | 32176 32177 32178 32179 32180 32181 32182 32183 32184 32185 32186 32187 32188 32189 32190 | va_start(ap,zFormat); sqlite3_str_vappendf(p, zFormat, ap); va_end(ap); } /***************************************************************************** ** Reference counted string/blob storage *****************************************************************************/ /* ** Increase the reference count of the string by one. ** ** The input parameter is returned. */ |
| ︙ | ︙ | |||
32933 32934 32935 32936 32937 32938 32939 |
case TK_BETWEEN: {
const Expr *pX, *pY, *pZ;
pX = pExpr->pLeft;
assert( ExprUseXList(pExpr) );
assert( pExpr->x.pList->nExpr==2 );
pY = pExpr->x.pList->a[0].pExpr;
pZ = pExpr->x.pList->a[1].pExpr;
| | | 33028 33029 33030 33031 33032 33033 33034 33035 33036 33037 33038 33039 33040 33041 33042 |
case TK_BETWEEN: {
const Expr *pX, *pY, *pZ;
pX = pExpr->pLeft;
assert( ExprUseXList(pExpr) );
assert( pExpr->x.pList->nExpr==2 );
pY = pExpr->x.pList->a[0].pExpr;
pZ = pExpr->x.pList->a[1].pExpr;
sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs);
sqlite3TreeViewExpr(pView, pX, 1);
sqlite3TreeViewExpr(pView, pY, 1);
sqlite3TreeViewExpr(pView, pZ, 0);
break;
}
case TK_TRIGGER: {
/* If the opcode is TK_TRIGGER, then the expression is a reference
|
| ︙ | ︙ | |||
34068 34069 34070 34071 34072 34073 34074 |
if( c<0x80
|| (c&0xFFFFF800)==0xD800
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; }
}
return c;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | 34163 34164 34165 34166 34167 34168 34169 34170 34171 34172 34173 34174 34175 34176 34177 34178 34179 34180 34181 34182 34183 34184 34185 34186 34187 34188 34189 34190 34191 34192 34193 34194 34195 34196 34197 34198 34199 34200 34201 34202 34203 34204 34205 34206 34207 34208 |
if( c<0x80
|| (c&0xFFFFF800)==0xD800
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; }
}
return c;
}
/*
** Read a single UTF8 character out of buffer z[], but reading no
** more than n characters from the buffer. z[] is not zero-terminated.
**
** Return the number of bytes used to construct the character.
**
** Invalid UTF8 might generate a strange result. No effort is made
** to detect invalid UTF8.
**
** At most 4 bytes will be read out of z[]. The return value will always
** be between 1 and 4.
*/
SQLITE_PRIVATE int sqlite3Utf8ReadLimited(
const u8 *z,
int n,
u32 *piOut
){
u32 c;
int i = 1;
assert( n>0 );
c = z[0];
if( c>=0xc0 ){
c = sqlite3Utf8Trans1[c-0xc0];
if( n>4 ) n = 4;
while( i<n && (z[i] & 0xc0)==0x80 ){
c = (c<<6) + (0x3f & z[i]);
i++;
}
}
*piOut = c;
return i;
}
/*
** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
*/
/* #define TRANSLATE_TRACE 1 */
|
| ︙ | ︙ | |||
34583 34584 34585 34586 34587 34588 34589 |
/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
if( rc==SQLITE_IOERR_NOMEM ) return;
| | | 34709 34710 34711 34712 34713 34714 34715 34716 34717 34718 34719 34720 34721 34722 34723 |
/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
if( rc==SQLITE_IOERR_NOMEM ) return;
#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
if( rc==SQLITE_IOERR_IN_PAGE ){
int ii;
int iErr;
sqlite3BtreeEnterAll(db);
for(ii=0; ii<db->nDb; ii++){
if( db->aDb[ii].pBt ){
iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt));
|
| ︙ | ︙ | |||
36837 36838 36839 36840 36841 36842 36843 |
/* 174 */ "VCheck" OpHelp(""),
/* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"),
/* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
/* 177 */ "VRename" OpHelp(""),
/* 178 */ "Pagecount" OpHelp(""),
/* 179 */ "MaxPgcnt" OpHelp(""),
/* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"),
| > > | | | | | | | | 36963 36964 36965 36966 36967 36968 36969 36970 36971 36972 36973 36974 36975 36976 36977 36978 36979 36980 36981 36982 36983 36984 36985 |
/* 174 */ "VCheck" OpHelp(""),
/* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"),
/* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
/* 177 */ "VRename" OpHelp(""),
/* 178 */ "Pagecount" OpHelp(""),
/* 179 */ "MaxPgcnt" OpHelp(""),
/* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"),
/* 181 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"),
/* 182 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"),
/* 183 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"),
/* 184 */ "Trace" OpHelp(""),
/* 185 */ "CursorHint" OpHelp(""),
/* 186 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"),
/* 187 */ "Noop" OpHelp(""),
/* 188 */ "Explain" OpHelp(""),
/* 189 */ "Abortable" OpHelp(""),
};
return azName[i];
}
#endif
/************** End of opcodes.c *********************************************/
/************** Begin file os_kv.c *******************************************/
|
| ︙ | ︙ | |||
41889 41890 41891 41892 41893 41894 41895 41896 41897 41898 41899 41900 41901 41902 41903 |
case SQLITE_FCNTL_HAS_MOVED: {
*(int*)pArg = fileHasMoved(pFile);
return SQLITE_OK;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
int iOld = pFile->iBusyTimeout;
pFile->iBusyTimeout = *(int*)pArg;
*(int*)pArg = iOld;
return SQLITE_OK;
}
#endif
#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
| > > > > > > | 42017 42018 42019 42020 42021 42022 42023 42024 42025 42026 42027 42028 42029 42030 42031 42032 42033 42034 42035 42036 42037 |
case SQLITE_FCNTL_HAS_MOVED: {
*(int*)pArg = fileHasMoved(pFile);
return SQLITE_OK;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
int iOld = pFile->iBusyTimeout;
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
pFile->iBusyTimeout = *(int*)pArg;
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
pFile->iBusyTimeout = !!(*(int*)pArg);
#else
# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
#endif
*(int*)pArg = iOld;
return SQLITE_OK;
}
#endif
#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
|
| ︙ | ︙ | |||
42142 42143 42144 42145 42146 42147 42148 42149 42150 42151 42152 42153 42154 42155 42156 42157 42158 42159 42160 42161 42162 42163 |
**
** hShm
** zFilename
**
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
*/
struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
sqlite3_mutex *pShmMutex; /* Mutex to access this object */
char *zFilename; /* Name of the mmapped file */
int hShm; /* Open file descriptor */
int szRegion; /* Size of shared-memory regions */
u16 nRegion; /* Size of array apRegion */
u8 isReadonly; /* True if read-only */
u8 isUnlocked; /* True if no DMS lock held */
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG
| > > > > > > > > > > > > > > > > > > > > > > < < | 42276 42277 42278 42279 42280 42281 42282 42283 42284 42285 42286 42287 42288 42289 42290 42291 42292 42293 42294 42295 42296 42297 42298 42299 42300 42301 42302 42303 42304 42305 42306 42307 42308 42309 42310 42311 42312 42313 42314 42315 42316 42317 42318 42319 42320 42321 42322 42323 42324 42325 42326 |
**
** hShm
** zFilename
**
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
**
** aLock[SQLITE_SHM_NLOCK]:
** This array records the various locks held by clients on each of the
** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no
** locks are held by the process on this slot. If it is set to -1, then
** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[]
** value is set to a positive value, then it is the number of shared
** locks currently held on the slot.
**
** aMutex[SQLITE_SHM_NLOCK]:
** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
** pShmMutex is used to protect the aLock[] array and the right to
** call fcntl() on unixShmNode.hShm to obtain or release locks.
**
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array
** of mutexes - one for each locking slot. To read or write locking
** slot aLock[iSlot], the caller must hold the corresponding mutex
** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a
** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held.
*/
struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
sqlite3_mutex *pShmMutex; /* Mutex to access this object */
char *zFilename; /* Name of the mmapped file */
int hShm; /* Open file descriptor */
int szRegion; /* Size of shared-memory regions */
u16 nRegion; /* Size of array apRegion */
u8 isReadonly; /* True if read-only */
u8 isUnlocked; /* True if no DMS lock held */
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
#endif
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG
u8 nextShmId; /* Next available unixShm.id value */
#endif
};
/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.
|
| ︙ | ︙ | |||
42241 42242 42243 42244 42245 42246 42247 |
int ofst, /* First byte of the locking range */
int n /* Number of bytes to lock */
){
unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */
struct flock f; /* The posix advisory locking structure */
int rc = SQLITE_OK; /* Result code form fcntl() */
| < > > > > > > > > > | > > > > > > > > | > > > | | < < | < < | < < | < < | | | < < < | 42395 42396 42397 42398 42399 42400 42401 42402 42403 42404 42405 42406 42407 42408 42409 42410 42411 42412 42413 42414 42415 42416 42417 42418 42419 42420 42421 42422 42423 42424 42425 42426 42427 42428 42429 42430 42431 42432 42433 42434 42435 42436 42437 42438 42439 42440 42441 42442 42443 42444 42445 42446 42447 42448 42449 42450 42451 42452 42453 42454 42455 42456 42457 42458 42459 42460 42461 42462 42463 42464 42465 42466 42467 42468 42469 42470 42471 42472 42473 42474 42475 42476 |
int ofst, /* First byte of the locking range */
int n /* Number of bytes to lock */
){
unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */
struct flock f; /* The posix advisory locking structure */
int rc = SQLITE_OK; /* Result code form fcntl() */
pShmNode = pFile->pInode->pShmNode;
/* Assert that the parameters are within expected range and that the
** correct mutex or mutexes are held. */
assert( pShmNode->nRef>=0 );
assert( (ofst==UNIX_SHM_DMS && n==1)
|| (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK))
);
if( ofst==UNIX_SHM_DMS ){
assert( pShmNode->nRef>0 || unixMutexHeld() );
assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
}else{
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int ii;
for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){
assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) );
}
#else
assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
assert( pShmNode->nRef>0 );
#endif
}
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) );
if( pShmNode->hShm>=0 ){
int res;
/* Initialize the locking parameters */
f.l_type = lockType;
f.l_whence = SEEK_SET;
f.l_start = ofst;
f.l_len = n;
res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
if( res==-1 ){
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1
rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
#else
rc = SQLITE_BUSY;
#endif
}
}
/* Do debug tracing */
#ifdef SQLITE_DEBUG
OSTRACE(("SHM-LOCK "));
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1));
}
}else{
if( lockType==F_UNLCK ){
OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1));
}
}
#endif
return rc;
}
/*
|
| ︙ | ︙ | |||
42338 42339 42340 42341 42342 42343 42344 42345 42346 42347 42348 42349 42350 42351 |
unixShmNode *p = pFd->pInode->pShmNode;
assert( unixMutexHeld() );
if( p && ALWAYS(p->nRef==0) ){
int nShmPerMap = unixShmRegionPerMap();
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->pShmMutex);
for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
}else{
sqlite3_free(p->apRegion[i]);
}
}
| > > > > > | 42500 42501 42502 42503 42504 42505 42506 42507 42508 42509 42510 42511 42512 42513 42514 42515 42516 42517 42518 |
unixShmNode *p = pFd->pInode->pShmNode;
assert( unixMutexHeld() );
if( p && ALWAYS(p->nRef==0) ){
int nShmPerMap = unixShmRegionPerMap();
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->pShmMutex);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
for(i=0; i<SQLITE_SHM_NLOCK; i++){
sqlite3_mutex_free(p->aMutex[i]);
}
#endif
for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
}else{
sqlite3_free(p->apRegion[i]);
}
}
|
| ︙ | ︙ | |||
42397 42398 42399 42400 42401 42402 42403 42404 42405 42406 42407 42408 42409 42410 42411 |
if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) {
rc = SQLITE_IOERR_LOCK;
}else if( lock.l_type==F_UNLCK ){
if( pShmNode->isReadonly ){
pShmNode->isUnlocked = 1;
rc = SQLITE_READONLY_CANTINIT;
}else{
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
/* The first connection to attach must truncate the -shm file. We
** truncate to 3 bytes (an arbitrary small number, less than the
** -shm header size) rather than 0 as a system debugging aid, to
** help detect if a -shm file truncation is legitimate or is the work
** or a rogue process. */
if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){
rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);
| > > > > > > > > > > > > > | 42564 42565 42566 42567 42568 42569 42570 42571 42572 42573 42574 42575 42576 42577 42578 42579 42580 42581 42582 42583 42584 42585 42586 42587 42588 42589 42590 42591 |
if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) {
rc = SQLITE_IOERR_LOCK;
}else if( lock.l_type==F_UNLCK ){
if( pShmNode->isReadonly ){
pShmNode->isUnlocked = 1;
rc = SQLITE_READONLY_CANTINIT;
}else{
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/* Do not use a blocking lock here. If the lock cannot be obtained
** immediately, it means some other connection is truncating the
** *-shm file. And after it has done so, it will not release its
** lock, but only downgrade it to a shared lock. So no point in
** blocking here. The call below to obtain the shared DMS lock may
** use a blocking lock. */
int iSaveTimeout = pDbFd->iBusyTimeout;
pDbFd->iBusyTimeout = 0;
#endif
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
pDbFd->iBusyTimeout = iSaveTimeout;
#endif
/* The first connection to attach must truncate the -shm file. We
** truncate to 3 bytes (an arbitrary small number, less than the
** -shm header size) rather than 0 as a system debugging aid, to
** help detect if a -shm file truncation is legitimate or is the work
** or a rogue process. */
if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){
rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);
|
| ︙ | ︙ | |||
42518 42519 42520 42521 42522 42523 42524 42525 42526 42527 42528 42529 42530 42531 |
pShmNode->pInode = pDbFd->pInode;
if( sqlite3GlobalConfig.bCoreMutex ){
pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pShmNode->pShmMutex==0 ){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
}
if( pInode->bProcessLock==0 ){
if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW,
(sStat.st_mode&0777));
}
| > > > > > > > > > > > > | 42698 42699 42700 42701 42702 42703 42704 42705 42706 42707 42708 42709 42710 42711 42712 42713 42714 42715 42716 42717 42718 42719 42720 42721 42722 42723 |
pShmNode->pInode = pDbFd->pInode;
if( sqlite3GlobalConfig.bCoreMutex ){
pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pShmNode->pShmMutex==0 ){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
{
int ii;
for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pShmNode->aMutex[ii]==0 ){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
}
}
#endif
}
if( pInode->bProcessLock==0 ){
if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW,
(sStat.st_mode&0777));
}
|
| ︙ | ︙ | |||
42739 42740 42741 42742 42743 42744 42745 42746 42747 |
** held by each client. Return true if it does, or false otherwise. This
** is to be used in an assert(). e.g.
**
** assert( assertLockingArrayOk(pShmNode) );
*/
#ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){
unixShm *pX;
int aLock[SQLITE_SHM_NLOCK];
| > > > < > | | | 42931 42932 42933 42934 42935 42936 42937 42938 42939 42940 42941 42942 42943 42944 42945 42946 42947 42948 42949 42950 42951 42952 42953 42954 42955 42956 42957 42958 42959 42960 42961 42962 42963 42964 42965 42966 42967 42968 42969 42970 42971 42972 42973 42974 42975 42976 42977 42978 42979 42980 42981 42982 42983 42984 42985 42986 42987 42988 42989 |
** held by each client. Return true if it does, or false otherwise. This
** is to be used in an assert(). e.g.
**
** assert( assertLockingArrayOk(pShmNode) );
*/
#ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
return 1;
#else
unixShm *pX;
int aLock[SQLITE_SHM_NLOCK];
memset(aLock, 0, sizeof(aLock));
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
int i;
for(i=0; i<SQLITE_SHM_NLOCK; i++){
if( pX->exclMask & (1<<i) ){
assert( aLock[i]==0 );
aLock[i] = -1;
}else if( pX->sharedMask & (1<<i) ){
assert( aLock[i]>=0 );
aLock[i]++;
}
}
}
assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
#endif
}
#endif
/*
** Change the lock state for a shared-memory segment.
**
** Note that the relationship between SHARED and EXCLUSIVE locks is a little
** different here than in posix. In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back. But one may
** not go from shared to exclusive or from exclusive to shared.
*/
static int unixShmLock(
sqlite3_file *fd, /* Database file holding the shared memory */
int ofst, /* First lock to acquire or release */
int n, /* Number of locks to acquire or release */
int flags /* What to do with the lock */
){
unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */
unixShm *p; /* The shared memory being locked */
unixShmNode *pShmNode; /* The underlying file iNode */
int rc = SQLITE_OK; /* Result code */
u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */
int *aLock;
p = pDbFd->pShm;
if( p==0 ) return SQLITE_IOERR_SHMLOCK;
pShmNode = p->pShmNode;
if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;
aLock = pShmNode->aLock;
|
| ︙ | ︙ | |||
42815 42816 42817 42818 42819 42820 42821 | ** In other words, if this is a blocking lock, none of the locks that ** occur later in the above list than the lock being obtained may be ** held. ** ** It is not permitted to block on the RECOVER lock. */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT | > > | | | | | | > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | < < > | > > > > > > > > > | | | > > | | | | | | < | < < < < < < | < | | | > | < < | > | | | | | | | | | > > > | > > > | | | < | < | | | | | | | | | | < | | | | | | | | > > > > > > > > > | > > > | 43010 43011 43012 43013 43014 43015 43016 43017 43018 43019 43020 43021 43022 43023 43024 43025 43026 43027 43028 43029 43030 43031 43032 43033 43034 43035 43036 43037 43038 43039 43040 43041 43042 43043 43044 43045 43046 43047 43048 43049 43050 43051 43052 43053 43054 43055 43056 43057 43058 43059 43060 43061 43062 43063 43064 43065 43066 43067 43068 43069 43070 43071 43072 43073 43074 43075 43076 43077 43078 43079 43080 43081 43082 43083 43084 43085 43086 43087 43088 43089 43090 43091 43092 43093 43094 43095 43096 43097 43098 43099 43100 43101 43102 43103 43104 43105 43106 43107 43108 43109 43110 43111 43112 43113 43114 43115 43116 43117 43118 43119 43120 43121 43122 43123 43124 43125 43126 43127 43128 43129 43130 43131 43132 43133 43134 43135 43136 43137 43138 43139 43140 43141 43142 43143 43144 43145 43146 43147 43148 43149 43150 43151 43152 43153 43154 43155 43156 43157 43158 43159 43160 43161 43162 43163 43164 43165 43166 43167 43168 |
** In other words, if this is a blocking lock, none of the locks that
** occur later in the above list than the lock being obtained may be
** held.
**
** It is not permitted to block on the RECOVER lock.
*/
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
{
u16 lockMask = (p->exclMask|p->sharedMask);
assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
(ofst!=2) /* not RECOVER */
&& (ofst!=1 || lockMask==0 || lockMask==2)
&& (ofst!=0 || lockMask<3)
&& (ofst<3 || lockMask<(1<<ofst))
));
}
#endif
/* Check if there is any work to do. There are three cases:
**
** a) An unlock operation where there are locks to unlock,
** b) An shared lock where the requested lock is not already held
** c) An exclusive lock where the requested lock is not already held
**
** The SQLite core never requests an exclusive lock that it already holds.
** This is assert()ed below.
*/
assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
|| 0==(p->exclMask & mask)
);
if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
|| (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
/* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
** other thread is holding this mutex, then it is either holding or about
** to hold a lock exclusive to the one being requested, and we may
** therefore return SQLITE_BUSY to the caller.
**
** Doing this prevents some deadlock scenarios. For example, thread 1 may
** be a checkpointer blocked waiting on the WRITER lock. And thread 2
** may be a normal SQL client upgrading to a write transaction. In this
** case thread 2 does a non-blocking request for the WRITER lock. But -
** if it were to use sqlite3_mutex_enter() then it would effectively
** become a (doomed) blocking request, as thread 2 would block until thread
** 1 obtained WRITER and released the mutex. Since thread 2 already holds
** a lock on a read-locking slot at this point, this breaks the
** anti-deadlock rules (see above). */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int iMutex;
for(iMutex=ofst; iMutex<ofst+n; iMutex++){
if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes;
}else{
sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
}
}
#else
sqlite3_mutex_enter(pShmNode->pShmMutex);
#endif
if( ALWAYS(rc==SQLITE_OK) ){
if( flags & SQLITE_SHM_UNLOCK ){
/* Case (a) - unlock. */
int bUnlock = 1;
assert( (p->exclMask & p->sharedMask)==0 );
assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
/* If this is a SHARED lock being unlocked, it is possible that other
** clients within this process are holding the same SHARED lock. In
** this case, set bUnlock to 0 so that the posix lock is not removed
** from the file-descriptor below. */
if( flags & SQLITE_SHM_SHARED ){
assert( n==1 );
assert( aLock[ofst]>=1 );
if( aLock[ofst]>1 ){
bUnlock = 0;
aLock[ofst]--;
p->sharedMask &= ~mask;
}
}
if( bUnlock ){
rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
if( rc==SQLITE_OK ){
memset(&aLock[ofst], 0, sizeof(int)*n);
p->sharedMask &= ~mask;
p->exclMask &= ~mask;
}
}
}else if( flags & SQLITE_SHM_SHARED ){
/* Case (b) - a shared lock. */
if( aLock[ofst]<0 ){
/* An exclusive lock is held by some other connection. BUSY. */
rc = SQLITE_BUSY;
}else if( aLock[ofst]==0 ){
rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
}
/* Get the local shared locks */
if( rc==SQLITE_OK ){
p->sharedMask |= mask;
aLock[ofst]++;
}
}else{
/* Case (c) - an exclusive lock. */
int ii;
assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
assert( (p->sharedMask & mask)==0 );
assert( (p->exclMask & mask)==0 );
/* Make sure no sibling connections hold locks that will block this
** lock. If any do, return SQLITE_BUSY right away. */
for(ii=ofst; ii<ofst+n; ii++){
if( aLock[ii] ){
rc = SQLITE_BUSY;
break;
}
}
/* Get the exclusive locks at the system level. Then if successful
** also update the in-memory values. */
if( rc==SQLITE_OK ){
rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
if( rc==SQLITE_OK ){
p->exclMask |= mask;
for(ii=ofst; ii<ofst+n; ii++){
aLock[ii] = -1;
}
}
}
}
assert( assertLockingArrayOk(pShmNode) );
}
/* Drop the mutexes acquired above. */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
leave_shmnode_mutexes:
for(iMutex--; iMutex>=ofst; iMutex--){
sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
}
#else
sqlite3_mutex_leave(pShmNode->pShmMutex);
#endif
}
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc;
}
/*
** Implement a memory barrier or memory fence on shared memory.
|
| ︙ | ︙ | |||
43146 43147 43148 43149 43150 43151 43152 43153 43154 43155 43156 |
#if SQLITE_MAX_MMAP_SIZE>0
unixFile *pFd = (unixFile *)fd; /* The underlying database file */
#endif
*pp = 0;
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
if( pFd->pMapRegion==0 ){
int rc = unixMapfile(pFd, -1);
if( rc!=SQLITE_OK ) return rc;
}
| > > > > > | | 43404 43405 43406 43407 43408 43409 43410 43411 43412 43413 43414 43415 43416 43417 43418 43419 43420 43421 43422 43423 43424 43425 43426 43427 |
#if SQLITE_MAX_MMAP_SIZE>0
unixFile *pFd = (unixFile *)fd; /* The underlying database file */
#endif
*pp = 0;
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
/* Ensure that there is always at least a 256 byte buffer of addressable
** memory following the returned page. If the database is corrupt,
** SQLite may overread the page slightly (in practice only a few bytes,
** but 256 is safe, round, number). */
const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = unixMapfile(pFd, -1);
if( rc!=SQLITE_OK ) return rc;
}
if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
}
#endif
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
50503 50504 50505 50506 50507 50508 50509 50510 50511 50512 50513 50514 50515 50516 50517 |
*pp = 0;
OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
osGetCurrentProcessId(), fd, iOff, nAmt, pp));
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
if( pFd->pMapRegion==0 ){
int rc = winMapfile(pFd, -1);
if( rc!=SQLITE_OK ){
OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
return rc;
}
}
| > > > > > | | 50766 50767 50768 50769 50770 50771 50772 50773 50774 50775 50776 50777 50778 50779 50780 50781 50782 50783 50784 50785 50786 50787 50788 50789 50790 50791 50792 50793 |
*pp = 0;
OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
osGetCurrentProcessId(), fd, iOff, nAmt, pp));
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
/* Ensure that there is always at least a 256 byte buffer of addressable
** memory following the returned page. If the database is corrupt,
** SQLite may overread the page slightly (in practice only a few bytes,
** but 256 is safe, round, number). */
const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = winMapfile(pFd, -1);
if( rc!=SQLITE_OK ){
OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
return rc;
}
}
if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
assert( pFd->pMapRegion!=0 );
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
}
#endif
|
| ︙ | ︙ | |||
57114 57115 57116 57117 57118 57119 57120 | Pgno lckPgno; /* Page number for the locking page */ i64 pageSize; /* Number of bytes in a page */ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ | | | 57382 57383 57384 57385 57386 57387 57388 57389 57390 57391 57392 57393 57394 57395 57396 | Pgno lckPgno; /* Page number for the locking page */ i64 pageSize; /* Number of bytes in a page */ i64 journalSizeLimit; /* Size limit for persistent journal files */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ u32 aStat[4]; /* Total cache hits, misses, writes, spills */ #ifdef SQLITE_TEST int nRead; /* Database pages read */ #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ |
| ︙ | ︙ | |||
57244 57245 57246 57247 57248 57249 57250 |
*/
SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
if( pPager->fd->pMethods==0 ) return 0;
if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0;
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
| < | | | 57512 57513 57514 57515 57516 57517 57518 57519 57520 57521 57522 57523 57524 57525 57526 57527 |
*/
SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
if( pPager->fd->pMethods==0 ) return 0;
if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0;
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
(void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
return iRead==0;
}
#endif
return 1;
}
#endif
#ifndef SQLITE_OMIT_WAL
|
| ︙ | ︙ | |||
63258 63259 63260 63261 63262 63263 63264 | static int a[11]; a[0] = sqlite3PcacheRefCount(pPager->pPCache); a[1] = sqlite3PcachePagecount(pPager->pPCache); a[2] = sqlite3PcacheGetCachesize(pPager->pPCache); a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; a[4] = pPager->eState; a[5] = pPager->errCode; | | | | | | 63525 63526 63527 63528 63529 63530 63531 63532 63533 63534 63535 63536 63537 63538 63539 63540 63541 63542 63543 63544 63545 63546 63547 63548 63549 63550 63551 63552 63553 63554 63555 63556 63557 63558 63559 |
static int a[11];
a[0] = sqlite3PcacheRefCount(pPager->pPCache);
a[1] = sqlite3PcachePagecount(pPager->pPCache);
a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff;
a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff;
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff;
return a;
}
#endif
/*
** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE,
** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation
** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because
** it was added later.
**
** Before returning, *pnVal is incremented by the
** current cache hit or miss count, according to the value of eStat. If the
** reset parameter is non-zero, the cache hit or miss count is zeroed before
** returning.
*/
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
|| eStat==SQLITE_DBSTATUS_CACHE_WRITE
|| eStat==SQLITE_DBSTATUS_CACHE_WRITE+1
);
|
| ︙ | ︙ | |||
64218 64219 64220 64221 64222 64223 64224 |
*/
SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
assert( pPager->eState>=PAGER_READER );
return sqlite3WalFramesize(pPager->pWal);
}
#endif
| | | 64485 64486 64487 64488 64489 64490 64491 64492 64493 64494 64495 64496 64497 64498 64499 |
*/
SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
assert( pPager->eState>=PAGER_READER );
return sqlite3WalFramesize(pPager->pWal);
}
#endif
#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){
return sqlite3WalSystemErrno(pPager->pWal);
}
#endif
#endif /* SQLITE_OMIT_DISKIO */
|
| ︙ | ︙ | |||
66234 66235 66236 66237 66238 66239 66240 66241 66242 66243 66244 66245 66246 66247 66248 66249 66250 66251 |
p = 0;
}
*pp = p;
return rc;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/*
** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
** they are supported by the VFS, and (b) the database handle is configured
** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
** or 0 otherwise.
*/
static int walEnableBlocking(Wal *pWal){
int res = 0;
if( pWal->db ){
int tmout = pWal->db->busyTimeout;
if( tmout ){
| > > > > > > > > > > > > > < < < < | | 66501 66502 66503 66504 66505 66506 66507 66508 66509 66510 66511 66512 66513 66514 66515 66516 66517 66518 66519 66520 66521 66522 66523 66524 66525 66526 66527 66528 66529 66530 66531 66532 66533 66534 66535 66536 66537 66538 66539 |
p = 0;
}
*pp = p;
return rc;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/*
** Attempt to enable blocking locks that block for nMs ms. Return 1 if
** blocking locks are successfully enabled, or 0 otherwise.
*/
static int walEnableBlockingMs(Wal *pWal, int nMs){
int rc = sqlite3OsFileControl(
pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs
);
return (rc==SQLITE_OK);
}
/*
** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
** they are supported by the VFS, and (b) the database handle is configured
** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
** or 0 otherwise.
*/
static int walEnableBlocking(Wal *pWal){
int res = 0;
if( pWal->db ){
int tmout = pWal->db->busyTimeout;
if( tmout ){
res = walEnableBlockingMs(pWal, tmout);
}
}
return res;
}
/*
** Disable blocking locks.
|
| ︙ | ︙ | |||
66298 66299 66300 66301 66302 66303 66304 |
/*
** Set the database handle used to determine if blocking locks are required.
*/
SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
pWal->db = db;
}
| < < < < < < < < < < | | 66574 66575 66576 66577 66578 66579 66580 66581 66582 66583 66584 66585 66586 66587 66588 66589 66590 66591 |
/*
** Set the database handle used to determine if blocking locks are required.
*/
SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
pWal->db = db;
}
#else
# define walEnableBlocking(x) 0
# define walDisableBlocking(x)
# define walEnableBlockingMs(pWal, ms) 0
# define sqlite3WalDb(pWal, db)
#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
/*
** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
** n. If the attempt fails and parameter xBusy is not NULL, then it is a
|
| ︙ | ︙ | |||
66912 66913 66914 66915 66916 66917 66918 |
if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
walUnlockShared(pWal, WAL_WRITE_LOCK);
rc = SQLITE_READONLY_RECOVERY;
}
}else{
int bWriteLock = pWal->writeLock;
| | > > | > | 67178 67179 67180 67181 67182 67183 67184 67185 67186 67187 67188 67189 67190 67191 67192 67193 67194 67195 67196 67197 67198 67199 67200 67201 67202 67203 |
if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
walUnlockShared(pWal, WAL_WRITE_LOCK);
rc = SQLITE_READONLY_RECOVERY;
}
}else{
int bWriteLock = pWal->writeLock;
if( bWriteLock
|| SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
){
pWal->writeLock = 1;
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
badHdr = walIndexTryHdr(pWal, pChanged);
if( badHdr ){
/* If the wal-index header is still malformed even while holding
** a WRITE lock, it can only mean that the header is corrupted and
** needs to be reconstructed. So run recovery to do exactly that.
** Disable blocking locks first. */
walDisableBlocking(pWal);
rc = walIndexRecover(pWal);
*pChanged = 1;
}
}
if( bWriteLock==0 ){
pWal->writeLock = 0;
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
|
| ︙ | ︙ | |||
67130 67131 67132 67133 67134 67135 67136 67137 67138 67139 67140 67141 67142 67143 |
pWal->bShmUnreliable = 0;
sqlite3WalEndReadTransaction(pWal);
*pChanged = 1;
}
return rc;
}
/*
** Attempt to start a read transaction. This might fail due to a race or
** other transient condition. When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
**
** On success return SQLITE_OK. On a permanent failure (such an
** I/O error or an SQLITE_BUSY because another process is running
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 67399 67400 67401 67402 67403 67404 67405 67406 67407 67408 67409 67410 67411 67412 67413 67414 67415 67416 67417 67418 67419 67420 67421 67422 67423 67424 67425 67426 67427 67428 67429 67430 67431 67432 67433 67434 67435 67436 67437 67438 67439 67440 67441 67442 67443 |
pWal->bShmUnreliable = 0;
sqlite3WalEndReadTransaction(pWal);
*pChanged = 1;
}
return rc;
}
/*
** The final argument passed to walTryBeginRead() is of type (int*). The
** caller should invoke walTryBeginRead as follows:
**
** int cnt = 0;
** do {
** rc = walTryBeginRead(..., &cnt);
** }while( rc==WAL_RETRY );
**
** The final value of "cnt" is of no use to the caller. It is used by
** the implementation of walTryBeginRead() as follows:
**
** + Each time walTryBeginRead() is called, it is incremented. Once
** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead()
** has many times been invoked and failed with WAL_RETRY - walTryBeginRead()
** returns SQLITE_PROTOCOL.
**
** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed
** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS
** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case
** the next invocation of walTryBeginRead() may omit an expected call to
** sqlite3OsSleep(). There has already been a delay when the previous call
** waited on a lock.
*/
#define WAL_RETRY_PROTOCOL_LIMIT 100
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
# define WAL_RETRY_BLOCKED_MASK 0x10000000
#else
# define WAL_RETRY_BLOCKED_MASK 0
#endif
/*
** Attempt to start a read transaction. This might fail due to a race or
** other transient condition. When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
**
** On success return SQLITE_OK. On a permanent failure (such an
** I/O error or an SQLITE_BUSY because another process is running
|
| ︙ | ︙ | |||
67180 67181 67182 67183 67184 67185 67186 | ** This routine uses the nBackfill and aReadMark[] fields of the header ** to select a particular WAL_READ_LOCK() that strives to let the ** checkpoint process do as much work as possible. This routine might ** update values of the aReadMark[] array in the header, but if it does ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ | | > > > | 67480 67481 67482 67483 67484 67485 67486 67487 67488 67489 67490 67491 67492 67493 67494 67495 67496 67497 67498 67499 67500 67501 67502 67503 |
** This routine uses the nBackfill and aReadMark[] fields of the header
** to select a particular WAL_READ_LOCK() that strives to let the
** checkpoint process do as much work as possible. This routine might
** update values of the aReadMark[] array in the header, but if it does
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
u32 mxReadMark; /* Largest aReadMark[] value */
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
u32 mxFrame; /* Wal frame to lock to */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int nBlockTmout = 0;
#endif
assert( pWal->readLock<0 ); /* Not currently locked */
/* useWal may only be set for read/write connections */
assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 );
/* Take steps to avoid spinning forever if there is a protocol error.
|
| ︙ | ︙ | |||
67210 67211 67212 67213 67214 67215 67216 | ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this ** is more of a scheduler yield than an actual delay. But on the 10th ** an subsequent retries, the delays start becoming longer and longer, ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. ** The total delay time before giving up is less than 10 seconds. */ | > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > | 67513 67514 67515 67516 67517 67518 67519 67520 67521 67522 67523 67524 67525 67526 67527 67528 67529 67530 67531 67532 67533 67534 67535 67536 67537 67538 67539 67540 67541 67542 67543 67544 67545 67546 67547 67548 67549 67550 67551 67552 67553 67554 67555 67556 67557 67558 67559 67560 67561 67562 67563 67564 67565 67566 67567 67568 |
** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few
** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this
** is more of a scheduler yield than an actual delay. But on the 10th
** an subsequent retries, the delays start becoming longer and longer,
** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
** The total delay time before giving up is less than 10 seconds.
*/
(*pCnt)++;
if( *pCnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK);
if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor
** to block for locks for approximately nDelay us. This affects three
** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if
** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the
** first attempted read fails, and (c) the shared lock taken on the
** read-mark.
**
** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error,
** then sleep for the minimum of 1us. The previous call already provided
** an extra delay while it was blocking on the lock.
*/
nBlockTmout = (nDelay+998) / 1000;
if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){
if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1;
}
#endif
sqlite3OsSleep(pWal->pVfs, nDelay);
*pCnt &= ~WAL_RETRY_BLOCKED_MASK;
}
if( !useWal ){
assert( rc==SQLITE_OK );
if( pWal->bShmUnreliable==0 ){
rc = walIndexReadHdr(pWal, pChanged);
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
walDisableBlocking(pWal);
if( rc==SQLITE_BUSY_TIMEOUT ){
rc = SQLITE_BUSY;
*pCnt |= WAL_RETRY_BLOCKED_MASK;
}
#endif
if( rc==SQLITE_BUSY ){
/* If there is not a recovery running in another thread or process
** then convert BUSY errors to WAL_RETRY. If recovery is known to
** be running, convert BUSY to BUSY_RECOVERY. There is a race here
** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY
** would be technically correct. But the race is benign since with
** WAL_RETRY this routine will be called again and will probably be
|
| ︙ | ︙ | |||
67339 67340 67341 67342 67343 67344 67345 67346 67347 |
}
}
if( mxI==0 ){
assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
}
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
if( rc ){
| > > > > > > > > > > | | 67669 67670 67671 67672 67673 67674 67675 67676 67677 67678 67679 67680 67681 67682 67683 67684 67685 67686 67687 67688 67689 67690 67691 67692 67693 67694 67695 |
}
}
if( mxI==0 ){
assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
}
(void)walEnableBlockingMs(pWal, nBlockTmout);
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
walDisableBlocking(pWal);
if( rc ){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
if( rc==SQLITE_BUSY_TIMEOUT ){
*pCnt |= WAL_RETRY_BLOCKED_MASK;
}
#else
assert( rc!=SQLITE_BUSY_TIMEOUT );
#endif
assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
}
/* Now that the read-lock has been obtained, check that neither the
** value in the aReadMark[] array or the contents of the wal-index
** header have changed.
**
** It is necessary to check that the wal-index header did not change
** between the time it was read and when the shared-lock was obtained
|
| ︙ | ︙ | |||
67529 67530 67531 67532 67533 67534 67535 |
return rc;
}
ckptLock = 1;
}
#endif
do{
| | | 67869 67870 67871 67872 67873 67874 67875 67876 67877 67878 67879 67880 67881 67882 67883 |
return rc;
}
ckptLock = 1;
}
#endif
do{
rc = walTryBeginRead(pWal, pChanged, 0, &cnt);
}while( rc==WAL_RETRY );
testcase( (rc&0xff)==SQLITE_BUSY );
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
#ifdef SQLITE_ENABLE_SNAPSHOT
|
| ︙ | ︙ | |||
67710 67711 67712 67713 67714 67715 67716 67717 67718 67719 67720 67721 67722 67723 |
while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){
u32 iFrame = iH + sLoc.iZero;
if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){
assert( iFrame>iRead || CORRUPT_DB );
iRead = iFrame;
}
if( (nCollide--)==0 ){
return SQLITE_CORRUPT_BKPT;
}
iKey = walNextHash(iKey);
}
if( iRead ) break;
}
| > | 68050 68051 68052 68053 68054 68055 68056 68057 68058 68059 68060 68061 68062 68063 68064 |
while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){
u32 iFrame = iH + sLoc.iZero;
if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){
assert( iFrame>iRead || CORRUPT_DB );
iRead = iFrame;
}
if( (nCollide--)==0 ){
*piRead = 0;
return SQLITE_CORRUPT_BKPT;
}
iKey = walNextHash(iKey);
}
if( iRead ) break;
}
|
| ︙ | ︙ | |||
68013 68014 68015 68016 68017 68018 68019 |
}
}
walUnlockShared(pWal, WAL_READ_LOCK(0));
pWal->readLock = -1;
cnt = 0;
do{
int notUsed;
| | | 68354 68355 68356 68357 68358 68359 68360 68361 68362 68363 68364 68365 68366 68367 68368 |
}
}
walUnlockShared(pWal, WAL_READ_LOCK(0));
pWal->readLock = -1;
cnt = 0;
do{
int notUsed;
rc = walTryBeginRead(pWal, ¬Used, 1, &cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
}
return rc;
|
| ︙ | ︙ | |||
68434 68435 68436 68437 68438 68439 68440 |
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
| | < | | 68775 68776 68777 68778 68779 68780 68781 68782 68783 68784 68785 68786 68787 68788 68789 68790 68791 |
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
/* Enable blocking locks, if possible. */
sqlite3WalDb(pWal, db);
if( xBusy2 ) (void)walEnableBlocking(pWal);
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
** "checkpoint" lock on the database file.
** EVIDENCE-OF: R-10421-19736 If any other process is running a
** checkpoint operation at the same time, the lock cannot be obtained and
** SQLITE_BUSY is returned.
** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
|
| ︙ | ︙ | |||
68478 68479 68480 68481 68482 68483 68484 68485 68486 |
}
}
/* Read the wal-index header. */
SEH_TRY {
if( rc==SQLITE_OK ){
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
| > > > > > | | 68818 68819 68820 68821 68822 68823 68824 68825 68826 68827 68828 68829 68830 68831 68832 68833 68834 68835 68836 68837 68838 68839 |
}
}
/* Read the wal-index header. */
SEH_TRY {
if( rc==SQLITE_OK ){
/* For a passive checkpoint, do not re-enable blocking locks after
** reading the wal-index header. A passive checkpoint should not block
** or invoke the busy handler. The only lock such a checkpoint may
** attempt to obtain is a lock on a read-slot, and it should give up
** immediately and do a partial checkpoint if it cannot obtain it. */
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
}
}
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
68817 68818 68819 68820 68821 68822 68823 | ** 18 1 File format write version ** 19 1 File format read version ** 20 1 Bytes of unused space at the end of each page ** 21 1 Max embedded payload fraction (must be 64) ** 22 1 Min embedded payload fraction (must be 32) ** 23 1 Min leaf payload fraction (must be 32) ** 24 4 File change counter | | | 69162 69163 69164 69165 69166 69167 69168 69169 69170 69171 69172 69173 69174 69175 69176 | ** 18 1 File format write version ** 19 1 File format read version ** 20 1 Bytes of unused space at the end of each page ** 21 1 Max embedded payload fraction (must be 64) ** 22 1 Min embedded payload fraction (must be 32) ** 23 1 Min leaf payload fraction (must be 32) ** 24 4 File change counter ** 28 4 The size of the database in pages ** 32 4 First freelist page ** 36 4 Number of freelist pages in the file ** 40 60 15 4-byte meta values passed to higher layers ** ** 40 4 Schema cookie ** 44 4 File format of schema layer ** 48 4 Size of page cache |
| ︙ | ︙ | |||
74944 74945 74946 74947 74948 74949 74950 |
){
sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
u8 aSave[4];
u8 *aWrite = &pBuf[-4];
assert( aWrite>=pBufStart ); /* due to (6) */
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
| < | 75289 75290 75291 75292 75293 75294 75295 75296 75297 75298 75299 75300 75301 75302 |
){
sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
u8 aSave[4];
u8 *aWrite = &pBuf[-4];
assert( aWrite>=pBufStart ); /* due to (6) */
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
nextPage = get4byte(aWrite);
memcpy(aWrite, aSave, 4);
}else
#endif
{
DbPage *pDbPage;
|
| ︙ | ︙ | |||
76064 76065 76066 76067 76068 76069 76070 |
if( CURSOR_SKIPNEXT==pCur->eState ){
pCur->eState = CURSOR_VALID;
if( pCur->skipNext<0 ) return SQLITE_OK;
}
}
pPage = pCur->pPage;
| > | > > | 76408 76409 76410 76411 76412 76413 76414 76415 76416 76417 76418 76419 76420 76421 76422 76423 76424 76425 |
if( CURSOR_SKIPNEXT==pCur->eState ){
pCur->eState = CURSOR_VALID;
if( pCur->skipNext<0 ) return SQLITE_OK;
}
}
pPage = pCur->pPage;
if( sqlite3FaultSim(412) ) pPage->isInit = 0;
if( !pPage->isInit ){
return SQLITE_CORRUPT_BKPT;
}
if( !pPage->leaf ){
int idx = pCur->ix;
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
if( rc ) return rc;
rc = moveToRightmost(pCur);
}else{
while( pCur->ix==0 ){
|
| ︙ | ︙ | |||
85368 85369 85370 85371 85372 85373 85374 85375 85376 85377 85378 85379 85380 85381 |
}
break;
}
case P4_VTAB : {
if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
break;
}
}
}
/*
** Free the space allocated for aOp and any p4 values allocated for the
** opcodes contained within. If aOp is not NULL it is assumed to contain
** nOp entries.
| > > > > | 85715 85716 85717 85718 85719 85720 85721 85722 85723 85724 85725 85726 85727 85728 85729 85730 85731 85732 |
}
break;
}
case P4_VTAB : {
if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
break;
}
case P4_TABLEREF: {
if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4);
break;
}
}
}
/*
** Free the space allocated for aOp and any p4 values allocated for the
** opcodes contained within. If aOp is not NULL it is assumed to contain
** nOp entries.
|
| ︙ | ︙ | |||
85495 85496 85497 85498 85499 85500 85501 |
static void SQLITE_NOINLINE vdbeChangeP4Full(
Vdbe *p,
Op *pOp,
const char *zP4,
int n
){
if( pOp->p4type ){
| | | 85846 85847 85848 85849 85850 85851 85852 85853 85854 85855 85856 85857 85858 85859 85860 |
static void SQLITE_NOINLINE vdbeChangeP4Full(
Vdbe *p,
Op *pOp,
const char *zP4,
int n
){
if( pOp->p4type ){
assert( pOp->p4type > P4_FREE_IF_LE );
pOp->p4type = 0;
pOp->p4.p = 0;
}
if( n<0 ){
sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n);
}else{
if( n==0 ) n = sqlite3Strlen30(zP4);
|
| ︙ | ︙ | |||
89618 89619 89620 89621 89622 89623 89624 |
** Set all the parameters in the compiled SQL statement to NULL.
*/
SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
int i;
int rc = SQLITE_OK;
Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
| | > > > > > > > > | 89969 89970 89971 89972 89973 89974 89975 89976 89977 89978 89979 89980 89981 89982 89983 89984 89985 89986 89987 89988 89989 89990 89991 |
** Set all the parameters in the compiled SQL statement to NULL.
*/
SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
int i;
int rc = SQLITE_OK;
Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
sqlite3_mutex *mutex;
#endif
#ifdef SQLITE_ENABLE_API_ARMOR
if( pStmt==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
#if SQLITE_THREADSAFE
mutex = p->db->mutex;
#endif
sqlite3_mutex_enter(mutex);
for(i=0; i<p->nVar; i++){
sqlite3VdbeMemRelease(&p->aVar[i]);
p->aVar[i].flags = MEM_Null;
}
assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 );
|
| ︙ | ︙ | |||
90408 90409 90410 90411 90412 90413 90414 |
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
*/
SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
#ifdef SQLITE_ENABLE_API_ARMOR
if( p==0 ) return 0;
| | < | 90767 90768 90769 90770 90771 90772 90773 90774 90775 90776 90777 90778 90779 90780 90781 90782 |
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
*/
SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
#ifdef SQLITE_ENABLE_API_ARMOR
if( p==0 ) return 0;
#endif
assert( p && p->pFunc );
return p->pFunc->pUserData;
}
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
**
|
| ︙ | ︙ | |||
94229 94230 94231 94232 94233 94234 94235 |
**
** To force any register to be an integer, just add 0.
*/
case OP_AddImm: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
| | | 94587 94588 94589 94590 94591 94592 94593 94594 94595 94596 94597 94598 94599 94600 94601 |
**
** To force any register to be an integer, just add 0.
*/
case OP_AddImm: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
*(u64*)&pIn1->u.i += (u64)pOp->p2;
break;
}
/* Opcode: MustBeInt P1 P2 * * *
**
** Force the value in register P1 to be an integer. If the value
** in P1 is not an integer and cannot be converted into an integer
|
| ︙ | ︙ | |||
100375 100376 100377 100378 100379 100380 100381 | Table *pTab; sqlite3_vtab *pVtab; const sqlite3_module *pModule; char *zErr = 0; pOut = &aMem[pOp->p2]; sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */ | | > < < | 100733 100734 100735 100736 100737 100738 100739 100740 100741 100742 100743 100744 100745 100746 100747 100748 100749 100750 100751 100752 100753 100754 100755 100756 100757 100758 100759 100760 100761 100762 100763 |
Table *pTab;
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
char *zErr = 0;
pOut = &aMem[pOp->p2];
sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */
assert( pOp->p4type==P4_TABLEREF );
pTab = pOp->p4.pTab;
assert( pTab!=0 );
assert( pTab->nTabRef>0 );
assert( IsVirtual(pTab) );
if( pTab->u.vtab.p==0 ) break;
pVtab = pTab->u.vtab.p->pVtab;
assert( pVtab!=0 );
pModule = pVtab->pModule;
assert( pModule!=0 );
assert( pModule->iVersion>=4 );
assert( pModule->xIntegrity!=0 );
sqlite3VtabLock(pTab->u.vtab.p);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName,
pOp->p3, &zErr);
sqlite3VtabUnlock(pTab->u.vtab.p);
if( rc ){
sqlite3_free(zErr);
goto abort_due_to_error;
}
if( zErr ){
sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free);
}
|
| ︙ | ︙ | |||
100517 100518 100519 100520 100521 100522 100523 100524 100525 100526 100527 100528 100529 100530 100531 100532 100533 100534 100535 100536 100537 100538 100539 100540 100541 100542 100543 100544 100545 100546 100547 |
** unused by OP_VColumn.
*/
case OP_VColumn: { /* ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur!=0 );
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
if( pCur->nullRow ){
sqlite3VdbeMemSetNull(pDest);
break;
}
assert( pCur->eCurType==CURTYPE_VTAB );
pVtab = pCur->uc.pVCur->pVtab;
pModule = pVtab->pModule;
assert( pModule->xColumn );
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
sContext.enc = encoding;
assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
if( pOp->p5 & OPFLAG_NOCHNG ){
sqlite3VdbeMemSetNull(pDest);
pDest->flags = MEM_Null|MEM_Zero;
pDest->u.nZero = 0;
}else{
MemSetTypeFlag(pDest, MEM_Null);
| > > > > | 100874 100875 100876 100877 100878 100879 100880 100881 100882 100883 100884 100885 100886 100887 100888 100889 100890 100891 100892 100893 100894 100895 100896 100897 100898 100899 100900 100901 100902 100903 100904 100905 100906 100907 100908 |
** unused by OP_VColumn.
*/
case OP_VColumn: { /* ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
FuncDef nullFunc;
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur!=0 );
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
if( pCur->nullRow ){
sqlite3VdbeMemSetNull(pDest);
break;
}
assert( pCur->eCurType==CURTYPE_VTAB );
pVtab = pCur->uc.pVCur->pVtab;
pModule = pVtab->pModule;
assert( pModule->xColumn );
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
sContext.enc = encoding;
nullFunc.pUserData = 0;
nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE;
sContext.pFunc = &nullFunc;
assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
if( pOp->p5 & OPFLAG_NOCHNG ){
sqlite3VdbeMemSetNull(pDest);
pDest->flags = MEM_Null|MEM_Zero;
pDest->u.nZero = 0;
}else{
MemSetTypeFlag(pDest, MEM_Null);
|
| ︙ | ︙ | |||
100865 100866 100867 100868 100869 100870 100871 100872 100873 100874 100875 100876 100877 100878 |
** Clear the subtype from register P1.
*/
case OP_ClrSubtype: { /* in1 */
pIn1 = &aMem[pOp->p1];
pIn1->flags &= ~MEM_Subtype;
break;
}
/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
** Compute a hash on the P4 registers starting with r[P3] and
** add that hash to the bloom filter contained in r[P1].
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 101226 101227 101228 101229 101230 101231 101232 101233 101234 101235 101236 101237 101238 101239 101240 101241 101242 101243 101244 101245 101246 101247 101248 101249 101250 101251 101252 101253 101254 101255 101256 101257 101258 101259 101260 101261 101262 101263 101264 101265 101266 101267 101268 101269 101270 101271 101272 101273 101274 101275 |
** Clear the subtype from register P1.
*/
case OP_ClrSubtype: { /* in1 */
pIn1 = &aMem[pOp->p1];
pIn1->flags &= ~MEM_Subtype;
break;
}
/* Opcode: GetSubtype P1 P2 * * *
** Synopsis: r[P2] = r[P1].subtype
**
** Extract the subtype value from register P1 and write that subtype
** into register P2. If P1 has no subtype, then P1 gets a NULL.
*/
case OP_GetSubtype: { /* in1 out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
if( pIn1->flags & MEM_Subtype ){
sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype);
}else{
sqlite3VdbeMemSetNull(pOut);
}
break;
}
/* Opcode: SetSubtype P1 P2 * * *
** Synopsis: r[P2].subtype = r[P1]
**
** Set the subtype value of register P2 to the integer from register P1.
** If P1 is NULL, clear the subtype from p2.
*/
case OP_SetSubtype: { /* in1 out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
if( pIn1->flags & MEM_Null ){
pOut->flags &= ~MEM_Subtype;
}else{
assert( pIn1->flags & MEM_Int );
pOut->flags |= MEM_Subtype;
pOut->eSubtype = (u8)(pIn1->u.i & 0xff);
}
break;
}
/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
** Compute a hash on the P4 registers starting with r[P3] and
** add that hash to the bloom filter contained in r[P1].
*/
|
| ︙ | ︙ | |||
105914 105915 105916 105917 105918 105919 105920 105921 105922 105923 105924 105925 105926 105927 |
int n;
Table *pExTab;
n = pExpr->iColumn;
assert( ExprUseYTab(pExpr) );
pExTab = pExpr->y.pTab;
assert( pExTab!=0 );
if( (pExTab->tabFlags & TF_HasGenerated)!=0
&& (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
){
testcase( pExTab->nCol==BMS-1 );
testcase( pExTab->nCol==BMS );
return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1;
}else{
| > | 106311 106312 106313 106314 106315 106316 106317 106318 106319 106320 106321 106322 106323 106324 106325 |
int n;
Table *pExTab;
n = pExpr->iColumn;
assert( ExprUseYTab(pExpr) );
pExTab = pExpr->y.pTab;
assert( pExTab!=0 );
assert( n < pExTab->nCol );
if( (pExTab->tabFlags & TF_HasGenerated)!=0
&& (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
){
testcase( pExTab->nCol==BMS-1 );
testcase( pExTab->nCol==BMS );
return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1;
}else{
|
| ︙ | ︙ | |||
106490 106491 106492 106493 106494 106495 106496 106497 106498 106499 106500 106501 106502 106503 |
sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
}else{
sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
}
sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
pParse->checkSchema = 1;
pTopNC->nNcErr++;
}
assert( pFJMatch==0 );
/* Remove all substructure from pExpr */
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
sqlite3ExprDelete(db, pExpr->pLeft);
pExpr->pLeft = 0;
| > | 106888 106889 106890 106891 106892 106893 106894 106895 106896 106897 106898 106899 106900 106901 106902 |
sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
}else{
sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
}
sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
pParse->checkSchema = 1;
pTopNC->nNcErr++;
eNewExprOp = TK_NULL;
}
assert( pFJMatch==0 );
/* Remove all substructure from pExpr */
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
sqlite3ExprDelete(db, pExpr->pLeft);
pExpr->pLeft = 0;
|
| ︙ | ︙ | |||
106516 106517 106518 106519 106520 106521 106522 | ** if the mask contains extra set bits. However, it is important to ** avoid setting bits beyond the maximum column number of the table. ** (See ticket [b92e5e8ec2cdbaa1]). ** ** If a generated column is referenced, set bits for every column ** of the table. */ | | | 106915 106916 106917 106918 106919 106920 106921 106922 106923 106924 106925 106926 106927 106928 106929 |
** if the mask contains extra set bits. However, it is important to
** avoid setting bits beyond the maximum column number of the table.
** (See ticket [b92e5e8ec2cdbaa1]).
**
** If a generated column is referenced, set bits for every column
** of the table.
*/
if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){
pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
}
pExpr->op = eNewExprOp;
lookupname_end:
if( cnt==1 ){
assert( pNC!=0 );
|
| ︙ | ︙ | |||
106981 106982 106983 106984 106985 106986 106987 |
sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
}
#endif
pNC2 = pNC;
while( pNC2
&& sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
){
| | > | 107380 107381 107382 107383 107384 107385 107386 107387 107388 107389 107390 107391 107392 107393 107394 107395 107396 107397 107398 107399 |
sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
}
#endif
pNC2 = pNC;
while( pNC2
&& sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
){
pExpr->op2 += (1 + pNC2->nNestedSelect);
pNC2 = pNC2->pNext;
}
assert( pDef!=0 || IN_RENAME_OBJECT );
if( pNC2 && pDef ){
pExpr->op2 += pNC2->nNestedSelect;
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 );
pNC2->ncFlags |= NC_HasAgg
| ((pDef->funcFlags^SQLITE_FUNC_ANYORDER)
& (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER));
|
| ︙ | ︙ | |||
107544 107545 107546 107547 107548 107549 107550 107551 107552 107553 107554 107555 107556 107557 |
assert( pSub->pPrior && pSub->pOrderBy==0 );
pSub->pOrderBy = p->pOrderBy;
p->pOrderBy = 0;
}
/* Recursively resolve names in all subqueries in the FROM clause
*/
for(i=0; i<p->pSrc->nSrc; i++){
SrcItem *pItem = &p->pSrc->a[i];
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
int nRef = pOuterNC ? pOuterNC->nRef : 0;
const char *zSavedContext = pParse->zAuthContext;
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
| > | 107944 107945 107946 107947 107948 107949 107950 107951 107952 107953 107954 107955 107956 107957 107958 |
assert( pSub->pPrior && pSub->pOrderBy==0 );
pSub->pOrderBy = p->pOrderBy;
p->pOrderBy = 0;
}
/* Recursively resolve names in all subqueries in the FROM clause
*/
if( pOuterNC ) pOuterNC->nNestedSelect++;
for(i=0; i<p->pSrc->nSrc; i++){
SrcItem *pItem = &p->pSrc->a[i];
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
int nRef = pOuterNC ? pOuterNC->nRef : 0;
const char *zSavedContext = pParse->zAuthContext;
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
|
| ︙ | ︙ | |||
107567 107568 107569 107570 107571 107572 107573 107574 107575 107576 107577 107578 107579 107580 |
** the refcount on all contexts between the current one and the
** context containing the column when it resolves a name. */
if( pOuterNC ){
assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
}
}
}
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
** resolve the result-set expression list.
*/
sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
sNC.pSrcList = p->pSrc;
| > > > | 107968 107969 107970 107971 107972 107973 107974 107975 107976 107977 107978 107979 107980 107981 107982 107983 107984 |
** the refcount on all contexts between the current one and the
** context containing the column when it resolves a name. */
if( pOuterNC ){
assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
}
}
}
if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){
pOuterNC->nNestedSelect--;
}
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
** resolve the result-set expression list.
*/
sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
sNC.pSrcList = p->pSrc;
|
| ︙ | ︙ | |||
109155 109156 109157 109158 109159 109160 109161 |
return;
}
assert( pExpr->op==TK_FUNCTION );
assert( pExpr->pLeft==0 );
assert( ExprUseXList(pExpr) );
if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
/* Ignore ORDER BY on zero-argument aggregates */
| | < < | 109559 109560 109561 109562 109563 109564 109565 109566 109567 109568 109569 109570 109571 109572 109573 |
return;
}
assert( pExpr->op==TK_FUNCTION );
assert( pExpr->pLeft==0 );
assert( ExprUseXList(pExpr) );
if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
/* Ignore ORDER BY on zero-argument aggregates */
sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy);
return;
}
if( IsWindowFunc(pExpr) ){
sqlite3ExprOrderByAggregateError(pParse, pExpr);
sqlite3ExprListDelete(db, pOrderBy);
return;
}
|
| ︙ | ︙ | |||
109338 109339 109340 109341 109342 109343 109344 109345 109346 109347 109348 109349 109350 109351 |
if( !ExprHasProperty(p, EP_Static) ){
sqlite3DbNNFreeNN(db, p);
}
}
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p ) sqlite3ExprDeleteNN(db, p);
}
/*
** Clear both elements of an OnOrUsing object
*/
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
if( p==0 ){
/* Nothing to clear */
| > > > | 109740 109741 109742 109743 109744 109745 109746 109747 109748 109749 109750 109751 109752 109753 109754 109755 109756 |
if( !ExprHasProperty(p, EP_Static) ){
sqlite3DbNNFreeNN(db, p);
}
}
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p ) sqlite3ExprDeleteNN(db, p);
}
SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){
if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p);
}
/*
** Clear both elements of an OnOrUsing object
*/
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
if( p==0 ){
/* Nothing to clear */
|
| ︙ | ︙ | |||
109363 109364 109365 109366 109367 109368 109369 |
**
** The pExpr might be deleted immediately on an OOM error.
**
** The deferred delete is (currently) implemented by adding the
** pExpr to the pParse->pConstExpr list with a register number of 0.
*/
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
| | < < | 109768 109769 109770 109771 109772 109773 109774 109775 109776 109777 109778 109779 109780 109781 109782 |
**
** The pExpr might be deleted immediately on an OOM error.
**
** The deferred delete is (currently) implemented by adding the
** pExpr to the pParse->pConstExpr list with a register number of 0.
*/
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);
}
/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
** expression.
*/
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){
if( p ){
|
| ︙ | ︙ | |||
110171 110172 110173 110174 110175 110176 110177 110178 110179 110180 110181 110182 110183 110184 |
pItem++;
}while( --i>0 );
sqlite3DbNNFreeNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
if( pList ) exprListDeleteNN(db, pList);
}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
** ExprList.
*/
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
int i;
| > > > | 110574 110575 110576 110577 110578 110579 110580 110581 110582 110583 110584 110585 110586 110587 110588 110589 110590 |
pItem++;
}while( --i>0 );
sqlite3DbNNFreeNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
if( pList ) exprListDeleteNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){
if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList);
}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
** ExprList.
*/
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
int i;
|
| ︙ | ︙ | |||
110670 110671 110672 110673 110674 110675 110676 |
case TK_STRING:
case TK_FLOAT:
case TK_BLOB:
return 0;
case TK_COLUMN:
assert( ExprUseYTab(p) );
return ExprHasProperty(p, EP_CanBeNull) ||
| | > | 111076 111077 111078 111079 111080 111081 111082 111083 111084 111085 111086 111087 111088 111089 111090 111091 111092 111093 |
case TK_STRING:
case TK_FLOAT:
case TK_BLOB:
return 0;
case TK_COLUMN:
assert( ExprUseYTab(p) );
return ExprHasProperty(p, EP_CanBeNull) ||
NEVER(p->y.pTab==0) || /* Reference to column of index on expr */
(p->iColumn>=0
&& p->y.pTab->aCol!=0 /* Possible due to prior error */
&& ALWAYS(p->iColumn<p->y.pTab->nCol)
&& p->y.pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
}
}
/*
|
| ︙ | ︙ | |||
113254 113255 113256 113257 113258 113259 113260 |
assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
assert( target>0 && target<=pParse->nMem );
assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
if( pParse->pVdbe==0 ) return;
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
if( inReg!=target ){
u8 op;
| > > | | | 113661 113662 113663 113664 113665 113666 113667 113668 113669 113670 113671 113672 113673 113674 113675 113676 113677 113678 |
assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
assert( target>0 && target<=pParse->nMem );
assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
if( pParse->pVdbe==0 ) return;
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
if( inReg!=target ){
u8 op;
Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr);
testcase( pX!=pExpr );
if( ALWAYS(pX)
&& (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER)
){
op = OP_Copy;
}else{
op = OP_SCopy;
}
sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);
}
|
| ︙ | ︙ | |||
114701 114702 114703 114704 114705 114706 114707 114708 114709 114710 114711 114712 114713 |
} /* end loop over pSrcList */
}
return WRC_Continue;
}
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
&& pWalker->walkerDepth==pExpr->op2
){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
| > | | 115110 115111 115112 115113 115114 115115 115116 115117 115118 115119 115120 115121 115122 115123 115124 115125 115126 115127 115128 115129 115130 115131 |
} /* end loop over pSrcList */
}
return WRC_Continue;
}
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
&& pWalker->walkerDepth==pExpr->op2
&& pExpr->pAggInfo==0
){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
if( NEVER(pItem->pFExpr==pExpr) ) break;
if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
break;
}
}
if( i>=pAggInfo->nFunc ){
/* pExpr is original. Make a new entry in pAggInfo->aFunc[]
*/
|
| ︙ | ︙ | |||
114750 114751 114752 114753 114754 114755 114756 114757 114758 114759 114760 114761 114762 114763 |
pExpr->x.pList->a[0].pExpr,0)==0
){
pItem->bOBPayload = 0;
pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
}else{
pItem->bOBPayload = 1;
}
}else{
pItem->iOBTab = -1;
}
if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
pItem->iDistinct = pParse->nTab++;
}else{
pItem->iDistinct = -1;
| > > | 115160 115161 115162 115163 115164 115165 115166 115167 115168 115169 115170 115171 115172 115173 115174 115175 |
pExpr->x.pList->a[0].pExpr,0)==0
){
pItem->bOBPayload = 0;
pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
}else{
pItem->bOBPayload = 1;
}
pItem->bUseSubtype =
(pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0;
}else{
pItem->iOBTab = -1;
}
if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
pItem->iDistinct = pParse->nTab++;
}else{
pItem->iDistinct = -1;
|
| ︙ | ︙ | |||
117516 117517 117518 117519 117520 117521 117522 |
** Three SQL functions - stat_init(), stat_push(), and stat_get() -
** share an instance of the following structure to hold their state
** information.
*/
typedef struct StatAccum StatAccum;
typedef struct StatSample StatSample;
struct StatSample {
| < > | 117928 117929 117930 117931 117932 117933 117934 117935 117936 117937 117938 117939 117940 117941 117942 117943 117944 |
** Three SQL functions - stat_init(), stat_push(), and stat_get() -
** share an instance of the following structure to hold their state
** information.
*/
typedef struct StatAccum StatAccum;
typedef struct StatSample StatSample;
struct StatSample {
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
#ifdef SQLITE_ENABLE_STAT4
tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
i64 iRowid; /* Rowid in main table of the key */
u8 *aRowid; /* Key for WITHOUT ROWID tables */
} u;
u32 nRowid; /* Sizeof aRowid[] */
u8 isPSample; /* True if a periodic sample */
|
| ︙ | ︙ | |||
117676 117677 117678 117679 117680 117681 117682 | nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol; nKeyCol = sqlite3_value_int(argv[1]); assert( nKeyCol<=nCol ); assert( nKeyCol>0 ); /* Allocate the space required for the StatAccum object */ n = sizeof(*p) | < | > < > | 118088 118089 118090 118091 118092 118093 118094 118095 118096 118097 118098 118099 118100 118101 118102 118103 118104 118105 118106 118107 118108 118109 118110 118111 118112 118113 118114 118115 118116 118117 118118 118119 118120 118121 118122 118123 118124 118125 118126 118127 |
nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol;
nKeyCol = sqlite3_value_int(argv[1]);
assert( nKeyCol<=nCol );
assert( nKeyCol>0 );
/* Allocate the space required for the StatAccum object */
n = sizeof(*p)
+ sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
#ifdef SQLITE_ENABLE_STAT4
n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */
if( mxSample ){
n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
+ sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
}
#endif
p = sqlite3DbMallocZero(db, n);
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
p->db = db;
p->nEst = sqlite3_value_int64(argv[2]);
p->nRow = 0;
p->nLimit = sqlite3_value_int64(argv[3]);
p->nCol = nCol;
p->nKeyCol = nKeyCol;
p->nSkipAhead = 0;
p->current.anDLt = (tRowcnt*)&p[1];
#ifdef SQLITE_ENABLE_STAT4
p->current.anEq = &p->current.anDLt[nColUp];
p->mxSample = p->nLimit==0 ? mxSample : 0;
if( mxSample ){
u8 *pSpace; /* Allocated space not yet assigned */
int i; /* Used to iterate through p->aSample[] */
p->iGet = -1;
p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
|
| ︙ | ︙ | |||
117968 117969 117970 117971 117972 117973 117974 117975 117976 117977 117978 117979 117980 117981 117982 117983 117984 117985 117986 117987 117988 117989 117990 |
UNUSED_PARAMETER( argc );
UNUSED_PARAMETER( context );
assert( p->nCol>0 );
assert( iChng<p->nCol );
if( p->nRow==0 ){
/* This is the first call to this function. Do initialization. */
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
}else{
/* Second and subsequent calls get processed here */
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ) samplePushPrevious(p, iChng);
#endif
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
** to the current row of the index. */
for(i=0; i<iChng; i++){
p->current.anEq[i]++;
}
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
| > > > > < > | 118380 118381 118382 118383 118384 118385 118386 118387 118388 118389 118390 118391 118392 118393 118394 118395 118396 118397 118398 118399 118400 118401 118402 118403 118404 118405 118406 118407 118408 118409 118410 118411 118412 118413 118414 118415 |
UNUSED_PARAMETER( argc );
UNUSED_PARAMETER( context );
assert( p->nCol>0 );
assert( iChng<p->nCol );
if( p->nRow==0 ){
/* This is the first call to this function. Do initialization. */
#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
#endif
}else{
/* Second and subsequent calls get processed here */
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ) samplePushPrevious(p, iChng);
#endif
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
** to the current row of the index. */
#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<iChng; i++){
p->current.anEq[i]++;
}
#endif
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
p->current.anEq[i] = 1;
#endif
}
}
p->nRow++;
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ){
tRowcnt nLt;
|
| ︙ | ︙ | |||
118119 118120 118121 118122 118123 118124 118125 118126 118127 118128 118129 118130 118131 118132 118133 |
sqlite3_str_appendf(&sStat, "%llu",
p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
for(i=0; i<p->nKeyCol; i++){
u64 nDistinct = p->current.anDLt[i] + 1;
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
sqlite3_str_appendf(&sStat, " %llu", iVal);
assert( p->current.anEq[i] );
}
sqlite3ResultStrAccum(context, &sStat);
}
#ifdef SQLITE_ENABLE_STAT4
else if( eCall==STAT_GET_ROWID ){
if( p->iGet<0 ){
samplePushPrevious(p, 0);
| > > | 118535 118536 118537 118538 118539 118540 118541 118542 118543 118544 118545 118546 118547 118548 118549 118550 118551 |
sqlite3_str_appendf(&sStat, "%llu",
p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
for(i=0; i<p->nKeyCol; i++){
u64 nDistinct = p->current.anDLt[i] + 1;
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
sqlite3_str_appendf(&sStat, " %llu", iVal);
#ifdef SQLITE_ENABLE_STAT4
assert( p->current.anEq[i] );
#endif
}
sqlite3ResultStrAccum(context, &sStat);
}
#ifdef SQLITE_ENABLE_STAT4
else if( eCall==STAT_GET_ROWID ){
if( p->iGet<0 ){
samplePushPrevious(p, 0);
|
| ︙ | ︙ | |||
118808 118809 118810 118811 118812 118813 118814 118815 118816 118817 118818 118819 118820 118821 |
else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
}
#endif
while( z[0]!=0 && z[0]!=' ' ) z++;
while( z[0]==' ' ) z++;
}
}
}
/*
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
| > > > > > > > > > > | 119226 119227 119228 119229 119230 119231 119232 119233 119234 119235 119236 119237 119238 119239 119240 119241 119242 119243 119244 119245 119246 119247 119248 119249 |
else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
}
#endif
while( z[0]!=0 && z[0]!=' ' ) z++;
while( z[0]==' ' ) z++;
}
/* Set the bLowQual flag if the peak number of rows obtained
** from a full equality match is so large that a full table scan
** seems likely to be faster than using the index.
*/
if( aLog[0] > 66 /* Index has more than 100 rows */
&& aLog[0] <= aLog[nOut-1] /* And only a single value seen */
){
pIndex->bLowQual = 1;
}
}
}
/*
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
|
| ︙ | ︙ | |||
120854 120855 120856 120857 120858 120859 120860 |
/*
** Return the expression associated with a column. The expression might be
** the DEFAULT clause or the AS clause of a generated column.
** Return NULL if the column has no associated expression.
*/
SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
if( pCol->iDflt==0 ) return 0;
| | | 121282 121283 121284 121285 121286 121287 121288 121289 121290 121291 121292 121293 121294 121295 121296 |
/*
** Return the expression associated with a column. The expression might be
** the DEFAULT clause or the AS clause of a generated column.
** Return NULL if the column has no associated expression.
*/
SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
if( pCol->iDflt==0 ) return 0;
if( !IsOrdinaryTable(pTab) ) return 0;
if( NEVER(pTab->u.tab.pDfltList==0) ) return 0;
if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0;
return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
}
/*
** Set the collating sequence name for a column.
|
| ︙ | ︙ | |||
121006 121007 121008 121009 121010 121011 121012 121013 121014 121015 121016 121017 121018 121019 |
}
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Do not delete the table until the reference count reaches zero. */
assert( db!=0 );
if( !pTable ) return;
if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
deleteTable(db, pTable);
}
/*
** Unlink the given table from the hash tables and the delete the
** table structure with all its indices and foreign keys.
*/
| > > > | 121434 121435 121436 121437 121438 121439 121440 121441 121442 121443 121444 121445 121446 121447 121448 121449 121450 |
}
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Do not delete the table until the reference count reaches zero. */
assert( db!=0 );
if( !pTable ) return;
if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
deleteTable(db, pTable);
}
SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){
sqlite3DeleteTable(db, (Table*)pTable);
}
/*
** Unlink the given table from the hash tables and the delete the
** table structure with all its indices and foreign keys.
*/
|
| ︙ | ︙ | |||
121544 121545 121546 121547 121548 121549 121550 | } } #endif /* ** Clean up the data structures associated with the RETURNING clause. */ | | > | 121975 121976 121977 121978 121979 121980 121981 121982 121983 121984 121985 121986 121987 121988 121989 121990 |
}
}
#endif
/*
** Clean up the data structures associated with the RETURNING clause.
*/
static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){
Returning *pRet = (Returning*)pArg;
Hash *pHash;
pHash = &(db->aDb[1].pSchema->trigHash);
sqlite3HashInsert(pHash, pRet->zName, 0);
sqlite3ExprListDelete(db, pRet->pReturnEL);
sqlite3DbFree(db, pRet);
}
|
| ︙ | ︙ | |||
121586 121587 121588 121589 121590 121591 121592 |
if( pRet==0 ){
sqlite3ExprListDelete(db, pList);
return;
}
pParse->u1.pReturning = pRet;
pRet->pParse = pParse;
pRet->pReturnEL = pList;
| | < | 122018 122019 122020 122021 122022 122023 122024 122025 122026 122027 122028 122029 122030 122031 122032 |
if( pRet==0 ){
sqlite3ExprListDelete(db, pList);
return;
}
pParse->u1.pReturning = pRet;
pRet->pParse = pParse;
pRet->pReturnEL = pList;
sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet);
testcase( pParse->earlyCleanup );
if( db->mallocFailed ) return;
sqlite3_snprintf(sizeof(pRet->zName), pRet->zName,
"sqlite_returning_%p", pParse);
pRet->retTrig.zName = pRet->zName;
pRet->retTrig.op = TK_RETURNING;
pRet->retTrig.tr_tm = TRIGGER_AFTER;
|
| ︙ | ︙ | |||
121786 121787 121788 121789 121790 121791 121792 |
SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
u32 h = 0;
char aff = SQLITE_AFF_NUMERIC;
const char *zChar = 0;
assert( zIn!=0 );
while( zIn[0] ){
| > | | 122217 122218 122219 122220 122221 122222 122223 122224 122225 122226 122227 122228 122229 122230 122231 122232 |
SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
u32 h = 0;
char aff = SQLITE_AFF_NUMERIC;
const char *zChar = 0;
assert( zIn!=0 );
while( zIn[0] ){
u8 x = *(u8*)zIn;
h = (h<<8) + sqlite3UpperToLower[x];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
aff = SQLITE_AFF_TEXT;
zChar = zIn;
}else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */
aff = SQLITE_AFF_TEXT;
}else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */
|
| ︙ | ︙ | |||
125651 125652 125653 125654 125655 125656 125657 |
}
sqlite3DbFree(db, zColl);
}
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName);
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
| | > | 126083 126084 126085 126086 126087 126088 126089 126090 126091 126092 126093 126094 126095 126096 126097 126098 126099 126100 126101 126102 126103 126104 126105 126106 126107 |
}
sqlite3DbFree(db, zColl);
}
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName);
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
zDb = pName2->n ? db->aDb[iDb].zDbSName : 0;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
sqlite3DbFree(db, z);
return;
}
pIndex = sqlite3FindIndex(db, z, zDb);
sqlite3DbFree(db, z);
if( pIndex ){
iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3RefillIndex(pParse, pIndex, -1);
return;
}
sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed");
}
#endif
|
| ︙ | ︙ | |||
125825 125826 125827 125828 125829 125830 125831 125832 125833 125834 125835 125836 125837 125838 |
if( pWith ){
int i;
for(i=0; i<pWith->nCte; i++){
cteClear(db, &pWith->a[i]);
}
sqlite3DbFree(db, pWith);
}
}
#endif /* !defined(SQLITE_OMIT_CTE) */
/************** End of build.c ***********************************************/
/************** Begin file callback.c ****************************************/
/*
** 2005 May 23
| > > > | 126258 126259 126260 126261 126262 126263 126264 126265 126266 126267 126268 126269 126270 126271 126272 126273 126274 |
if( pWith ){
int i;
for(i=0; i<pWith->nCte; i++){
cteClear(db, &pWith->a[i]);
}
sqlite3DbFree(db, pWith);
}
}
SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){
sqlite3WithDelete(db, (With*)pWith);
}
#endif /* !defined(SQLITE_OMIT_CTE) */
/************** End of build.c ***********************************************/
/************** Begin file callback.c ****************************************/
/*
** 2005 May 23
|
| ︙ | ︙ | |||
139051 139052 139053 139054 139055 139056 139057 |
if( pTab->u.vtab.p==0 ) continue;
pVTab = pTab->u.vtab.p->pVtab;
if( NEVER(pVTab==0) ) continue;
if( NEVER(pVTab->pModule==0) ) continue;
if( pVTab->pModule->iVersion<4 ) continue;
if( pVTab->pModule->xIntegrity==0 ) continue;
sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
| > | | 139487 139488 139489 139490 139491 139492 139493 139494 139495 139496 139497 139498 139499 139500 139501 139502 |
if( pTab->u.vtab.p==0 ) continue;
pVTab = pTab->u.vtab.p->pVtab;
if( NEVER(pVTab==0) ) continue;
if( NEVER(pVTab->pModule==0) ) continue;
if( pVTab->pModule->iVersion<4 ) continue;
if( pVTab->pModule->xIntegrity==0 ) continue;
sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
pTab->nTabRef++;
sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, a1);
#endif
continue;
}
if( isQuick || HasRowid(pTab) ){
|
| ︙ | ︙ | |||
141078 141079 141080 141081 141082 141083 141084 141085 141086 141087 141088 141089 141090 141091 |
}while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY)
|| (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
sqlite3BtreeLeaveAll(db);
rc = sqlite3ApiExit(db, rc);
assert( (rc&db->errMask)==rc );
db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
return rc;
}
/*
** Rerun the compilation of a statement after a schema change.
**
| > | 141515 141516 141517 141518 141519 141520 141521 141522 141523 141524 141525 141526 141527 141528 141529 |
}while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY)
|| (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
sqlite3BtreeLeaveAll(db);
rc = sqlite3ApiExit(db, rc);
assert( (rc&db->errMask)==rc );
db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
assert( rc==SQLITE_OK || (*ppStmt)==0 );
return rc;
}
/*
** Rerun the compilation of a statement after a schema change.
**
|
| ︙ | ︙ | |||
141474 141475 141476 141477 141478 141479 141480 141481 141482 141483 141484 141485 141486 141487 |
/*
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
}
/*
** Return a pointer to the right-most SELECT statement in a compound.
*/
static Select *findRightmost(Select *p){
while( p->pNext ) p = p->pNext;
| > > > | 141912 141913 141914 141915 141916 141917 141918 141919 141920 141921 141922 141923 141924 141925 141926 141927 141928 |
/*
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
}
SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){
if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1);
}
/*
** Return a pointer to the right-most SELECT statement in a compound.
*/
static Select *findRightmost(Select *p){
while( p->pNext ) p = p->pNext;
|
| ︙ | ︙ | |||
144495 144496 144497 144498 144499 144500 144501 |
sqlite3KeyInfoUnref(pKeyInfo);
}
multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
if( pDelete ){
| | < < | 144936 144937 144938 144939 144940 144941 144942 144943 144944 144945 144946 144947 144948 144949 144950 |
sqlite3KeyInfoUnref(pKeyInfo);
}
multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
if( pDelete ){
sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
}
return rc;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/*
** Error message for when two or more terms of a compound select have different
|
| ︙ | ︙ | |||
145048 145049 145050 145051 145052 145053 145054 |
/* Jump to the this point in order to terminate the query.
*/
sqlite3VdbeResolveLabel(v, labelEnd);
/* Make arrangements to free the 2nd and subsequent arms of the compound
** after the parse has finished */
if( pSplit->pPrior ){
| | < | 145487 145488 145489 145490 145491 145492 145493 145494 145495 145496 145497 145498 145499 145500 145501 |
/* Jump to the this point in order to terminate the query.
*/
sqlite3VdbeResolveLabel(v, labelEnd);
/* Make arrangements to free the 2nd and subsequent arms of the compound
** after the parse has finished */
if( pSplit->pPrior ){
sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior);
}
pSplit->pPrior = pPrior;
pPrior->pNext = pSplit;
sqlite3ExprListDelete(db, pPrior->pOrderBy);
pPrior->pOrderBy = 0;
/*** TBD: Insert subroutine calls to close cursors on incomplete
|
| ︙ | ︙ | |||
145870 145871 145872 145873 145874 145875 145876 |
**
** pSubitem->pTab is always non-NULL by test restrictions and tests above.
*/
if( ALWAYS(pSubitem->pTab!=0) ){
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
| | < < | 146308 146309 146310 146311 146312 146313 146314 146315 146316 146317 146318 146319 146320 146321 146322 |
**
** pSubitem->pTab is always non-NULL by test restrictions and tests above.
*/
if( ALWAYS(pSubitem->pTab!=0) ){
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel);
testcase( pToplevel->earlyCleanup );
}else{
pTabToDel->nTabRef--;
}
pSubitem->pTab = 0;
}
|
| ︙ | ︙ | |||
146919 146920 146921 146922 146923 146924 146925 |
**
** If bFree is true, do not continue to use the pWith pointer after
** calling this routine, Instead, use only the return value.
*/
SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
if( pWith ){
if( bFree ){
| | < | 147355 147356 147357 147358 147359 147360 147361 147362 147363 147364 147365 147366 147367 147368 147369 |
**
** If bFree is true, do not continue to use the pWith pointer after
** calling this routine, Instead, use only the return value.
*/
SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
if( pWith ){
if( bFree ){
pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric,
pWith);
if( pWith==0 ) return 0;
}
if( pParse->nErr==0 ){
assert( pParse->pWith!=pWith );
pWith->pOuter = pParse->pWith;
pParse->pWith = pWith;
|
| ︙ | ︙ | |||
147953 147954 147955 147956 147957 147958 147959 147960 147961 147962 147963 147964 147965 147966 147967 147968 147969 147970 147971 147972 147973 147974 147975 |
if( pFunc->iOBTab>=0 ){
ExprList *pOBList;
KeyInfo *pKeyInfo;
int nExtra = 0;
assert( pFunc->pFExpr->pLeft!=0 );
assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
assert( ExprUseXList(pFunc->pFExpr->pLeft) );
pOBList = pFunc->pFExpr->pLeft->x.pList;
if( !pFunc->bOBUnique ){
nExtra++; /* One extra column for the OP_Sequence */
}
if( pFunc->bOBPayload ){
/* extra columns for the function arguments */
assert( ExprUseXList(pFunc->pFExpr) );
nExtra += pFunc->pFExpr->x.pList->nExpr;
}
pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
if( !pFunc->bOBUnique && pParse->nErr==0 ){
pKeyInfo->nKeyField++;
}
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
(char*)pKeyInfo, P4_KEYINFO);
| > > > > | 148388 148389 148390 148391 148392 148393 148394 148395 148396 148397 148398 148399 148400 148401 148402 148403 148404 148405 148406 148407 148408 148409 148410 148411 148412 148413 148414 |
if( pFunc->iOBTab>=0 ){
ExprList *pOBList;
KeyInfo *pKeyInfo;
int nExtra = 0;
assert( pFunc->pFExpr->pLeft!=0 );
assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
assert( ExprUseXList(pFunc->pFExpr->pLeft) );
assert( pFunc->pFunc!=0 );
pOBList = pFunc->pFExpr->pLeft->x.pList;
if( !pFunc->bOBUnique ){
nExtra++; /* One extra column for the OP_Sequence */
}
if( pFunc->bOBPayload ){
/* extra columns for the function arguments */
assert( ExprUseXList(pFunc->pFExpr) );
nExtra += pFunc->pFExpr->x.pList->nExpr;
}
if( pFunc->bUseSubtype ){
nExtra += pFunc->pFExpr->x.pList->nExpr;
}
pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
if( !pFunc->bOBUnique && pParse->nErr==0 ){
pKeyInfo->nKeyField++;
}
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
(char*)pKeyInfo, P4_KEYINFO);
|
| ︙ | ︙ | |||
147988 147989 147990 147991 147992 147993 147994 |
int i;
struct AggInfo_func *pF;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
if( pF->iOBTab>=0 ){
| | | | > > > > > > > > > > | 148427 148428 148429 148430 148431 148432 148433 148434 148435 148436 148437 148438 148439 148440 148441 148442 148443 148444 148445 148446 148447 148448 148449 148450 148451 148452 148453 148454 148455 148456 148457 148458 148459 148460 148461 148462 148463 148464 148465 148466 148467 148468 148469 148470 148471 148472 148473 148474 148475 148476 |
int i;
struct AggInfo_func *pF;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
if( pF->iOBTab>=0 ){
/* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs
** were stored in emphermal table pF->iOBTab. Here, we extract those
** inputs (in ORDER BY order) and make all calls to OP_AggStep
** before doing the OP_AggFinal call. */
int iTop; /* Start of loop for extracting columns */
int nArg; /* Number of columns to extract */
int nKey; /* Key columns to be skipped */
int regAgg; /* Extract into this array */
int j; /* Loop counter */
assert( pF->pFunc!=0 );
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
if( pF->bOBPayload==0 ){
nKey = 0;
}else{
assert( pF->pFExpr->pLeft!=0 );
assert( ExprUseXList(pF->pFExpr->pLeft) );
assert( pF->pFExpr->pLeft->x.pList!=0 );
nKey = pF->pFExpr->pLeft->x.pList->nExpr;
if( ALWAYS(!pF->bOBUnique) ) nKey++;
}
iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v);
for(j=nArg-1; j>=0; j--){
sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
}
if( pF->bUseSubtype ){
int regSubtype = sqlite3GetTempReg(pParse);
int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0);
for(j=nArg-1; j>=0; j--){
sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype);
sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j);
}
sqlite3ReleaseTempReg(pParse, regSubtype);
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, iTop);
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
|
| ︙ | ︙ | |||
148068 148069 148070 148071 148072 148073 148074 148075 148076 148077 148078 148079 148080 148081 |
int addrNext = 0;
int regAgg;
int regAggSz = 0;
int regDistinct = 0;
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
assert( !IsWindowFunc(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
if( pAggInfo->nAccumulator
&& (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
&& regAcc
){
| > | 148517 148518 148519 148520 148521 148522 148523 148524 148525 148526 148527 148528 148529 148530 148531 |
int addrNext = 0;
int regAgg;
int regAggSz = 0;
int regDistinct = 0;
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
assert( !IsWindowFunc(pF->pFExpr) );
assert( pF->pFunc!=0 );
pList = pF->pFExpr->x.pList;
if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
if( pAggInfo->nAccumulator
&& (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
&& regAcc
){
|
| ︙ | ︙ | |||
148111 148112 148113 148114 148115 148116 148117 148118 148119 148120 148121 148122 148123 148124 148125 148126 148127 148128 148129 148130 148131 148132 148133 148134 148135 148136 148137 |
assert( pOBList->nExpr>0 );
regAggSz = pOBList->nExpr;
if( !pF->bOBUnique ){
regAggSz++; /* One register for OP_Sequence */
}
if( pF->bOBPayload ){
regAggSz += nArg;
}
regAggSz++; /* One extra register to hold result of MakeRecord */
regAgg = sqlite3GetTempRange(pParse, regAggSz);
regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
jj = pOBList->nExpr;
if( !pF->bOBUnique ){
sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
jj++;
}
if( pF->bOBPayload ){
regDistinct = regAgg+jj;
sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);
}
}else if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
| > > > > > > > > > > > | 148561 148562 148563 148564 148565 148566 148567 148568 148569 148570 148571 148572 148573 148574 148575 148576 148577 148578 148579 148580 148581 148582 148583 148584 148585 148586 148587 148588 148589 148590 148591 148592 148593 148594 148595 148596 148597 148598 |
assert( pOBList->nExpr>0 );
regAggSz = pOBList->nExpr;
if( !pF->bOBUnique ){
regAggSz++; /* One register for OP_Sequence */
}
if( pF->bOBPayload ){
regAggSz += nArg;
}
if( pF->bUseSubtype ){
regAggSz += nArg;
}
regAggSz++; /* One extra register to hold result of MakeRecord */
regAgg = sqlite3GetTempRange(pParse, regAggSz);
regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
jj = pOBList->nExpr;
if( !pF->bOBUnique ){
sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
jj++;
}
if( pF->bOBPayload ){
regDistinct = regAgg+jj;
sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);
jj += nArg;
}
if( pF->bUseSubtype ){
int kk;
int regBase = pF->bOBPayload ? regDistinct : regAgg;
for(kk=0; kk<nArg; kk++, jj++){
sqlite3VdbeAddOp2(v, OP_GetSubtype, regBase+kk, regAgg+jj);
}
}
}else if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
|
| ︙ | ︙ | |||
148328 148329 148330 148331 148332 148333 148334 | } return 0; } /* ** Deallocate a single AggInfo object */ | | > | 148789 148790 148791 148792 148793 148794 148795 148796 148797 148798 148799 148800 148801 148802 148803 148804 |
}
return 0;
}
/*
** Deallocate a single AggInfo object
*/
static void agginfoFree(sqlite3 *db, void *pArg){
AggInfo *p = (AggInfo*)pArg;
sqlite3DbFree(db, p->aCol);
sqlite3DbFree(db, p->aFunc);
sqlite3DbFreeNN(db, p);
}
/*
** Attempt to transform a query of the form
|
| ︙ | ︙ | |||
148402 148403 148404 148405 148406 148407 148408 |
Expr *pTerm;
pPrior = pSub->pPrior;
pSub->pPrior = 0;
pSub->pNext = 0;
pSub->selFlags |= SF_Aggregate;
pSub->selFlags &= ~SF_Compound;
pSub->nSelectRow = 0;
| | | 148864 148865 148866 148867 148868 148869 148870 148871 148872 148873 148874 148875 148876 148877 148878 |
Expr *pTerm;
pPrior = pSub->pPrior;
pSub->pPrior = 0;
pSub->pNext = 0;
pSub->selFlags |= SF_Aggregate;
pSub->selFlags &= ~SF_Compound;
pSub->nSelectRow = 0;
sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
sqlite3PExprAddSelect(pParse, pTerm, pSub);
if( pExpr==0 ){
pExpr = pTerm;
}else{
|
| ︙ | ︙ | |||
148582 148583 148584 148585 148586 148587 148588 |
if( p->pOrderBy ){
#if TREETRACE_ENABLED
TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
if( sqlite3TreeTrace & 0x800 ){
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
}
#endif
| | < | | 149044 149045 149046 149047 149048 149049 149050 149051 149052 149053 149054 149055 149056 149057 149058 149059 |
if( p->pOrderBy ){
#if TREETRACE_ENABLED
TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
if( sqlite3TreeTrace & 0x800 ){
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
}
#endif
sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
p->pOrderBy);
testcase( pParse->earlyCleanup );
p->pOrderBy = 0;
}
p->selFlags &= ~SF_Distinct;
p->selFlags |= SF_NoopOrderBy;
}
sqlite3SelectPrep(pParse, p, 0);
|
| ︙ | ︙ | |||
148776 148777 148778 148779 148780 148781 148782 |
&& pSub->pLimit==0 /* Condition (1) */
&& (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */
&& (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */
&& OptimizationEnabled(db, SQLITE_OmitOrderBy)
){
TREETRACE(0x800,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
| | < | | 149237 149238 149239 149240 149241 149242 149243 149244 149245 149246 149247 149248 149249 149250 149251 149252 |
&& pSub->pLimit==0 /* Condition (1) */
&& (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */
&& (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */
&& OptimizationEnabled(db, SQLITE_OmitOrderBy)
){
TREETRACE(0x800,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
pSub->pOrderBy);
pSub->pOrderBy = 0;
}
/* If the outer query contains a "complex" result set (that is,
** if the result set of the outer query uses functions or subqueries)
** and if the subquery contains an ORDER BY clause and if
** it will be implemented as a co-routine, then do not flatten. This
|
| ︙ | ︙ | |||
149307 149308 149309 149310 149311 149312 149313 |
/* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
** SELECT statement.
*/
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
if( pAggInfo ){
| | < | 149767 149768 149769 149770 149771 149772 149773 149774 149775 149776 149777 149778 149779 149780 149781 |
/* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
** SELECT statement.
*/
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
if( pAggInfo ){
sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo);
testcase( pParse->earlyCleanup );
}
if( db->mallocFailed ){
goto select_end;
}
pAggInfo->selId = p->selId;
#ifdef SQLITE_DEBUG
|
| ︙ | ︙ | |||
153957 153958 153959 153960 153961 153962 153963 |
VTable *p = db->pDisconnect;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3_mutex_held(db->mutex) );
if( p ){
db->pDisconnect = 0;
| < | 154416 154417 154418 154419 154420 154421 154422 154423 154424 154425 154426 154427 154428 154429 |
VTable *p = db->pDisconnect;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3_mutex_held(db->mutex) );
if( p ){
db->pDisconnect = 0;
do {
VTable *pNext = p->pNext;
sqlite3VtabUnlock(p);
p = pNext;
}while( p );
}
}
|
| ︙ | ︙ | |||
155523 155524 155525 155526 155527 155528 155529 | ** ** where.c: */ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); #ifdef WHERETRACE_ENABLED SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC); SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm); | | | 155981 155982 155983 155984 155985 155986 155987 155988 155989 155990 155991 155992 155993 155994 155995 | ** ** where.c: */ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); #ifdef WHERETRACE_ENABLED SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC); SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm); SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC); #endif SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm( WhereClause *pWC, /* The WHERE clause to be searched */ int iCur, /* Cursor number of LHS */ int iColumn, /* Column number of LHS */ Bitmask notReady, /* RHS must not overlap with this mask */ u32 op, /* Mask of WO_xx values describing operator */ |
| ︙ | ︙ | |||
160985 160986 160987 160988 160989 160990 160991 160992 160993 160994 160995 160996 160997 160998 160999 161000 161001 161002 161003 161004 |
Vdbe *v = pParse->pVdbe;
VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
int iEnd = sqlite3VdbeCurrentAddr(v);
if( pParse->db->mallocFailed ) return;
for(; iStart<iEnd; iStart++, pOp++){
if( pOp->p1!=iTabCur ) continue;
if( pOp->opcode==OP_Column ){
pOp->opcode = OP_Copy;
pOp->p1 = pOp->p2 + iRegister;
pOp->p2 = pOp->p3;
pOp->p3 = 0;
pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
}else if( pOp->opcode==OP_Rowid ){
pOp->opcode = OP_Sequence;
pOp->p1 = iAutoidxCur;
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
if( iAutoidxCur==0 ){
pOp->opcode = OP_Null;
pOp->p3 = 0;
}
| > > > > > > > > > > | 161443 161444 161445 161446 161447 161448 161449 161450 161451 161452 161453 161454 161455 161456 161457 161458 161459 161460 161461 161462 161463 161464 161465 161466 161467 161468 161469 161470 161471 161472 |
Vdbe *v = pParse->pVdbe;
VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
int iEnd = sqlite3VdbeCurrentAddr(v);
if( pParse->db->mallocFailed ) return;
for(; iStart<iEnd; iStart++, pOp++){
if( pOp->p1!=iTabCur ) continue;
if( pOp->opcode==OP_Column ){
#ifdef SQLITE_DEBUG
if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart);
}
#endif
pOp->opcode = OP_Copy;
pOp->p1 = pOp->p2 + iRegister;
pOp->p2 = pOp->p3;
pOp->p3 = 0;
pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
}else if( pOp->opcode==OP_Rowid ){
#ifdef SQLITE_DEBUG
if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart);
}
#endif
pOp->opcode = OP_Sequence;
pOp->p1 = iAutoidxCur;
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
if( iAutoidxCur==0 ){
pOp->opcode = OP_Null;
pOp->p3 = 0;
}
|
| ︙ | ︙ | |||
162317 162318 162319 162320 162321 162322 162323 |
if( rc==SQLITE_OK ){
if( iUpper>iLower ){
nNew = sqlite3LogEst(iUpper - iLower);
/* TUNING: If both iUpper and iLower are derived from the same
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
** if estimated without the use of STAT4 tables. */
| | > | 162785 162786 162787 162788 162789 162790 162791 162792 162793 162794 162795 162796 162797 162798 162799 162800 |
if( rc==SQLITE_OK ){
if( iUpper>iLower ){
nNew = sqlite3LogEst(iUpper - iLower);
/* TUNING: If both iUpper and iLower are derived from the same
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
** if estimated without the use of STAT4 tables. */
if( iLwrIdx==iUprIdx ){ nNew -= 20; }
assert( 20==sqlite3LogEst(4) );
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
}
if( nNew<nOut ){
nOut = nNew;
}
WHERETRACE(0x20, ("STAT4 range scan: %u..%u est=%d\n",
|
| ︙ | ︙ | |||
162541 162542 162543 162544 162545 162546 162547 162548 | } } #endif #ifdef WHERETRACE_ENABLED /* ** Print a WhereLoop object for debugging purposes */ | > > > > > > > > > > > > | > | | | | | | | | | > > > > | 163010 163011 163012 163013 163014 163015 163016 163017 163018 163019 163020 163021 163022 163023 163024 163025 163026 163027 163028 163029 163030 163031 163032 163033 163034 163035 163036 163037 163038 163039 163040 163041 163042 163043 163044 163045 163046 163047 163048 163049 163050 163051 |
}
}
#endif
#ifdef WHERETRACE_ENABLED
/*
** Print a WhereLoop object for debugging purposes
**
** Format example:
**
** .--- Position in WHERE clause rSetup, rRun, nOut ---.
** | |
** | .--- selfMask nTerm ------. |
** | | | |
** | | .-- prereq Idx wsFlags----. | |
** | | | Name | | |
** | | | __|__ nEq ---. ___|__ | __|__
** | / \ / \ / \ | / \ / \ / \
** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31
*/
SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
if( pWC ){
WhereInfo *pWInfo = pWC->pWInfo;
int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
Table *pTab = pItem->pTab;
Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
sqlite3DebugPrintf(" %12s",
pItem->zAlias ? pItem->zAlias : pTab->zName);
}else{
sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d",
p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab);
}
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
const char *zName;
if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
int i = sqlite3Strlen30(zName) - 1;
while( zName[i]!='_' ) i--;
zName += i;
|
| ︙ | ︙ | |||
162587 162588 162589 162590 162591 162592 162593 162594 162595 162596 162597 162598 162599 162600 |
sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){
int i;
for(i=0; i<p->nLTerm; i++){
sqlite3WhereTermPrint(p->aLTerm[i], i);
}
}
}
#endif
/*
** Convert bulk memory into a valid WhereLoop that can be passed
** to whereLoopClear harmlessly.
*/
| > > > > > > > > > | 163073 163074 163075 163076 163077 163078 163079 163080 163081 163082 163083 163084 163085 163086 163087 163088 163089 163090 163091 163092 163093 163094 163095 |
sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){
int i;
for(i=0; i<p->nLTerm; i++){
sqlite3WhereTermPrint(p->aLTerm[i], i);
}
}
}
SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){
if( p ) sqlite3WhereLoopPrint(p, 0);
}
SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){
while( p ){
sqlite3ShowWhereLoop(p);
p = p->pNextLoop;
}
}
#endif
/*
** Convert bulk memory into a valid WhereLoop that can be passed
** to whereLoopClear harmlessly.
*/
|
| ︙ | ︙ | |||
162700 162701 162702 162703 162704 162705 162706 |
sqlite3DbNNFreeNN(db, pWInfo->pMemToFree);
pWInfo->pMemToFree = pNext;
}
sqlite3DbNNFreeNN(db, pWInfo);
}
/*
| | > > > > > > > > > > > > > > > | | | | | | < < < < < < < < < > > > > > > > > > | < | | | | | 163195 163196 163197 163198 163199 163200 163201 163202 163203 163204 163205 163206 163207 163208 163209 163210 163211 163212 163213 163214 163215 163216 163217 163218 163219 163220 163221 163222 163223 163224 163225 163226 163227 163228 163229 163230 163231 163232 163233 163234 163235 163236 163237 163238 163239 163240 163241 163242 163243 163244 163245 163246 163247 163248 163249 163250 163251 163252 163253 163254 163255 163256 163257 163258 163259 163260 163261 163262 |
sqlite3DbNNFreeNN(db, pWInfo->pMemToFree);
pWInfo->pMemToFree = pNext;
}
sqlite3DbNNFreeNN(db, pWInfo);
}
/*
** Return TRUE if X is a proper subset of Y but is of equal or less cost.
** In other words, return true if all constraints of X are also part of Y
** and Y has additional constraints that might speed the search that X lacks
** but the cost of running X is not more than the cost of running Y.
**
** In other words, return true if the cost relationwship between X and Y
** is inverted and needs to be adjusted.
**
** Case 1:
**
** (1a) X and Y use the same index.
** (1b) X has fewer == terms than Y
** (1c) Neither X nor Y use skip-scan
** (1d) X does not have a a greater cost than Y
**
** Case 2:
**
** (2a) X has the same or lower cost, or returns the same or fewer rows,
** than Y.
** (2b) X uses fewer WHERE clause terms than Y
** (2c) Every WHERE clause term used by X is also used by Y
** (2d) X skips at least as many columns as Y
** (2e) If X is a covering index, than Y is too
*/
static int whereLoopCheaperProperSubset(
const WhereLoop *pX, /* First WhereLoop to compare */
const WhereLoop *pY /* Compare against this WhereLoop */
){
int i, j;
if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */
assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 );
assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 );
if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */
&& pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */
&& pX->nSkip==0 && pY->nSkip==0 /* (1c) */
){
return 1; /* Case 1 is true */
}
if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
return 0; /* (2b) */
}
if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */
for(i=pX->nLTerm-1; i>=0; i--){
if( pX->aLTerm[i]==0 ) continue;
for(j=pY->nLTerm-1; j>=0; j--){
if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
}
if( j<0 ) return 0; /* (2c) */
}
if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
&& (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
return 0; /* (2e) */
}
return 1; /* Case 2 is true */
}
/*
** Try to adjust the cost and number of output rows of WhereLoop pTemplate
** upwards or downwards so that:
**
** (1) pTemplate costs less than any other WhereLoops that are a proper
|
| ︙ | ︙ | |||
163229 163230 163231 163232 163233 163234 163235 |
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
opMask = WO_LT|WO_LE;
}else{
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
| > | > > | 163738 163739 163740 163741 163742 163743 163744 163745 163746 163747 163748 163749 163750 163751 163752 163753 163754 163755 |
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
opMask = WO_LT|WO_LE;
}else{
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
if( pProbe->bUnordered || pProbe->bLowQual ){
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
if( pProbe->bLowQual ) opMask &= ~(WO_EQ|WO_IN|WO_IS);
}
assert( pNew->u.btree.nEq<pProbe->nColumn );
assert( pNew->u.btree.nEq<pProbe->nKeyCol
|| pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY );
saved_nEq = pNew->u.btree.nEq;
saved_nBtm = pNew->u.btree.nBtm;
|
| ︙ | ︙ | |||
166309 166310 166311 166312 166313 166314 166315 | /* Variable initialization */ db = pParse->db; memset(&sWLB, 0, sizeof(sWLB)); /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); | | > > > | 166821 166822 166823 166824 166825 166826 166827 166828 166829 166830 166831 166832 166833 166834 166835 166836 166837 166838 |
/* Variable initialization */
db = pParse->db;
memset(&sWLB, 0, sizeof(sWLB));
/* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
if( pOrderBy && pOrderBy->nExpr>=BMS ){
pOrderBy = 0;
wctrlFlags &= ~WHERE_WANT_DISTINCT;
}
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
*/
testcase( pTabList->nSrc==BMS );
if( pTabList->nSrc>BMS ){
sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS);
|
| ︙ | ︙ | |||
166334 166335 166336 166337 166338 166339 166340 | /* Allocate and initialize the WhereInfo structure that will become the ** return value. A single allocation is used to store the WhereInfo ** struct, the contents of WhereInfo.a[], the WhereClause structure ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte ** field (type Bitmask) it must be aligned on an 8-byte boundary on ** some architectures. Hence the ROUND8() below. */ | | > > > | 166849 166850 166851 166852 166853 166854 166855 166856 166857 166858 166859 166860 166861 166862 166863 166864 166865 166866 |
/* Allocate and initialize the WhereInfo structure that will become the
** return value. A single allocation is used to store the WhereInfo
** struct, the contents of WhereInfo.a[], the WhereClause structure
** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
nByteWInfo = ROUND8P(sizeof(WhereInfo));
if( nTabList>1 ){
nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
}
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
pWInfo = 0;
goto whereBeginError;
}
pWInfo->pParse = pParse;
|
| ︙ | ︙ | |||
166896 166897 166898 166899 166900 166901 166902 166903 166904 166905 166906 166907 166908 166909 |
/* Jump here if malloc fails */
whereBeginError:
if( pWInfo ){
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
}
return 0;
}
/*
** Part of sqlite3WhereEnd() will rewrite opcodes to reference the
** index rather than the main table. In SQLITE_DEBUG mode, we want
** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine
| > > > > > | 167414 167415 167416 167417 167418 167419 167420 167421 167422 167423 167424 167425 167426 167427 167428 167429 167430 167431 167432 |
/* Jump here if malloc fails */
whereBeginError:
if( pWInfo ){
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
}
#ifdef WHERETRACE_ENABLED
/* Prevent harmless compiler warnings about debugging routines
** being declared but never used */
sqlite3ShowWhereLoopList(0);
#endif /* WHERETRACE_ENABLED */
return 0;
}
/*
** Part of sqlite3WhereEnd() will rewrite opcodes to reference the
** index rather than the main table. In SQLITE_DEBUG mode, we want
** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine
|
| ︙ | ︙ | |||
182232 182233 182234 182235 182236 182237 182238 182239 182240 182241 182242 182243 182244 182245 |
*piValue = Tuning(-id);
}else{
rc = SQLITE_NOTFOUND;
}
break;
}
#endif
}
va_end(ap);
#endif /* SQLITE_UNTESTABLE */
return rc;
}
/*
| > > > > > > > > > > > > > > > > > > > > > > | 182755 182756 182757 182758 182759 182760 182761 182762 182763 182764 182765 182766 182767 182768 182769 182770 182771 182772 182773 182774 182775 182776 182777 182778 182779 182780 182781 182782 182783 182784 182785 182786 182787 182788 182789 182790 |
*piValue = Tuning(-id);
}else{
rc = SQLITE_NOTFOUND;
}
break;
}
#endif
/* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff);
**
** Activate or deactivate validation of JSONB that is generated from
** text. Off by default, as the validation is slow. Validation is
** only available if compiled using SQLITE_DEBUG.
**
** If onOff is initially 1, then turn it on. If onOff is initially
** off, turn it off. If onOff is initially -1, then change onOff
** to be the current setting.
*/
case SQLITE_TESTCTRL_JSON_SELFCHECK: {
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
int *pOnOff = va_arg(ap, int*);
if( *pOnOff<0 ){
*pOnOff = sqlite3Config.bJsonSelfcheck;
}else{
sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff);
}
#endif
break;
}
}
va_end(ap);
#endif /* SQLITE_UNTESTABLE */
return rc;
}
/*
|
| ︙ | ︙ | |||
184215 184216 184217 184218 184219 184220 184221 184222 184223 184224 184225 184226 184227 184228 | #ifndef SQLITE_DISABLE_FTS3_UNICODE SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); #endif SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ /************** End of fts3Int.h *********************************************/ /************** Continuing where we left off in fts3.c ***********************/ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) | > > | 184760 184761 184762 184763 184764 184765 184766 184767 184768 184769 184770 184771 184772 184773 184774 184775 | #ifndef SQLITE_DISABLE_FTS3_UNICODE SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); #endif SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk); #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ /************** End of fts3Int.h *********************************************/ /************** Continuing where we left off in fts3.c ***********************/ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) |
| ︙ | ︙ | |||
187938 187939 187940 187941 187942 187943 187944 | return 0; } /* ** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual ** table. */ | | < | < < | < < < < < < < | < < | | > > > | | 188485 188486 188487 188488 188489 188490 188491 188492 188493 188494 188495 188496 188497 188498 188499 188500 188501 188502 188503 188504 188505 188506 188507 188508 188509 188510 188511 188512 188513 188514 188515 188516 188517 188518 188519 188520 188521 |
return 0;
}
/*
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
** table.
*/
static int fts3IntegrityMethod(
sqlite3_vtab *pVtab, /* The virtual table to be checked */
const char *zSchema, /* Name of schema in which pVtab lives */
const char *zTabname, /* Name of the pVTab table */
int isQuick, /* True if this is a quick_check */
char **pzErr /* Write error message here */
){
Fts3Table *p = (Fts3Table*)pVtab;
int rc;
int bOk = 0;
UNUSED_PARAMETER(isQuick);
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );
if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS%d table %s.%s: %s",
p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
}else if( bOk==0 ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
p->bFts4 ? 4 : 3, zSchema, zTabname);
}
sqlite3Fts3SegmentsClose(p);
return SQLITE_OK;
}
static const sqlite3_module fts3Module = {
/* iVersion */ 4,
|
| ︙ | ︙ | |||
188000 188001 188002 188003 188004 188005 188006 | /* xRollback */ fts3RollbackMethod, /* xFindFunction */ fts3FindFunctionMethod, /* xRename */ fts3RenameMethod, /* xSavepoint */ fts3SavepointMethod, /* xRelease */ fts3ReleaseMethod, /* xRollbackTo */ fts3RollbackToMethod, /* xShadowName */ fts3ShadowName, | | | 188538 188539 188540 188541 188542 188543 188544 188545 188546 188547 188548 188549 188550 188551 188552 | /* xRollback */ fts3RollbackMethod, /* xFindFunction */ fts3FindFunctionMethod, /* xRename */ fts3RenameMethod, /* xSavepoint */ fts3SavepointMethod, /* xRelease */ fts3ReleaseMethod, /* xRollbackTo */ fts3RollbackToMethod, /* xShadowName */ fts3ShadowName, /* xIntegrity */ fts3IntegrityMethod, }; /* ** This function is registered as the module destructor (called when an ** FTS3 enabled database connection is closed). It frees the memory ** allocated for the tokenizer hash table. */ |
| ︙ | ︙ | |||
199554 199555 199556 199557 199558 199559 199560 | ** content table. If no error occurs and the contents do match, set *pbOk ** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk ** to false before returning. ** ** If an error occurs (e.g. an OOM or IO error), return an SQLite error ** code. The final value of *pbOk is undefined in this case. */ | | | 200092 200093 200094 200095 200096 200097 200098 200099 200100 200101 200102 200103 200104 200105 200106 |
** content table. If no error occurs and the contents do match, set *pbOk
** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk
** to false before returning.
**
** If an error occurs (e.g. an OOM or IO error), return an SQLite error
** code. The final value of *pbOk is undefined in this case.
*/
SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
int rc = SQLITE_OK; /* Return code */
u64 cksum1 = 0; /* Checksum based on FTS index contents */
u64 cksum2 = 0; /* Checksum based on %_content contents */
sqlite3_stmt *pAllLangid = 0; /* Statement to return all language-ids */
/* This block calculates the checksum according to the FTS index. */
rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
|
| ︙ | ︙ | |||
199632 199633 199634 199635 199636 199637 199638 |
}
}
}
sqlite3_finalize(pStmt);
}
| | | 200170 200171 200172 200173 200174 200175 200176 200177 200178 200179 200180 200181 200182 200183 200184 |
}
}
}
sqlite3_finalize(pStmt);
}
*pbOk = (rc==SQLITE_OK && cksum1==cksum2);
return rc;
}
/*
** Run the integrity-check. If no error occurs and the current contents of
** the FTS index are correct, return SQLITE_OK. Or, if the contents of the
** FTS index are incorrect, return SQLITE_CORRUPT_VTAB.
|
| ︙ | ︙ | |||
199672 199673 199674 199675 199676 199677 199678 |
** passed.
*/
static int fts3DoIntegrityCheck(
Fts3Table *p /* FTS3 table handle */
){
int rc;
int bOk = 0;
| | | 200210 200211 200212 200213 200214 200215 200216 200217 200218 200219 200220 200221 200222 200223 200224 |
** passed.
*/
static int fts3DoIntegrityCheck(
Fts3Table *p /* FTS3 table handle */
){
int rc;
int bOk = 0;
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
return rc;
}
/*
** Handle a 'special' INSERT of the form:
**
|
| ︙ | ︙ | |||
202646 202647 202648 202649 202650 202651 202652 | ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** | | | > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > | | > > | 203184 203185 203186 203187 203188 203189 203190 203191 203192 203193 203194 203195 203196 203197 203198 203199 203200 203201 203202 203203 203204 203205 203206 203207 203208 203209 203210 203211 203212 203213 203214 203215 203216 203217 203218 203219 203220 203221 203222 203223 203224 203225 203226 203227 203228 203229 203230 203231 203232 203233 203234 203235 203236 203237 203238 203239 203240 203241 203242 203243 203244 203245 203246 203247 203248 203249 203250 203251 203252 203253 203254 203255 203256 203257 203258 203259 203260 203261 203262 203263 203264 203265 203266 203267 203268 203269 203270 203271 203272 203273 203274 203275 203276 203277 203278 203279 203280 203281 203282 203283 203284 203285 203286 203287 203288 203289 203290 203291 203292 203293 203294 203295 203296 203297 203298 203299 203300 203301 203302 203303 203304 203305 203306 203307 203308 203309 203310 203311 203312 203313 203314 203315 203316 203317 203318 203319 203320 203321 203322 203323 203324 203325 203326 203327 203328 203329 203330 203331 203332 203333 203334 203335 203336 203337 203338 203339 203340 203341 203342 203343 203344 203345 203346 203347 203348 203349 203350 203351 203352 203353 203354 203355 203356 203357 203358 203359 203360 203361 203362 203363 203364 203365 203366 203367 203368 203369 |
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** SQLite JSON functions.
**
** This file began as an extension in ext/misc/json1.c in 2015. That
** extension proved so useful that it has now been moved into the core.
**
** The original design stored all JSON as pure text, canonical RFC-8259.
** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16).
** All generated JSON text still conforms strictly to RFC-8259, but text
** with JSON-5 extensions is accepted as input.
**
** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
** accept BLOB values that have JSON encoded using a binary representation
** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk
** format SQLite JSONB is completely different and incompatible with
** PostgreSQL JSONB.
**
** Decoding and interpreting JSONB is still O(N) where N is the size of
** the input, the same as text JSON. However, the constant of proportionality
** for JSONB is much smaller due to faster parsing. The size of each
** element in JSONB is encoded in its header, so there is no need to search
** for delimiters using persnickety syntax rules. JSONB seems to be about
** 3x faster than text JSON as a result. JSONB is also tends to be slightly
** smaller than text JSON, by 5% or 10%, but there are corner cases where
** JSONB can be slightly larger. So you are not far mistaken to say that
** a JSONB blob is the same size as the equivalent RFC-8259 text.
**
**
** THE JSONB ENCODING:
**
** Every JSON element is encoded in JSONB as a header and a payload.
** The header is between 1 and 9 bytes in size. The payload is zero
** or more bytes.
**
** The lower 4 bits of the first byte of the header determines the
** element type:
**
** 0: NULL
** 1: TRUE
** 2: FALSE
** 3: INT -- RFC-8259 integer literal
** 4: INT5 -- JSON5 integer literal
** 5: FLOAT -- RFC-8259 floating point literal
** 6: FLOAT5 -- JSON5 floating point literal
** 7: TEXT -- Text literal acceptable to both SQL and JSON
** 8: TEXTJ -- Text containing RFC-8259 escapes
** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes
** 10: TEXTRAW -- Text containing unescaped syntax characters
** 11: ARRAY
** 12: OBJECT
**
** The other three possible values (13-15) are reserved for future
** enhancements.
**
** The upper 4 bits of the first byte determine the size of the header
** and sometimes also the size of the payload. If X is the first byte
** of the element and if X>>4 is between 0 and 11, then the payload
** will be that many bytes in size and the header is exactly one byte
** in size. Other four values for X>>4 (12-15) indicate that the header
** is more than one byte in size and that the payload size is determined
** by the remainder of the header, interpreted as a unsigned big-endian
** integer.
**
** Value of X>>4 Size integer Total header size
** ------------- -------------------- -----------------
** 12 1 byte (0-255) 2
** 13 2 byte (0-65535) 3
** 14 4 byte (0-4294967295) 5
** 15 8 byte (0-1.8e19) 9
**
** The payload size need not be expressed in its minimal form. For example,
** if the payload size is 10, the size can be expressed in any of 5 different
** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte,
** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by
** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and
** a single byte of 0x0a. The shorter forms are preferred, of course, but
** sometimes when generating JSONB, the payload size is not known in advance
** and it is convenient to reserve sufficient header space to cover the
** largest possible payload size and then come back later and patch up
** the size when it becomes known, resulting in a non-minimal encoding.
**
** The value (X>>4)==15 is not actually used in the current implementation
** (as SQLite is currently unable handle BLOBs larger than about 2GB)
** but is included in the design to allow for future enhancements.
**
** The payload follows the header. NULL, TRUE, and FALSE have no payload and
** their payload size must always be zero. The payload for INT, INT5,
** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the
** "..." or '...' delimiters are omitted from the various text encodings.
** The payload for ARRAY and OBJECT is a list of additional elements that
** are the content for the array or object. The payload for an OBJECT
** must be an even number of elements. The first element of each pair is
** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW.
**
** A valid JSONB blob consists of a single element, as described above.
** Usually this will be an ARRAY or OBJECT element which has many more
** elements as its content. But the overall blob is just a single element.
**
** Input validation for JSONB blobs simply checks that the element type
** code is between 0 and 12 and that the total size of the element
** (header plus payload) is the same as the size of the BLOB. If those
** checks are true, the BLOB is assumed to be JSONB and processing continues.
** Errors are only raised if some other miscoding is discovered during
** processing.
**
** Additional information can be found in the doc/jsonb.md file of the
** canonical SQLite source tree.
*/
#ifndef SQLITE_OMIT_JSON
/* #include "sqliteInt.h" */
/* JSONB element types
*/
#define JSONB_NULL 0 /* "null" */
#define JSONB_TRUE 1 /* "true" */
#define JSONB_FALSE 2 /* "false" */
#define JSONB_INT 3 /* integer acceptable to JSON and SQL */
#define JSONB_INT5 4 /* integer in 0x000 notation */
#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */
#define JSONB_FLOAT5 6 /* float with JSON5 extensions */
#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */
#define JSONB_TEXTJ 8 /* Text with JSON escapes */
#define JSONB_TEXT5 9 /* Text with JSON-5 escape */
#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */
#define JSONB_ARRAY 11 /* An array */
#define JSONB_OBJECT 12 /* An object */
/* Human-readable names for the JSONB values. The index for each
** string must correspond to the JSONB_* integer above.
*/
static const char * const jsonbType[] = {
"null", "true", "false", "integer", "integer",
"real", "real", "text", "text", "text",
"text", "array", "object", "", "", "", ""
};
/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
/*
** The set of all space characters recognized by jsonIsspace().
** Useful as the second argument to strspn().
*/
static const char jsonSpaces[] = "\011\012\015\040";
/*
** Characters that are special to JSON. Control characters,
** '"' and '\\' and '\''. Actually, '\'' is not special to
** canonical JSON, but it is special in JSON-5, so we include
** it in the set of special characters.
*/
static const char jsonIsOk[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
| ︙ | ︙ | |||
202710 202711 202712 202713 202714 202715 202716 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; | < < < < < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | < < < < < < < < | < < < | | | < < < < < | > > < < < < < < | | < < | | < | | | < < < < < < < < < < < < < < < < < < < < < | < | | > < | | | < < < < < < < < < > > | | | | | | | | > | | | | < | | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | > > | < < < < < | < | < | | > > > | > > > | < > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | | | > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | 203377 203378 203379 203380 203381 203382 203383 203384 203385 203386 203387 203388 203389 203390 203391 203392 203393 203394 203395 203396 203397 203398 203399 203400 203401 203402 203403 203404 203405 203406 203407 203408 203409 203410 203411 203412 203413 203414 203415 203416 203417 203418 203419 203420 203421 203422 203423 203424 203425 203426 203427 203428 203429 203430 203431 203432 203433 203434 203435 203436 203437 203438 203439 203440 203441 203442 203443 203444 203445 203446 203447 203448 203449 203450 203451 203452 203453 203454 203455 203456 203457 203458 203459 203460 203461 203462 203463 203464 203465 203466 203467 203468 203469 203470 203471 203472 203473 203474 203475 203476 203477 203478 203479 203480 203481 203482 203483 203484 203485 203486 203487 203488 203489 203490 203491 203492 203493 203494 203495 203496 203497 203498 203499 203500 203501 203502 203503 203504 203505 203506 203507 203508 203509 203510 203511 203512 203513 203514 203515 203516 203517 203518 203519 203520 203521 203522 203523 203524 203525 203526 203527 203528 203529 203530 203531 203532 203533 203534 203535 203536 203537 203538 203539 203540 203541 203542 203543 203544 203545 203546 203547 203548 203549 203550 203551 203552 203553 203554 203555 203556 203557 203558 203559 203560 203561 203562 203563 203564 203565 203566 203567 203568 203569 203570 203571 203572 203573 203574 203575 203576 203577 203578 203579 203580 203581 203582 203583 203584 203585 203586 203587 203588 203589 203590 203591 203592 203593 203594 203595 203596 203597 203598 203599 203600 203601 203602 203603 203604 203605 203606 203607 203608 203609 203610 203611 203612 203613 203614 203615 203616 203617 203618 203619 203620 203621 203622 203623 203624 203625 203626 203627 203628 203629 203630 203631 203632 203633 203634 203635 203636 203637 203638 203639 203640 203641 203642 203643 203644 203645 203646 203647 203648 203649 203650 203651 203652 203653 203654 203655 203656 203657 203658 203659 203660 203661 203662 203663 203664 203665 203666 203667 203668 203669 203670 203671 203672 203673 203674 203675 203676 203677 203678 203679 203680 203681 203682 203683 203684 203685 203686 203687 203688 203689 203690 203691 203692 203693 203694 203695 203696 203697 203698 203699 203700 203701 203702 203703 203704 203705 203706 203707 203708 203709 203710 203711 203712 203713 203714 203715 203716 203717 203718 203719 203720 203721 203722 203723 203724 203725 203726 203727 203728 203729 203730 203731 203732 203733 203734 203735 203736 203737 203738 203739 203740 203741 203742 203743 203744 203745 203746 203747 203748 203749 203750 203751 203752 203753 203754 203755 203756 203757 203758 203759 203760 203761 203762 203763 203764 203765 203766 203767 203768 203769 203770 203771 203772 203773 203774 203775 203776 203777 203778 203779 203780 203781 203782 203783 203784 203785 203786 203787 203788 203789 203790 203791 203792 203793 203794 203795 203796 203797 203798 203799 203800 203801 203802 203803 203804 203805 203806 203807 203808 203809 203810 203811 203812 203813 203814 203815 203816 203817 203818 203819 203820 203821 203822 203823 203824 203825 203826 203827 203828 203829 203830 203831 203832 203833 203834 203835 203836 203837 203838 203839 203840 203841 203842 203843 203844 203845 203846 203847 203848 203849 203850 203851 203852 203853 203854 203855 203856 203857 203858 203859 203860 203861 203862 203863 203864 203865 203866 203867 203868 203869 203870 203871 203872 203873 203874 203875 203876 203877 203878 203879 203880 203881 203882 203883 203884 203885 203886 203887 203888 203889 203890 203891 203892 203893 203894 203895 203896 203897 203898 203899 203900 203901 203902 203903 203904 203905 203906 203907 203908 203909 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
/* Objects */
typedef struct JsonCache JsonCache;
typedef struct JsonString JsonString;
typedef struct JsonParse JsonParse;
/*
** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
*/
#define JSON_CACHE_ID (-429938) /* Cache entry */
#define JSON_CACHE_SIZE 4 /* Max number of cache entries */
/*
** jsonUnescapeOneChar() returns this invalid code point if it encounters
** a syntax error.
*/
#define JSON_INVALID_CHAR 0x99999
/* A cache mapping JSON text into JSONB blobs.
**
** Each cache entry is a JsonParse object with the following restrictions:
**
** * The bReadOnly flag must be set
**
** * The aBlob[] array must be owned by the JsonParse object. In other
** words, nBlobAlloc must be non-zero.
**
** * eEdit and delta must be zero.
**
** * zJson must be an RCStr. In other words bJsonIsRCStr must be true.
*/
struct JsonCache {
sqlite3 *db; /* Database connection */
int nUsed; /* Number of active entries in the cache */
JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */
};
/* An instance of this object represents a JSON string
** under construction. Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON.
**
** If the generated string is longer than will fit into the zSpace[] buffer,
** then it will be an RCStr string. This aids with caching of large
** JSON strings.
*/
struct JsonString {
sqlite3_context *pCtx; /* Function context - put error messages here */
char *zBuf; /* Append JSON content here */
u64 nAlloc; /* Bytes of storage available in zBuf[] */
u64 nUsed; /* Bytes of zBuf[] currently used */
u8 bStatic; /* True if zBuf is static space */
u8 eErr; /* True if an error has been encountered */
char zSpace[100]; /* Initial static space */
};
/* Allowed values for JsonString.eErr */
#define JSTRING_OOM 0x01 /* Out of memory */
#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */
#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */
/* The "subtype" set for text JSON values passed through using
** sqlite3_result_subtype() and sqlite3_value_subtype().
*/
#define JSON_SUBTYPE 74 /* Ascii for "J" */
/*
** Bit values for the flags passed into various SQL function implementations
** via the sqlite3_user_data() value.
*/
#define JSON_JSON 0x01 /* Result is always JSON */
#define JSON_SQL 0x02 /* Result is always SQL */
#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
#define JSON_BLOB 0x08 /* Use the BLOB output format */
/* A parsed JSON value. Lifecycle:
**
** 1. JSON comes in and is parsed into a JSONB value in aBlob. The
** original text is stored in zJson. This step is skipped if the
** input is JSONB instead of text JSON.
**
** 2. The aBlob[] array is searched using the JSON path notation, if needed.
**
** 3. Zero or more changes are made to aBlob[] (via json_remove() or
** json_replace() or json_patch() or similar).
**
** 4. New JSON text is generated from the aBlob[] for output. This step
** is skipped if the function is one of the jsonb_* functions that
** returns JSONB instead of text JSON.
*/
struct JsonParse {
u8 *aBlob; /* JSONB representation of JSON value */
u32 nBlob; /* Bytes of aBlob[] actually used */
u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */
char *zJson; /* Json text used for parsing */
sqlite3 *db; /* The database connection to which this object belongs */
int nJson; /* Length of the zJson string in bytes */
u32 nJPRef; /* Number of references to this object */
u32 iErr; /* Error location in zJson[] */
u16 iDepth; /* Nesting depth */
u8 nErr; /* Number of errors seen */
u8 oom; /* Set to true if out of memory */
u8 bJsonIsRCStr; /* True if zJson is an RCStr */
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
u8 bReadOnly; /* Do not modify. */
/* Search and edit information. See jsonLookupStep() */
u8 eEdit; /* Edit operation to apply */
int delta; /* Size change due to the edit */
u32 nIns; /* Number of bytes to insert */
u32 iLabel; /* Location of label if search landed on an object value */
u8 *aIns; /* Content to be inserted */
};
/* Allowed values for JsonParse.eEdit */
#define JEDIT_DEL 1 /* Delete if exists */
#define JEDIT_REPL 2 /* Overwrite if exists */
#define JEDIT_INS 3 /* Insert if not exists */
#define JEDIT_SET 4 /* Insert or overwrite */
/*
** Maximum nesting depth of JSON for this implementation.
**
** This limit is needed to avoid a stack overflow in the recursive
** descent parser. A depth of 1000 is far deeper than any sane JSON
** should go. Historical note: This limit was 2000 prior to version 3.42.0
*/
#ifndef SQLITE_JSON_MAX_DEPTH
# define JSON_MAX_DEPTH 1000
#else
# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH
#endif
/*
** Allowed values for the flgs argument to jsonParseFuncArg();
*/
#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */
#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */
/**************************************************************************
** Forward references
**************************************************************************/
static void jsonReturnStringAsBlob(JsonString*);
static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
static void jsonReturnParse(sqlite3_context*,JsonParse*);
static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
static void jsonParseFree(JsonParse*);
static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
static u32 jsonUnescapeOneChar(const char*, u32, u32*);
/**************************************************************************
** Utility routines for dealing with JsonCache objects
**************************************************************************/
/*
** Free a JsonCache object.
*/
static void jsonCacheDelete(JsonCache *p){
int i;
for(i=0; i<p->nUsed; i++){
jsonParseFree(p->a[i]);
}
sqlite3DbFree(p->db, p);
}
static void jsonCacheDeleteGeneric(void *p){
jsonCacheDelete((JsonCache*)p);
}
/*
** Insert a new entry into the cache. If the cache is full, expel
** the least recently used entry. Return SQLITE_OK on success or a
** result code otherwise.
**
** Cache entries are stored in age order, oldest first.
*/
static int jsonCacheInsert(
sqlite3_context *ctx, /* The SQL statement context holding the cache */
JsonParse *pParse /* The parse object to be added to the cache */
){
JsonCache *p;
assert( pParse->zJson!=0 );
assert( pParse->bJsonIsRCStr );
assert( pParse->delta==0 );
p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
if( p==0 ){
sqlite3 *db = sqlite3_context_db_handle(ctx);
p = sqlite3DbMallocZero(db, sizeof(*p));
if( p==0 ) return SQLITE_NOMEM;
p->db = db;
sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric);
p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
if( p==0 ) return SQLITE_NOMEM;
}
if( p->nUsed >= JSON_CACHE_SIZE ){
jsonParseFree(p->a[0]);
memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0]));
p->nUsed = JSON_CACHE_SIZE-1;
}
assert( pParse->nBlobAlloc>0 );
pParse->eEdit = 0;
pParse->nJPRef++;
pParse->bReadOnly = 1;
p->a[p->nUsed] = pParse;
p->nUsed++;
return SQLITE_OK;
}
/*
** Search for a cached translation the json text supplied by pArg. Return
** the JsonParse object if found. Return NULL if not found.
**
** When a match if found, the matching entry is moved to become the
** most-recently used entry if it isn't so already.
**
** The JsonParse object returned still belongs to the Cache and might
** be deleted at any moment. If the caller whants the JsonParse to
** linger, it needs to increment the nPJRef reference counter.
*/
static JsonParse *jsonCacheSearch(
sqlite3_context *ctx, /* The SQL statement context holding the cache */
sqlite3_value *pArg /* Function argument containing SQL text */
){
JsonCache *p;
int i;
const char *zJson;
int nJson;
if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){
return 0;
}
zJson = (const char*)sqlite3_value_text(pArg);
if( zJson==0 ) return 0;
nJson = sqlite3_value_bytes(pArg);
p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
if( p==0 ){
return 0;
}
for(i=0; i<p->nUsed; i++){
if( p->a[i]->zJson==zJson ) break;
}
if( i>=p->nUsed ){
for(i=0; i<p->nUsed; i++){
if( p->a[i]->nJson!=nJson ) continue;
if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break;
}
}
if( i<p->nUsed ){
if( i<p->nUsed-1 ){
/* Make the matching entry the most recently used entry */
JsonParse *tmp = p->a[i];
memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp));
p->a[p->nUsed-1] = tmp;
i = p->nUsed - 1;
}
assert( p->a[i]->delta==0 );
return p->a[i];
}else{
return 0;
}
}
/**************************************************************************
** Utility routines for dealing with JsonString objects
**************************************************************************/
/* Turn uninitialized bulk memory into a valid JsonString object
** holding a zero-length string.
*/
static void jsonStringZero(JsonString *p){
p->zBuf = p->zSpace;
p->nAlloc = sizeof(p->zSpace);
p->nUsed = 0;
p->bStatic = 1;
}
/* Initialize the JsonString object
*/
static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){
p->pCtx = pCtx;
p->eErr = 0;
jsonStringZero(p);
}
/* Free all allocated memory and reset the JsonString object back to its
** initial state.
*/
static void jsonStringReset(JsonString *p){
if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf);
jsonStringZero(p);
}
/* Report an out-of-memory (OOM) condition
*/
static void jsonStringOom(JsonString *p){
p->eErr |= JSTRING_OOM;
if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx);
jsonStringReset(p);
}
/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success. Return non-zero on an OOM error
*/
static int jsonStringGrow(JsonString *p, u32 N){
u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
char *zNew;
if( p->bStatic ){
if( p->eErr ) return 1;
zNew = sqlite3RCStrNew(nTotal);
if( zNew==0 ){
jsonStringOom(p);
return SQLITE_NOMEM;
}
memcpy(zNew, p->zBuf, (size_t)p->nUsed);
p->zBuf = zNew;
p->bStatic = 0;
}else{
p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal);
if( p->zBuf==0 ){
p->eErr |= JSTRING_OOM;
jsonStringZero(p);
return SQLITE_NOMEM;
}
}
p->nAlloc = nTotal;
return SQLITE_OK;
}
/* Append N bytes from zIn onto the end of the JsonString string.
*/
static SQLITE_NOINLINE void jsonStringExpandAndAppend(
JsonString *p,
const char *zIn,
u32 N
){
assert( N>0 );
if( jsonStringGrow(p,N) ) return;
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
if( N==0 ) return;
if( N+p->nUsed >= p->nAlloc ){
jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
}
static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
assert( N>0 );
if( N+p->nUsed >= p->nAlloc ){
jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
}
/* Append formatted text (not to exceed N bytes) to the JsonString.
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
va_list ap;
if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return;
va_start(ap, zFormat);
sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
va_end(ap);
p->nUsed += (int)strlen(p->zBuf+p->nUsed);
}
/* Append a single character
*/
static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){
if( jsonStringGrow(p,1) ) return;
p->zBuf[p->nUsed++] = c;
}
static void jsonAppendChar(JsonString *p, char c){
if( p->nUsed>=p->nAlloc ){
jsonAppendCharExpand(p,c);
}else{
p->zBuf[p->nUsed++] = c;
}
}
/* Remove a single character from the end of the string
*/
static void jsonStringTrimOneChar(JsonString *p){
if( p->eErr==0 ){
assert( p->nUsed>0 );
p->nUsed--;
}
}
/* Make sure there is a zero terminator on p->zBuf[]
**
** Return true on success. Return false if an OOM prevents this
** from happening.
*/
static int jsonStringTerminate(JsonString *p){
jsonAppendChar(p, 0);
jsonStringTrimOneChar(p);
return p->eErr==0;
}
/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
*/
static void jsonAppendSeparator(JsonString *p){
char c;
if( p->nUsed==0 ) return;
c = p->zBuf[p->nUsed-1];
if( c=='[' || c=='{' ) return;
jsonAppendChar(p, ',');
}
/* Append the N-byte string in zIn to the end of the JsonString string
** under construction. Enclose the string in double-quotes ("...") and
** escape any double-quotes or backslash characters contained within the
** string.
**
** This routine is a high-runner. There is a measurable performance
** increase associated with unwinding the jsonIsOk[] loop.
*/
static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
u32 k;
u8 c;
const u8 *z = (const u8*)zIn;
if( z==0 ) return;
if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return;
p->zBuf[p->nUsed++] = '"';
while( 1 /*exit-by-break*/ ){
k = 0;
/* The following while() is the 4-way unwound equivalent of
**
** while( k<N && jsonIsOk[z[k]] ){ k++; }
*/
while( 1 /* Exit by break */ ){
if( k+3>=N ){
while( k<N && jsonIsOk[z[k]] ){ k++; }
break;
}
if( !jsonIsOk[z[k]] ){
break;
}
if( !jsonIsOk[z[k+1]] ){
k += 1;
break;
}
if( !jsonIsOk[z[k+2]] ){
k += 2;
break;
}
if( !jsonIsOk[z[k+3]] ){
k += 3;
break;
}else{
k += 4;
}
}
if( k>=N ){
if( k>0 ){
memcpy(&p->zBuf[p->nUsed], z, k);
p->nUsed += k;
}
break;
}
if( k>0 ){
memcpy(&p->zBuf[p->nUsed], z, k);
p->nUsed += k;
z += k;
N -= k;
}
c = z[0];
if( c=='"' || c=='\\' ){
json_simple_escape:
if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
p->zBuf[p->nUsed++] = c;
}else if( c=='\'' ){
p->zBuf[p->nUsed++] = c;
}else{
static const char aSpecial[] = {
0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
assert( sizeof(aSpecial)==32 );
assert( aSpecial['\b']=='b' );
assert( aSpecial['\f']=='f' );
assert( aSpecial['\n']=='n' );
assert( aSpecial['\r']=='r' );
assert( aSpecial['\t']=='t' );
assert( c>=0 && c<sizeof(aSpecial) );
if( aSpecial[c] ){
c = aSpecial[c];
goto json_simple_escape;
}
if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
p->zBuf[p->nUsed++] = 'u';
p->zBuf[p->nUsed++] = '0';
p->zBuf[p->nUsed++] = '0';
p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
}
z++;
N--;
}
p->zBuf[p->nUsed++] = '"';
assert( p->nUsed<p->nAlloc );
}
/*
** Append an sqlite3_value (such as a function parameter) to the JSON
** string under construction in p.
*/
static void jsonAppendSqlValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
){
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
jsonAppendRawNZ(p, "null", 4);
break;
|
| ︙ | ︙ | |||
203221 203222 203223 203224 203225 203226 203227 |
jsonAppendRaw(p, z, n);
}else{
jsonAppendString(p, z, n);
}
break;
}
default: {
| > > > > > > | | | | | | > > > > | > > > > > > | > > > | | > > | > > > > > > > > > | | > | | > > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | > | | > | | < < < < > | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < | < < < < < < < < < < < < < < > | | > < | | | > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 203925 203926 203927 203928 203929 203930 203931 203932 203933 203934 203935 203936 203937 203938 203939 203940 203941 203942 203943 203944 203945 203946 203947 203948 203949 203950 203951 203952 203953 203954 203955 203956 203957 203958 203959 203960 203961 203962 203963 203964 203965 203966 203967 203968 203969 203970 203971 203972 203973 203974 203975 203976 203977 203978 203979 203980 203981 203982 203983 203984 203985 203986 203987 203988 203989 203990 203991 203992 203993 203994 203995 203996 203997 203998 203999 204000 204001 204002 204003 204004 204005 204006 204007 204008 204009 204010 204011 204012 204013 204014 204015 204016 204017 204018 204019 204020 204021 204022 204023 204024 204025 204026 204027 204028 204029 204030 204031 204032 204033 204034 204035 204036 204037 204038 204039 204040 204041 204042 204043 204044 204045 204046 204047 204048 204049 204050 204051 204052 204053 204054 204055 204056 204057 204058 204059 204060 204061 204062 204063 204064 204065 204066 204067 204068 204069 204070 204071 204072 204073 204074 204075 |
jsonAppendRaw(p, z, n);
}else{
jsonAppendString(p, z, n);
}
break;
}
default: {
if( jsonFuncArgMightBeBinary(pValue) ){
JsonParse px;
memset(&px, 0, sizeof(px));
px.aBlob = (u8*)sqlite3_value_blob(pValue);
px.nBlob = sqlite3_value_bytes(pValue);
jsonTranslateBlobToText(&px, 0, p);
}else if( p->eErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
p->eErr = JSTRING_ERR;
jsonStringReset(p);
}
break;
}
}
}
/* Make the text in p (which is probably a generated JSON text string)
** the result of the SQL function.
**
** The JsonString is reset.
**
** If pParse and ctx are both non-NULL, then the SQL string in p is
** loaded into the zJson field of the pParse object as a RCStr and the
** pParse is added to the cache.
*/
static void jsonReturnString(
JsonString *p, /* String to return */
JsonParse *pParse, /* JSONB source or NULL */
sqlite3_context *ctx /* Where to cache */
){
assert( (pParse!=0)==(ctx!=0) );
assert( ctx==0 || ctx==p->pCtx );
if( p->eErr==0 ){
int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx));
if( flags & JSON_BLOB ){
jsonReturnStringAsBlob(p);
}else if( p->bStatic ){
sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
SQLITE_TRANSIENT, SQLITE_UTF8);
}else if( jsonStringTerminate(p) ){
if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){
int rc;
pParse->zJson = sqlite3RCStrRef(p->zBuf);
pParse->nJson = p->nUsed;
pParse->bJsonIsRCStr = 1;
rc = jsonCacheInsert(ctx, pParse);
if( rc==SQLITE_NOMEM ){
sqlite3_result_error_nomem(ctx);
jsonStringReset(p);
return;
}
}
sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed,
sqlite3RCStrUnref,
SQLITE_UTF8);
}else{
sqlite3_result_error_nomem(p->pCtx);
}
}else if( p->eErr & JSTRING_OOM ){
sqlite3_result_error_nomem(p->pCtx);
}else if( p->eErr & JSTRING_MALFORMED ){
sqlite3_result_error(p->pCtx, "malformed JSON", -1);
}
jsonStringReset(p);
}
/**************************************************************************
** Utility routines for dealing with JsonParse objects
**************************************************************************/
/*
** Reclaim all memory allocated by a JsonParse object. But do not
** delete the JsonParse object itself.
*/
static void jsonParseReset(JsonParse *pParse){
assert( pParse->nJPRef<=1 );
if( pParse->bJsonIsRCStr ){
sqlite3RCStrUnref(pParse->zJson);
pParse->zJson = 0;
pParse->nJson = 0;
pParse->bJsonIsRCStr = 0;
}
if( pParse->nBlobAlloc ){
sqlite3DbFree(pParse->db, pParse->aBlob);
pParse->aBlob = 0;
pParse->nBlob = 0;
pParse->nBlobAlloc = 0;
}
}
/*
** Decrement the reference count on the JsonParse object. When the
** count reaches zero, free the object.
*/
static void jsonParseFree(JsonParse *pParse){
if( pParse ){
if( pParse->nJPRef>1 ){
pParse->nJPRef--;
}else{
jsonParseReset(pParse);
sqlite3DbFree(pParse->db, pParse);
}
}
}
/**************************************************************************
** Utility routines for the JSON text parser
**************************************************************************/
/*
** Translate a single byte of Hex into an integer.
** This routine only gives a correct answer if h really is a valid hexadecimal
** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not
** assert() if the digit is not hex.
*/
static u8 jsonHexToInt(int h){
#ifdef SQLITE_ASCII
h += 9*(1&(h>>6));
#endif
#ifdef SQLITE_EBCDIC
h += 9*(1&~(h>>4));
#endif
return (u8)(h & 0xf);
}
/*
** Convert a 4-byte hex string into an integer
*/
static u32 jsonHexToInt4(const char *z){
u32 v;
v = (jsonHexToInt(z[0])<<12)
+ (jsonHexToInt(z[1])<<8)
+ (jsonHexToInt(z[2])<<4)
+ jsonHexToInt(z[3]);
return v;
}
/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]);
}
|
| ︙ | ︙ | |||
203955 203956 203957 203958 203959 203960 203961 |
char c2;
char n;
char eType;
char nRepl;
char *zMatch;
char *zRepl;
} aNanInfName[] = {
| | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > > | | | | | | | | | > | < > | | > | > | | > | | | < | > | | | | | | | > | > | < | | | | | | | | | | > | > > > > > > > > > | | | > > > | | | | | | < | < | | | | | | > | | | | | > > > > | | 204215 204216 204217 204218 204219 204220 204221 204222 204223 204224 204225 204226 204227 204228 204229 204230 204231 204232 204233 204234 204235 204236 204237 204238 204239 204240 204241 204242 204243 204244 204245 204246 204247 204248 204249 204250 204251 204252 204253 204254 204255 204256 204257 204258 204259 204260 204261 204262 204263 204264 204265 204266 204267 204268 204269 204270 204271 204272 204273 204274 204275 204276 204277 204278 204279 204280 204281 204282 204283 204284 204285 204286 204287 204288 204289 204290 204291 204292 204293 204294 204295 204296 204297 204298 204299 204300 204301 204302 204303 204304 204305 204306 204307 204308 204309 204310 204311 204312 204313 204314 204315 204316 204317 204318 204319 204320 204321 204322 204323 204324 204325 204326 204327 204328 204329 204330 204331 204332 204333 204334 204335 204336 204337 204338 204339 204340 204341 204342 204343 204344 204345 204346 204347 204348 204349 204350 204351 204352 204353 204354 204355 204356 204357 204358 204359 204360 204361 204362 204363 204364 204365 204366 204367 204368 204369 204370 204371 204372 204373 204374 204375 204376 204377 204378 204379 204380 204381 204382 204383 204384 204385 204386 204387 204388 204389 204390 204391 204392 204393 204394 204395 204396 204397 204398 204399 204400 204401 204402 204403 204404 204405 204406 204407 204408 204409 204410 204411 204412 204413 204414 204415 204416 204417 204418 204419 204420 204421 204422 204423 204424 204425 204426 204427 204428 204429 204430 204431 204432 204433 204434 204435 204436 204437 204438 204439 204440 204441 204442 204443 204444 204445 204446 204447 204448 204449 204450 204451 204452 204453 204454 204455 204456 204457 204458 204459 204460 204461 204462 204463 204464 204465 204466 204467 204468 204469 204470 204471 204472 204473 204474 204475 204476 204477 204478 204479 204480 204481 204482 204483 204484 204485 204486 204487 204488 204489 204490 204491 204492 204493 204494 204495 204496 204497 204498 204499 204500 204501 204502 204503 204504 204505 204506 204507 204508 204509 204510 204511 204512 204513 204514 204515 204516 204517 204518 204519 204520 204521 204522 204523 204524 204525 204526 204527 204528 204529 204530 204531 204532 204533 204534 204535 204536 204537 204538 204539 204540 204541 204542 204543 204544 204545 204546 204547 204548 204549 204550 204551 204552 204553 204554 204555 204556 204557 204558 204559 204560 204561 204562 204563 204564 204565 204566 204567 204568 204569 204570 204571 204572 204573 204574 204575 204576 204577 204578 204579 204580 204581 204582 204583 204584 204585 204586 204587 204588 204589 204590 204591 204592 204593 204594 204595 204596 204597 204598 204599 204600 204601 204602 204603 204604 204605 204606 204607 204608 204609 204610 204611 204612 204613 204614 204615 204616 204617 204618 204619 204620 204621 204622 204623 204624 204625 204626 204627 204628 204629 204630 204631 204632 204633 204634 204635 204636 204637 204638 204639 204640 204641 204642 204643 204644 204645 204646 204647 204648 204649 204650 204651 204652 204653 204654 204655 204656 204657 204658 204659 204660 204661 204662 204663 204664 204665 204666 204667 204668 204669 204670 204671 204672 204673 204674 204675 204676 204677 204678 204679 204680 204681 204682 204683 204684 204685 204686 204687 204688 204689 204690 204691 204692 204693 204694 204695 204696 204697 204698 204699 204700 204701 204702 204703 204704 204705 204706 204707 204708 204709 204710 204711 204712 204713 204714 204715 204716 204717 204718 204719 204720 204721 204722 204723 204724 204725 204726 204727 204728 204729 204730 204731 204732 204733 204734 204735 204736 204737 204738 204739 204740 204741 204742 204743 204744 204745 204746 204747 204748 204749 204750 204751 204752 204753 204754 204755 204756 204757 204758 204759 204760 204761 204762 204763 204764 204765 204766 204767 204768 204769 204770 204771 204772 204773 204774 204775 204776 204777 204778 204779 204780 204781 204782 204783 204784 204785 204786 204787 204788 204789 204790 204791 204792 204793 204794 204795 204796 204797 204798 204799 204800 204801 204802 204803 204804 204805 204806 204807 204808 204809 204810 204811 204812 204813 204814 204815 204816 204817 204818 204819 204820 204821 204822 204823 204824 204825 204826 204827 204828 204829 204830 204831 204832 204833 204834 204835 204836 204837 204838 204839 204840 204841 204842 204843 204844 204845 204846 204847 204848 204849 204850 204851 204852 204853 204854 204855 204856 204857 204858 204859 204860 204861 204862 204863 204864 204865 204866 204867 204868 204869 204870 204871 204872 204873 204874 204875 204876 204877 204878 204879 204880 204881 204882 204883 204884 204885 204886 204887 204888 204889 204890 204891 204892 204893 204894 204895 204896 204897 204898 204899 204900 204901 204902 204903 204904 204905 204906 204907 204908 204909 204910 204911 204912 204913 204914 204915 204916 204917 204918 204919 204920 204921 204922 204923 204924 204925 204926 204927 204928 204929 204930 204931 204932 204933 204934 204935 204936 204937 204938 204939 204940 204941 204942 204943 204944 204945 204946 204947 204948 204949 204950 204951 204952 204953 204954 204955 204956 204957 204958 204959 204960 204961 204962 204963 204964 204965 204966 204967 204968 204969 204970 204971 204972 204973 204974 204975 204976 204977 204978 204979 204980 204981 204982 204983 204984 204985 204986 204987 204988 204989 204990 204991 204992 204993 204994 204995 204996 204997 204998 204999 205000 205001 205002 205003 205004 205005 205006 205007 205008 205009 205010 205011 205012 205013 205014 205015 205016 205017 205018 205019 205020 205021 205022 205023 205024 205025 205026 205027 205028 205029 205030 205031 205032 205033 205034 205035 205036 205037 205038 205039 205040 205041 205042 205043 205044 205045 205046 205047 205048 205049 205050 205051 205052 205053 205054 205055 205056 205057 |
char c2;
char n;
char eType;
char nRepl;
char *zMatch;
char *zRepl;
} aNanInfName[] = {
{ 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" },
{ 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" },
{ 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" },
{ 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" },
{ 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" },
};
/*
** Report the wrong number of arguments for json_insert(), json_replace()
** or json_set().
*/
static void jsonWrongNumArgs(
sqlite3_context *pCtx,
const char *zFuncName
){
char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
zFuncName);
sqlite3_result_error(pCtx, zMsg, -1);
sqlite3_free(zMsg);
}
/****************************************************************************
** Utility routines for dealing with the binary BLOB representation of JSON
****************************************************************************/
/*
** Expand pParse->aBlob so that it holds at least N bytes.
**
** Return the number of errors.
*/
static int jsonBlobExpand(JsonParse *pParse, u32 N){
u8 *aNew;
u32 t;
assert( N>pParse->nBlobAlloc );
if( pParse->nBlobAlloc==0 ){
t = 100;
}else{
t = pParse->nBlobAlloc*2;
}
if( t<N ) t = N+100;
aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t);
if( aNew==0 ){ pParse->oom = 1; return 1; }
pParse->aBlob = aNew;
pParse->nBlobAlloc = t;
return 0;
}
/*
** If pParse->aBlob is not previously editable (because it is taken
** from sqlite3_value_blob(), as indicated by the fact that
** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable
** by making a copy into space obtained from malloc.
**
** Return true on success. Return false on OOM.
*/
static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){
u8 *aOld;
u32 nSize;
assert( !pParse->bReadOnly );
if( pParse->oom ) return 0;
if( pParse->nBlobAlloc>0 ) return 1;
aOld = pParse->aBlob;
nSize = pParse->nBlob + nExtra;
pParse->aBlob = 0;
if( jsonBlobExpand(pParse, nSize) ){
return 0;
}
assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra );
memcpy(pParse->aBlob, aOld, pParse->nBlob);
return 1;
}
/* Expand pParse->aBlob and append one bytes.
*/
static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte(
JsonParse *pParse,
u8 c
){
jsonBlobExpand(pParse, pParse->nBlob+1);
if( pParse->oom==0 ){
assert( pParse->nBlob+1<=pParse->nBlobAlloc );
pParse->aBlob[pParse->nBlob++] = c;
}
}
/* Append a single character.
*/
static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
if( pParse->nBlob >= pParse->nBlobAlloc ){
jsonBlobExpandAndAppendOneByte(pParse, c);
}else{
pParse->aBlob[pParse->nBlob++] = c;
}
}
/* Slow version of jsonBlobAppendNode() that first resizes the
** pParse->aBlob structure.
*/
static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*);
static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode(
JsonParse *pParse,
u8 eType,
u32 szPayload,
const void *aPayload
){
if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return;
jsonBlobAppendNode(pParse, eType, szPayload, aPayload);
}
/* Append an node type byte together with the payload size and
** possibly also the payload.
**
** If aPayload is not NULL, then it is a pointer to the payload which
** is also appended. If aPayload is NULL, the pParse->aBlob[] array
** is resized (if necessary) so that it is big enough to hold the
** payload, but the payload is not appended and pParse->nBlob is left
** pointing to where the first byte of payload will eventually be.
*/
static void jsonBlobAppendNode(
JsonParse *pParse, /* The JsonParse object under construction */
u8 eType, /* Node type. One of JSONB_* */
u32 szPayload, /* Number of bytes of payload */
const void *aPayload /* The payload. Might be NULL */
){
u8 *a;
if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){
jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload);
return;
}
assert( pParse->aBlob!=0 );
a = &pParse->aBlob[pParse->nBlob];
if( szPayload<=11 ){
a[0] = eType | (szPayload<<4);
pParse->nBlob += 1;
}else if( szPayload<=0xff ){
a[0] = eType | 0xc0;
a[1] = szPayload & 0xff;
pParse->nBlob += 2;
}else if( szPayload<=0xffff ){
a[0] = eType | 0xd0;
a[1] = (szPayload >> 8) & 0xff;
a[2] = szPayload & 0xff;
pParse->nBlob += 3;
}else{
a[0] = eType | 0xe0;
a[1] = (szPayload >> 24) & 0xff;
a[2] = (szPayload >> 16) & 0xff;
a[3] = (szPayload >> 8) & 0xff;
a[4] = szPayload & 0xff;
pParse->nBlob += 5;
}
if( aPayload ){
pParse->nBlob += szPayload;
memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload);
}
}
/* Change the payload size for the node at index i to be szPayload.
*/
static int jsonBlobChangePayloadSize(
JsonParse *pParse,
u32 i,
u32 szPayload
){
u8 *a;
u8 szType;
u8 nExtra;
u8 nNeeded;
int delta;
if( pParse->oom ) return 0;
a = &pParse->aBlob[i];
szType = a[0]>>4;
if( szType<=11 ){
nExtra = 0;
}else if( szType==12 ){
nExtra = 1;
}else if( szType==13 ){
nExtra = 2;
}else{
nExtra = 4;
}
if( szPayload<=11 ){
nNeeded = 0;
}else if( szPayload<=0xff ){
nNeeded = 1;
}else if( szPayload<=0xffff ){
nNeeded = 2;
}else{
nNeeded = 4;
}
delta = nNeeded - nExtra;
if( delta ){
u32 newSize = pParse->nBlob + delta;
if( delta>0 ){
if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){
return 0; /* OOM error. Error state recorded in pParse->oom. */
}
a = &pParse->aBlob[i];
memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1));
}else{
memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta));
}
pParse->nBlob = newSize;
}
if( nNeeded==0 ){
a[0] = (a[0] & 0x0f) | (szPayload<<4);
}else if( nNeeded==1 ){
a[0] = (a[0] & 0x0f) | 0xc0;
a[1] = szPayload & 0xff;
}else if( nNeeded==2 ){
a[0] = (a[0] & 0x0f) | 0xd0;
a[1] = (szPayload >> 8) & 0xff;
a[2] = szPayload & 0xff;
}else{
a[0] = (a[0] & 0x0f) | 0xe0;
a[1] = (szPayload >> 24) & 0xff;
a[2] = (szPayload >> 16) & 0xff;
a[3] = (szPayload >> 8) & 0xff;
a[4] = szPayload & 0xff;
}
return delta;
}
/*
** If z[0] is 'u' and is followed by exactly 4 hexadecimal character,
** then set *pOp to JSONB_TEXTJ and return true. If not, do not make
** any changes to *pOp and return false.
*/
static int jsonIs4HexB(const char *z, int *pOp){
if( z[0]!='u' ) return 0;
if( !jsonIs4Hex(&z[1]) ) return 0;
*pOp = JSONB_TEXTJ;
return 1;
}
/*
** Check a single element of the JSONB in pParse for validity.
**
** The element to be checked starts at offset i and must end at on the
** last byte before iEnd.
**
** Return 0 if everything is correct. Return the 1-based byte offset of the
** error if a problem is detected. (In other words, if the error is at offset
** 0, return 1).
*/
static u32 jsonbValidityCheck(
const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */
u32 i, /* Start of element as pParse->aBlob[i] */
u32 iEnd, /* One more than the last byte of the element */
u32 iDepth /* Current nesting depth */
){
u32 n, sz, j, k;
const u8 *z;
u8 x;
if( iDepth>JSON_MAX_DEPTH ) return i+1;
sz = 0;
n = jsonbPayloadSize(pParse, i, &sz);
if( NEVER(n==0) ) return i+1; /* Checked by caller */
if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */
z = pParse->aBlob;
x = z[i] & 0x0f;
switch( x ){
case JSONB_NULL:
case JSONB_TRUE:
case JSONB_FALSE: {
return n+sz==1 ? 0 : i+1;
}
case JSONB_INT: {
if( sz<1 ) return i+1;
j = i+n;
if( z[j]=='-' ){
j++;
if( sz<2 ) return i+1;
}
k = i+n+sz;
while( j<k ){
if( sqlite3Isdigit(z[j]) ){
j++;
}else{
return j+1;
}
}
return 0;
}
case JSONB_INT5: {
if( sz<3 ) return i+1;
j = i+n;
if( z[j]=='-' ){
if( sz<4 ) return i+1;
j++;
}
if( z[j]!='0' ) return i+1;
if( z[j+1]!='x' && z[j+1]!='X' ) return j+2;
j += 2;
k = i+n+sz;
while( j<k ){
if( sqlite3Isxdigit(z[j]) ){
j++;
}else{
return j+1;
}
}
return 0;
}
case JSONB_FLOAT:
case JSONB_FLOAT5: {
u8 seen = 0; /* 0: initial. 1: '.' seen 2: 'e' seen */
if( sz<2 ) return i+1;
j = i+n;
k = j+sz;
if( z[j]=='-' ){
j++;
if( sz<3 ) return i+1;
}
if( z[j]=='.' ){
if( x==JSONB_FLOAT ) return j+1;
if( !sqlite3Isdigit(z[j+1]) ) return j+1;
j += 2;
seen = 1;
}else if( z[j]=='0' && x==JSONB_FLOAT ){
if( j+3>k ) return j+1;
if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1;
j++;
}
for(; j<k; j++){
if( sqlite3Isdigit(z[j]) ) continue;
if( z[j]=='.' ){
if( seen>0 ) return j+1;
if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){
return j+1;
}
seen = 1;
continue;
}
if( z[j]=='e' || z[j]=='E' ){
if( seen==2 ) return j+1;
if( j==k-1 ) return j+1;
if( z[j+1]=='+' || z[j+1]=='-' ){
j++;
if( j==k-1 ) return j+1;
}
seen = 2;
continue;
}
return j+1;
}
if( seen==0 ) return i+1;
return 0;
}
case JSONB_TEXT: {
j = i+n;
k = j+sz;
while( j<k ){
if( !jsonIsOk[z[j]] && z[j]!='\'' ) return j+1;
j++;
}
return 0;
}
case JSONB_TEXTJ:
case JSONB_TEXT5: {
j = i+n;
k = j+sz;
while( j<k ){
if( !jsonIsOk[z[j]] && z[j]!='\'' ){
if( z[j]=='"' ){
if( x==JSONB_TEXTJ ) return j+1;
}else if( z[j]!='\\' || j+1>=k ){
return j+1;
}else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){
j++;
}else if( z[j+1]=='u' ){
if( j+5>=k ) return j+1;
if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1;
j++;
}else if( x!=JSONB_TEXT5 ){
return j+1;
}else{
u32 c = 0;
u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c);
if( c==JSON_INVALID_CHAR ) return j+1;
j += szC - 1;
}
}
j++;
}
return 0;
}
case JSONB_TEXTRAW: {
return 0;
}
case JSONB_ARRAY: {
u32 sub;
j = i+n;
k = j+sz;
while( j<k ){
sz = 0;
n = jsonbPayloadSize(pParse, j, &sz);
if( n==0 ) return j+1;
if( j+n+sz>k ) return j+1;
sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
if( sub ) return sub;
j += n + sz;
}
assert( j==k );
return 0;
}
case JSONB_OBJECT: {
u32 cnt = 0;
u32 sub;
j = i+n;
k = j+sz;
while( j<k ){
sz = 0;
n = jsonbPayloadSize(pParse, j, &sz);
if( n==0 ) return j+1;
if( j+n+sz>k ) return j+1;
if( (cnt & 1)==0 ){
x = z[j] & 0x0f;
if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return j+1;
}
sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
if( sub ) return sub;
cnt++;
j += n + sz;
}
assert( j==k );
if( (cnt & 1)!=0 ) return j+1;
return 0;
}
default: {
return i+1;
}
}
}
/*
** Translate a single element of JSON text at pParse->zJson[i] into
** its equivalent binary JSONB representation. Append the translation into
** pParse->aBlob[] beginning at pParse->nBlob. The size of
** pParse->aBlob[] is increased as necessary.
**
** Return the index of the first character past the end of the element parsed,
** or one of the following special result codes:
**
** 0 End of input
** -1 Syntax error or OOM
** -2 '}' seen \
** -3 ']' seen \___ For these returns, pParse->iErr is set to
** -4 ',' seen / the index in zJson[] of the seen character
** -5 ':' seen /
*/
static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){
char c;
u32 j;
u32 iThis, iStart;
int x;
u8 t;
const char *z = pParse->zJson;
json_parse_restart:
switch( (u8)z[i] ){
case '{': {
/* Parse object */
iThis = pParse->nBlob;
jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0);
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
iStart = pParse->nBlob;
for(j=i+1;;j++){
u32 iBlob = pParse->nBlob;
x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
int op;
if( x==(-2) ){
j = pParse->iErr;
if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1;
break;
}
j += json5Whitespace(&z[j]);
op = JSONB_TEXT;
if( sqlite3JsonId1(z[j])
|| (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op))
){
int k = j+1;
while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
|| (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op))
){
k++;
}
assert( iBlob==pParse->nBlob );
jsonBlobAppendNode(pParse, op, k-j, &z[j]);
pParse->hasNonstd = 1;
x = k;
}else{
if( x!=-1 ) pParse->iErr = j;
return -1;
}
}
if( pParse->oom ) return -1;
t = pParse->aBlob[iBlob] & 0x0f;
if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){
pParse->iErr = j;
return -1;
}
j = x;
if( z[j]==':' ){
j++;
}else{
if( jsonIsspace(z[j]) ){
/* strspn() is not helpful here */
do{ j++; }while( jsonIsspace(z[j]) );
if( z[j]==':' ){
j++;
goto parse_object_value;
}
}
x = jsonTranslateTextToBlob(pParse, j);
if( x!=(-5) ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
}
j = pParse->iErr+1;
}
parse_object_value:
x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
}
j = x;
if( z[j]==',' ){
continue;
}else if( z[j]=='}' ){
break;
}else{
if( jsonIsspace(z[j]) ){
j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]=='}' ){
break;
}
}
x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
}
if( x==(-2) ){
j = pParse->iErr;
break;
}
}
pParse->iErr = j;
return -1;
}
jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '[': {
/* Parse array */
iThis = pParse->nBlob;
jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
iStart = pParse->nBlob;
if( pParse->oom ) return -1;
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
for(j=i+1;;j++){
x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x==(-3) ){
j = pParse->iErr;
if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1;
break;
}
if( x!=(-1) ) pParse->iErr = j;
return -1;
}
j = x;
if( z[j]==',' ){
continue;
}else if( z[j]==']' ){
break;
}else{
if( jsonIsspace(z[j]) ){
j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]==']' ){
break;
}
}
x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
}
if( x==(-3) ){
j = pParse->iErr;
break;
}
}
pParse->iErr = j;
return -1;
}
jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '\'': {
u8 opcode;
char cDelim;
pParse->hasNonstd = 1;
opcode = JSONB_TEXT;
goto parse_string;
case '"':
/* Parse string */
opcode = JSONB_TEXT;
parse_string:
cDelim = z[i];
j = i+1;
while( 1 /*exit-by-break*/ ){
if( jsonIsOk[(u8)z[j]] ){
if( !jsonIsOk[(u8)z[j+1]] ){
j += 1;
}else if( !jsonIsOk[(u8)z[j+2]] ){
j += 2;
}else{
j += 3;
continue;
}
}
c = z[j];
if( c==cDelim ){
break;
}else if( c=='\\' ){
c = z[++j];
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|| c=='n' || c=='r' || c=='t'
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
}else if( c=='\'' || c=='0' || c=='v' || c=='\n'
|| (0xe2==(u8)c && 0x80==(u8)z[j+1]
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else if( c=='\r' ){
if( z[j+1]=='\n' ) j++;
opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else{
pParse->iErr = j;
return -1;
}
}else if( c<=0x1f ){
/* Control characters are not allowed in strings */
pParse->iErr = j;
return -1;
}else if( c=='"' ){
opcode = JSONB_TEXT5;
}
j++;
}
jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]);
return j+1;
}
case 't': {
if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
jsonBlobAppendOneByte(pParse, JSONB_TRUE);
return i+4;
}
pParse->iErr = i;
return -1;
}
case 'f': {
if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
jsonBlobAppendOneByte(pParse, JSONB_FALSE);
return i+5;
}
pParse->iErr = i;
return -1;
}
case '+': {
u8 seenE;
pParse->hasNonstd = 1;
t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
goto parse_number;
case '.':
if( sqlite3Isdigit(z[i+1]) ){
pParse->hasNonstd = 1;
t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
seenE = 0;
goto parse_number_2;
}
pParse->iErr = i;
return -1;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/* Parse number */
t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
parse_number:
seenE = 0;
assert( '-' < '0' );
assert( '+' < '0' );
assert( '.' < '0' );
c = z[i];
if( c<='0' ){
if( c=='0' ){
if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
assert( t==0x00 );
pParse->hasNonstd = 1;
t = 0x01;
for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}else if( sqlite3Isdigit(z[i+1]) ){
pParse->iErr = i+1;
return -1;
}
}else{
if( !sqlite3Isdigit(z[i+1]) ){
/* JSON5 allows for "+Infinity" and "-Infinity" using exactly
** that case. SQLite also allows these in any case and it allows
** "+inf" and "-inf". */
if( (z[i+1]=='I' || z[i+1]=='i')
&& sqlite3StrNICmp(&z[i+1], "inf",3)==0
){
pParse->hasNonstd = 1;
if( z[i]=='-' ){
jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
}else{
jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
}
return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
}
if( z[i+1]=='.' ){
pParse->hasNonstd = 1;
t |= 0x01;
goto parse_number_2;
}
pParse->iErr = i;
return -1;
}
if( z[i+1]=='0' ){
if( sqlite3Isdigit(z[i+2]) ){
pParse->iErr = i+1;
return -1;
}else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
pParse->hasNonstd = 1;
t |= 0x01;
for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}
}
}
}
parse_number_2:
for(j=i+1;; j++){
c = z[j];
if( sqlite3Isdigit(c) ) continue;
if( c=='.' ){
if( (t & 0x02)!=0 ){
pParse->iErr = j;
return -1;
}
t |= 0x02;
continue;
}
if( c=='e' || c=='E' ){
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
t |= 0x01;
}else{
pParse->iErr = j;
return -1;
}
}
if( seenE ){
pParse->iErr = j;
return -1;
}
t |= 0x02;
seenE = 1;
c = z[j+1];
if( c=='+' || c=='-' ){
j++;
c = z[j+1];
}
if( c<'0' || c>'9' ){
pParse->iErr = j;
return -1;
}
continue;
}
break;
}
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
t |= 0x01;
}else{
pParse->iErr = j;
return -1;
}
}
parse_number_finish:
assert( JSONB_INT+0x01==JSONB_INT5 );
assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 );
assert( JSONB_INT+0x02==JSONB_FLOAT );
if( z[i]=='+' ) i++;
jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]);
return j;
}
case '}': {
pParse->iErr = i;
return -2; /* End of {...} */
}
case ']': {
|
| ︙ | ︙ | |||
204355 204356 204357 204358 204359 204360 204361 |
case 0: {
return 0; /* End of file */
}
case 0x09:
case 0x0a:
case 0x0d:
case 0x20: {
| | < < | 205069 205070 205071 205072 205073 205074 205075 205076 205077 205078 205079 205080 205081 205082 205083 |
case 0: {
return 0; /* End of file */
}
case 0x09:
case 0x0a:
case 0x0d:
case 0x20: {
i += 1 + (u32)strspn(&z[i+1], jsonSpaces);
goto json_parse_restart;
}
case 0x0b:
case 0x0c:
case '/':
case 0xc2:
case 0xe1:
|
| ︙ | ︙ | |||
204379 204380 204381 204382 204383 204384 204385 |
goto json_parse_restart;
}
pParse->iErr = i;
return -1;
}
case 'n': {
if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
| | | | > > > > | | > > > > > | > > > > > > > > > > > > > > > > > > > > > | > | < > > > > | < > | > > > > > | | > > | < | > > | > > > > > | > > | | > > > > > > > > | | > > > > > | < > > > | > > > | > > > | | | | > | > > > | > > > | > > > | > > > | | | | > | > > > > | | > | | | > > > > > > > > > > > > > > > > > > > > > | < > | < > > > > > > > > > > > | < > > > > > > | < < < < < < < > > > > > | < < > > > | < < > | > | | | | < > | | | | | < > | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | > > > > > > > > > > > > > > > | > > > > > | < > | | > | > | > > > > > | < | > | > > > > > | < | < > > > | > > > > > > > > > > > | > > > > > > > > | > | < < < | > > > > > | > > > > > > > | > > | > > > | > > > > > | > > > > | > > > > | | < | | | < > | < | > > > > > > > > > > | > | > > > > > | | > > > > > > > > > > > > > > > > > > > > > | < | | < < | | > > > > > | < < < < < | < < > | > > > > > > > > > > > | > > | | > > > > | | | > | | < | > > > | | < | < | < < < < < | > > | < > > > > | | > | > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | > > | > > > | | > | | > | > > | > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | < | < < < < | | | > | | < > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > > | > > | | < | > > > > | < | | > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > | < > | > > > > < > | | < < > | < | > < | > > > | < | > > > > > > > > > > | > > > > > | > > | | > | < < | < < < | > | | > | | | > | > | | > > > | > > | | | > | < > | | > | > > > | > > > | | | | | | > | > > > > > > > > > > > > > > > > > | > > > > > | > < > | | | | | > | > > > > > > > | > > > > > > > > > > > | > > > > > > > > > | | | | | | | | > > > | | > > > > > > > > > > > | | > > | > | > | < > | > > > > > > | | | > | | > > > > | > > > | > > > > > > > | < < > | > > > | < < > > > > > > > > | < > | | | > > > | > | > > > > < < > > > > > > > > > > > > > > > > > | > > > | | | | | | > > > > | > | | > > > > > > > > > | > > > > > > > > | < > > > > > > > > > | | > > | < | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > > > > | > > > > > > | < > > | > > > | < < < < | > > > > > > > > | | < | > > > | | | | | > > > > > > > > | > > | > | > | > > | > > > > > > > > > > > > > > > > > > > | > > | > > > | | > > | | > > > > > > > > > > | > > > > > > > > > > > | < < < < < < < | | < | | > > | < | | > | > > > > | > > > > > > > > > | > > > > > > | | > | > > > > > > > > > > | > > > | > > | > > | > > > | > > > > > > > > | < | > | | | > > | > > > > > > > | > > > > > > | > | > > > > > > | | | > > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > | < > | < < < < < < < < | > > > > > > > > > > > | < < < < > > | > > > > > > > > > > > > | > | > > > > | < > | < > | | | > | > | > > > > > | | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > | < < > > > > > > > | > > > > > > > > > > > | < < < < < > < | > | | < < < < < > > > > > | | < < < < | | | | | > > > > > > > > > > > > > > > > > | > > > > > | < < < > > > > | < < | > > > | > > > > > > > > > > > > > > > > > > > | > | > > > > > | > > > | | | < > | > | < < | < < < < < < < < < | < > | > | < < < < < | < < < < < < | | | | | | | < | | | < | | | | > | > > > > | > | < < | < < | | < < | | | > > | > | < | < < | | > > | > > > > > > | 205091 205092 205093 205094 205095 205096 205097 205098 205099 205100 205101 205102 205103 205104 205105 205106 205107 205108 205109 205110 205111 205112 205113 205114 205115 205116 205117 205118 205119 205120 205121 205122 205123 205124 205125 205126 205127 205128 205129 205130 205131 205132 205133 205134 205135 205136 205137 205138 205139 205140 205141 205142 205143 205144 205145 205146 205147 205148 205149 205150 205151 205152 205153 205154 205155 205156 205157 205158 205159 205160 205161 205162 205163 205164 205165 205166 205167 205168 205169 205170 205171 205172 205173 205174 205175 205176 205177 205178 205179 205180 205181 205182 205183 205184 205185 205186 205187 205188 205189 205190 205191 205192 205193 205194 205195 205196 205197 205198 205199 205200 205201 205202 205203 205204 205205 205206 205207 205208 205209 205210 205211 205212 205213 205214 205215 205216 205217 205218 205219 205220 205221 205222 205223 205224 205225 205226 205227 205228 205229 205230 205231 205232 205233 205234 205235 205236 205237 205238 205239 205240 205241 205242 205243 205244 205245 205246 205247 205248 205249 205250 205251 205252 205253 205254 205255 205256 205257 205258 205259 205260 205261 205262 205263 205264 205265 205266 205267 205268 205269 205270 205271 205272 205273 205274 205275 205276 205277 205278 205279 205280 205281 205282 205283 205284 205285 205286 205287 205288 205289 205290 205291 205292 205293 205294 205295 205296 205297 205298 205299 205300 205301 205302 205303 205304 205305 205306 205307 205308 205309 205310 205311 205312 205313 205314 205315 205316 205317 205318 205319 205320 205321 205322 205323 205324 205325 205326 205327 205328 205329 205330 205331 205332 205333 205334 205335 205336 205337 205338 205339 205340 205341 205342 205343 205344 205345 205346 205347 205348 205349 205350 205351 205352 205353 205354 205355 205356 205357 205358 205359 205360 205361 205362 205363 205364 205365 205366 205367 205368 205369 205370 205371 205372 205373 205374 205375 205376 205377 205378 205379 205380 205381 205382 205383 205384 205385 205386 205387 205388 205389 205390 205391 205392 205393 205394 205395 205396 205397 205398 205399 205400 205401 205402 205403 205404 205405 205406 205407 205408 205409 205410 205411 205412 205413 205414 205415 205416 205417 205418 205419 205420 205421 205422 205423 205424 205425 205426 205427 205428 205429 205430 205431 205432 205433 205434 205435 205436 205437 205438 205439 205440 205441 205442 205443 205444 205445 205446 205447 205448 205449 205450 205451 205452 205453 205454 205455 205456 205457 205458 205459 205460 205461 205462 205463 205464 205465 205466 205467 205468 205469 205470 205471 205472 205473 205474 205475 205476 205477 205478 205479 205480 205481 205482 205483 205484 205485 205486 205487 205488 205489 205490 205491 205492 205493 205494 205495 205496 205497 205498 205499 205500 205501 205502 205503 205504 205505 205506 205507 205508 205509 205510 205511 205512 205513 205514 205515 205516 205517 205518 205519 205520 205521 205522 205523 205524 205525 205526 205527 205528 205529 205530 205531 205532 205533 205534 205535 205536 205537 205538 205539 205540 205541 205542 205543 205544 205545 205546 205547 205548 205549 205550 205551 205552 205553 205554 205555 205556 205557 205558 205559 205560 205561 205562 205563 205564 205565 205566 205567 205568 205569 205570 205571 205572 205573 205574 205575 205576 205577 205578 205579 205580 205581 205582 205583 205584 205585 205586 205587 205588 205589 205590 205591 205592 205593 205594 205595 205596 205597 205598 205599 205600 205601 205602 205603 205604 205605 205606 205607 205608 205609 205610 205611 205612 205613 205614 205615 205616 205617 205618 205619 205620 205621 205622 205623 205624 205625 205626 205627 205628 205629 205630 205631 205632 205633 205634 205635 205636 205637 205638 205639 205640 205641 205642 205643 205644 205645 205646 205647 205648 205649 205650 205651 205652 205653 205654 205655 205656 205657 205658 205659 205660 205661 205662 205663 205664 205665 205666 205667 205668 205669 205670 205671 205672 205673 205674 205675 205676 205677 205678 205679 205680 205681 205682 205683 205684 205685 205686 205687 205688 205689 205690 205691 205692 205693 205694 205695 205696 205697 205698 205699 205700 205701 205702 205703 205704 205705 205706 205707 205708 205709 205710 205711 205712 205713 205714 205715 205716 205717 205718 205719 205720 205721 205722 205723 205724 205725 205726 205727 205728 205729 205730 205731 205732 205733 205734 205735 205736 205737 205738 205739 205740 205741 205742 205743 205744 205745 205746 205747 205748 205749 205750 205751 205752 205753 205754 205755 205756 205757 205758 205759 205760 205761 205762 205763 205764 205765 205766 205767 205768 205769 205770 205771 205772 205773 205774 205775 205776 205777 205778 205779 205780 205781 205782 205783 205784 205785 205786 205787 205788 205789 205790 205791 205792 205793 205794 205795 205796 205797 205798 205799 205800 205801 205802 205803 205804 205805 205806 205807 205808 205809 205810 205811 205812 205813 205814 205815 205816 205817 205818 205819 205820 205821 205822 205823 205824 205825 205826 205827 205828 205829 205830 205831 205832 205833 205834 205835 205836 205837 205838 205839 205840 205841 205842 205843 205844 205845 205846 205847 205848 205849 205850 205851 205852 205853 205854 205855 205856 205857 205858 205859 205860 205861 205862 205863 205864 205865 205866 205867 205868 205869 205870 205871 205872 205873 205874 205875 205876 205877 205878 205879 205880 205881 205882 205883 205884 205885 205886 205887 205888 205889 205890 205891 205892 205893 205894 205895 205896 205897 205898 205899 205900 205901 205902 205903 205904 205905 205906 205907 205908 205909 205910 205911 205912 205913 205914 205915 205916 205917 205918 205919 205920 205921 205922 205923 205924 205925 205926 205927 205928 205929 205930 205931 205932 205933 205934 205935 205936 205937 205938 205939 205940 205941 205942 205943 205944 205945 205946 205947 205948 205949 205950 205951 205952 205953 205954 205955 205956 205957 205958 205959 205960 205961 205962 205963 205964 205965 205966 205967 205968 205969 205970 205971 205972 205973 205974 205975 205976 205977 205978 205979 205980 205981 205982 205983 205984 205985 205986 205987 205988 205989 205990 205991 205992 205993 205994 205995 205996 205997 205998 205999 206000 206001 206002 206003 206004 206005 206006 206007 206008 206009 206010 206011 206012 206013 206014 206015 206016 206017 206018 206019 206020 206021 206022 206023 206024 206025 206026 206027 206028 206029 206030 206031 206032 206033 206034 206035 206036 206037 206038 206039 206040 206041 206042 206043 206044 206045 206046 206047 206048 206049 206050 206051 206052 206053 206054 206055 206056 206057 206058 206059 206060 206061 206062 206063 206064 206065 206066 206067 206068 206069 206070 206071 206072 206073 206074 206075 206076 206077 206078 206079 206080 206081 206082 206083 206084 206085 206086 206087 206088 206089 206090 206091 206092 206093 206094 206095 206096 206097 206098 206099 206100 206101 206102 206103 206104 206105 206106 206107 206108 206109 206110 206111 206112 206113 206114 206115 206116 206117 206118 206119 206120 206121 206122 206123 206124 206125 206126 206127 206128 206129 206130 206131 206132 206133 206134 206135 206136 206137 206138 206139 206140 206141 206142 206143 206144 206145 206146 206147 206148 206149 206150 206151 206152 206153 206154 206155 206156 206157 206158 206159 206160 206161 206162 206163 206164 206165 206166 206167 206168 206169 206170 206171 206172 206173 206174 206175 206176 206177 206178 206179 206180 206181 206182 206183 206184 206185 206186 206187 206188 206189 206190 206191 206192 206193 206194 206195 206196 206197 206198 206199 206200 206201 206202 206203 206204 206205 206206 206207 206208 206209 206210 206211 206212 206213 206214 206215 206216 206217 206218 206219 206220 206221 206222 206223 206224 206225 206226 206227 206228 206229 206230 206231 206232 206233 206234 206235 206236 206237 206238 206239 206240 206241 206242 206243 206244 206245 206246 206247 206248 206249 206250 206251 206252 206253 206254 206255 206256 206257 206258 206259 206260 206261 206262 206263 206264 206265 206266 206267 206268 206269 206270 206271 206272 206273 206274 206275 206276 206277 206278 206279 206280 206281 206282 206283 206284 206285 206286 206287 206288 206289 206290 206291 206292 206293 206294 206295 206296 206297 206298 206299 206300 206301 206302 206303 206304 206305 206306 206307 206308 206309 206310 206311 206312 206313 206314 206315 206316 206317 206318 206319 206320 206321 206322 206323 206324 206325 206326 206327 206328 206329 206330 206331 206332 206333 206334 206335 206336 206337 206338 206339 206340 206341 206342 206343 206344 206345 206346 206347 206348 206349 206350 206351 206352 206353 206354 206355 206356 206357 206358 206359 206360 206361 206362 206363 206364 206365 206366 206367 206368 206369 206370 206371 206372 206373 206374 206375 206376 206377 206378 206379 206380 206381 206382 206383 206384 206385 206386 206387 206388 206389 206390 206391 206392 206393 206394 206395 206396 206397 206398 206399 206400 206401 206402 206403 206404 206405 206406 206407 206408 206409 206410 206411 206412 206413 206414 206415 206416 206417 206418 206419 206420 206421 206422 206423 206424 206425 206426 206427 206428 206429 206430 206431 206432 206433 206434 206435 206436 206437 206438 206439 206440 206441 206442 206443 206444 206445 206446 206447 206448 206449 206450 206451 206452 206453 206454 206455 206456 206457 206458 206459 206460 206461 206462 206463 206464 206465 206466 206467 206468 206469 206470 206471 206472 206473 206474 206475 206476 206477 206478 206479 206480 206481 206482 206483 206484 206485 206486 206487 206488 206489 206490 206491 206492 206493 206494 206495 206496 206497 206498 206499 206500 206501 206502 206503 206504 206505 206506 206507 206508 206509 206510 206511 206512 206513 206514 206515 206516 206517 206518 206519 206520 206521 206522 206523 206524 206525 206526 206527 206528 206529 206530 206531 206532 206533 206534 206535 206536 206537 206538 206539 206540 206541 206542 206543 206544 206545 206546 206547 206548 206549 206550 206551 206552 206553 206554 206555 206556 206557 206558 206559 206560 206561 206562 206563 206564 206565 206566 206567 206568 206569 206570 206571 206572 206573 206574 206575 206576 206577 206578 206579 206580 206581 206582 206583 206584 206585 206586 206587 206588 206589 206590 206591 206592 206593 206594 206595 206596 206597 206598 206599 206600 206601 206602 206603 206604 206605 206606 206607 206608 206609 206610 206611 206612 206613 206614 206615 206616 206617 206618 206619 206620 206621 206622 206623 206624 206625 206626 206627 206628 206629 206630 206631 206632 206633 206634 206635 206636 206637 206638 206639 206640 206641 206642 206643 206644 206645 206646 206647 206648 206649 206650 206651 206652 206653 206654 206655 206656 206657 206658 206659 206660 206661 206662 206663 206664 206665 206666 206667 206668 206669 206670 206671 206672 206673 206674 206675 206676 206677 206678 206679 206680 206681 206682 206683 206684 206685 206686 206687 206688 206689 206690 206691 206692 206693 206694 206695 206696 206697 206698 206699 206700 206701 206702 206703 206704 206705 206706 206707 206708 206709 206710 206711 206712 206713 206714 206715 206716 206717 206718 206719 206720 206721 206722 206723 206724 206725 206726 206727 206728 206729 206730 206731 206732 206733 206734 206735 206736 206737 206738 206739 206740 206741 206742 206743 206744 206745 206746 206747 206748 206749 206750 206751 206752 206753 206754 206755 206756 206757 206758 206759 206760 206761 206762 206763 206764 206765 206766 206767 206768 206769 206770 206771 206772 206773 206774 206775 206776 206777 206778 206779 206780 206781 206782 206783 206784 206785 206786 206787 206788 206789 206790 206791 206792 206793 206794 206795 206796 206797 206798 206799 206800 206801 206802 206803 206804 206805 206806 206807 206808 206809 206810 206811 206812 206813 206814 206815 206816 206817 206818 206819 206820 206821 206822 206823 206824 206825 206826 206827 206828 206829 206830 206831 206832 206833 206834 206835 206836 206837 206838 206839 206840 206841 206842 206843 206844 206845 206846 206847 206848 206849 206850 206851 206852 206853 206854 |
goto json_parse_restart;
}
pParse->iErr = i;
return -1;
}
case 'n': {
if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
jsonBlobAppendOneByte(pParse, JSONB_NULL);
return i+4;
}
/* fall-through into the default case that checks for NaN */
}
default: {
u32 k;
int nn;
c = z[i];
for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
nn = aNanInfName[k].n;
if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
continue;
}
if( sqlite3Isalnum(z[i+nn]) ) continue;
if( aNanInfName[k].eType==JSONB_FLOAT ){
jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
}else{
jsonBlobAppendOneByte(pParse, JSONB_NULL);
}
pParse->hasNonstd = 1;
return i + nn;
}
pParse->iErr = i;
return -1; /* Syntax error */
}
} /* End switch(z[i]) */
}
/*
** Parse a complete JSON string. Return 0 on success or non-zero if there
** are any errors. If an error occurs, free all memory held by pParse,
** but not pParse itself.
**
** pParse must be initialized to an empty parse object prior to calling
** this routine.
*/
static int jsonConvertTextToBlob(
JsonParse *pParse, /* Initialize and fill this JsonParse object */
sqlite3_context *pCtx /* Report errors here */
){
int i;
const char *zJson = pParse->zJson;
i = jsonTranslateTextToBlob(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
#ifdef SQLITE_DEBUG
assert( pParse->iDepth==0 );
if( sqlite3Config.bJsonSelfcheck ){
assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 );
}
#endif
while( jsonIsspace(zJson[i]) ) i++;
if( zJson[i] ){
i += json5Whitespace(&zJson[i]);
if( zJson[i] ){
if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1);
jsonParseReset(pParse);
return 1;
}
pParse->hasNonstd = 1;
}
}
if( i<=0 ){
if( pCtx!=0 ){
if( pParse->oom ){
sqlite3_result_error_nomem(pCtx);
}else{
sqlite3_result_error(pCtx, "malformed JSON", -1);
}
}
jsonParseReset(pParse);
return 1;
}
return 0;
}
/*
** The input string pStr is a well-formed JSON text string. Convert
** this into the JSONB format and make it the return value of the
** SQL function.
*/
static void jsonReturnStringAsBlob(JsonString *pStr){
JsonParse px;
memset(&px, 0, sizeof(px));
jsonStringTerminate(pStr);
px.zJson = pStr->zBuf;
px.nJson = pStr->nUsed;
px.db = sqlite3_context_db_handle(pStr->pCtx);
(void)jsonTranslateTextToBlob(&px, 0);
if( px.oom ){
sqlite3DbFree(px.db, px.aBlob);
sqlite3_result_error_nomem(pStr->pCtx);
}else{
assert( px.nBlobAlloc>0 );
assert( !px.bReadOnly );
sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC);
}
}
/* The byte at index i is a node type-code. This routine
** determines the payload size for that node and writes that
** payload size in to *pSz. It returns the offset from i to the
** beginning of the payload. Return 0 on error.
*/
static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
u8 x;
u32 sz;
u32 n;
if( NEVER(i>pParse->nBlob) ){
*pSz = 0;
return 0;
}
x = pParse->aBlob[i]>>4;
if( x<=11 ){
sz = x;
n = 1;
}else if( x==12 ){
if( i+1>=pParse->nBlob ){
*pSz = 0;
return 0;
}
sz = pParse->aBlob[i+1];
n = 2;
}else if( x==13 ){
if( i+2>=pParse->nBlob ){
*pSz = 0;
return 0;
}
sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2];
n = 3;
}else if( x==14 ){
if( i+4>=pParse->nBlob ){
*pSz = 0;
return 0;
}
sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) +
(pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
n = 5;
}else{
if( i+8>=pParse->nBlob
|| pParse->aBlob[i+1]!=0
|| pParse->aBlob[i+2]!=0
|| pParse->aBlob[i+3]!=0
|| pParse->aBlob[i+4]!=0
){
*pSz = 0;
return 0;
}
sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
(pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
n = 9;
}
if( (i64)i+sz+n > pParse->nBlob
&& (i64)i+sz+n > pParse->nBlob-pParse->delta
){
sz = 0;
n = 0;
}
*pSz = sz;
return n;
}
/*
** Translate the binary JSONB representation of JSON beginning at
** pParse->aBlob[i] into a JSON text string. Append the JSON
** text onto the end of pOut. Return the index in pParse->aBlob[]
** of the first byte past the end of the element that is translated.
**
** If an error is detected in the BLOB input, the pOut->eErr flag
** might get set to JSTRING_MALFORMED. But not all BLOB input errors
** are detected. So a malformed JSONB input might either result
** in an error, or in incorrect JSON.
**
** The pOut->eErr JSTRING_OOM flag is set on a OOM.
*/
static u32 jsonTranslateBlobToText(
const JsonParse *pParse, /* the complete parse of the JSON */
u32 i, /* Start rendering at this index */
JsonString *pOut /* Write JSON here */
){
u32 sz, n, j, iEnd;
n = jsonbPayloadSize(pParse, i, &sz);
if( n==0 ){
pOut->eErr |= JSTRING_MALFORMED;
return pParse->nBlob+1;
}
switch( pParse->aBlob[i] & 0x0f ){
case JSONB_NULL: {
jsonAppendRawNZ(pOut, "null", 4);
return i+1;
}
case JSONB_TRUE: {
jsonAppendRawNZ(pOut, "true", 4);
return i+1;
}
case JSONB_FALSE: {
jsonAppendRawNZ(pOut, "false", 5);
return i+1;
}
case JSONB_INT:
case JSONB_FLOAT: {
if( sz==0 ) goto malformed_jsonb;
jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
break;
}
case JSONB_INT5: { /* Integer literal in hexadecimal notation */
u32 k = 2;
sqlite3_uint64 u = 0;
const char *zIn = (const char*)&pParse->aBlob[i+n];
int bOverflow = 0;
if( sz==0 ) goto malformed_jsonb;
if( zIn[0]=='-' ){
jsonAppendChar(pOut, '-');
k++;
}else if( zIn[0]=='+' ){
k++;
}
for(; k<sz; k++){
if( !sqlite3Isxdigit(zIn[k]) ){
pOut->eErr |= JSTRING_MALFORMED;
break;
}else if( (u>>60)!=0 ){
bOverflow = 1;
}else{
u = u*16 + sqlite3HexToInt(zIn[k]);
}
}
jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u);
break;
}
case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
u32 k = 0;
const char *zIn = (const char*)&pParse->aBlob[i+n];
if( sz==0 ) goto malformed_jsonb;
if( zIn[0]=='-' ){
jsonAppendChar(pOut, '-');
k++;
}
if( zIn[k]=='.' ){
jsonAppendChar(pOut, '0');
}
for(; k<sz; k++){
jsonAppendChar(pOut, zIn[k]);
if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){
jsonAppendChar(pOut, '0');
}
}
break;
}
case JSONB_TEXT:
case JSONB_TEXTJ: {
jsonAppendChar(pOut, '"');
jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
jsonAppendChar(pOut, '"');
break;
}
case JSONB_TEXT5: {
const char *zIn;
u32 k;
u32 sz2 = sz;
zIn = (const char*)&pParse->aBlob[i+n];
jsonAppendChar(pOut, '"');
while( sz2>0 ){
for(k=0; k<sz2 && zIn[k]!='\\' && zIn[k]!='"'; k++){}
if( k>0 ){
jsonAppendRawNZ(pOut, zIn, k);
if( k>=sz2 ){
break;
}
zIn += k;
sz2 -= k;
}
if( zIn[0]=='"' ){
jsonAppendRawNZ(pOut, "\\\"", 2);
zIn++;
sz2--;
continue;
}
assert( zIn[0]=='\\' );
assert( sz2>=1 );
if( sz2<2 ){
pOut->eErr |= JSTRING_MALFORMED;
break;
}
switch( (u8)zIn[1] ){
case '\'':
jsonAppendChar(pOut, '\'');
break;
case 'v':
jsonAppendRawNZ(pOut, "\\u0009", 6);
break;
case 'x':
if( sz2<4 ){
pOut->eErr |= JSTRING_MALFORMED;
sz2 = 2;
break;
}
jsonAppendRawNZ(pOut, "\\u00", 4);
jsonAppendRawNZ(pOut, &zIn[2], 2);
zIn += 2;
sz2 -= 2;
break;
case '0':
jsonAppendRawNZ(pOut, "\\u0000", 6);
break;
case '\r':
if( sz2>2 && zIn[2]=='\n' ){
zIn++;
sz2--;
}
break;
case '\n':
break;
case 0xe2:
/* '\' followed by either U+2028 or U+2029 is ignored as
** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29.
** U+2029 is the same except for the last byte */
if( sz2<4
|| 0x80!=(u8)zIn[2]
|| (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3])
){
pOut->eErr |= JSTRING_MALFORMED;
sz2 = 2;
break;
}
zIn += 2;
sz2 -= 2;
break;
default:
jsonAppendRawNZ(pOut, zIn, 2);
break;
}
assert( sz2>=2 );
zIn += 2;
sz2 -= 2;
}
jsonAppendChar(pOut, '"');
break;
}
case JSONB_TEXTRAW: {
jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz);
break;
}
case JSONB_ARRAY: {
jsonAppendChar(pOut, '[');
j = i+n;
iEnd = j+sz;
while( j<iEnd && pOut->eErr==0 ){
j = jsonTranslateBlobToText(pParse, j, pOut);
jsonAppendChar(pOut, ',');
}
if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
if( sz>0 ) jsonStringTrimOneChar(pOut);
jsonAppendChar(pOut, ']');
break;
}
case JSONB_OBJECT: {
int x = 0;
jsonAppendChar(pOut, '{');
j = i+n;
iEnd = j+sz;
while( j<iEnd && pOut->eErr==0 ){
j = jsonTranslateBlobToText(pParse, j, pOut);
jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
}
if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
if( sz>0 ) jsonStringTrimOneChar(pOut);
jsonAppendChar(pOut, '}');
break;
}
default: {
malformed_jsonb:
pOut->eErr |= JSTRING_MALFORMED;
break;
}
}
return i+n+sz;
}
/* Return true if the input pJson
**
** For performance reasons, this routine does not do a detailed check of the
** input BLOB to ensure that it is well-formed. Hence, false positives are
** possible. False negatives should never occur, however.
*/
static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
u32 sz, n;
const u8 *aBlob;
int nBlob;
JsonParse s;
if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
aBlob = sqlite3_value_blob(pJson);
nBlob = sqlite3_value_bytes(pJson);
if( nBlob<1 ) return 0;
if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
memset(&s, 0, sizeof(s));
s.aBlob = (u8*)aBlob;
s.nBlob = nBlob;
n = jsonbPayloadSize(&s, 0, &sz);
if( n==0 ) return 0;
if( sz+n!=(u32)nBlob ) return 0;
if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
return sz+n==(u32)nBlob;
}
/*
** Given that a JSONB_ARRAY object starts at offset i, return
** the number of entries in that array.
*/
static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
u32 n, sz, i, iEnd;
u32 k = 0;
n = jsonbPayloadSize(pParse, iRoot, &sz);
iEnd = iRoot+n+sz;
for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){
n = jsonbPayloadSize(pParse, i, &sz);
}
return k;
}
/*
** Edit the payload size of the element at iRoot by the amount in
** pParse->delta.
*/
static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
u32 sz = 0;
u32 nBlob;
assert( pParse->delta!=0 );
assert( pParse->nBlobAlloc >= pParse->nBlob );
nBlob = pParse->nBlob;
pParse->nBlob = pParse->nBlobAlloc;
(void)jsonbPayloadSize(pParse, iRoot, &sz);
pParse->nBlob = nBlob;
sz += pParse->delta;
pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz);
}
/*
** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
** content beginning at iDel, and replacing them with nIns bytes of
** content given by aIns.
**
** nDel may be zero, in which case no bytes are removed. But iDel is
** still important as new bytes will be insert beginning at iDel.
**
** aIns may be zero, in which case space is created to hold nIns bytes
** beginning at iDel, but that space is uninitialized.
**
** Set pParse->oom if an OOM occurs.
*/
static void jsonBlobEdit(
JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */
u32 iDel, /* First byte to be removed */
u32 nDel, /* Number of bytes to remove */
const u8 *aIns, /* Content to insert */
u32 nIns /* Bytes of content to insert */
){
i64 d = (i64)nIns - (i64)nDel;
if( d!=0 ){
if( pParse->nBlob + d > pParse->nBlobAlloc ){
jsonBlobExpand(pParse, pParse->nBlob+d);
if( pParse->oom ) return;
}
memmove(&pParse->aBlob[iDel+nIns],
&pParse->aBlob[iDel+nDel],
pParse->nBlob - (iDel+nDel));
pParse->nBlob += d;
pParse->delta += d;
}
if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);
}
/*
** Return the number of escaped newlines to be ignored.
** An escaped newline is a one of the following byte sequences:
**
** 0x5c 0x0a
** 0x5c 0x0d
** 0x5c 0x0d 0x0a
** 0x5c 0xe2 0x80 0xa8
** 0x5c 0xe2 0x80 0xa9
*/
static u32 jsonBytesToBypass(const char *z, u32 n){
u32 i = 0;
while( i+1<n ){
if( z[i]!='\\' ) return i;
if( z[i+1]=='\n' ){
i += 2;
continue;
}
if( z[i+1]=='\r' ){
if( i+2<n && z[i+2]=='\n' ){
i += 3;
}else{
i += 2;
}
continue;
}
if( 0xe2==(u8)z[i+1]
&& i+3<n
&& 0x80==(u8)z[i+2]
&& (0xa8==(u8)z[i+3] || 0xa9==(u8)z[i+3])
){
i += 4;
continue;
}
break;
}
return i;
}
/*
** Input z[0..n] defines JSON escape sequence including the leading '\\'.
** Decode that escape sequence into a single character. Write that
** character into *piOut. Return the number of bytes in the escape sequence.
**
** If there is a syntax error of some kind (for example too few characters
** after the '\\' to complete the encoding) then *piOut is set to
** JSON_INVALID_CHAR.
*/
static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){
assert( n>0 );
assert( z[0]=='\\' );
if( n<2 ){
*piOut = JSON_INVALID_CHAR;
return n;
}
switch( (u8)z[1] ){
case 'u': {
u32 v, vlo;
if( n<6 ){
*piOut = JSON_INVALID_CHAR;
return n;
}
v = jsonHexToInt4(&z[2]);
if( (v & 0xfc00)==0xd800
&& n>=12
&& z[6]=='\\'
&& z[7]=='u'
&& ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00
){
*piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
return 12;
}else{
*piOut = v;
return 6;
}
}
case 'b': { *piOut = '\b'; return 2; }
case 'f': { *piOut = '\f'; return 2; }
case 'n': { *piOut = '\n'; return 2; }
case 'r': { *piOut = '\r'; return 2; }
case 't': { *piOut = '\t'; return 2; }
case 'v': { *piOut = '\v'; return 2; }
case '0': { *piOut = 0; return 2; }
case '\'':
case '"':
case '/':
case '\\':{ *piOut = z[1]; return 2; }
case 'x': {
if( n<4 ){
*piOut = JSON_INVALID_CHAR;
return n;
}
*piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]);
return 4;
}
case 0xe2:
case '\r':
case '\n': {
u32 nSkip = jsonBytesToBypass(z, n);
if( nSkip==0 ){
*piOut = JSON_INVALID_CHAR;
return n;
}else if( nSkip==n ){
*piOut = 0;
return n;
}else if( z[nSkip]=='\\' ){
return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut);
}else{
int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut);
return nSkip + sz;
}
}
default: {
*piOut = JSON_INVALID_CHAR;
return 2;
}
}
}
/*
** Compare two object labels. Return 1 if they are equal and
** 0 if they differ.
**
** In this version, we know that one or the other or both of the
** two comparands contains an escape sequence.
*/
static SQLITE_NOINLINE int jsonLabelCompareEscaped(
const char *zLeft, /* The left label */
u32 nLeft, /* Size of the left label in bytes */
int rawLeft, /* True if zLeft contains no escapes */
const char *zRight, /* The right label */
u32 nRight, /* Size of the right label in bytes */
int rawRight /* True if zRight is escape-free */
){
u32 cLeft, cRight;
assert( rawLeft==0 || rawRight==0 );
while( 1 /*exit-by-return*/ ){
if( nLeft==0 ){
cLeft = 0;
}else if( rawLeft || zLeft[0]!='\\' ){
cLeft = ((u8*)zLeft)[0];
if( cLeft>=0xc0 ){
int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft);
zLeft += sz;
nLeft -= sz;
}else{
zLeft++;
nLeft--;
}
}else{
u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft);
zLeft += n;
assert( n<=nLeft );
nLeft -= n;
}
if( nRight==0 ){
cRight = 0;
}else if( rawRight || zRight[0]!='\\' ){
cRight = ((u8*)zRight)[0];
if( cRight>=0xc0 ){
int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight);
zRight += sz;
nRight -= sz;
}else{
zRight++;
nRight--;
}
}else{
u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight);
zRight += n;
assert( n<=nRight );
nRight -= n;
}
if( cLeft!=cRight ) return 0;
if( cLeft==0 ) return 1;
}
}
/*
** Compare two object labels. Return 1 if they are equal and
** 0 if they differ. Return -1 if an OOM occurs.
*/
static int jsonLabelCompare(
const char *zLeft, /* The left label */
u32 nLeft, /* Size of the left label in bytes */
int rawLeft, /* True if zLeft contains no escapes */
const char *zRight, /* The right label */
u32 nRight, /* Size of the right label in bytes */
int rawRight /* True if zRight is escape-free */
){
if( rawLeft && rawRight ){
/* Simpliest case: Neither label contains escapes. A simple
** memcmp() is sufficient. */
if( nLeft!=nRight ) return 0;
return memcmp(zLeft, zRight, nLeft)==0;
}else{
return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft,
zRight, nRight, rawRight);
}
}
/*
** Error returns from jsonLookupStep()
*/
#define JSON_LOOKUP_ERROR 0xffffffff
#define JSON_LOOKUP_NOTFOUND 0xfffffffe
#define JSON_LOOKUP_PATHERROR 0xfffffffd
#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR)
/* Forward declaration */
static u32 jsonLookupStep(JsonParse*,u32,const char*,u32);
/* This helper routine for jsonLookupStep() populates pIns with
** binary data that is to be inserted into pParse.
**
** In the common case, pIns just points to pParse->aIns and pParse->nIns.
** But if the zPath of the original edit operation includes path elements
** that go deeper, additional substructure must be created.
**
** For example:
**
** json_insert('{}', '$.a.b.c', 123);
**
** The search stops at '$.a' But additional substructure must be
** created for the ".b.c" part of the patch so that the final result
** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with
** the binary equivalent of {"b":{"c":123}} so that it can be inserted.
**
** The caller is responsible for resetting pIns when it has finished
** using the substructure.
*/
static u32 jsonCreateEditSubstructure(
JsonParse *pParse, /* The original JSONB that is being edited */
JsonParse *pIns, /* Populate this with the blob data to insert */
const char *zTail /* Tail of the path that determins substructure */
){
static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT };
int rc;
memset(pIns, 0, sizeof(*pIns));
pIns->db = pParse->db;
if( zTail[0]==0 ){
/* No substructure. Just insert what is given in pParse. */
pIns->aBlob = pParse->aIns;
pIns->nBlob = pParse->nIns;
rc = 0;
}else{
/* Construct the binary substructure */
pIns->nBlob = 1;
pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.'];
pIns->eEdit = pParse->eEdit;
pIns->nIns = pParse->nIns;
pIns->aIns = pParse->aIns;
rc = jsonLookupStep(pIns, 0, zTail, 0);
pParse->oom |= pIns->oom;
}
return rc; /* Error code only */
}
/*
** Search along zPath to find the Json element specified. Return an
** index into pParse->aBlob[] for the start of that element's value.
**
** If the value found by this routine is the value half of label/value pair
** within an object, then set pPath->iLabel to the start of the corresponding
** label, before returning.
**
** Return one of the JSON_LOOKUP error codes if problems are seen.
**
** This routine will also modify the blob. If pParse->eEdit is one of
** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be
** made to the selected value. If an edit is performed, then the return
** value does not necessarily point to the select element. If an edit
** is performed, the return value is only useful for detecting error
** conditions.
*/
static u32 jsonLookupStep(
JsonParse *pParse, /* The JSON to search */
u32 iRoot, /* Begin the search at this element of aBlob[] */
const char *zPath, /* The path to search */
u32 iLabel /* Label if iRoot is a value of in an object */
){
u32 i, j, k, nKey, sz, n, iEnd, rc;
const char *zKey;
u8 x;
if( zPath[0]==0 ){
if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){
n = jsonbPayloadSize(pParse, iRoot, &sz);
sz += n;
if( pParse->eEdit==JEDIT_DEL ){
if( iLabel>0 ){
sz += iRoot - iLabel;
iRoot = iLabel;
}
jsonBlobEdit(pParse, iRoot, sz, 0, 0);
}else if( pParse->eEdit==JEDIT_INS ){
/* Already exists, so json_insert() is a no-op */
}else{
/* json_set() or json_replace() */
jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns);
}
}
pParse->iLabel = iLabel;
return iRoot;
}
if( zPath[0]=='.' ){
int rawKey = 1;
x = pParse->aBlob[iRoot];
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
for(i=1; zPath[i] && zPath[i]!='"'; i++){}
nKey = i-1;
if( zPath[i] ){
i++;
}else{
return JSON_LOOKUP_PATHERROR;
}
testcase( nKey==0 );
rawKey = memchr(zKey, '\\', nKey)==0;
}else{
zKey = zPath;
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
if( nKey==0 ){
return JSON_LOOKUP_PATHERROR;
}
}
if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND;
n = jsonbPayloadSize(pParse, iRoot, &sz);
j = iRoot + n; /* j is the index of a label */
iEnd = j+sz;
while( j<iEnd ){
int rawLabel;
const char *zLabel;
x = pParse->aBlob[j] & 0x0f;
if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_LOOKUP_ERROR;
n = jsonbPayloadSize(pParse, j, &sz);
if( n==0 ) return JSON_LOOKUP_ERROR;
k = j+n; /* k is the index of the label text */
if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR;
zLabel = (const char*)&pParse->aBlob[k];
rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW;
if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){
u32 v = k+sz; /* v is the index of the value */
if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
n = jsonbPayloadSize(pParse, v, &sz);
if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR;
assert( j>0 );
rc = jsonLookupStep(pParse, v, &zPath[i], j);
if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
return rc;
}
j = k+sz;
if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
n = jsonbPayloadSize(pParse, j, &sz);
if( n==0 ) return JSON_LOOKUP_ERROR;
j += n+sz;
}
if( j>iEnd ) return JSON_LOOKUP_ERROR;
if( pParse->eEdit>=JEDIT_INS ){
u32 nIns; /* Total bytes to insert (label+value) */
JsonParse v; /* BLOB encoding of the value to be inserted */
JsonParse ix; /* Header of the label to be inserted */
testcase( pParse->eEdit==JEDIT_INS );
testcase( pParse->eEdit==JEDIT_SET );
memset(&ix, 0, sizeof(ix));
ix.db = pParse->db;
jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0);
pParse->oom |= ix.oom;
rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]);
if( !JSON_LOOKUP_ISERROR(rc)
&& jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob)
){
assert( !pParse->oom );
nIns = ix.nBlob + nKey + v.nBlob;
jsonBlobEdit(pParse, j, 0, 0, nIns);
if( !pParse->oom ){
assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */
assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */
memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob);
k = j + ix.nBlob;
memcpy(&pParse->aBlob[k], zKey, nKey);
k += nKey;
memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob);
if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot);
}
}
jsonParseReset(&v);
jsonParseReset(&ix);
return rc;
}
}else if( zPath[0]=='[' ){
x = pParse->aBlob[iRoot] & 0x0f;
if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND;
n = jsonbPayloadSize(pParse, iRoot, &sz);
k = 0;
i = 1;
while( sqlite3Isdigit(zPath[i]) ){
k = k*10 + zPath[i] - '0';
i++;
}
if( i<2 || zPath[i]!=']' ){
if( zPath[1]=='#' ){
k = jsonbArrayCount(pParse, iRoot);
i = 2;
if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
unsigned int nn = 0;
i = 3;
do{
nn = nn*10 + zPath[i] - '0';
i++;
}while( sqlite3Isdigit(zPath[i]) );
if( nn>k ) return JSON_LOOKUP_NOTFOUND;
k -= nn;
}
if( zPath[i]!=']' ){
return JSON_LOOKUP_PATHERROR;
}
}else{
return JSON_LOOKUP_PATHERROR;
}
}
j = iRoot+n;
iEnd = j+sz;
while( j<iEnd ){
if( k==0 ){
rc = jsonLookupStep(pParse, j, &zPath[i+1], 0);
if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
return rc;
}
k--;
n = jsonbPayloadSize(pParse, j, &sz);
if( n==0 ) return JSON_LOOKUP_ERROR;
j += n+sz;
}
if( j>iEnd ) return JSON_LOOKUP_ERROR;
if( k>0 ) return JSON_LOOKUP_NOTFOUND;
if( pParse->eEdit>=JEDIT_INS ){
JsonParse v;
testcase( pParse->eEdit==JEDIT_INS );
testcase( pParse->eEdit==JEDIT_SET );
rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]);
if( !JSON_LOOKUP_ISERROR(rc)
&& jsonBlobMakeEditable(pParse, v.nBlob)
){
assert( !pParse->oom );
jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob);
}
jsonParseReset(&v);
if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
return rc;
}
}else{
return JSON_LOOKUP_PATHERROR;
}
return JSON_LOOKUP_NOTFOUND;
}
/*
** Convert a JSON BLOB into text and make that text the return value
** of an SQL function.
*/
static void jsonReturnTextJsonFromBlob(
sqlite3_context *ctx,
const u8 *aBlob,
u32 nBlob
){
JsonParse x;
JsonString s;
if( NEVER(aBlob==0) ) return;
memset(&x, 0, sizeof(x));
x.aBlob = (u8*)aBlob;
x.nBlob = nBlob;
jsonStringInit(&s, ctx);
jsonTranslateBlobToText(&x, 0, &s);
jsonReturnString(&s, 0, 0);
}
/*
** Return the value of the BLOB node at index i.
**
** If the value is a primitive, return it as an SQL value.
** If the value is an array or object, return it as either
** JSON text or the BLOB encoding, depending on the JSON_B flag
** on the userdata.
*/
static void jsonReturnFromBlob(
JsonParse *pParse, /* Complete JSON parse tree */
u32 i, /* Index of the node */
sqlite3_context *pCtx, /* Return value for this function */
int textOnly /* return text JSON. Disregard user-data */
){
u32 n, sz;
int rc;
sqlite3 *db = sqlite3_context_db_handle(pCtx);
n = jsonbPayloadSize(pParse, i, &sz);
if( n==0 ){
sqlite3_result_error(pCtx, "malformed JSON", -1);
return;
}
switch( pParse->aBlob[i] & 0x0f ){
case JSONB_NULL: {
if( sz ) goto returnfromblob_malformed;
sqlite3_result_null(pCtx);
break;
}
case JSONB_TRUE: {
if( sz ) goto returnfromblob_malformed;
sqlite3_result_int(pCtx, 1);
break;
}
case JSONB_FALSE: {
if( sz ) goto returnfromblob_malformed;
sqlite3_result_int(pCtx, 0);
break;
}
case JSONB_INT5:
case JSONB_INT: {
sqlite3_int64 iRes = 0;
char *z;
int bNeg = 0;
char x;
if( sz==0 ) goto returnfromblob_malformed;
x = (char)pParse->aBlob[i+n];
if( x=='-' ){
if( sz<2 ) goto returnfromblob_malformed;
n++;
sz--;
bNeg = 1;
}
z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
if( z==0 ) goto returnfromblob_oom;
rc = sqlite3DecOrHexToI64(z, &iRes);
sqlite3DbFree(db, z);
if( rc==0 ){
sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
}else if( rc==3 && bNeg ){
sqlite3_result_int64(pCtx, SMALLEST_INT64);
}else if( rc==1 ){
goto returnfromblob_malformed;
}else{
if( bNeg ){ n--; sz++; }
goto to_double;
}
break;
}
case JSONB_FLOAT5:
case JSONB_FLOAT: {
double r;
char *z;
if( sz==0 ) goto returnfromblob_malformed;
to_double:
z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
if( z==0 ) goto returnfromblob_oom;
rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
sqlite3DbFree(db, z);
if( rc<=0 ) goto returnfromblob_malformed;
sqlite3_result_double(pCtx, r);
break;
}
case JSONB_TEXTRAW:
case JSONB_TEXT: {
sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz,
SQLITE_TRANSIENT);
break;
}
case JSONB_TEXT5:
case JSONB_TEXTJ: {
/* Translate JSON formatted string into raw text */
u32 iIn, iOut;
const char *z;
char *zOut;
u32 nOut = sz;
z = (const char*)&pParse->aBlob[i+n];
zOut = sqlite3DbMallocRaw(db, nOut+1);
if( zOut==0 ) goto returnfromblob_oom;
for(iIn=iOut=0; iIn<sz; iIn++){
char c = z[iIn];
if( c=='\\' ){
u32 v;
u32 szEscape = jsonUnescapeOneChar(&z[iIn], sz-iIn, &v);
if( v<=0x7f ){
zOut[iOut++] = (char)v;
}else if( v<=0x7ff ){
assert( szEscape>=2 );
zOut[iOut++] = (char)(0xc0 | (v>>6));
zOut[iOut++] = 0x80 | (v&0x3f);
}else if( v<0x10000 ){
assert( szEscape>=3 );
zOut[iOut++] = 0xe0 | (v>>12);
zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
zOut[iOut++] = 0x80 | (v&0x3f);
}else if( v==JSON_INVALID_CHAR ){
/* Silently ignore illegal unicode */
}else{
assert( szEscape>=4 );
zOut[iOut++] = 0xf0 | (v>>18);
zOut[iOut++] = 0x80 | ((v>>12)&0x3f);
zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
zOut[iOut++] = 0x80 | (v&0x3f);
}
iIn += szEscape - 1;
}else{
zOut[iOut++] = c;
}
} /* end for() */
assert( iOut<=nOut );
zOut[iOut] = 0;
sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
break;
}
case JSONB_ARRAY:
case JSONB_OBJECT: {
int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
if( flags & JSON_BLOB ){
sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
}else{
jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
}
break;
}
default: {
goto returnfromblob_malformed;
}
}
return;
returnfromblob_oom:
sqlite3_result_error_nomem(pCtx);
return;
returnfromblob_malformed:
sqlite3_result_error(pCtx, "malformed JSON", -1);
return;
}
/*
** pArg is a function argument that might be an SQL value or a JSON
** value. Figure out what it is and encode it as a JSONB blob.
** Return the results in pParse.
**
** pParse is uninitialized upon entry. This routine will handle the
** initialization of pParse. The result will be contained in
** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically
** allocated (if pParse->nBlobAlloc is greater than zero) in which case
** the caller is responsible for freeing the space allocated to pParse->aBlob
** when it has finished with it. Or pParse->aBlob might be a static string
** or a value obtained from sqlite3_value_blob(pArg).
**
** If the argument is a BLOB that is clearly not a JSONB, then this
** function might set an error message in ctx and return non-zero.
** It might also set an error message and return non-zero on an OOM error.
*/
static int jsonFunctionArgToBlob(
sqlite3_context *ctx,
sqlite3_value *pArg,
JsonParse *pParse
){
int eType = sqlite3_value_type(pArg);
static u8 aNull[] = { 0x00 };
memset(pParse, 0, sizeof(pParse[0]));
pParse->db = sqlite3_context_db_handle(ctx);
switch( eType ){
default: {
pParse->aBlob = aNull;
pParse->nBlob = 1;
return 0;
}
case SQLITE_BLOB: {
if( jsonFuncArgMightBeBinary(pArg) ){
pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
pParse->nBlob = sqlite3_value_bytes(pArg);
}else{
sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
return 1;
}
break;
}
case SQLITE_TEXT: {
const char *zJson = (const char*)sqlite3_value_text(pArg);
int nJson = sqlite3_value_bytes(pArg);
if( zJson==0 ) return 1;
if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){
pParse->zJson = (char*)zJson;
pParse->nJson = nJson;
if( jsonConvertTextToBlob(pParse, ctx) ){
sqlite3_result_error(ctx, "malformed JSON", -1);
sqlite3DbFree(pParse->db, pParse->aBlob);
memset(pParse, 0, sizeof(pParse[0]));
return 1;
}
}else{
jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson);
}
break;
}
case SQLITE_FLOAT: {
double r = sqlite3_value_double(pArg);
if( NEVER(sqlite3IsNaN(r)) ){
jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0);
}else{
int n = sqlite3_value_bytes(pArg);
const char *z = (const char*)sqlite3_value_text(pArg);
if( z==0 ) return 1;
if( z[0]=='I' ){
jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
}else if( z[0]=='-' && z[1]=='I' ){
jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
}else{
jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z);
}
}
break;
}
case SQLITE_INTEGER: {
int n = sqlite3_value_bytes(pArg);
const char *z = (const char*)sqlite3_value_text(pArg);
if( z==0 ) return 1;
jsonBlobAppendNode(pParse, JSONB_INT, n, z);
break;
}
}
if( pParse->oom ){
sqlite3_result_error_nomem(ctx);
return 1;
}else{
return 0;
}
}
/*
** Generate a bad path error.
**
** If ctx is not NULL then push the error message into ctx and return NULL.
** If ctx is NULL, then return the text of the error message.
*/
static char *jsonBadPathError(
sqlite3_context *ctx, /* The function call containing the error */
const char *zPath /* The path with the problem */
){
char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
if( ctx==0 ) return zMsg;
if( zMsg ){
sqlite3_result_error(ctx, zMsg, -1);
sqlite3_free(zMsg);
}else{
sqlite3_result_error_nomem(ctx);
}
return 0;
}
/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent
** arguments come in parse where each pair contains a JSON path and
** content to insert or set at that patch. Do the updates
** and return the result.
**
** The specific operation is determined by eEdit, which can be one
** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET.
*/
static void jsonInsertIntoBlob(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv,
int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */
){
int i;
u32 rc = 0;
const char *zPath = 0;
int flgs;
JsonParse *p;
JsonParse ax;
assert( (argc&1)==1 );
flgs = argc==1 ? 0 : JSON_EDITABLE;
p = jsonParseFuncArg(ctx, argv[0], flgs);
if( p==0 ) return;
for(i=1; i<argc-1; i+=2){
if( sqlite3_value_type(argv[i])==SQLITE_NULL ) continue;
zPath = (const char*)sqlite3_value_text(argv[i]);
if( zPath==0 ){
sqlite3_result_error_nomem(ctx);
jsonParseFree(p);
return;
}
if( zPath[0]!='$' ) goto jsonInsertIntoBlob_patherror;
if( jsonFunctionArgToBlob(ctx, argv[i+1], &ax) ){
jsonParseReset(&ax);
jsonParseFree(p);
return;
}
if( zPath[1]==0 ){
if( eEdit==JEDIT_REPL || eEdit==JEDIT_SET ){
jsonBlobEdit(p, 0, p->nBlob, ax.aBlob, ax.nBlob);
}
rc = 0;
}else{
p->eEdit = eEdit;
p->nIns = ax.nBlob;
p->aIns = ax.aBlob;
p->delta = 0;
rc = jsonLookupStep(p, 0, zPath+1, 0);
}
jsonParseReset(&ax);
if( rc==JSON_LOOKUP_NOTFOUND ) continue;
if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror;
}
jsonReturnParse(ctx, p);
jsonParseFree(p);
return;
jsonInsertIntoBlob_patherror:
jsonParseFree(p);
if( rc==JSON_LOOKUP_ERROR ){
sqlite3_result_error(ctx, "malformed JSON", -1);
}else{
jsonBadPathError(ctx, zPath);
}
return;
}
/*
** If pArg is a blob that seems like a JSONB blob, then initialize
** p to point to that JSONB and return TRUE. If pArg does not seem like
** a JSONB blob, then return FALSE;
**
** This routine is only called if it is already known that pArg is a
** blob. The only open question is whether or not the blob appears
** to be a JSONB blob.
*/
static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
u32 n, sz = 0;
p->aBlob = (u8*)sqlite3_value_blob(pArg);
p->nBlob = (u32)sqlite3_value_bytes(pArg);
if( p->nBlob==0 ){
p->aBlob = 0;
return 0;
}
if( NEVER(p->aBlob==0) ){
return 0;
}
if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
&& (n = jsonbPayloadSize(p, 0, &sz))>0
&& sz+n==p->nBlob
&& ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
){
return 1;
}
p->aBlob = 0;
p->nBlob = 0;
return 0;
}
/*
** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
** from the SQL function argument pArg. Return a pointer to the new
** JsonParse object.
**
** Ownership of the new JsonParse object is passed to the caller. The
** caller should invoke jsonParseFree() on the return value when it
** has finished using it.
**
** If any errors are detected, an appropriate error messages is set
** using sqlite3_result_error() or the equivalent and this routine
** returns NULL. This routine also returns NULL if the pArg argument
** is an SQL NULL value, but no error message is set in that case. This
** is so that SQL functions that are given NULL arguments will return
** a NULL value.
*/
static JsonParse *jsonParseFuncArg(
sqlite3_context *ctx,
sqlite3_value *pArg,
u32 flgs
){
int eType; /* Datatype of pArg */
JsonParse *p = 0; /* Value to be returned */
JsonParse *pFromCache = 0; /* Value taken from cache */
sqlite3 *db; /* The database connection */
assert( ctx!=0 );
eType = sqlite3_value_type(pArg);
if( eType==SQLITE_NULL ){
return 0;
}
pFromCache = jsonCacheSearch(ctx, pArg);
if( pFromCache ){
pFromCache->nJPRef++;
if( (flgs & JSON_EDITABLE)==0 ){
return pFromCache;
}
}
db = sqlite3_context_db_handle(ctx);
rebuild_from_cache:
p = sqlite3DbMallocZero(db, sizeof(*p));
if( p==0 ) goto json_pfa_oom;
memset(p, 0, sizeof(*p));
p->db = db;
p->nJPRef = 1;
if( pFromCache!=0 ){
u32 nBlob = pFromCache->nBlob;
p->aBlob = sqlite3DbMallocRaw(db, nBlob);
if( p->aBlob==0 ) goto json_pfa_oom;
memcpy(p->aBlob, pFromCache->aBlob, nBlob);
p->nBlobAlloc = p->nBlob = nBlob;
p->hasNonstd = pFromCache->hasNonstd;
jsonParseFree(pFromCache);
return p;
}
if( eType==SQLITE_BLOB ){
if( jsonArgIsJsonb(pArg,p) ){
if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
goto json_pfa_oom;
}
return p;
}
/* If the blob is not valid JSONB, fall through into trying to cast
** the blob into text which is then interpreted as JSON. (tag-20240123-a)
**
** This goes against all historical documentation about how the SQLite
** JSON functions were suppose to work. From the beginning, blob was
** reserved for expansion and a blob value should have raised an error.
** But it did not, due to a bug. And many applications came to depend
** upon this buggy behavior, espeically when using the CLI and reading
** JSON text using readfile(), which returns a blob. For this reason
** we will continue to support the bug moving forward.
** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
*/
}
p->zJson = (char*)sqlite3_value_text(pArg);
p->nJson = sqlite3_value_bytes(pArg);
if( p->nJson==0 ) goto json_pfa_malformed;
if( NEVER(p->zJson==0) ) goto json_pfa_oom;
if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
if( flgs & JSON_KEEPERROR ){
p->nErr = 1;
return p;
}else{
jsonParseFree(p);
return 0;
}
}else{
int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref);
int rc;
if( !isRCStr ){
char *zNew = sqlite3RCStrNew( p->nJson );
if( zNew==0 ) goto json_pfa_oom;
memcpy(zNew, p->zJson, p->nJson);
p->zJson = zNew;
p->zJson[p->nJson] = 0;
}else{
sqlite3RCStrRef(p->zJson);
}
p->bJsonIsRCStr = 1;
rc = jsonCacheInsert(ctx, p);
if( rc==SQLITE_NOMEM ) goto json_pfa_oom;
if( flgs & JSON_EDITABLE ){
pFromCache = p;
p = 0;
goto rebuild_from_cache;
}
}
return p;
json_pfa_malformed:
if( flgs & JSON_KEEPERROR ){
p->nErr = 1;
return p;
}else{
jsonParseFree(p);
sqlite3_result_error(ctx, "malformed JSON", -1);
return 0;
}
json_pfa_oom:
jsonParseFree(pFromCache);
jsonParseFree(p);
sqlite3_result_error_nomem(ctx);
return 0;
}
/*
** Make the return value of a JSON function either the raw JSONB blob
** or make it JSON text, depending on whether the JSON_BLOB flag is
** set on the function.
*/
static void jsonReturnParse(
sqlite3_context *ctx,
JsonParse *p
){
int flgs;
if( p->oom ){
sqlite3_result_error_nomem(ctx);
return;
}
flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
if( flgs & JSON_BLOB ){
if( p->nBlobAlloc>0 && !p->bReadOnly ){
sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC);
p->nBlobAlloc = 0;
}else{
sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT);
}
}else{
JsonString s;
jsonStringInit(&s, ctx);
p->delta = 0;
jsonTranslateBlobToText(p, 0, &s);
jsonReturnString(&s, p, ctx);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
}
/****************************************************************************
** SQL functions used for testing and debugging
****************************************************************************/
#if SQLITE_DEBUG
/*
** Decode JSONB bytes in aBlob[] starting at iStart through but not
** including iEnd. Indent the
** content by nIndent spaces.
*/
static void jsonDebugPrintBlob(
JsonParse *pParse, /* JSON content */
u32 iStart, /* Start rendering here */
u32 iEnd, /* Do not render this byte or any byte after this one */
int nIndent, /* Indent by this many spaces */
sqlite3_str *pOut /* Generate output into this sqlite3_str object */
){
while( iStart<iEnd ){
u32 i, n, nn, sz = 0;
int showContent = 1;
u8 x = pParse->aBlob[iStart] & 0x0f;
u32 savedNBlob = pParse->nBlob;
sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, "");
if( pParse->nBlobAlloc>pParse->nBlob ){
pParse->nBlob = pParse->nBlobAlloc;
}
nn = n = jsonbPayloadSize(pParse, iStart, &sz);
if( nn==0 ) nn = 1;
if( sz>0 && x<JSONB_ARRAY ){
nn += sz;
}
for(i=0; i<nn; i++){
sqlite3_str_appendf(pOut, " %02x", pParse->aBlob[iStart+i]);
}
if( n==0 ){
sqlite3_str_appendf(pOut, " ERROR invalid node size\n");
iStart = n==0 ? iStart+1 : iEnd;
continue;
}
pParse->nBlob = savedNBlob;
if( iStart+n+sz>iEnd ){
iEnd = iStart+n+sz;
if( iEnd>pParse->nBlob ){
if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){
iEnd = pParse->nBlobAlloc;
}else{
iEnd = pParse->nBlob;
}
}
}
sqlite3_str_appendall(pOut," <-- ");
switch( x ){
case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break;
case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break;
case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break;
case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break;
case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break;
case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break;
case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break;
case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break;
case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break;
case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break;
case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break;
case JSONB_ARRAY: {
sqlite3_str_appendf(pOut,"array, %u bytes\n", sz);
jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
showContent = 0;
break;
}
case JSONB_OBJECT: {
sqlite3_str_appendf(pOut, "object, %u bytes\n", sz);
jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
showContent = 0;
break;
}
default: {
sqlite3_str_appendall(pOut, "ERROR: unknown node type\n");
showContent = 0;
break;
}
}
if( showContent ){
if( sz==0 && x<=JSONB_FALSE ){
sqlite3_str_append(pOut, "\n", 1);
}else{
u32 i;
sqlite3_str_appendall(pOut, ": \"");
for(i=iStart+n; i<iStart+n+sz; i++){
u8 c = pParse->aBlob[i];
if( c<0x20 || c>=0x7f ) c = '.';
sqlite3_str_append(pOut, (char*)&c, 1);
}
sqlite3_str_append(pOut, "\"\n", 2);
}
}
iStart += n + sz;
}
}
static void jsonShowParse(JsonParse *pParse){
sqlite3_str out;
char zBuf[1000];
if( pParse==0 ){
printf("NULL pointer\n");
return;
}else{
printf("nBlobAlloc = %u\n", pParse->nBlobAlloc);
printf("nBlob = %u\n", pParse->nBlob);
printf("delta = %d\n", pParse->delta);
if( pParse->nBlob==0 ) return;
printf("content (bytes 0..%u):\n", pParse->nBlob-1);
}
sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000);
jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out);
printf("%s", sqlite3_str_value(&out));
sqlite3_str_reset(&out);
}
#endif /* SQLITE_DEBUG */
#ifdef SQLITE_DEBUG
/*
** SQL function: json_parse(JSON)
**
** Parse JSON using jsonParseFuncArg(). Return text that is a
** human-readable dump of the binary JSONB for the input parameter.
*/
static void jsonParseFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p; /* The parse */
sqlite3_str out;
assert( argc>=1 );
sqlite3StrAccumInit(&out, 0, 0, 0, 1000000);
p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
if( argc==1 ){
jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out);
sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8);
}else{
jsonShowParse(p);
}
jsonParseFree(p);
}
#endif /* SQLITE_DEBUG */
/****************************************************************************
** Scalar SQL function implementations
****************************************************************************/
/*
** Implementation of the json_quote(VALUE) function. Return a JSON value
** corresponding to the SQL value input. Mostly this means putting
** double-quotes around strings and returning the unquoted string "null"
** when given a NULL input.
*/
static void jsonQuoteFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonString jx;
UNUSED_PARAMETER(argc);
jsonStringInit(&jx, ctx);
jsonAppendSqlValue(&jx, argv[0]);
jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
/*
** Implementation of the json_array(VALUE,...) function. Return a JSON
** array that contains all values given in arguments. Or if any argument
** is a BLOB, throw an error.
*/
static void jsonArrayFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
int i;
JsonString jx;
jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=0; i<argc; i++){
jsonAppendSeparator(&jx);
jsonAppendSqlValue(&jx, argv[i]);
}
jsonAppendChar(&jx, ']');
jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
/*
** json_array_length(JSON)
** json_array_length(JSON, PATH)
**
** Return the number of elements in the top-level JSON array.
** Return 0 if the input is not a well-formed JSON array.
*/
static void jsonArrayLengthFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p; /* The parse */
sqlite3_int64 cnt = 0;
u32 i;
u8 eErr = 0;
p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 ){
jsonParseFree(p);
return;
}
i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0);
if( JSON_LOOKUP_ISERROR(i) ){
if( i==JSON_LOOKUP_NOTFOUND ){
/* no-op */
}else if( i==JSON_LOOKUP_PATHERROR ){
jsonBadPathError(ctx, zPath);
}else{
sqlite3_result_error(ctx, "malformed JSON", -1);
}
eErr = 1;
i = 0;
}
}else{
i = 0;
}
if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){
cnt = jsonbArrayCount(p, i);
}
if( !eErr ) sqlite3_result_int64(ctx, cnt);
jsonParseFree(p);
}
/* True if the string is all digits */
static int jsonAllDigits(const char *z, int n){
int i;
for(i=0; i<n && sqlite3Isdigit(z[i]); i++){}
return i==n;
}
/* True if the string is all alphanumerics and underscores */
static int jsonAllAlphanum(const char *z, int n){
int i;
for(i=0; i<n && (sqlite3Isalnum(z[i]) || z[i]=='_'); i++){}
return i==n;
}
/*
** json_extract(JSON, PATH, ...)
** "->"(JSON,PATH)
** "->>"(JSON,PATH)
**
** Return the element described by PATH. Return NULL if that PATH element
|
| ︙ | ︙ | |||
205177 205178 205179 205180 205181 205182 205183 |
** compatibility with PG.
*/
static void jsonExtractFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
| | < < | > | | > > | > > > | > > | > > > | < | | | | | | | | | | | | | > > > > > | | | | | > | | | | > | > | > > | > > > | > > > > | | | | < > | > > > | < < < < < < < < < | > > | > | | > | | | | | | > > > | > > | | > > > > > > > > | | > > > > > | < > > > > > > > | > | < | > > > | > | | | < > > > > > > > > > > > | > > | > > > | > | | | > > > > > > > > > > > > > > | < < > > > | > > > > > | | | | > > > > | | | | | | > | | | < | < | | < | > > > > > > | > | > > > > | | > > > > > > > > | < | < > | > > > > | > > | > > > > > > > > > > > > > > > | > > | > > > > > > > > | < > > > > > > > > | > > > | < < > > > > > | | > > | | | > > | > | | | > | | < < | | < < | < | | > | < | | | > > > | 206867 206868 206869 206870 206871 206872 206873 206874 206875 206876 206877 206878 206879 206880 206881 206882 206883 206884 206885 206886 206887 206888 206889 206890 206891 206892 206893 206894 206895 206896 206897 206898 206899 206900 206901 206902 206903 206904 206905 206906 206907 206908 206909 206910 206911 206912 206913 206914 206915 206916 206917 206918 206919 206920 206921 206922 206923 206924 206925 206926 206927 206928 206929 206930 206931 206932 206933 206934 206935 206936 206937 206938 206939 206940 206941 206942 206943 206944 206945 206946 206947 206948 206949 206950 206951 206952 206953 206954 206955 206956 206957 206958 206959 206960 206961 206962 206963 206964 206965 206966 206967 206968 206969 206970 206971 206972 206973 206974 206975 206976 206977 206978 206979 206980 206981 206982 206983 206984 206985 206986 206987 206988 206989 206990 206991 206992 206993 206994 206995 206996 206997 206998 206999 207000 207001 207002 207003 207004 207005 207006 207007 207008 207009 207010 207011 207012 207013 207014 207015 207016 207017 207018 207019 207020 207021 207022 207023 207024 207025 207026 207027 207028 207029 207030 207031 207032 207033 207034 207035 207036 207037 207038 207039 207040 207041 207042 207043 207044 207045 207046 207047 207048 207049 207050 207051 207052 207053 207054 207055 207056 207057 207058 207059 207060 207061 207062 207063 207064 207065 207066 207067 207068 207069 207070 207071 207072 207073 207074 207075 207076 207077 207078 207079 207080 207081 207082 207083 207084 207085 207086 207087 207088 207089 207090 207091 207092 207093 207094 207095 207096 207097 207098 207099 207100 207101 207102 207103 207104 207105 207106 207107 207108 207109 207110 207111 207112 207113 207114 207115 207116 207117 207118 207119 207120 207121 207122 207123 207124 207125 207126 207127 207128 207129 207130 207131 207132 207133 207134 207135 207136 207137 207138 207139 207140 207141 207142 207143 207144 207145 207146 207147 207148 207149 207150 207151 207152 207153 207154 207155 207156 207157 207158 207159 207160 207161 207162 207163 207164 207165 207166 207167 207168 207169 207170 207171 207172 207173 207174 207175 207176 207177 207178 207179 207180 207181 207182 207183 207184 207185 207186 207187 207188 207189 207190 207191 207192 207193 207194 207195 207196 207197 207198 207199 207200 207201 207202 207203 207204 207205 207206 207207 207208 207209 207210 207211 207212 |
** compatibility with PG.
*/
static void jsonExtractFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p = 0; /* The parse */
int flags; /* Flags associated with the function */
int i; /* Loop counter */
JsonString jx; /* String for array result */
if( argc<2 ) return;
p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
jsonStringInit(&jx, ctx);
if( argc>2 ){
jsonAppendChar(&jx, '[');
}
for(i=1; i<argc; i++){
/* With a single PATH argument */
const char *zPath = (const char*)sqlite3_value_text(argv[i]);
int nPath;
u32 j;
if( zPath==0 ) goto json_extract_error;
nPath = sqlite3Strlen30(zPath);
if( zPath[0]=='$' ){
j = jsonLookupStep(p, 0, zPath+1, 0);
}else if( (flags & JSON_ABPATH) ){
/* The -> and ->> operators accept abbreviated PATH arguments. This
** is mostly for compatibility with PostgreSQL, but also for
** convenience.
**
** NUMBER ==> $[NUMBER] // PG compatible
** LABEL ==> $.LABEL // PG compatible
** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
*/
jsonStringInit(&jx, ctx);
if( jsonAllDigits(zPath, nPath) ){
jsonAppendRawNZ(&jx, "[", 1);
jsonAppendRaw(&jx, zPath, nPath);
jsonAppendRawNZ(&jx, "]", 2);
}else if( jsonAllAlphanum(zPath, nPath) ){
jsonAppendRawNZ(&jx, ".", 1);
jsonAppendRaw(&jx, zPath, nPath);
}else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){
jsonAppendRaw(&jx, zPath, nPath);
}else{
jsonAppendRawNZ(&jx, ".\"", 2);
jsonAppendRaw(&jx, zPath, nPath);
jsonAppendRawNZ(&jx, "\"", 1);
}
jsonStringTerminate(&jx);
j = jsonLookupStep(p, 0, jx.zBuf, 0);
jsonStringReset(&jx);
}else{
jsonBadPathError(ctx, zPath);
goto json_extract_error;
}
if( j<p->nBlob ){
if( argc==2 ){
if( flags & JSON_JSON ){
jsonStringInit(&jx, ctx);
jsonTranslateBlobToText(p, j, &jx);
jsonReturnString(&jx, 0, 0);
jsonStringReset(&jx);
assert( (flags & JSON_BLOB)==0 );
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}else{
jsonReturnFromBlob(p, j, ctx, 0);
if( (flags & (JSON_SQL|JSON_BLOB))==0
&& (p->aBlob[j]&0x0f)>=JSONB_ARRAY
){
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
}
}else{
jsonAppendSeparator(&jx);
jsonTranslateBlobToText(p, j, &jx);
}
}else if( j==JSON_LOOKUP_NOTFOUND ){
if( argc==2 ){
goto json_extract_error; /* Return NULL if not found */
}else{
jsonAppendSeparator(&jx);
jsonAppendRawNZ(&jx, "null", 4);
}
}else if( j==JSON_LOOKUP_ERROR ){
sqlite3_result_error(ctx, "malformed JSON", -1);
goto json_extract_error;
}else{
jsonBadPathError(ctx, zPath);
goto json_extract_error;
}
}
if( argc>2 ){
jsonAppendChar(&jx, ']');
jsonReturnString(&jx, 0, 0);
if( (flags & JSON_BLOB)==0 ){
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
}
json_extract_error:
jsonStringReset(&jx);
jsonParseFree(p);
return;
}
/*
** Return codes for jsonMergePatch()
*/
#define JSON_MERGE_OK 0 /* Success */
#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */
#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */
#define JSON_MERGE_OOM 3 /* Out-of-memory condition */
/*
** RFC-7396 MergePatch for two JSONB blobs.
**
** pTarget is the target. pPatch is the patch. The target is updated
** in place. The patch is read-only.
**
** The original RFC-7396 algorithm is this:
**
** define MergePatch(Target, Patch):
** if Patch is an Object:
** if Target is not an Object:
** Target = {} # Ignore the contents and set it to an empty Object
** for each Name/Value pair in Patch:
** if Value is null:
** if Name exists in Target:
** remove the Name/Value pair from Target
** else:
** Target[Name] = MergePatch(Target[Name], Value)
** return Target
** else:
** return Patch
**
** Here is an equivalent algorithm restructured to show the actual
** implementation:
**
** 01 define MergePatch(Target, Patch):
** 02 if Patch is not an Object:
** 03 return Patch
** 04 else: // if Patch is an Object
** 05 if Target is not an Object:
** 06 Target = {}
** 07 for each Name/Value pair in Patch:
** 08 if Name exists in Target:
** 09 if Value is null:
** 10 remove the Name/Value pair from Target
** 11 else
** 12 Target[name] = MergePatch(Target[Name], Value)
** 13 else if Value is not NULL:
** 14 if Value is not an Object:
** 15 Target[name] = Value
** 16 else:
** 17 Target[name] = MergePatch('{}',value)
** 18 return Target
** |
** ^---- Line numbers referenced in comments in the implementation
*/
static int jsonMergePatch(
JsonParse *pTarget, /* The JSON parser that contains the TARGET */
u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */
const JsonParse *pPatch, /* The PATCH */
u32 iPatch /* Index of PATCH in pPatch->aBlob[] */
){
u8 x; /* Type of a single node */
u32 n, sz=0; /* Return values from jsonbPayloadSize() */
u32 iTCursor; /* Cursor position while scanning the target object */
u32 iTStart; /* First label in the target object */
u32 iTEndBE; /* Original first byte past end of target, before edit */
u32 iTEnd; /* Current first byte past end of target */
u8 eTLabel; /* Node type of the target label */
u32 iTLabel = 0; /* Index of the label */
u32 nTLabel = 0; /* Header size in bytes for the target label */
u32 szTLabel = 0; /* Size of the target label payload */
u32 iTValue = 0; /* Index of the target value */
u32 nTValue = 0; /* Header size of the target value */
u32 szTValue = 0; /* Payload size for the target value */
u32 iPCursor; /* Cursor position while scanning the patch */
u32 iPEnd; /* First byte past the end of the patch */
u8 ePLabel; /* Node type of the patch label */
u32 iPLabel; /* Start of patch label */
u32 nPLabel; /* Size of header on the patch label */
u32 szPLabel; /* Payload size of the patch label */
u32 iPValue; /* Start of patch value */
u32 nPValue; /* Header size for the patch value */
u32 szPValue; /* Payload size of the patch value */
assert( iTarget>=0 && iTarget<pTarget->nBlob );
assert( iPatch>=0 && iPatch<pPatch->nBlob );
x = pPatch->aBlob[iPatch] & 0x0f;
if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */
u32 szPatch; /* Total size of the patch, header+payload */
u32 szTarget; /* Total size of the target, header+payload */
n = jsonbPayloadSize(pPatch, iPatch, &sz);
szPatch = n+sz;
sz = 0;
n = jsonbPayloadSize(pTarget, iTarget, &sz);
szTarget = n+sz;
jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch);
return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */
}
x = pTarget->aBlob[iTarget] & 0x0f;
if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */
n = jsonbPayloadSize(pTarget, iTarget, &sz);
jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0);
x = pTarget->aBlob[iTarget];
pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT;
}
n = jsonbPayloadSize(pPatch, iPatch, &sz);
if( NEVER(n==0) ) return JSON_MERGE_BADPATCH;
iPCursor = iPatch+n;
iPEnd = iPCursor+sz;
n = jsonbPayloadSize(pTarget, iTarget, &sz);
if( NEVER(n==0) ) return JSON_MERGE_BADTARGET;
iTStart = iTarget+n;
iTEndBE = iTStart+sz;
while( iPCursor<iPEnd ){ /* Algorithm line 07 */
iPLabel = iPCursor;
ePLabel = pPatch->aBlob[iPCursor] & 0x0f;
if( ePLabel<JSONB_TEXT || ePLabel>JSONB_TEXTRAW ){
return JSON_MERGE_BADPATCH;
}
nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel);
if( nPLabel==0 ) return JSON_MERGE_BADPATCH;
iPValue = iPCursor + nPLabel + szPLabel;
if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH;
nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue);
if( nPValue==0 ) return JSON_MERGE_BADPATCH;
iPCursor = iPValue + nPValue + szPValue;
if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH;
iTCursor = iTStart;
iTEnd = iTEndBE + pTarget->delta;
while( iTCursor<iTEnd ){
int isEqual; /* true if the patch and target labels match */
iTLabel = iTCursor;
eTLabel = pTarget->aBlob[iTCursor] & 0x0f;
if( eTLabel<JSONB_TEXT || eTLabel>JSONB_TEXTRAW ){
return JSON_MERGE_BADTARGET;
}
nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel);
if( nTLabel==0 ) return JSON_MERGE_BADTARGET;
iTValue = iTLabel + nTLabel + szTLabel;
if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET;
nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue);
if( nTValue==0 ) return JSON_MERGE_BADTARGET;
if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET;
isEqual = jsonLabelCompare(
(const char*)&pPatch->aBlob[iPLabel+nPLabel],
szPLabel,
(ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW),
(const char*)&pTarget->aBlob[iTLabel+nTLabel],
szTLabel,
(eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW));
if( isEqual ) break;
iTCursor = iTValue + nTValue + szTValue;
}
x = pPatch->aBlob[iPValue] & 0x0f;
if( iTCursor<iTEnd ){
/* A match was found. Algorithm line 08 */
if( x==0 ){
/* Patch value is NULL. Algorithm line 09 */
jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0);
/* vvvvvv----- No OOM on a delete-only edit */
if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM;
}else{
/* Algorithm line 12 */
int rc, savedDelta = pTarget->delta;
pTarget->delta = 0;
rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue);
if( rc ) return rc;
pTarget->delta += savedDelta;
}
}else if( x>0 ){ /* Algorithm line 13 */
/* No match and patch value is not NULL */
u32 szNew = szPLabel+nPLabel;
if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */
jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew);
if( pTarget->oom ) return JSON_MERGE_OOM;
memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
memcpy(&pTarget->aBlob[iTEnd+szNew],
&pPatch->aBlob[iPValue], szPValue+nPValue);
}else{
int rc, savedDelta;
jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1);
if( pTarget->oom ) return JSON_MERGE_OOM;
memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
pTarget->aBlob[iTEnd+szNew] = 0x00;
savedDelta = pTarget->delta;
pTarget->delta = 0;
rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue);
if( rc ) return rc;
pTarget->delta += savedDelta;
}
}
}
if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget);
return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;
}
/*
** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
** object that is the result of running the RFC 7396 MergePatch() algorithm
** on the two arguments.
*/
static void jsonPatchFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *pTarget; /* The TARGET */
JsonParse *pPatch; /* The PATCH */
int rc; /* Result code */
UNUSED_PARAMETER(argc);
assert( argc==2 );
pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE);
if( pTarget==0 ) return;
pPatch = jsonParseFuncArg(ctx, argv[1], 0);
if( pPatch ){
rc = jsonMergePatch(pTarget, 0, pPatch, 0);
if( rc==JSON_MERGE_OK ){
jsonReturnParse(ctx, pTarget);
}else if( rc==JSON_MERGE_OOM ){
sqlite3_result_error_nomem(ctx);
}else{
sqlite3_result_error(ctx, "malformed JSON", -1);
}
jsonParseFree(pPatch);
}
jsonParseFree(pTarget);
}
/*
** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON
** object that contains all name/value given in arguments. Or if any name
** is not a string or if any value is a BLOB, throw an error.
|
| ︙ | ︙ | |||
205377 205378 205379 205380 205381 205382 205383 |
u32 n;
if( argc&1 ){
sqlite3_result_error(ctx, "json_object() requires an even number "
"of arguments", -1);
return;
}
| | | | | | | < | > | | | | | < | < < < < < | | | > > | < | | < < < < < < | < < | | < < < < < | < < < < | | < < < < | < < < < < < < < < | < < < | < | < < < < < | | < < < < < < < < < < | | < < < | < < | | | < < < < | < | | < < | < < | < < < < < < < | < < < < < < < < < < < < < < < | < < < | | | < < < < < < < < < < < < < < < < < < < < | | | > > > > > | > > > > > | < > | < | | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > | | | > | | > > | < < < < > | < | < > | < | < < < < < | < < < < > | > > > | < | | | > | > > | > > > | < > | < > | | | > > > > > > | < | > | | | | > > | > > > > | | > > > > | | 207222 207223 207224 207225 207226 207227 207228 207229 207230 207231 207232 207233 207234 207235 207236 207237 207238 207239 207240 207241 207242 207243 207244 207245 207246 207247 207248 207249 207250 207251 207252 207253 207254 207255 207256 207257 207258 207259 207260 207261 207262 207263 207264 207265 207266 207267 207268 207269 207270 207271 207272 207273 207274 207275 207276 207277 207278 207279 207280 207281 207282 207283 207284 207285 207286 207287 207288 207289 207290 207291 207292 207293 207294 207295 207296 207297 207298 207299 207300 207301 207302 207303 207304 207305 207306 207307 207308 207309 207310 207311 207312 207313 207314 207315 207316 207317 207318 207319 207320 207321 207322 207323 207324 207325 207326 207327 207328 207329 207330 207331 207332 207333 207334 207335 207336 207337 207338 207339 207340 207341 207342 207343 207344 207345 207346 207347 207348 207349 207350 207351 207352 207353 207354 207355 207356 207357 207358 207359 207360 207361 207362 207363 207364 207365 207366 207367 207368 207369 207370 207371 207372 207373 207374 207375 207376 207377 207378 207379 207380 207381 207382 207383 207384 207385 207386 207387 207388 207389 207390 207391 207392 207393 207394 207395 207396 207397 207398 207399 207400 207401 207402 207403 207404 207405 207406 207407 207408 207409 207410 207411 207412 207413 207414 207415 207416 207417 207418 207419 207420 207421 207422 207423 207424 207425 207426 207427 207428 207429 207430 207431 207432 207433 207434 207435 207436 207437 207438 207439 207440 207441 207442 207443 207444 207445 207446 207447 207448 207449 207450 207451 207452 207453 207454 207455 207456 207457 207458 207459 207460 207461 207462 207463 207464 207465 207466 207467 207468 207469 207470 207471 207472 207473 207474 207475 207476 207477 207478 207479 207480 207481 207482 207483 207484 207485 207486 207487 207488 207489 207490 207491 207492 207493 207494 207495 207496 207497 207498 207499 207500 207501 207502 207503 207504 207505 207506 207507 207508 207509 207510 207511 207512 207513 207514 207515 207516 207517 207518 207519 207520 207521 207522 207523 207524 207525 207526 207527 207528 207529 207530 207531 207532 207533 207534 207535 207536 207537 207538 207539 207540 207541 207542 207543 207544 207545 207546 207547 207548 207549 207550 207551 207552 207553 207554 207555 207556 207557 207558 207559 207560 207561 207562 207563 207564 207565 207566 207567 207568 207569 207570 207571 207572 207573 207574 207575 207576 207577 207578 207579 207580 207581 207582 207583 207584 207585 207586 207587 207588 207589 207590 207591 207592 207593 207594 207595 207596 207597 207598 207599 207600 207601 207602 207603 207604 207605 207606 207607 207608 207609 207610 207611 207612 207613 207614 207615 207616 207617 207618 207619 207620 207621 207622 207623 207624 207625 207626 207627 207628 207629 207630 207631 207632 207633 207634 207635 207636 207637 207638 207639 207640 207641 207642 207643 207644 207645 |
u32 n;
if( argc&1 ){
sqlite3_result_error(ctx, "json_object() requires an even number "
"of arguments", -1);
return;
}
jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '{');
for(i=0; i<argc; i+=2){
if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
jsonStringReset(&jx);
return;
}
jsonAppendSeparator(&jx);
z = (const char*)sqlite3_value_text(argv[i]);
n = sqlite3_value_bytes(argv[i]);
jsonAppendString(&jx, z, n);
jsonAppendChar(&jx, ':');
jsonAppendSqlValue(&jx, argv[i+1]);
}
jsonAppendChar(&jx, '}');
jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
/*
** json_remove(JSON, PATH, ...)
**
** Remove the named elements from JSON and return the result. malformed
** JSON or PATH arguments result in an error.
*/
static void jsonRemoveFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p; /* The parse */
const char *zPath = 0; /* Path of element to be removed */
int i; /* Loop counter */
u32 rc; /* Subroutine return code */
if( argc<1 ) return;
p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0);
if( p==0 ) return;
for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
if( zPath==0 ){
goto json_remove_done;
}
if( zPath[0]!='$' ){
goto json_remove_patherror;
}
if( zPath[1]==0 ){
/* json_remove(j,'$') returns NULL */
goto json_remove_done;
}
p->eEdit = JEDIT_DEL;
p->delta = 0;
rc = jsonLookupStep(p, 0, zPath+1, 0);
if( JSON_LOOKUP_ISERROR(rc) ){
if( rc==JSON_LOOKUP_NOTFOUND ){
continue; /* No-op */
}else if( rc==JSON_LOOKUP_PATHERROR ){
jsonBadPathError(ctx, zPath);
}else{
sqlite3_result_error(ctx, "malformed JSON", -1);
}
goto json_remove_done;
}
}
jsonReturnParse(ctx, p);
jsonParseFree(p);
return;
json_remove_patherror:
jsonBadPathError(ctx, zPath);
json_remove_done:
jsonParseFree(p);
return;
}
/*
** json_replace(JSON, PATH, VALUE, ...)
**
** Replace the value at PATH with VALUE. If PATH does not already exist,
** this routine is a no-op. If JSON or PATH is malformed, throw an error.
*/
static void jsonReplaceFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, "replace");
return;
}
jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL);
}
/*
** json_set(JSON, PATH, VALUE, ...)
**
** Set the value at PATH to VALUE. Create the PATH if it does not already
** exist. Overwrite existing values that do exist.
** If JSON or PATH is malformed, throw an error.
**
** json_insert(JSON, PATH, VALUE, ...)
**
** Create PATH and initialize it to VALUE. If PATH already exists, this
** routine is a no-op. If JSON or PATH is malformed, throw an error.
*/
static void jsonSetFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
int bIsSet = (flags&JSON_ISSET)!=0;
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
return;
}
jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS);
}
/*
** json_type(JSON)
** json_type(JSON, PATH)
**
** Return the top-level "type" of a JSON string. json_type() raises an
** error if either the JSON or PATH inputs are not well-formed.
*/
static void jsonTypeFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p; /* The parse */
const char *zPath = 0;
u32 i;
p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 ) goto json_type_done;
if( zPath[0]!='$' ){
jsonBadPathError(ctx, zPath);
goto json_type_done;
}
i = jsonLookupStep(p, 0, zPath+1, 0);
if( JSON_LOOKUP_ISERROR(i) ){
if( i==JSON_LOOKUP_NOTFOUND ){
/* no-op */
}else if( i==JSON_LOOKUP_PATHERROR ){
jsonBadPathError(ctx, zPath);
}else{
sqlite3_result_error(ctx, "malformed JSON", -1);
}
goto json_type_done;
}
}else{
i = 0;
}
sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC);
json_type_done:
jsonParseFree(p);
}
/*
** json_valid(JSON)
** json_valid(JSON, FLAGS)
**
** Check the JSON argument to see if it is well-formed. The FLAGS argument
** encodes the various constraints on what is meant by "well-formed":
**
** 0x01 Canonical RFC-8259 JSON text
** 0x02 JSON text with optional JSON-5 extensions
** 0x04 Superficially appears to be JSONB
** 0x08 Strictly well-formed JSONB
**
** If the FLAGS argument is omitted, it defaults to 1. Useful values for
** FLAGS include:
**
** 1 Strict canonical JSON text
** 2 JSON text perhaps with JSON-5 extensions
** 4 Superficially appears to be JSONB
** 5 Canonical JSON text or superficial JSONB
** 6 JSON-5 text or superficial JSONB
** 8 Strict JSONB
** 9 Canonical JSON text or strict JSONB
** 10 JSON-5 text or strict JSONB
**
** Other flag combinations are redundant. For example, every canonical
** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3
** are the same. Similarly, any input that passes a strict JSONB validation
** will also pass the superficial validation so 12 through 15 are the same
** as 8 through 11 respectively.
**
** This routine runs in linear time to validate text and when doing strict
** JSONB validation. Superficial JSONB validation is constant time,
** assuming the BLOB is already in memory. The performance advantage
** of superficial JSONB validation is why that option is provided.
** Application developers can choose to do fast superficial validation or
** slower strict validation, according to their specific needs.
**
** Only the lower four bits of the FLAGS argument are currently used.
** Higher bits are reserved for future expansion. To facilitate
** compatibility, the current implementation raises an error if any bit
** in FLAGS is set other than the lower four bits.
**
** The original circa 2015 implementation of the JSON routines in
** SQLite only supported canonical RFC-8259 JSON text and the json_valid()
** function only accepted one argument. That is why the default value
** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only
** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS
** argument was added when the JSON routines were extended to support
** JSON5-like extensions and binary JSONB stored in BLOBs.
**
** Return Values:
**
** * Raise an error if FLAGS is outside the range of 1 to 15.
** * Return NULL if the input is NULL
** * Return 1 if the input is well-formed.
** * Return 0 if the input is not well-formed.
*/
static void jsonValidFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p; /* The parse */
u8 flags = 1;
u8 res = 0;
if( argc==2 ){
i64 f = sqlite3_value_int64(argv[1]);
if( f<1 || f>15 ){
sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be"
" between 1 and 15", -1);
return;
}
flags = f & 0x0f;
}
switch( sqlite3_value_type(argv[0]) ){
case SQLITE_NULL: {
#ifdef SQLITE_LEGACY_JSON_VALID
/* Incorrect legacy behavior was to return FALSE for a NULL input */
sqlite3_result_int(ctx, 0);
#endif
return;
}
case SQLITE_BLOB: {
if( jsonFuncArgMightBeBinary(argv[0]) ){
if( flags & 0x04 ){
/* Superficial checking only - accomplished by the
** jsonFuncArgMightBeBinary() call above. */
res = 1;
}else if( flags & 0x08 ){
/* Strict checking. Check by translating BLOB->TEXT->BLOB. If
** no errors occur, call that a "strict check". */
JsonParse px;
u32 iErr;
memset(&px, 0, sizeof(px));
px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
px.nBlob = sqlite3_value_bytes(argv[0]);
iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
res = iErr==0;
}
break;
}
/* Fall through into interpreting the input as text. See note
** above at tag-20240123-a. */
/* no break */ deliberate_fall_through
}
default: {
JsonParse px;
if( (flags & 0x3)==0 ) break;
memset(&px, 0, sizeof(px));
p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR);
if( p ){
if( p->oom ){
sqlite3_result_error_nomem(ctx);
}else if( p->nErr ){
/* no-op */
}else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){
res = 1;
}
jsonParseFree(p);
}else{
sqlite3_result_error_nomem(ctx);
}
break;
}
}
sqlite3_result_int(ctx, res);
}
/*
** json_error_position(JSON)
**
** If the argument is NULL, return NULL
**
** If the argument is BLOB, do a full validity check and return non-zero
** if the check fails. The return value is the approximate 1-based offset
** to the byte of the element that contains the first error.
**
** Otherwise interpret the argument is TEXT (even if it is numeric) and
** return the 1-based character position for where the parser first recognized
** that the input was not valid JSON, or return 0 if the input text looks
** ok. JSON-5 extensions are accepted.
*/
static void jsonErrorFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
i64 iErrPos = 0; /* Error position to be returned */
JsonParse s;
assert( argc==1 );
UNUSED_PARAMETER(argc);
memset(&s, 0, sizeof(s));
s.db = sqlite3_context_db_handle(ctx);
if( jsonFuncArgMightBeBinary(argv[0]) ){
s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
s.nBlob = sqlite3_value_bytes(argv[0]);
iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
}else{
s.zJson = (char*)sqlite3_value_text(argv[0]);
if( s.zJson==0 ) return; /* NULL input or OOM */
s.nJson = sqlite3_value_bytes(argv[0]);
if( jsonConvertTextToBlob(&s,0) ){
if( s.oom ){
iErrPos = -1;
}else{
/* Convert byte-offset s.iErr into a character offset */
u32 k;
assert( s.zJson!=0 ); /* Because s.oom is false */
for(k=0; k<s.iErr && ALWAYS(s.zJson[k]); k++){
if( (s.zJson[k] & 0xc0)!=0x80 ) iErrPos++;
}
iErrPos++;
}
}
}
jsonParseReset(&s);
if( iErrPos<0 ){
sqlite3_result_error_nomem(ctx);
}else{
sqlite3_result_int64(ctx, iErrPos);
}
}
/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
/*
** json_group_array(VALUE)
**
** Return a JSON array composed of all values in the aggregate.
*/
static void jsonArrayStep(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonString *pStr;
UNUSED_PARAMETER(argc);
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '[');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
jsonAppendSqlValue(pStr, argv[0]);
}
}
static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
int flags;
pStr->pCtx = ctx;
jsonAppendChar(pStr, ']');
flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
if( pStr->eErr ){
jsonReturnString(pStr, 0, 0);
return;
}else if( flags & JSON_BLOB ){
jsonReturnStringAsBlob(pStr);
if( isFinal ){
if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
}else{
jsonStringTrimOneChar(pStr);
}
return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
}
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
static void jsonArrayValue(sqlite3_context *ctx){
|
| ︙ | ︙ | |||
205855 205856 205857 205858 205859 205860 205861 |
JsonString *pStr;
const char *z;
u32 n;
UNUSED_PARAMETER(argc);
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
| | | | > > > | > > > > | | > > > > | > > > > > > > > > < | > | | > > | > > > > > > > | | 207712 207713 207714 207715 207716 207717 207718 207719 207720 207721 207722 207723 207724 207725 207726 207727 207728 207729 207730 207731 207732 207733 207734 207735 207736 207737 207738 207739 207740 207741 207742 207743 207744 207745 207746 207747 207748 207749 207750 207751 207752 207753 207754 207755 207756 207757 207758 207759 207760 207761 207762 207763 207764 207765 207766 207767 207768 207769 207770 207771 207772 207773 207774 207775 207776 207777 207778 207779 207780 207781 207782 207783 207784 207785 207786 207787 207788 207789 207790 207791 207792 207793 207794 207795 207796 207797 207798 207799 207800 207801 207802 207803 207804 207805 207806 207807 207808 207809 207810 207811 207812 207813 207814 207815 207816 207817 207818 207819 207820 207821 207822 207823 207824 207825 |
JsonString *pStr;
const char *z;
u32 n;
UNUSED_PARAMETER(argc);
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '{');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
z = (const char*)sqlite3_value_text(argv[0]);
n = sqlite3Strlen30(z);
jsonAppendString(pStr, z, n);
jsonAppendChar(pStr, ':');
jsonAppendSqlValue(pStr, argv[1]);
}
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
int flags;
jsonAppendChar(pStr, '}');
pStr->pCtx = ctx;
flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
if( pStr->eErr ){
jsonReturnString(pStr, 0, 0);
return;
}else if( flags & JSON_BLOB ){
jsonReturnStringAsBlob(pStr);
if( isFinal ){
if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
}else{
jsonStringTrimOneChar(pStr);
}
return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
}
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
static void jsonObjectValue(sqlite3_context *ctx){
jsonObjectCompute(ctx, 0);
}
static void jsonObjectFinal(sqlite3_context *ctx){
jsonObjectCompute(ctx, 1);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/****************************************************************************
** The json_each virtual table
****************************************************************************/
typedef struct JsonParent JsonParent;
struct JsonParent {
u32 iHead; /* Start of object or array */
u32 iValue; /* Start of the value */
u32 iEnd; /* First byte past the end */
u32 nPath; /* Length of path */
i64 iKey; /* Key for JSONB_ARRAY */
};
typedef struct JsonEachCursor JsonEachCursor;
struct JsonEachCursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
u32 i; /* Index in sParse.aBlob[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
u32 nRoot; /* Size of the root path in bytes */
u8 eType; /* Type of the container for element i */
u8 bRecursive; /* True for json_tree(). False for json_each() */
u32 nParent; /* Current nesting depth */
u32 nParentAlloc; /* Space allocated for aParent[] */
JsonParent *aParent; /* Parent elements of i */
sqlite3 *db; /* Database connection */
JsonString path; /* Current path */
JsonParse sParse; /* Parse of the input JSON */
};
typedef struct JsonEachConnection JsonEachConnection;
struct JsonEachConnection {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db; /* Database connection */
};
/* Constructor for the json_each virtual table */
static int jsonEachConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
JsonEachConnection *pNew;
int rc;
/* Column numbers */
#define JEACH_KEY 0
#define JEACH_VALUE 1
#define JEACH_TYPE 2
#define JEACH_ATOM 3
|
| ︙ | ︙ | |||
205951 205952 205953 205954 205955 205956 205957 |
UNUSED_PARAMETER(argv);
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(pAux);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
| > | < > > | > | | > < > > > > > < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > | | | < | > | | > | > | | < | | < < | < < < | | < | > | | | > > < > | < | > < > | > > | | < < < < < < < < | | < < | | < | < < < < < < | > > > > > < > > | > > | < < | > > | < | > > | < > > | > > | | | > > | < < < < < < < < < < < < | > > | < | | > | | | < | | | > | > > > < > > > > > > > | | | | > | | | | > | < | | < < < < < | < < | < < | > | < < < < < < < | | > | | < < < < < < | > > > | | > | 207837 207838 207839 207840 207841 207842 207843 207844 207845 207846 207847 207848 207849 207850 207851 207852 207853 207854 207855 207856 207857 207858 207859 207860 207861 207862 207863 207864 207865 207866 207867 207868 207869 207870 207871 207872 207873 207874 207875 207876 207877 207878 207879 207880 207881 207882 207883 207884 207885 207886 207887 207888 207889 207890 207891 207892 207893 207894 207895 207896 207897 207898 207899 207900 207901 207902 207903 207904 207905 207906 207907 207908 207909 207910 207911 207912 207913 207914 207915 207916 207917 207918 207919 207920 207921 207922 207923 207924 207925 207926 207927 207928 207929 207930 207931 207932 207933 207934 207935 207936 207937 207938 207939 207940 207941 207942 207943 207944 207945 207946 207947 207948 207949 207950 207951 207952 207953 207954 207955 207956 207957 207958 207959 207960 207961 207962 207963 207964 207965 207966 207967 207968 207969 207970 207971 207972 207973 207974 207975 207976 207977 207978 207979 207980 207981 207982 207983 207984 207985 207986 207987 207988 207989 207990 207991 207992 207993 207994 207995 207996 207997 207998 207999 208000 208001 208002 208003 208004 208005 208006 208007 208008 208009 208010 208011 208012 208013 208014 208015 208016 208017 208018 208019 208020 208021 208022 208023 208024 208025 208026 208027 208028 208029 208030 208031 208032 208033 208034 208035 208036 208037 208038 208039 208040 208041 208042 208043 208044 208045 208046 208047 208048 208049 208050 208051 208052 208053 208054 208055 208056 208057 208058 208059 208060 208061 208062 208063 208064 208065 208066 208067 208068 208069 208070 208071 208072 208073 208074 208075 208076 208077 208078 208079 208080 208081 208082 208083 208084 208085 208086 208087 208088 208089 208090 208091 208092 208093 208094 208095 208096 208097 208098 208099 208100 208101 208102 208103 208104 208105 208106 208107 208108 208109 208110 208111 208112 208113 208114 208115 208116 208117 208118 208119 208120 208121 208122 208123 208124 208125 208126 208127 208128 208129 208130 208131 208132 208133 208134 208135 208136 208137 208138 208139 208140 208141 208142 208143 208144 208145 |
UNUSED_PARAMETER(argv);
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(pAux);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
pNew->db = db;
}
return rc;
}
/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
JsonEachConnection *p = (JsonEachConnection*)pVtab;
sqlite3DbFree(p->db, pVtab);
return SQLITE_OK;
}
/* constructor for a JsonEachCursor object for json_each(). */
static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
JsonEachConnection *pVtab = (JsonEachConnection*)p;
JsonEachCursor *pCur;
UNUSED_PARAMETER(p);
pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
if( pCur==0 ) return SQLITE_NOMEM;
pCur->db = pVtab->db;
jsonStringZero(&pCur->path);
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/* constructor for a JsonEachCursor object for json_tree(). */
static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
int rc = jsonEachOpenEach(p, ppCursor);
if( rc==SQLITE_OK ){
JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
pCur->bRecursive = 1;
}
return rc;
}
/* Reset a JsonEachCursor back to its original state. Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
jsonParseReset(&p->sParse);
jsonStringReset(&p->path);
sqlite3DbFree(p->db, p->aParent);
p->iRowid = 0;
p->i = 0;
p->aParent = 0;
p->nParent = 0;
p->nParentAlloc = 0;
p->iEnd = 0;
p->eType = 0;
}
/* Destructor for a jsonEachCursor object */
static int jsonEachClose(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
jsonEachCursorReset(p);
sqlite3DbFree(p->db, cur);
return SQLITE_OK;
}
/* Return TRUE if the jsonEachCursor object has been advanced off the end
** of the JSON object */
static int jsonEachEof(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
return p->i >= p->iEnd;
}
/*
** If the cursor is currently pointing at the label of a object entry,
** then return the index of the value. For all other cases, return the
** current pointer position, which is the value.
*/
static int jsonSkipLabel(JsonEachCursor *p){
if( p->eType==JSONB_OBJECT ){
u32 sz = 0;
u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz);
return p->i + n + sz;
}else{
return p->i;
}
}
/*
** Append the path name for the current element.
*/
static void jsonAppendPathName(JsonEachCursor *p){
assert( p->nParent>0 );
assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT );
if( p->eType==JSONB_ARRAY ){
jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey);
}else{
u32 n, sz = 0, k, i;
const char *z;
int needQuote = 0;
n = jsonbPayloadSize(&p->sParse, p->i, &sz);
k = p->i + n;
z = (const char*)&p->sParse.aBlob[k];
if( sz==0 || !sqlite3Isalpha(z[0]) ){
needQuote = 1;
}else{
for(i=0; i<sz; i++){
if( !sqlite3Isalnum(z[i]) ){
needQuote = 1;
break;
}
}
}
if( needQuote ){
jsonPrintf(sz+4,&p->path,".\"%.*s\"", sz, z);
}else{
jsonPrintf(sz+2,&p->path,".%.*s", sz, z);
}
}
}
/* Advance the cursor to the next element for json_tree() */
static int jsonEachNext(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
int rc = SQLITE_OK;
if( p->bRecursive ){
u8 x;
u8 levelChange = 0;
u32 n, sz = 0;
u32 i = jsonSkipLabel(p);
x = p->sParse.aBlob[i] & 0x0f;
n = jsonbPayloadSize(&p->sParse, i, &sz);
if( x==JSONB_OBJECT || x==JSONB_ARRAY ){
JsonParent *pParent;
if( p->nParent>=p->nParentAlloc ){
JsonParent *pNew;
u64 nNew;
nNew = p->nParentAlloc*2 + 3;
pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew);
if( pNew==0 ) return SQLITE_NOMEM;
p->nParentAlloc = (u32)nNew;
p->aParent = pNew;
}
levelChange = 1;
pParent = &p->aParent[p->nParent];
pParent->iHead = p->i;
pParent->iValue = i;
pParent->iEnd = i + n + sz;
pParent->iKey = -1;
pParent->nPath = (u32)p->path.nUsed;
if( p->eType && p->nParent ){
jsonAppendPathName(p);
if( p->path.eErr ) rc = SQLITE_NOMEM;
}
p->nParent++;
p->i = i + n;
}else{
p->i = i + n + sz;
}
while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){
p->nParent--;
p->path.nUsed = p->aParent[p->nParent].nPath;
levelChange = 1;
}
if( levelChange ){
if( p->nParent>0 ){
JsonParent *pParent = &p->aParent[p->nParent-1];
u32 iVal = pParent->iValue;
p->eType = p->sParse.aBlob[iVal] & 0x0f;
}else{
p->eType = 0;
}
}
}else{
u32 n, sz = 0;
u32 i = jsonSkipLabel(p);
n = jsonbPayloadSize(&p->sParse, i, &sz);
p->i = i + n + sz;
}
if( p->eType==JSONB_ARRAY && p->nParent ){
p->aParent[p->nParent-1].iKey++;
}
p->iRowid++;
return rc;
}
/* Length of the path for rowid==0 in bRecursive mode.
*/
static int jsonEachPathLength(JsonEachCursor *p){
u32 n = p->path.nUsed;
char *z = p->path.zBuf;
if( p->iRowid==0 && p->bRecursive && n>=2 ){
while( n>1 ){
n--;
if( z[n]=='[' || z[n]=='.' ){
u32 x, sz = 0;
char cSaved = z[n];
z[n] = 0;
assert( p->sParse.eEdit==0 );
x = jsonLookupStep(&p->sParse, 0, z+1, 0);
z[n] = cSaved;
if( JSON_LOOKUP_ISERROR(x) ) continue;
if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break;
}
}
}
return n;
}
/* Return the value of a column */
static int jsonEachColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int iColumn /* Which column to return */
){
JsonEachCursor *p = (JsonEachCursor*)cur;
switch( iColumn ){
case JEACH_KEY: {
if( p->nParent==0 ){
u32 n, j;
if( p->nRoot==1 ) break;
j = jsonEachPathLength(p);
n = p->nRoot - j;
if( n==0 ){
break;
}else if( p->path.zBuf[j]=='[' ){
i64 x;
sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8);
sqlite3_result_int64(ctx, x);
}else if( p->path.zBuf[j+1]=='"' ){
sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT);
}else{
sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT);
}
break;
}
if( p->eType==JSONB_OBJECT ){
jsonReturnFromBlob(&p->sParse, p->i, ctx, 1);
}else{
assert( p->eType==JSONB_ARRAY );
sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
}
break;
}
case JEACH_VALUE: {
u32 i = jsonSkipLabel(p);
jsonReturnFromBlob(&p->sParse, i, ctx, 1);
break;
}
case JEACH_TYPE: {
u32 i = jsonSkipLabel(p);
u8 eType = p->sParse.aBlob[i] & 0x0f;
sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC);
break;
}
case JEACH_ATOM: {
u32 i = jsonSkipLabel(p);
if( (p->sParse.aBlob[i] & 0x0f)<JSONB_ARRAY ){
jsonReturnFromBlob(&p->sParse, i, ctx, 1);
}
break;
}
case JEACH_ID: {
sqlite3_result_int64(ctx, (sqlite3_int64)p->i);
break;
}
case JEACH_PARENT: {
if( p->nParent>0 && p->bRecursive ){
sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead);
}
break;
}
case JEACH_FULLKEY: {
u64 nBase = p->path.nUsed;
if( p->nParent ) jsonAppendPathName(p);
sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed,
SQLITE_TRANSIENT, SQLITE_UTF8);
p->path.nUsed = nBase;
break;
}
case JEACH_PATH: {
u32 n = jsonEachPathLength(p);
sqlite3_result_text64(ctx, p->path.zBuf, n,
SQLITE_TRANSIENT, SQLITE_UTF8);
break;
}
default: {
sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC);
break;
}
case JEACH_JSON: {
if( p->sParse.zJson==0 ){
sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
SQLITE_STATIC);
}else{
sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
}
break;
}
}
return SQLITE_OK;
}
/* Return the current rowid value */
|
| ︙ | ︙ | |||
206299 206300 206301 206302 206303 206304 206305 |
/* Start a search on a new JSON string */
static int jsonEachFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
JsonEachCursor *p = (JsonEachCursor*)cur;
| < | < < > > | | > | < | | > | < < | < | < < | | | < < < < < < > > | < | | < < < < | > > > > > > | > > | | > > > > > > | < | < < > > > | | > | < < < < < < < < < < < | > | | > > > | | > | > > > > > > > > > > > | > | > > > > | | 208222 208223 208224 208225 208226 208227 208228 208229 208230 208231 208232 208233 208234 208235 208236 208237 208238 208239 208240 208241 208242 208243 208244 208245 208246 208247 208248 208249 208250 208251 208252 208253 208254 208255 208256 208257 208258 208259 208260 208261 208262 208263 208264 208265 208266 208267 208268 208269 208270 208271 208272 208273 208274 208275 208276 208277 208278 208279 208280 208281 208282 208283 208284 208285 208286 208287 208288 208289 208290 208291 208292 208293 208294 208295 208296 208297 208298 208299 208300 208301 208302 208303 208304 208305 208306 208307 208308 208309 208310 208311 208312 208313 208314 208315 208316 208317 208318 208319 208320 208321 208322 208323 208324 208325 208326 |
/* Start a search on a new JSON string */
static int jsonEachFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
JsonEachCursor *p = (JsonEachCursor*)cur;
const char *zRoot = 0;
u32 i, n, sz;
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(argc);
jsonEachCursorReset(p);
if( idxNum==0 ) return SQLITE_OK;
memset(&p->sParse, 0, sizeof(p->sParse));
p->sParse.nJPRef = 1;
p->sParse.db = p->db;
if( jsonFuncArgMightBeBinary(argv[0]) ){
p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
}else{
p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
p->sParse.nJson = sqlite3_value_bytes(argv[0]);
if( p->sParse.zJson==0 ){
p->i = p->iEnd = 0;
return SQLITE_OK;
}
if( jsonConvertTextToBlob(&p->sParse, 0) ){
if( p->sParse.oom ){
return SQLITE_NOMEM;
}
goto json_each_malformed_input;
}
}
if( idxNum==3 ){
zRoot = (const char*)sqlite3_value_text(argv[1]);
if( zRoot==0 ) return SQLITE_OK;
if( zRoot[0]!='$' ){
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}
p->nRoot = sqlite3Strlen30(zRoot);
if( zRoot[1]==0 ){
i = p->i = 0;
p->eType = 0;
}else{
i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0);
if( JSON_LOOKUP_ISERROR(i) ){
if( i==JSON_LOOKUP_NOTFOUND ){
p->i = 0;
p->eType = 0;
p->iEnd = 0;
return SQLITE_OK;
}
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}
if( p->sParse.iLabel ){
p->i = p->sParse.iLabel;
p->eType = JSONB_OBJECT;
}else{
p->i = i;
p->eType = JSONB_ARRAY;
}
}
jsonAppendRaw(&p->path, zRoot, p->nRoot);
}else{
i = p->i = 0;
p->eType = 0;
p->nRoot = 1;
jsonAppendRaw(&p->path, "$", 1);
}
p->nParent = 0;
n = jsonbPayloadSize(&p->sParse, i, &sz);
p->iEnd = i+n+sz;
if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){
p->i = i + n;
p->eType = p->sParse.aBlob[i] & 0x0f;
p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent));
if( p->aParent==0 ) return SQLITE_NOMEM;
p->nParent = 1;
p->nParentAlloc = 1;
p->aParent[0].iKey = 0;
p->aParent[0].iEnd = p->iEnd;
p->aParent[0].iHead = p->i;
p->aParent[0].iValue = i;
}
return SQLITE_OK;
json_each_malformed_input:
sqlite3_free(cur->pVtab->zErrMsg);
cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}
/* The methods of the json_each virtual table */
static sqlite3_module jsonEachModule = {
0, /* iVersion */
0, /* xCreate */
jsonEachConnect, /* xConnect */
|
| ︙ | ︙ | |||
206447 206448 206449 206450 206451 206452 206453 |
/*
** Register JSON functions.
*/
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
| | | | | | | < | > | > | | | | > | | | > | > | > | | > | > | > | | | > | | < > > > > > > | 208381 208382 208383 208384 208385 208386 208387 208388 208389 208390 208391 208392 208393 208394 208395 208396 208397 208398 208399 208400 208401 208402 208403 208404 208405 208406 208407 208408 208409 208410 208411 208412 208413 208414 208415 208416 208417 208418 208419 208420 208421 208422 208423 208424 208425 208426 208427 208428 208429 208430 208431 208432 208433 208434 208435 208436 208437 208438 208439 208440 208441 208442 |
/*
** Register JSON functions.
*/
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
/* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */
/* | | */
/* Uses cache ------, | | ,---- Returns JSONB */
/* | | | | */
/* Number of arguments ---, | | | | ,--- Flags */
/* | | | | | | */
JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc),
JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc),
JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc),
JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc),
JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc),
JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc),
JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc),
JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc),
JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc),
JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc),
JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc),
JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc),
JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc),
JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc),
JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc),
JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc),
JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc),
JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc),
JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc),
JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc),
JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc),
JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc),
JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc),
JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc),
JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc),
JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc),
JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc),
JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc),
#if SQLITE_DEBUG
JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc),
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
SQLITE_DETERMINISTIC),
WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
WAGGREGATE(json_group_object, 2, 0, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
SQLITE_DETERMINISTIC)
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
}
|
| ︙ | ︙ | |||
207231 207232 207233 207234 207235 207236 207237 |
int rc = SQLITE_OK;
RtreeNode *pNode = 0;
/* Check if the requested node is already in the hash table. If so,
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
| | | 209179 209180 209181 209182 209183 209184 209185 209186 209187 209188 209189 209190 209191 209192 209193 |
int rc = SQLITE_OK;
RtreeNode *pNode = 0;
/* Check if the requested node is already in the hash table. If so,
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
if( pParent && ALWAYS(pParent!=pNode->pParent) ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
pNode->nRef++;
*ppNode = pNode;
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
209966 209967 209968 209969 209970 209971 209972 |
if( zSql ){
rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0);
}else{
rc = SQLITE_NOMEM;
}
sqlite3_free(zSql);
}
| | | 211914 211915 211916 211917 211918 211919 211920 211921 211922 211923 211924 211925 211926 211927 211928 |
if( zSql ){
rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0);
}else{
rc = SQLITE_NOMEM;
}
sqlite3_free(zSql);
}
if( pRtree->nAux && rc!=SQLITE_NOMEM ){
pRtree->zReadAuxSql = sqlite3_mprintf(
"SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
zDb, zPrefix);
if( pRtree->zReadAuxSql==0 ){
rc = SQLITE_NOMEM;
}else{
sqlite3_str *p = sqlite3_str_new(db);
|
| ︙ | ︙ | |||
210655 210656 210657 210658 210659 210660 210661 | /* Initialize the context object */ memset(&check, 0, sizeof(check)); check.db = db; check.zDb = zDb; check.zTab = zTab; /* Find the number of auxiliary columns */ | < | | | | | | | < | 212603 212604 212605 212606 212607 212608 212609 212610 212611 212612 212613 212614 212615 212616 212617 212618 212619 212620 212621 212622 212623 |
/* Initialize the context object */
memset(&check, 0, sizeof(check));
check.db = db;
check.zDb = zDb;
check.zTab = zTab;
/* Find the number of auxiliary columns */
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
if( pStmt ){
nAux = sqlite3_column_count(pStmt) - 2;
sqlite3_finalize(pStmt);
}else
if( check.rc!=SQLITE_NOMEM ){
check.rc = SQLITE_OK;
}
/* Find number of dimensions in the rtree table. */
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab);
if( pStmt ){
int rc;
check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2;
|
| ︙ | ︙ | |||
210718 210719 210720 210721 210722 210723 210724 210725 210726 210727 210728 210729 210730 210731 |
UNUSED_PARAMETER(zSchema);
UNUSED_PARAMETER(zName);
UNUSED_PARAMETER(isQuick);
rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
if( rc==SQLITE_OK && *pzErr ){
*pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
pRtree->zDb, pRtree->zName, *pzErr);
}
return rc;
}
/*
** Usage:
**
| > | 212664 212665 212666 212667 212668 212669 212670 212671 212672 212673 212674 212675 212676 212677 212678 |
UNUSED_PARAMETER(zSchema);
UNUSED_PARAMETER(zName);
UNUSED_PARAMETER(isQuick);
rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
if( rc==SQLITE_OK && *pzErr ){
*pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
pRtree->zDb, pRtree->zName, *pzErr);
if( (*pzErr)==0 ) rc = SQLITE_NOMEM;
}
return rc;
}
/*
** Usage:
**
|
| ︙ | ︙ | |||
223388 223389 223390 223391 223392 223393 223394 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); sqlite3ValueFree(pSession->pZeroBlob); /* Delete all attached table objects. And the contents of their ** associated hash-tables. */ sessionDeleteTable(pSession, pSession->pTable); | < | < | 225335 225336 225337 225338 225339 225340 225341 225342 225343 225344 225345 225346 225347 225348 225349 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); sqlite3ValueFree(pSession->pZeroBlob); /* Delete all attached table objects. And the contents of their ** associated hash-tables. */ sessionDeleteTable(pSession, pSession->pTable); /* Free the session object. */ sqlite3_free(pSession); } /* ** Set a table filter on a Session Object. */ SQLITE_API void sqlite3session_table_filter( |
| ︙ | ︙ | |||
227603 227604 227605 227606 227607 227608 227609 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: | > > > | | > > | | | > | | | | 229548 229549 229550 229551 229552 229553 229554 229555 229556 229557 229558 229559 229560 229561 229562 229563 229564 229565 229566 229567 229568 229569 229570 229571 229572 229573 229574 229575 229576 229577 229578 229579 229580 229581 229582 229583 229584 229585 229586 229587 229588 229589 229590 229591 229592 229593 229594 229595 229596 229597 229598 229599 229600 229601 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. ** ** Otherwise, this function attempts to retrieve the text of column iCol of ** the current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values ** of (*pz) and (*pn) are undefined. ** ** xPhraseCount: ** Returns the number of phrases in the current query expression. ** ** xPhraseSize: ** If parameter iCol is less than zero, or greater than or equal to the ** number of phrases in the current query, as returned by xPhraseCount, ** 0 is returned. Otherwise, this function returns the number of tokens in ** phrase iPhrase of the query. Phrases are numbered starting from zero. ** ** xInstCount: ** Set *pnInst to the total number of occurrences of all phrases within ** the query within the current row. Return SQLITE_OK if successful, or ** an error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. If the FTS5 table is created ** with either "detail=none" or "detail=column" and "content=" option ** (i.e. if it is a contentless table), then this API always returns 0. ** ** xInst: ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value ** output by xInstCount(). If iIdx is less than zero or greater than ** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. ** ** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the ** first token of the phrase. SQLITE_OK is returned if successful, or an ** error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xRowid: ** Returns the rowid of the current row. ** |
| ︙ | ︙ | |||
227661 227662 227663 227664 227665 227666 227667 227668 227669 227670 227671 227672 227673 227674 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. | > > > > | 229612 229613 229614 229615 229616 229617 229618 229619 229620 229621 229622 229623 229624 229625 229626 229627 229628 229629 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If parameter iPhrase is less than zero, or greater than or equal to ** the number of phrases in the query, as returned by xPhraseCount(), ** this function returns SQLITE_RANGE. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. |
| ︙ | ︙ | |||
227776 227777 227778 227779 227780 227781 227782 227783 227784 |
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
*/
struct Fts5ExtensionApi {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 229731 229732 229733 229734 229735 229736 229737 229738 229739 229740 229741 229742 229743 229744 229745 229746 229747 229748 229749 229750 229751 229752 229753 229754 229755 229756 229757 229758 229759 229760 229761 229762 229763 229764 229765 229766 229767 229768 229769 229770 229771 229772 229773 229774 229775 229776 229777 229778 229779 229780 |
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
**
** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
** This is used to access token iToken of phrase iPhrase of the current
** query. Before returning, output parameter *ppToken is set to point
** to a buffer containing the requested token, and *pnToken to the
** size of this buffer in bytes.
**
** If iPhrase or iToken are less than zero, or if iPhrase is greater than
** or equal to the number of phrases in the query as reported by
** xPhraseCount(), or if iToken is equal to or greater than the number of
** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
are both zeroed.
**
** The output text is not a copy of the query text that specified the
** token. It is the output of the tokenizer module. For tokendata=1
** tables, this includes any embedded 0x00 and trailing data.
**
** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
** This is used to access token iToken of phrase hit iIdx within the
** current row. If iIdx is less than zero or greater than or equal to the
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
** output variable (*ppToken) is set to point to a buffer containing the
** matching document token, and (*pnToken) to the size of that buffer in
** bytes. This API is not available if the specified token matches a
** prefix query term. In that case both output variables are always set
** to 0.
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
int (*xColumnCount)(Fts5Context*);
int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
|
| ︙ | ︙ | |||
227813 227814 227815 227816 227817 227818 227819 227820 227821 227822 227823 227824 227825 227826 | void *(*xGetAuxdata)(Fts5Context*, int bClear); int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* | > > > > > > > | 229801 229802 229803 229804 229805 229806 229807 229808 229809 229810 229811 229812 229813 229814 229815 229816 229817 229818 229819 229820 229821 |
void *(*xGetAuxdata)(Fts5Context*, int bClear);
int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
/* Below this point are iVersion>=3 only */
int (*xQueryToken)(Fts5Context*,
int iPhrase, int iToken,
const char **ppToken, int *pnToken
);
int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
|
| ︙ | ︙ | |||
228287 228288 228289 228290 228291 228292 228293 228294 228295 228296 228297 228298 228299 228300 | int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; int bLock; /* True when table is preparing statement */ int ePattern; /* FTS_PATTERN_XXX constant */ | > | 230282 230283 230284 230285 230286 230287 230288 230289 230290 230291 230292 230293 230294 230295 230296 | int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int bTokendata; /* "tokendata=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; int bLock; /* True when table is preparing statement */ int ePattern; /* FTS_PATTERN_XXX constant */ |
| ︙ | ︙ | |||
228475 228476 228477 228478 228479 228480 228481 | }; #define sqlite3Fts5IterEof(x) ((x)->bEof) /* ** Values used as part of the flags argument passed to IndexQuery(). */ | | | | | | | | > > | 230471 230472 230473 230474 230475 230476 230477 230478 230479 230480 230481 230482 230483 230484 230485 230486 230487 230488 230489 230490 230491 230492 230493 230494 230495 230496 230497 | }; #define sqlite3Fts5IterEof(x) ((x)->bEof) /* ** Values used as part of the flags argument passed to IndexQuery(). */ #define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ #define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ #define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ #define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ /* The following are used internally by the fts5_index.c module. They are ** defined here only to make it easier to avoid clashes with the flags ** above. */ #define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 #define FTS5INDEX_QUERY_SKIPHASH 0x0040 #define FTS5INDEX_QUERY_NOTOKENDATA 0x0080 #define FTS5INDEX_QUERY_SCANONETERM 0x0100 /* ** Create/destroy an Fts5Index object. */ static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); static int sqlite3Fts5IndexClose(Fts5Index *p); |
| ︙ | ︙ | |||
228554 228555 228556 228557 228558 228559 228560 228561 228562 228563 228564 228565 228566 228567 | */ static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); static int sqlite3Fts5IterNextScan(Fts5IndexIter*); static void *sqlite3Fts5StructureRef(Fts5Index*); static void sqlite3Fts5StructureRelease(void*); static int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Insert or remove data to or from the index. Each time a document is ** added to or removed from the index, this function is called one or more ** times. ** ** For an insert, it must be called once for each token in the new document. | > > > > | 230552 230553 230554 230555 230556 230557 230558 230559 230560 230561 230562 230563 230564 230565 230566 230567 230568 230569 | */ static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); static int sqlite3Fts5IterNextScan(Fts5IndexIter*); static void *sqlite3Fts5StructureRef(Fts5Index*); static void sqlite3Fts5StructureRelease(void*); static int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Used by xInstToken(): */ static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); /* ** Insert or remove data to or from the index. Each time a document is ** added to or removed from the index, this function is called one or more ** times. ** ** For an insert, it must be called once for each token in the new document. |
| ︙ | ︙ | |||
228630 228631 228632 228633 228634 228635 228636 228637 228638 228639 228640 228641 228642 228643 | static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge); static int sqlite3Fts5IndexReset(Fts5Index *p); static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); /* ** End of interface to code in fts5_index.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_varint.c. | > > > > > > > | 230632 230633 230634 230635 230636 230637 230638 230639 230640 230641 230642 230643 230644 230645 230646 230647 230648 230649 230650 230651 230652 |
static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
static int sqlite3Fts5IndexReset(Fts5Index *p);
static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*);
/* Used to populate hash tables for xInstToken in detail=none/column mode. */
static int sqlite3Fts5IndexIterWriteTokendata(
Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff
);
/*
** End of interface to code in fts5_index.c.
**************************************************************************/
/**************************************************************************
** Interface to code in fts5_varint.c.
|
| ︙ | ︙ | |||
228736 228737 228738 228739 228740 228741 228742 228743 228744 228745 228746 228747 228748 228749 | Fts5Hash*, /* Hash table to query */ const char *pTerm, int nTerm /* Query prefix */ ); static void sqlite3Fts5HashScanNext(Fts5Hash*); static int sqlite3Fts5HashScanEof(Fts5Hash*); static void sqlite3Fts5HashScanEntry(Fts5Hash *, const char **pzTerm, /* OUT: term (nul-terminated) */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ); /* | > | 230745 230746 230747 230748 230749 230750 230751 230752 230753 230754 230755 230756 230757 230758 230759 | Fts5Hash*, /* Hash table to query */ const char *pTerm, int nTerm /* Query prefix */ ); static void sqlite3Fts5HashScanNext(Fts5Hash*); static int sqlite3Fts5HashScanEof(Fts5Hash*); static void sqlite3Fts5HashScanEntry(Fts5Hash *, const char **pzTerm, /* OUT: term (nul-terminated) */ int *pnTerm, /* OUT: Size of term in bytes */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ); /* |
| ︙ | ︙ | |||
228861 228862 228863 228864 228865 228866 228867 228868 228869 228870 228871 228872 228873 228874 |
Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int
);
static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64);
static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);
static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
/*******************************************
** The fts5_expr.c API above this point is used by the other hand-written
** C code in this module. The interfaces below this point are called by
** the parser code in fts5parse.y. */
static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...);
| > > > > | 230871 230872 230873 230874 230875 230876 230877 230878 230879 230880 230881 230882 230883 230884 230885 230886 230887 230888 |
Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int
);
static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64);
static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);
static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*);
static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*);
static void sqlite3Fts5ExprClearTokens(Fts5Expr*);
/*******************************************
** The fts5_expr.c API above this point is used by the other hand-written
** C code in this module. The interfaces below this point are called by
** the parser code in fts5parse.y. */
static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...);
|
| ︙ | ︙ | |||
230677 230678 230679 230680 230681 230682 230683 230684 230685 230686 230687 230688 230689 230690 |
if( rc==SQLITE_OK ){
rc = fts5CInstIterNext(&p->iter);
}
}
if( iPos==p->iRangeEnd ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
p->iOff = iEndOff;
}
return rc;
}
| > > > > > > > > | 232691 232692 232693 232694 232695 232696 232697 232698 232699 232700 232701 232702 232703 232704 232705 232706 232707 232708 232709 232710 232711 232712 |
if( rc==SQLITE_OK ){
rc = fts5CInstIterNext(&p->iter);
}
}
if( iPos==p->iRangeEnd ){
if( p->bOpen ){
if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
p->iOff = iEndOff;
}
fts5HighlightAppend(&rc, p, p->zClose, -1);
p->bOpen = 0;
}
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
p->iOff = iEndOff;
}
return rc;
}
|
| ︙ | ︙ | |||
230710 230711 230712 230713 230714 230715 230716 | iCol = sqlite3_value_int(apVal[0]); memset(&ctx, 0, sizeof(HighlightContext)); ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.iRangeEnd = -1; rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); | | > > | | 232732 232733 232734 232735 232736 232737 232738 232739 232740 232741 232742 232743 232744 232745 232746 232747 232748 232749 |
iCol = sqlite3_value_int(apVal[0]);
memset(&ctx, 0, sizeof(HighlightContext));
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
ctx.iRangeEnd = -1;
rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
if( rc==SQLITE_RANGE ){
sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC);
rc = SQLITE_OK;
}else if( ctx.zIn ){
if( rc==SQLITE_OK ){
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
}
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
|
| ︙ | ︙ | |||
231278 231279 231280 231281 231282 231283 231284 231285 231286 231287 231288 231289 231290 231291 |
int *pRc,
Fts5Buffer *pBuf,
u32 nData,
const u8 *pData
){
if( nData ){
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
memcpy(&pBuf->p[pBuf->n], pData, nData);
pBuf->n += nData;
}
}
/*
** Append the nul-terminated string zStr to the buffer pBuf. This function
| > | 233302 233303 233304 233305 233306 233307 233308 233309 233310 233311 233312 233313 233314 233315 233316 |
int *pRc,
Fts5Buffer *pBuf,
u32 nData,
const u8 *pData
){
if( nData ){
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
assert( pBuf->p!=0 );
memcpy(&pBuf->p[pBuf->n], pData, nData);
pBuf->n += nData;
}
}
/*
** Append the nul-terminated string zStr to the buffer pBuf. This function
|
| ︙ | ︙ | |||
231379 231380 231381 231382 231383 231384 231385 231386 231387 231388 231389 231390 231391 231392 231393 231394 231395 231396 231397 231398 231399 |
static int sqlite3Fts5PoslistNext64(
const u8 *a, int n, /* Buffer containing poslist */
int *pi, /* IN/OUT: Offset within a[] */
i64 *piOff /* IN/OUT: Current offset */
){
int i = *pi;
if( i>=n ){
/* EOF */
*piOff = -1;
return 1;
}else{
i64 iOff = *piOff;
u32 iVal;
fts5FastGetVarint32(a, i, iVal);
if( iVal<=1 ){
if( iVal==0 ){
*pi = i;
return 0;
}
fts5FastGetVarint32(a, i, iVal);
| > > | 233404 233405 233406 233407 233408 233409 233410 233411 233412 233413 233414 233415 233416 233417 233418 233419 233420 233421 233422 233423 233424 233425 233426 |
static int sqlite3Fts5PoslistNext64(
const u8 *a, int n, /* Buffer containing poslist */
int *pi, /* IN/OUT: Offset within a[] */
i64 *piOff /* IN/OUT: Current offset */
){
int i = *pi;
assert( a!=0 || i==0 );
if( i>=n ){
/* EOF */
*piOff = -1;
return 1;
}else{
i64 iOff = *piOff;
u32 iVal;
assert( a!=0 );
fts5FastGetVarint32(a, i, iVal);
if( iVal<=1 ){
if( iVal==0 ){
*pi = i;
return 0;
}
fts5FastGetVarint32(a, i, iVal);
|
| ︙ | ︙ | |||
232016 232017 232018 232019 232020 232021 232022 232023 232024 232025 232026 232027 232028 232029 |
};
if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
*pzErr = sqlite3_mprintf("malformed detail=... directive");
}
return rc;
}
*pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
return SQLITE_ERROR;
}
/*
** Allocate an instance of the default tokenizer ("simple") at
| > > > > > > > > > > | 234043 234044 234045 234046 234047 234048 234049 234050 234051 234052 234053 234054 234055 234056 234057 234058 234059 234060 234061 234062 234063 234064 234065 234066 |
};
if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
*pzErr = sqlite3_mprintf("malformed detail=... directive");
}
return rc;
}
if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
*pzErr = sqlite3_mprintf("malformed tokendata=... directive");
rc = SQLITE_ERROR;
}else{
pConfig->bTokendata = (zArg[0]=='1');
}
return rc;
}
*pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
return SQLITE_ERROR;
}
/*
** Allocate an instance of the default tokenizer ("simple") at
|
| ︙ | ︙ | |||
232750 232751 232752 232753 232754 232755 232756 |
/*
** An instance of the following structure represents a single search term
** or term prefix.
*/
struct Fts5ExprTerm {
u8 bPrefix; /* True for a prefix term */
u8 bFirst; /* True if token must be first in column */
| | > > | 234787 234788 234789 234790 234791 234792 234793 234794 234795 234796 234797 234798 234799 234800 234801 234802 234803 |
/*
** An instance of the following structure represents a single search term
** or term prefix.
*/
struct Fts5ExprTerm {
u8 bPrefix; /* True for a prefix term */
u8 bFirst; /* True if token must be first in column */
char *pTerm; /* Term data */
int nQueryTerm; /* Effective size of term in bytes */
int nFullTerm; /* Size of term in bytes incl. tokendata */
Fts5IndexIter *pIter; /* Iterator for this term */
Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */
};
/*
** A phrase. One or more terms that must appear in a contiguous sequence
** within a document for it to match.
|
| ︙ | ︙ | |||
233617 233618 233619 233620 233621 233622 233623 |
for(p=pTerm; p; p=p->pSynonym){
int rc;
if( p->pIter ){
sqlite3Fts5IterClose(p->pIter);
p->pIter = 0;
}
rc = sqlite3Fts5IndexQuery(
| | | 235656 235657 235658 235659 235660 235661 235662 235663 235664 235665 235666 235667 235668 235669 235670 |
for(p=pTerm; p; p=p->pSynonym){
int rc;
if( p->pIter ){
sqlite3Fts5IterClose(p->pIter);
p->pIter = 0;
}
rc = sqlite3Fts5IndexQuery(
pExpr->pIndex, p->pTerm, p->nQueryTerm,
(pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
(pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
pNear->pColset,
&p->pIter
);
assert( (rc==SQLITE_OK)==(p->pIter!=0) );
if( rc!=SQLITE_OK ) return rc;
|
| ︙ | ︙ | |||
234254 234255 234256 234257 234258 234259 234260 |
static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
if( pPhrase ){
int i;
for(i=0; i<pPhrase->nTerm; i++){
Fts5ExprTerm *pSyn;
Fts5ExprTerm *pNext;
Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
| | | 236293 236294 236295 236296 236297 236298 236299 236300 236301 236302 236303 236304 236305 236306 236307 |
static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
if( pPhrase ){
int i;
for(i=0; i<pPhrase->nTerm; i++){
Fts5ExprTerm *pSyn;
Fts5ExprTerm *pNext;
Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
sqlite3_free(pTerm->pTerm);
sqlite3Fts5IterClose(pTerm->pIter);
for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
pNext = pSyn->pSynonym;
sqlite3Fts5IterClose(pSyn->pIter);
fts5BufferFree((Fts5Buffer*)&pSyn[1]);
sqlite3_free(pSyn);
}
|
| ︙ | ︙ | |||
234352 234353 234354 234355 234356 234357 234358 234359 234360 234361 234362 234363 234364 234365 |
}
return pRet;
}
typedef struct TokenCtx TokenCtx;
struct TokenCtx {
Fts5ExprPhrase *pPhrase;
int rc;
};
/*
** Callback for tokenizing terms used by ParseTerm().
*/
static int fts5ParseTokenize(
| > | 236391 236392 236393 236394 236395 236396 236397 236398 236399 236400 236401 236402 236403 236404 236405 |
}
return pRet;
}
typedef struct TokenCtx TokenCtx;
struct TokenCtx {
Fts5ExprPhrase *pPhrase;
Fts5Config *pConfig;
int rc;
};
/*
** Callback for tokenizing terms used by ParseTerm().
*/
static int fts5ParseTokenize(
|
| ︙ | ︙ | |||
234385 234386 234387 234388 234389 234390 234391 |
Fts5ExprTerm *pSyn;
sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte);
if( pSyn==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pSyn, 0, (size_t)nByte);
| | > > > > | | 236425 236426 236427 236428 236429 236430 236431 236432 236433 236434 236435 236436 236437 236438 236439 236440 236441 236442 236443 236444 |
Fts5ExprTerm *pSyn;
sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte);
if( pSyn==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pSyn, 0, (size_t)nByte);
pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
pSyn->nFullTerm = pSyn->nQueryTerm = nToken;
if( pCtx->pConfig->bTokendata ){
pSyn->nQueryTerm = (int)strlen(pSyn->pTerm);
}
memcpy(pSyn->pTerm, pToken, nToken);
pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
}
}else{
Fts5ExprTerm *pTerm;
if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
Fts5ExprPhrase *pNew;
|
| ︙ | ︙ | |||
234411 234412 234413 234414 234415 234416 234417 |
pNew->nTerm = nNew - SZALLOC;
}
}
if( rc==SQLITE_OK ){
pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
memset(pTerm, 0, sizeof(Fts5ExprTerm));
| | > > > > | 236455 236456 236457 236458 236459 236460 236461 236462 236463 236464 236465 236466 236467 236468 236469 236470 236471 236472 236473 |
pNew->nTerm = nNew - SZALLOC;
}
}
if( rc==SQLITE_OK ){
pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
memset(pTerm, 0, sizeof(Fts5ExprTerm));
pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
pTerm->nFullTerm = pTerm->nQueryTerm = nToken;
if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){
pTerm->nQueryTerm = (int)strlen(pTerm->pTerm);
}
}
}
pCtx->rc = rc;
return rc;
}
|
| ︙ | ︙ | |||
234478 234479 234480 234481 234482 234483 234484 234485 234486 234487 234488 234489 234490 234491 |
Fts5Config *pConfig = pParse->pConfig;
TokenCtx sCtx; /* Context object passed to callback */
int rc; /* Tokenize return code */
char *z = 0;
memset(&sCtx, 0, sizeof(TokenCtx));
sCtx.pPhrase = pAppend;
rc = fts5ParseStringFromToken(pToken, &z);
if( rc==SQLITE_OK ){
int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_PREFIX : 0);
int n;
sqlite3Fts5Dequote(z);
n = (int)strlen(z);
| > | 236526 236527 236528 236529 236530 236531 236532 236533 236534 236535 236536 236537 236538 236539 236540 |
Fts5Config *pConfig = pParse->pConfig;
TokenCtx sCtx; /* Context object passed to callback */
int rc; /* Tokenize return code */
char *z = 0;
memset(&sCtx, 0, sizeof(TokenCtx));
sCtx.pPhrase = pAppend;
sCtx.pConfig = pConfig;
rc = fts5ParseStringFromToken(pToken, &z);
if( rc==SQLITE_OK ){
int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_PREFIX : 0);
int n;
sqlite3Fts5Dequote(z);
n = (int)strlen(z);
|
| ︙ | ︙ | |||
234525 234526 234527 234528 234529 234530 234531 |
*/
static int sqlite3Fts5ExprClonePhrase(
Fts5Expr *pExpr,
int iPhrase,
Fts5Expr **ppNew
){
int rc = SQLITE_OK; /* Return code */
| | | | > > | | > | > | | > | | | | < | < | | | | | | | | | | | > | 236574 236575 236576 236577 236578 236579 236580 236581 236582 236583 236584 236585 236586 236587 236588 236589 236590 236591 236592 236593 236594 236595 236596 236597 236598 236599 236600 236601 236602 236603 236604 236605 236606 236607 236608 236609 236610 236611 236612 236613 236614 236615 236616 236617 236618 236619 236620 236621 236622 236623 236624 236625 236626 236627 236628 236629 236630 236631 236632 236633 236634 236635 236636 236637 236638 236639 236640 236641 236642 236643 |
*/
static int sqlite3Fts5ExprClonePhrase(
Fts5Expr *pExpr,
int iPhrase,
Fts5Expr **ppNew
){
int rc = SQLITE_OK; /* Return code */
Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */
Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */
if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
rc = SQLITE_RANGE;
}else{
pOrig = pExpr->apExprPhrase[iPhrase];
pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
}
if( rc==SQLITE_OK ){
pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprPhrase*));
}
if( rc==SQLITE_OK ){
pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprNode));
}
if( rc==SQLITE_OK ){
pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
}
if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
if( pColsetOrig ){
sqlite3_int64 nByte;
Fts5Colset *pColset;
nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
if( pColset ){
memcpy(pColset, pColsetOrig, (size_t)nByte);
}
pNew->pRoot->pNear->pColset = pColset;
}
}
if( rc==SQLITE_OK ){
if( pOrig->nTerm ){
int i; /* Used to iterate through phrase terms */
sCtx.pConfig = pExpr->pConfig;
for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
int tflags = 0;
Fts5ExprTerm *p;
for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0);
tflags = FTS5_TOKEN_COLOCATED;
}
if( rc==SQLITE_OK ){
sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
}
}
}else{
/* This happens when parsing a token or quoted phrase that contains
** no token characters at all. (e.g ... MATCH '""'). */
sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
}
if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
/* All the allocations succeeded. Put the expression object together. */
pNew->pIndex = pExpr->pIndex;
pNew->pConfig = pExpr->pConfig;
pNew->nPhrase = 1;
|
| ︙ | ︙ | |||
234946 234947 234948 234949 234950 234951 234952 234953 234954 |
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
&pParse->rc, sizeof(Fts5ExprPhrase)
);
if( pPhrase ){
if( parseGrowPhraseArray(pParse) ){
fts5ExprPhraseFree(pPhrase);
}else{
pParse->apPhrase[pParse->nPhrase++] = pPhrase;
pPhrase->nTerm = 1;
| > > | < < > > | 236999 237000 237001 237002 237003 237004 237005 237006 237007 237008 237009 237010 237011 237012 237013 237014 237015 237016 237017 237018 237019 |
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
&pParse->rc, sizeof(Fts5ExprPhrase)
);
if( pPhrase ){
if( parseGrowPhraseArray(pParse) ){
fts5ExprPhraseFree(pPhrase);
}else{
Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii];
Fts5ExprTerm *pTo = &pPhrase->aTerm[0];
pParse->apPhrase[pParse->nPhrase++] = pPhrase;
pPhrase->nTerm = 1;
pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm);
pTo->nQueryTerm = p->nQueryTerm;
pTo->nFullTerm = p->nFullTerm;
pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
);
}
}
}
|
| ︙ | ︙ | |||
235135 235136 235137 235138 235139 235140 235141 |
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
sqlite3_int64 nByte = 0;
Fts5ExprTerm *p;
char *zQuoted;
/* Determine the maximum amount of space required. */
for(p=pTerm; p; p=p->pSynonym){
| | | > | | 237190 237191 237192 237193 237194 237195 237196 237197 237198 237199 237200 237201 237202 237203 237204 237205 237206 237207 237208 237209 237210 237211 237212 237213 237214 |
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
sqlite3_int64 nByte = 0;
Fts5ExprTerm *p;
char *zQuoted;
/* Determine the maximum amount of space required. */
for(p=pTerm; p; p=p->pSynonym){
nByte += pTerm->nQueryTerm * 2 + 3 + 2;
}
zQuoted = sqlite3_malloc64(nByte);
if( zQuoted ){
int i = 0;
for(p=pTerm; p; p=p->pSynonym){
char *zIn = p->pTerm;
char *zEnd = &zIn[p->nQueryTerm];
zQuoted[i++] = '"';
while( zIn<zEnd ){
if( *zIn=='"' ) zQuoted[i++] = '"';
zQuoted[i++] = *zIn++;
}
zQuoted[i++] = '"';
if( p->pSynonym ) zQuoted[i++] = '|';
}
if( pTerm->bPrefix ){
|
| ︙ | ︙ | |||
235222 235223 235224 235225 235226 235227 235228 |
if( zRet==0 ) return 0;
for(i=0; i<pNear->nPhrase; i++){
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
zRet = fts5PrintfAppend(zRet, " {");
for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
| | | > > | 237278 237279 237280 237281 237282 237283 237284 237285 237286 237287 237288 237289 237290 237291 237292 237293 237294 237295 |
if( zRet==0 ) return 0;
for(i=0; i<pNear->nPhrase; i++){
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
zRet = fts5PrintfAppend(zRet, " {");
for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
Fts5ExprTerm *p = &pPhrase->aTerm[iTerm];
zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ",
p->nQueryTerm, p->pTerm
);
if( pPhrase->aTerm[iTerm].bPrefix ){
zRet = fts5PrintfAppend(zRet, "*");
}
}
if( zRet ) zRet = fts5PrintfAppend(zRet, "}");
if( zRet==0 ) return 0;
|
| ︙ | ︙ | |||
235623 235624 235625 235626 235627 235628 235629 235630 235631 235632 235633 235634 235635 235636 235637 235638 235639 235640 235641 235642 235643 235644 |
static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
int i;
for(i=0; i<pColset->nCol; i++){
if( pColset->aiCol[i]==iCol ) return 1;
}
return 0;
}
static int fts5ExprPopulatePoslistsCb(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Pointer to buffer containing token */
int nToken, /* Size of token in bytes */
int iUnused1, /* Byte offset of token within input text */
int iUnused2 /* Byte offset of end of token within input text */
){
Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
Fts5Expr *pExpr = p->pExpr;
int i;
UNUSED_PARAM2(iUnused1, iUnused2);
| > > > > > > > > > > > > > | > > > | | < | | > > > > > > > | 237681 237682 237683 237684 237685 237686 237687 237688 237689 237690 237691 237692 237693 237694 237695 237696 237697 237698 237699 237700 237701 237702 237703 237704 237705 237706 237707 237708 237709 237710 237711 237712 237713 237714 237715 237716 237717 237718 237719 237720 237721 237722 237723 237724 237725 237726 237727 237728 237729 237730 237731 237732 237733 237734 237735 237736 237737 237738 237739 237740 237741 237742 237743 237744 |
static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
int i;
for(i=0; i<pColset->nCol; i++){
if( pColset->aiCol[i]==iCol ) return 1;
}
return 0;
}
/*
** pToken is a buffer nToken bytes in size that may or may not contain
** an embedded 0x00 byte. If it does, return the number of bytes in
** the buffer before the 0x00. If it does not, return nToken.
*/
static int fts5QueryTerm(const char *pToken, int nToken){
int ii;
for(ii=0; ii<nToken && pToken[ii]; ii++){}
return ii;
}
static int fts5ExprPopulatePoslistsCb(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Pointer to buffer containing token */
int nToken, /* Size of token in bytes */
int iUnused1, /* Byte offset of token within input text */
int iUnused2 /* Byte offset of end of token within input text */
){
Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
Fts5Expr *pExpr = p->pExpr;
int i;
int nQuery = nToken;
i64 iRowid = pExpr->pRoot->iRowid;
UNUSED_PARAM2(iUnused1, iUnused2);
if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE;
if( pExpr->pConfig->bTokendata ){
nQuery = fts5QueryTerm(pToken, nQuery);
}
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
for(i=0; i<pExpr->nPhrase; i++){
Fts5ExprTerm *pT;
if( p->aPopulator[i].bOk==0 ) continue;
for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
&& memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){
int iCol = p->iOff>>32;
int iTokOff = p->iOff & 0x7FFFFFFF;
rc = sqlite3Fts5IndexIterWriteTokendata(
pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
);
}
if( rc ) return rc;
break;
}
}
}
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
235785 235786 235787 235788 235789 235790 235791 235792 235793 235794 235795 235796 235797 235798 |
}else{
*ppCollist = 0;
*pnCollist = 0;
}
return rc;
}
/*
** 2014 August 11
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 237865 237866 237867 237868 237869 237870 237871 237872 237873 237874 237875 237876 237877 237878 237879 237880 237881 237882 237883 237884 237885 237886 237887 237888 237889 237890 237891 237892 237893 237894 237895 237896 237897 237898 237899 237900 237901 237902 237903 237904 237905 237906 237907 237908 237909 237910 237911 237912 237913 237914 237915 237916 237917 237918 237919 237920 237921 237922 237923 237924 237925 237926 237927 237928 237929 237930 237931 237932 237933 237934 237935 237936 237937 237938 237939 237940 237941 237942 237943 237944 237945 237946 237947 237948 237949 237950 237951 237952 237953 237954 237955 |
}else{
*ppCollist = 0;
*pnCollist = 0;
}
return rc;
}
/*
** Does the work of the fts5_api.xQueryToken() API method.
*/
static int sqlite3Fts5ExprQueryToken(
Fts5Expr *pExpr,
int iPhrase,
int iToken,
const char **ppOut,
int *pnOut
){
Fts5ExprPhrase *pPhrase = 0;
if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
return SQLITE_RANGE;
}
pPhrase = pExpr->apExprPhrase[iPhrase];
if( iToken<0 || iToken>=pPhrase->nTerm ){
return SQLITE_RANGE;
}
*ppOut = pPhrase->aTerm[iToken].pTerm;
*pnOut = pPhrase->aTerm[iToken].nFullTerm;
return SQLITE_OK;
}
/*
** Does the work of the fts5_api.xInstToken() API method.
*/
static int sqlite3Fts5ExprInstToken(
Fts5Expr *pExpr,
i64 iRowid,
int iPhrase,
int iCol,
int iOff,
int iToken,
const char **ppOut,
int *pnOut
){
Fts5ExprPhrase *pPhrase = 0;
Fts5ExprTerm *pTerm = 0;
int rc = SQLITE_OK;
if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
return SQLITE_RANGE;
}
pPhrase = pExpr->apExprPhrase[iPhrase];
if( iToken<0 || iToken>=pPhrase->nTerm ){
return SQLITE_RANGE;
}
pTerm = &pPhrase->aTerm[iToken];
if( pTerm->bPrefix==0 ){
if( pExpr->pConfig->bTokendata ){
rc = sqlite3Fts5IterToken(
pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut
);
}else{
*ppOut = pTerm->pTerm;
*pnOut = pTerm->nFullTerm;
}
}
return rc;
}
/*
** Clear the token mappings for all Fts5IndexIter objects mannaged by
** the expression passed as the only argument.
*/
static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
int ii;
for(ii=0; ii<pExpr->nPhrase; ii++){
Fts5ExprTerm *pT;
for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){
sqlite3Fts5IndexIterClearTokendata(pT->pIter);
}
}
}
/*
** 2014 August 11
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
|
| ︙ | ︙ | |||
235824 235825 235826 235827 235828 235829 235830 | int nSlot; /* Size of aSlot[] array */ Fts5HashEntry *pScan; /* Current ordered scan item */ Fts5HashEntry **aSlot; /* Array of hash slots */ }; /* ** Each entry in the hash table is represented by an object of the | | | | | > > > > > | 237981 237982 237983 237984 237985 237986 237987 237988 237989 237990 237991 237992 237993 237994 237995 237996 237997 237998 237999 238000 238001 238002 238003 | int nSlot; /* Size of aSlot[] array */ Fts5HashEntry *pScan; /* Current ordered scan item */ Fts5HashEntry **aSlot; /* Array of hash slots */ }; /* ** Each entry in the hash table is represented by an object of the ** following type. Each object, its key, and its current data are stored ** in a single memory allocation. The key immediately follows the object ** in memory. The position list data immediately follows the key data ** in memory. ** ** The key is Fts5HashEntry.nKey bytes in size. It consists of a single ** byte identifying the index (either the main term index or a prefix-index), ** followed by the term data. For example: "0token". There is no ** nul-terminator - in this case nKey=6. ** ** The data that follows the key is in a similar, but not identical format ** to the doclist data stored in the database. It is: ** ** * Rowid, as a varint ** * Position list, without 0x00 terminator. ** * Size of previous position list and rowid, as a 4 byte |
| ︙ | ︙ | |||
235962 235963 235964 235965 235966 235967 235968 |
memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));
for(i=0; i<pHash->nSlot; i++){
while( apOld[i] ){
unsigned int iHash;
Fts5HashEntry *p = apOld[i];
apOld[i] = p->pHashNext;
| | < | 238124 238125 238126 238127 238128 238129 238130 238131 238132 238133 238134 238135 238136 238137 238138 |
memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));
for(i=0; i<pHash->nSlot; i++){
while( apOld[i] ){
unsigned int iHash;
Fts5HashEntry *p = apOld[i];
apOld[i] = p->pHashNext;
iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey);
p->pHashNext = apNew[iHash];
apNew[iHash] = p;
}
}
sqlite3_free(apOld);
pHash->nSlot = nNew;
|
| ︙ | ︙ | |||
236047 236048 236049 236050 236051 236052 236053 |
bNew = (pHash->eDetail==FTS5_DETAIL_FULL);
/* Attempt to locate an existing hash entry */
iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
char *zKey = fts5EntryKey(p);
if( zKey[0]==bByte
| | | 238208 238209 238210 238211 238212 238213 238214 238215 238216 238217 238218 238219 238220 238221 238222 |
bNew = (pHash->eDetail==FTS5_DETAIL_FULL);
/* Attempt to locate an existing hash entry */
iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
char *zKey = fts5EntryKey(p);
if( zKey[0]==bByte
&& p->nKey==nToken+1
&& memcmp(&zKey[1], pToken, nToken)==0
){
break;
}
}
/* If an existing hash entry cannot be found, create a new one. */
|
| ︙ | ︙ | |||
236077 236078 236079 236080 236081 236082 236083 |
if( !p ) return SQLITE_NOMEM;
memset(p, 0, sizeof(Fts5HashEntry));
p->nAlloc = (int)nByte;
zKey = fts5EntryKey(p);
zKey[0] = bByte;
memcpy(&zKey[1], pToken, nToken);
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
| | | | 238238 238239 238240 238241 238242 238243 238244 238245 238246 238247 238248 238249 238250 238251 238252 238253 238254 |
if( !p ) return SQLITE_NOMEM;
memset(p, 0, sizeof(Fts5HashEntry));
p->nAlloc = (int)nByte;
zKey = fts5EntryKey(p);
zKey[0] = bByte;
memcpy(&zKey[1], pToken, nToken);
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
p->nKey = nToken+1;
zKey[nToken+1] = '\0';
p->nData = nToken+1 + sizeof(Fts5HashEntry);
p->pHashNext = pHash->aSlot[iHash];
pHash->aSlot[iHash] = p;
pHash->nEntry++;
/* Add the first rowid field to the hash-entry */
p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid);
p->iRowid = iRowid;
|
| ︙ | ︙ | |||
236196 236197 236198 236199 236200 236201 236202 |
if( p1==0 ){
*ppOut = p2;
p2 = 0;
}else if( p2==0 ){
*ppOut = p1;
p1 = 0;
}else{
| < > | > > > | > | > | 238357 238358 238359 238360 238361 238362 238363 238364 238365 238366 238367 238368 238369 238370 238371 238372 238373 238374 238375 238376 238377 238378 238379 238380 238381 |
if( p1==0 ){
*ppOut = p2;
p2 = 0;
}else if( p2==0 ){
*ppOut = p1;
p1 = 0;
}else{
char *zKey1 = fts5EntryKey(p1);
char *zKey2 = fts5EntryKey(p2);
int nMin = MIN(p1->nKey, p2->nKey);
int cmp = memcmp(zKey1, zKey2, nMin);
if( cmp==0 ){
cmp = p1->nKey - p2->nKey;
}
assert( cmp!=0 );
if( cmp>0 ){
/* p2 is smaller */
*ppOut = p2;
ppOut = &p2->pScanNext;
p2 = p2->pScanNext;
}else{
/* p1 is smaller */
*ppOut = p1;
|
| ︙ | ︙ | |||
236243 236244 236245 236246 236247 236248 236249 |
if( !ap ) return SQLITE_NOMEM;
memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);
for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
Fts5HashEntry *pIter;
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
if( pTerm==0
| | | 238409 238410 238411 238412 238413 238414 238415 238416 238417 238418 238419 238420 238421 238422 238423 |
if( !ap ) return SQLITE_NOMEM;
memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);
for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
Fts5HashEntry *pIter;
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
if( pTerm==0
|| (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
){
Fts5HashEntry *pEntry = pIter;
pEntry->pScanNext = 0;
for(i=0; ap[i]; i++){
pEntry = fts5HashEntryMerge(pEntry, ap[i]);
ap[i] = 0;
}
|
| ︙ | ︙ | |||
236282 236283 236284 236285 236286 236287 236288 |
){
unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
char *zKey = 0;
Fts5HashEntry *p;
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
zKey = fts5EntryKey(p);
| < | | | 238448 238449 238450 238451 238452 238453 238454 238455 238456 238457 238458 238459 238460 238461 238462 238463 238464 238465 238466 |
){
unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
char *zKey = 0;
Fts5HashEntry *p;
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
zKey = fts5EntryKey(p);
if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break;
}
if( p ){
int nHashPre = sizeof(Fts5HashEntry) + nTerm;
int nList = p->nData - nHashPre;
u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10));
if( pRet ){
Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre];
memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList);
nList += fts5HashAddPoslistSize(pHash, p, pFaux);
*pnDoclist = nList;
|
| ︙ | ︙ | |||
236348 236349 236350 236351 236352 236353 236354 236355 236356 236357 236358 236359 236360 |
static int sqlite3Fts5HashScanEof(Fts5Hash *p){
return (p->pScan==0);
}
static void sqlite3Fts5HashScanEntry(
Fts5Hash *pHash,
const char **pzTerm, /* OUT: term (nul-terminated) */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
){
Fts5HashEntry *p;
if( (p = pHash->pScan) ){
char *zKey = fts5EntryKey(p);
| > | > | | > | 238513 238514 238515 238516 238517 238518 238519 238520 238521 238522 238523 238524 238525 238526 238527 238528 238529 238530 238531 238532 238533 238534 238535 238536 238537 238538 238539 238540 238541 238542 |
static int sqlite3Fts5HashScanEof(Fts5Hash *p){
return (p->pScan==0);
}
static void sqlite3Fts5HashScanEntry(
Fts5Hash *pHash,
const char **pzTerm, /* OUT: term (nul-terminated) */
int *pnTerm, /* OUT: Size of term in bytes */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
){
Fts5HashEntry *p;
if( (p = pHash->pScan) ){
char *zKey = fts5EntryKey(p);
int nTerm = p->nKey;
fts5HashAddPoslistSize(pHash, p, 0);
*pzTerm = zKey;
*pnTerm = nTerm;
*ppDoclist = (const u8*)&zKey[nTerm];
*pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm);
}else{
*pzTerm = 0;
*pnTerm = 0;
*ppDoclist = 0;
*pnDoclist = 0;
}
}
/*
** 2014 May 31
|
| ︙ | ︙ | |||
236691 236692 236693 236694 236695 236696 236697 236698 236699 236700 236701 236702 236703 236704 |
typedef struct Fts5PageWriter Fts5PageWriter;
typedef struct Fts5SegIter Fts5SegIter;
typedef struct Fts5DoclistIter Fts5DoclistIter;
typedef struct Fts5SegWriter Fts5SegWriter;
typedef struct Fts5Structure Fts5Structure;
typedef struct Fts5StructureLevel Fts5StructureLevel;
typedef struct Fts5StructureSegment Fts5StructureSegment;
struct Fts5Data {
u8 *p; /* Pointer to buffer containing record */
int nn; /* Size of record in bytes */
int szLeaf; /* Size of leaf without page-index */
};
| > > > | 238859 238860 238861 238862 238863 238864 238865 238866 238867 238868 238869 238870 238871 238872 238873 238874 238875 |
typedef struct Fts5PageWriter Fts5PageWriter;
typedef struct Fts5SegIter Fts5SegIter;
typedef struct Fts5DoclistIter Fts5DoclistIter;
typedef struct Fts5SegWriter Fts5SegWriter;
typedef struct Fts5Structure Fts5Structure;
typedef struct Fts5StructureLevel Fts5StructureLevel;
typedef struct Fts5StructureSegment Fts5StructureSegment;
typedef struct Fts5TokenDataIter Fts5TokenDataIter;
typedef struct Fts5TokenDataMap Fts5TokenDataMap;
typedef struct Fts5TombstoneArray Fts5TombstoneArray;
struct Fts5Data {
u8 *p; /* Pointer to buffer containing record */
int nn; /* Size of record in bytes */
int szLeaf; /* Size of leaf without page-index */
};
|
| ︙ | ︙ | |||
236725 236726 236727 236728 236729 236730 236731 236732 236733 236734 236735 236736 236737 236738 236739 236740 236741 236742 236743 236744 236745 236746 | i64 iWriteRowid; /* Rowid for current doc being written */ int bDelete; /* Current write is a delete */ int nContentlessDelete; /* Number of contentless delete ops */ int nPendingRow; /* Number of INSERT in hash table */ /* Error state. */ int rc; /* Current error code */ /* State used by the fts5DataXXX() functions. */ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; sqlite3_stmt *pDataVersion; i64 iStructVersion; /* data_version when pStruct read */ Fts5Structure *pStruct; /* Current db structure (or NULL) */ | > > | 238896 238897 238898 238899 238900 238901 238902 238903 238904 238905 238906 238907 238908 238909 238910 238911 238912 238913 238914 238915 238916 238917 238918 238919 | i64 iWriteRowid; /* Rowid for current doc being written */ int bDelete; /* Current write is a delete */ int nContentlessDelete; /* Number of contentless delete ops */ int nPendingRow; /* Number of INSERT in hash table */ /* Error state. */ int rc; /* Current error code */ int flushRc; /* State used by the fts5DataXXX() functions. */ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; sqlite3_stmt *pIdxNextSelect; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; sqlite3_stmt *pDataVersion; i64 iStructVersion; /* data_version when pStruct read */ Fts5Structure *pStruct; /* Current db structure (or NULL) */ |
| ︙ | ︙ | |||
236886 236887 236888 236889 236890 236891 236892 |
struct Fts5SegIter {
Fts5StructureSegment *pSeg; /* Segment to iterate through */
int flags; /* Mask of configuration flags */
int iLeafPgno; /* Current leaf page number */
Fts5Data *pLeaf; /* Current leaf data */
Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */
i64 iLeafOffset; /* Byte offset within current leaf */
| | < | 239059 239060 239061 239062 239063 239064 239065 239066 239067 239068 239069 239070 239071 239072 239073 |
struct Fts5SegIter {
Fts5StructureSegment *pSeg; /* Segment to iterate through */
int flags; /* Mask of configuration flags */
int iLeafPgno; /* Current leaf page number */
Fts5Data *pLeaf; /* Current leaf data */
Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */
i64 iLeafOffset; /* Byte offset within current leaf */
Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */
/* Next method */
void (*xNext)(Fts5Index*, Fts5SegIter*, int*);
/* The page and offset from which the current term was read. The offset
** is the offset of the first rowid in the current doclist. */
int iTermLeafPgno;
|
| ︙ | ︙ | |||
236913 236914 236915 236916 236917 236918 236919 236920 236921 236922 236923 236924 236925 236926 |
/* Variables populated based on current entry. */
Fts5Buffer term; /* Current term */
i64 iRowid; /* Current rowid */
int nPos; /* Number of bytes in current position list */
u8 bDel; /* True if the delete flag is set */
};
/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
#define ASSERT_SZLEAF_OK(x) assert( \
(x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \
| > > > > > > > > > | 239085 239086 239087 239088 239089 239090 239091 239092 239093 239094 239095 239096 239097 239098 239099 239100 239101 239102 239103 239104 239105 239106 239107 |
/* Variables populated based on current entry. */
Fts5Buffer term; /* Current term */
i64 iRowid; /* Current rowid */
int nPos; /* Number of bytes in current position list */
u8 bDel; /* True if the delete flag is set */
};
/*
** Array of tombstone pages. Reference counted.
*/
struct Fts5TombstoneArray {
int nRef; /* Number of pointers to this object */
int nTombstone;
Fts5Data *apTombstone[1]; /* Array of tombstone pages */
};
/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
#define ASSERT_SZLEAF_OK(x) assert( \
(x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \
|
| ︙ | ︙ | |||
236958 236959 236960 236961 236962 236963 236964 236965 236966 236967 236968 236969 236970 236971 236972 236973 236974 236975 236976 236977 236978 236979 236980 236981 236982 236983 |
**
** aFirst[1] contains the index in aSeg[] of the iterator that points to
** the smallest key overall. aFirst[0] is unused.
**
** poslist:
** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
** There is no way to tell if this is populated or not.
*/
struct Fts5Iter {
Fts5IndexIter base; /* Base class containing output vars */
Fts5Index *pIndex; /* Index that owns this iterator */
Fts5Buffer poslist; /* Buffer containing current poslist */
Fts5Colset *pColset; /* Restrict matches to these columns */
/* Invoked to set output variables. */
void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);
int nSeg; /* Size of aSeg[] array */
int bRev; /* True to iterate in reverse order */
u8 bSkipEmpty; /* True to skip deleted entries */
i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
Fts5CResult *aFirst; /* Current merge state (see above) */
Fts5SegIter aSeg[1]; /* Array of segment iterators */
};
| > > > > > > > < | 239139 239140 239141 239142 239143 239144 239145 239146 239147 239148 239149 239150 239151 239152 239153 239154 239155 239156 239157 239158 239159 239160 239161 239162 239163 239164 239165 239166 239167 239168 239169 239170 239171 239172 239173 239174 239175 239176 239177 239178 |
**
** aFirst[1] contains the index in aSeg[] of the iterator that points to
** the smallest key overall. aFirst[0] is unused.
**
** poslist:
** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
** There is no way to tell if this is populated or not.
**
** pColset:
** If not NULL, points to an object containing a set of column indices.
** Only matches that occur in one of these columns will be returned.
** The Fts5Iter does not own the Fts5Colset object, and so it is not
** freed when the iterator is closed - it is owned by the upper layer.
*/
struct Fts5Iter {
Fts5IndexIter base; /* Base class containing output vars */
Fts5TokenDataIter *pTokenDataIter;
Fts5Index *pIndex; /* Index that owns this iterator */
Fts5Buffer poslist; /* Buffer containing current poslist */
Fts5Colset *pColset; /* Restrict matches to these columns */
/* Invoked to set output variables. */
void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);
int nSeg; /* Size of aSeg[] array */
int bRev; /* True to iterate in reverse order */
u8 bSkipEmpty; /* True to skip deleted entries */
i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
Fts5CResult *aFirst; /* Current merge state (see above) */
Fts5SegIter aSeg[1]; /* Array of segment iterators */
};
/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
**
** pData:
** Record containing the doclist-index data.
|
| ︙ | ︙ | |||
237896 237897 237898 237899 237900 237901 237902 |
}else{
int iOff;
for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
if( pData->p[iOff] ) break;
}
if( iOff<pData->nn ){
| | | | 240083 240084 240085 240086 240087 240088 240089 240090 240091 240092 240093 240094 240095 240096 240097 240098 240099 |
}else{
int iOff;
for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
if( pData->p[iOff] ) break;
}
if( iOff<pData->nn ){
u64 iVal;
pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
iOff += fts5GetVarint(&pData->p[iOff], &iVal);
pLvl->iRowid += iVal;
pLvl->iOff = iOff;
}else{
pLvl->bEof = 1;
}
}
|
| ︙ | ︙ | |||
238277 238278 238279 238280 238281 238282 238283 |
pIter->xNext = fts5SegIterNext_None;
}else{
pIter->xNext = fts5SegIterNext;
}
}
/*
| | | | | > | | < | > > | 240464 240465 240466 240467 240468 240469 240470 240471 240472 240473 240474 240475 240476 240477 240478 240479 240480 240481 240482 240483 240484 240485 240486 240487 240488 240489 240490 240491 |
pIter->xNext = fts5SegIterNext_None;
}else{
pIter->xNext = fts5SegIterNext;
}
}
/*
** Allocate a tombstone hash page array object (pIter->pTombArray) for
** the iterator passed as the second argument. If an OOM error occurs,
** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
const int nTomb = pIter->pSeg->nPgTombstone;
if( nTomb>0 ){
int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
Fts5TombstoneArray *pNew;
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
pNew->nTombstone = nTomb;
pNew->nRef = 1;
pIter->pTombArray = pNew;
}
}
}
/*
** Initialize the iterator object pIter to iterate through the entries in
** segment pSeg. The iterator is left pointing to the first entry when
|
| ︙ | ︙ | |||
238545 238546 238547 238548 238549 238550 238551 238552 238553 |
iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep);
}
pIter->iLeafOffset = iOff;
fts5SegIterLoadTerm(p, pIter, nKeep);
}else{
const u8 *pList = 0;
const char *zTerm = 0;
int nList;
sqlite3Fts5HashScanNext(p->pHash);
| > | | | 240734 240735 240736 240737 240738 240739 240740 240741 240742 240743 240744 240745 240746 240747 240748 240749 240750 240751 240752 240753 240754 240755 240756 240757 |
iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep);
}
pIter->iLeafOffset = iOff;
fts5SegIterLoadTerm(p, pIter, nKeep);
}else{
const u8 *pList = 0;
const char *zTerm = 0;
int nTerm = 0;
int nList;
sqlite3Fts5HashScanNext(p->pHash);
sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
if( pList==0 ) goto next_none_eof;
pIter->pLeaf->p = (u8*)pList;
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList;
sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
}
if( pbNewTerm ) *pbNewTerm = 1;
}else{
goto next_none_eof;
}
|
| ︙ | ︙ | |||
238619 238620 238621 238622 238623 238624 238625 238626 238627 238628 238629 |
assert_nc( iDelta>0 );
}
pIter->iLeafOffset = iOff;
}else if( pIter->pSeg==0 ){
const u8 *pList = 0;
const char *zTerm = 0;
int nList = 0;
assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
sqlite3Fts5HashScanNext(p->pHash);
| > | | < | 240809 240810 240811 240812 240813 240814 240815 240816 240817 240818 240819 240820 240821 240822 240823 240824 240825 240826 240827 240828 240829 240830 240831 240832 240833 240834 240835 240836 240837 240838 |
assert_nc( iDelta>0 );
}
pIter->iLeafOffset = iOff;
}else if( pIter->pSeg==0 ){
const u8 *pList = 0;
const char *zTerm = 0;
int nTerm = 0;
int nList = 0;
assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
sqlite3Fts5HashScanNext(p->pHash);
sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
}
if( pList==0 ){
fts5DataRelease(pIter->pLeaf);
pIter->pLeaf = 0;
}else{
pIter->pLeaf->p = (u8*)pList;
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList+1;
sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
*pbNewTerm = 1;
}
}else{
iOff = 0;
/* Next entry is not on the current page */
while( iOff==0 ){
|
| ︙ | ︙ | |||
239020 239021 239022 239023 239024 239025 239026 |
pIter->iLeafPgno = iPg - 1;
fts5SegIterNextPage(p, pIter);
if( pIter->pLeaf ){
fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
}
| | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 241210 241211 241212 241213 241214 241215 241216 241217 241218 241219 241220 241221 241222 241223 241224 241225 241226 241227 241228 241229 241230 241231 241232 241233 241234 241235 241236 241237 241238 241239 241240 241241 241242 241243 241244 241245 241246 241247 241248 241249 241250 241251 241252 241253 241254 241255 241256 241257 241258 241259 241260 241261 241262 241263 241264 241265 241266 241267 241268 241269 241270 241271 241272 241273 241274 241275 241276 241277 241278 241279 241280 241281 241282 241283 241284 241285 241286 241287 241288 241289 241290 241291 241292 241293 241294 241295 241296 241297 241298 241299 241300 241301 241302 241303 241304 241305 241306 241307 241308 241309 241310 241311 241312 241313 241314 241315 241316 241317 241318 241319 241320 241321 241322 241323 241324 241325 241326 241327 241328 241329 241330 241331 |
pIter->iLeafPgno = iPg - 1;
fts5SegIterNextPage(p, pIter);
if( pIter->pLeaf ){
fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
}
if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){
pIter->flags |= FTS5_SEGITER_ONETERM;
if( pIter->pLeaf ){
if( flags & FTS5INDEX_QUERY_DESC ){
pIter->flags |= FTS5_SEGITER_REVERSE;
}
if( bDlidx ){
fts5SegIterLoadDlidx(p, pIter);
}
if( flags & FTS5INDEX_QUERY_DESC ){
fts5SegIterReverse(p, pIter);
}
}
}
fts5SegIterSetNext(p, pIter);
if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){
fts5SegIterAllocTombstone(p, pIter);
}
/* Either:
**
** 1) an error has occurred, or
** 2) the iterator points to EOF, or
** 3) the iterator points to an entry with term (pTerm/nTerm), or
** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points
** to an entry with a term greater than or equal to (pTerm/nTerm).
*/
assert_nc( p->rc!=SQLITE_OK /* 1 */
|| pIter->pLeaf==0 /* 2 */
|| fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */
|| (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */
);
}
/*
** SQL used by fts5SegIterNextInit() to find the page to open.
*/
static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){
if( p->pIdxNextSelect==0 ){
Fts5Config *pConfig = p->pConfig;
fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf(
"SELECT pgno FROM '%q'.'%q_idx' WHERE "
"segid=? AND term>? ORDER BY term ASC LIMIT 1",
pConfig->zDb, pConfig->zName
));
}
return p->pIdxNextSelect;
}
/*
** This is similar to fts5SegIterSeekInit(), except that it initializes
** the segment iterator to point to the first term following the page
** with pToken/nToken on it.
*/
static void fts5SegIterNextInit(
Fts5Index *p,
const char *pTerm, int nTerm,
Fts5StructureSegment *pSeg, /* Description of segment */
Fts5SegIter *pIter /* Object to populate */
){
int iPg = -1; /* Page of segment to open */
int bDlidx = 0;
sqlite3_stmt *pSel = 0; /* SELECT to find iPg */
pSel = fts5IdxNextStmt(p);
if( pSel ){
assert( p->rc==SQLITE_OK );
sqlite3_bind_int(pSel, 1, pSeg->iSegid);
sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC);
if( sqlite3_step(pSel)==SQLITE_ROW ){
i64 val = sqlite3_column_int64(pSel, 0);
iPg = (int)(val>>1);
bDlidx = (val & 0x0001);
}
p->rc = sqlite3_reset(pSel);
sqlite3_bind_null(pSel, 2);
if( p->rc ) return;
}
memset(pIter, 0, sizeof(*pIter));
pIter->pSeg = pSeg;
pIter->flags |= FTS5_SEGITER_ONETERM;
if( iPg>=0 ){
pIter->iLeafPgno = iPg - 1;
fts5SegIterNextPage(p, pIter);
fts5SegIterSetNext(p, pIter);
}
if( pIter->pLeaf ){
const u8 *a = pIter->pLeaf->p;
int iTermOff = 0;
pIter->iPgidxOff = pIter->pLeaf->szLeaf;
pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff);
pIter->iLeafOffset = iTermOff;
fts5SegIterLoadTerm(p, pIter, 0);
fts5SegIterLoadNPos(p, pIter);
if( bDlidx ) fts5SegIterLoadDlidx(p, pIter);
assert( p->rc!=SQLITE_OK ||
fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0
);
}
}
/*
** Initialize the object pIter to point to term pTerm/nTerm within the
** in-memory hash table. If there is no such term in the hash-table, the
** iterator is set to EOF.
**
** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
** an error has already occurred when this function is called, it is a no-op.
|
| ︙ | ︙ | |||
239079 239080 239081 239082 239083 239084 239085 |
assert( p->pHash );
assert( p->rc==SQLITE_OK );
if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
const u8 *pList = 0;
p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
| | < | 241344 241345 241346 241347 241348 241349 241350 241351 241352 241353 241354 241355 241356 241357 241358 |
assert( p->pHash );
assert( p->rc==SQLITE_OK );
if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
const u8 *pList = 0;
p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList);
if( pList ){
pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
if( pLeaf ){
pLeaf->p = (u8*)pList;
}
}
|
| ︙ | ︙ | |||
239138 239139 239140 239141 239142 239143 239144 239145 239146 239147 239148 239149 239150 239151 239152 |
int ii;
for(ii=0; ii<n; ii++){
fts5DataRelease(ap[ii]);
}
sqlite3_free(ap);
}
}
/*
** Zero the iterator passed as the only argument.
*/
static void fts5SegIterClear(Fts5SegIter *pIter){
fts5BufferFree(&pIter->term);
fts5DataRelease(pIter->pLeaf);
fts5DataRelease(pIter->pNextLeaf);
| > > > > > > > > > > > > > > > > > | | 241402 241403 241404 241405 241406 241407 241408 241409 241410 241411 241412 241413 241414 241415 241416 241417 241418 241419 241420 241421 241422 241423 241424 241425 241426 241427 241428 241429 241430 241431 241432 241433 241434 241435 241436 241437 241438 241439 241440 241441 |
int ii;
for(ii=0; ii<n; ii++){
fts5DataRelease(ap[ii]);
}
sqlite3_free(ap);
}
}
/*
** Decrement the ref-count of the object passed as the only argument. If it
** reaches 0, free it and its contents.
*/
static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){
if( p ){
p->nRef--;
if( p->nRef<=0 ){
int ii;
for(ii=0; ii<p->nTombstone; ii++){
fts5DataRelease(p->apTombstone[ii]);
}
sqlite3_free(p);
}
}
}
/*
** Zero the iterator passed as the only argument.
*/
static void fts5SegIterClear(Fts5SegIter *pIter){
fts5BufferFree(&pIter->term);
fts5DataRelease(pIter->pLeaf);
fts5DataRelease(pIter->pNextLeaf);
fts5TombstoneArrayDelete(pIter->pTombArray);
fts5DlidxIterFree(pIter->pDlidx);
sqlite3_free(pIter->aRowidOffset);
memset(pIter, 0, sizeof(Fts5SegIter));
}
#ifdef SQLITE_DEBUG
|
| ︙ | ︙ | |||
239391 239392 239393 239394 239395 239396 239397 |
if( pIter->pLeaf==0 ) break;
if( bRev==0 && pIter->iRowid>=iMatch ) break;
if( bRev!=0 && pIter->iRowid<=iMatch ) break;
bMove = 1;
}while( p->rc==SQLITE_OK );
}
| < | 241672 241673 241674 241675 241676 241677 241678 241679 241680 241681 241682 241683 241684 241685 |
if( pIter->pLeaf==0 ) break;
if( bRev==0 && pIter->iRowid>=iMatch ) break;
if( bRev!=0 && pIter->iRowid<=iMatch ) break;
bMove = 1;
}while( p->rc==SQLITE_OK );
}
/*
** Free the iterator object passed as the second argument.
*/
static void fts5MultiIterFree(Fts5Iter *pIter){
if( pIter ){
int i;
for(i=0; i<pIter->nSeg; i++){
|
| ︙ | ︙ | |||
239536 239537 239538 239539 239540 239541 239542 239543 |
** Return true if the iterator passed as the only argument points
** to an segment entry for which there is a tombstone. Return false
** if there is no tombstone or if the iterator is already at EOF.
*/
static int fts5MultiIterIsDeleted(Fts5Iter *pIter){
int iFirst = pIter->aFirst[1].iFirst;
Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
| > | | | | | | | | 241816 241817 241818 241819 241820 241821 241822 241823 241824 241825 241826 241827 241828 241829 241830 241831 241832 241833 241834 241835 241836 241837 241838 241839 241840 241841 241842 241843 241844 241845 241846 241847 241848 |
** Return true if the iterator passed as the only argument points
** to an segment entry for which there is a tombstone. Return false
** if there is no tombstone or if the iterator is already at EOF.
*/
static int fts5MultiIterIsDeleted(Fts5Iter *pIter){
int iFirst = pIter->aFirst[1].iFirst;
Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
Fts5TombstoneArray *pArray = pSeg->pTombArray;
if( pSeg->pLeaf && pArray ){
/* Figure out which page the rowid might be present on. */
int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone;
assert( iPg>=0 );
/* If tombstone hash page iPg has not yet been loaded from the
** database, load it now. */
if( pArray->apTombstone[iPg]==0 ){
pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)
);
if( pArray->apTombstone[iPg]==0 ) return 0;
}
return fts5IndexTombstoneQuery(
pArray->apTombstone[iPg],
pArray->nTombstone,
pSeg->iRowid
);
}
return 0;
}
|
| ︙ | ︙ | |||
240092 240093 240094 240095 240096 240097 240098 240099 240100 240101 240102 240103 240104 240105 |
}else{
pIter->xSetOutputs = fts5IterSetOutputs_Col;
}
}
}
}
/*
** Allocate a new Fts5Iter object.
**
** The new object will be used to iterate through data in structure pStruct.
** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
** is zero or greater, data from the first nSegment segments on level iLevel
| > > > > > > > > > > > > > > > > > > > > > > > > > > | 242373 242374 242375 242376 242377 242378 242379 242380 242381 242382 242383 242384 242385 242386 242387 242388 242389 242390 242391 242392 242393 242394 242395 242396 242397 242398 242399 242400 242401 242402 242403 242404 242405 242406 242407 242408 242409 242410 242411 242412 |
}else{
pIter->xSetOutputs = fts5IterSetOutputs_Col;
}
}
}
}
/*
** All the component segment-iterators of pIter have been set up. This
** functions finishes setup for iterator pIter itself.
*/
static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){
int iIter;
for(iIter=pIter->nSeg-1; iIter>0; iIter--){
int iEq;
if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){
Fts5SegIter *pSeg = &pIter->aSeg[iEq];
if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
fts5MultiIterAdvanced(p, pIter, iEq, iIter);
}
}
fts5MultiIterSetEof(pIter);
fts5AssertMultiIterSetup(p, pIter);
if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter))
|| fts5MultiIterIsDeleted(pIter)
){
fts5MultiIterNext(p, pIter, 0, 0);
}else if( pIter->base.bEof==0 ){
Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
pIter->xSetOutputs(pIter, pSeg);
}
}
/*
** Allocate a new Fts5Iter object.
**
** The new object will be used to iterate through data in structure pStruct.
** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
** is zero or greater, data from the first nSegment segments on level iLevel
|
| ︙ | ︙ | |||
240173 240174 240175 240176 240177 240178 240179 |
for(iSeg=nSeg-1; iSeg>=0; iSeg--){
fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
}
}
assert( iIter==nSeg );
}
| | < < < < < < < < | < < < < < < < < < < < | 242480 242481 242482 242483 242484 242485 242486 242487 242488 242489 242490 242491 242492 242493 242494 242495 242496 242497 242498 242499 |
for(iSeg=nSeg-1; iSeg>=0; iSeg--){
fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
}
}
assert( iIter==nSeg );
}
/* If the above was successful, each component iterator now points
** to the first entry in its segment. In this case initialize the
** aFirst[] array. Or, if an error has occurred, free the iterator
** object and set the output variable to NULL. */
if( p->rc==SQLITE_OK ){
fts5MultiIterFinishSetup(p, pNew);
}else{
fts5MultiIterFree(pNew);
*ppOut = 0;
}
fts5MultiIterNew_post_check:
assert( (*ppOut)!=0 || p->rc!=SQLITE_OK );
|
| ︙ | ︙ | |||
240222 240223 240224 240225 240226 240227 240228 |
int bDesc, /* True for descending rowid order */
Fts5Iter **ppOut /* New object */
){
Fts5Iter *pNew;
pNew = fts5MultiIterAlloc(p, 2);
if( pNew ){
Fts5SegIter *pIter = &pNew->aSeg[1];
| < | 242510 242511 242512 242513 242514 242515 242516 242517 242518 242519 242520 242521 242522 242523 |
int bDesc, /* True for descending rowid order */
Fts5Iter **ppOut /* New object */
){
Fts5Iter *pNew;
pNew = fts5MultiIterAlloc(p, 2);
if( pNew ){
Fts5SegIter *pIter = &pNew->aSeg[1];
pIter->flags = FTS5_SEGITER_ONETERM;
if( pData->szLeaf>0 ){
pIter->pLeaf = pData;
pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
pIter->iEndofDoclist = pData->nn;
pNew->aFirst[1].iFirst = 1;
if( bDesc ){
|
| ︙ | ︙ | |||
240370 240371 240372 240373 240374 240375 240376 240377 240378 240379 240380 240381 240382 240383 |
*/
static void fts5IndexDiscardData(Fts5Index *p){
assert( p->pHash || p->nPendingData==0 );
if( p->pHash ){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
}
p->nContentlessDelete = 0;
}
/*
** Return the size of the prefix, in bytes, that buffer
** (pNew/<length-unknown>) shares with buffer (pOld/nOld).
| > | 242657 242658 242659 242660 242661 242662 242663 242664 242665 242666 242667 242668 242669 242670 242671 |
*/
static void fts5IndexDiscardData(Fts5Index *p){
assert( p->pHash || p->nPendingData==0 );
if( p->pHash ){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
p->flushRc = SQLITE_OK;
}
p->nContentlessDelete = 0;
}
/*
** Return the size of the prefix, in bytes, that buffer
** (pNew/<length-unknown>) shares with buffer (pOld/nOld).
|
| ︙ | ︙ | |||
240585 240586 240587 240588 240589 240590 240591 |
pDlidx->bPrevValid = 0;
pDlidx->pgno++;
}else{
bDone = 1;
}
if( pDlidx->bPrevValid ){
| | | 242873 242874 242875 242876 242877 242878 242879 242880 242881 242882 242883 242884 242885 242886 242887 |
pDlidx->bPrevValid = 0;
pDlidx->pgno++;
}else{
bDone = 1;
}
if( pDlidx->bPrevValid ){
iVal = (u64)iRowid - (u64)pDlidx->iPrev;
}else{
i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
assert( pDlidx->buf.n==0 );
sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone);
sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno);
iVal = iRowid;
}
|
| ︙ | ︙ | |||
241708 241709 241710 241711 241712 241713 241714 241715 241716 241717 |
** mode. It edits the segments within the database described by argument
** pStruct to remove the entries for term zTerm, rowid iRowid.
*/
static void fts5FlushSecureDelete(
Fts5Index *p,
Fts5Structure *pStruct,
const char *zTerm,
i64 iRowid
){
const int f = FTS5INDEX_QUERY_SKIPHASH;
| > < | 243996 243997 243998 243999 244000 244001 244002 244003 244004 244005 244006 244007 244008 244009 244010 244011 244012 244013 |
** mode. It edits the segments within the database described by argument
** pStruct to remove the entries for term zTerm, rowid iRowid.
*/
static void fts5FlushSecureDelete(
Fts5Index *p,
Fts5Structure *pStruct,
const char *zTerm,
int nTerm,
i64 iRowid
){
const int f = FTS5INDEX_QUERY_SKIPHASH;
Fts5Iter *pIter = 0; /* Used to find term instance */
fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
if( fts5MultiIterEof(p, pIter)==0 ){
i64 iThis = fts5MultiIterRowid(pIter);
if( iThis<iRowid ){
fts5MultiIterNextFrom(p, pIter, iRowid);
|
| ︙ | ︙ | |||
241785 241786 241787 241788 241789 241790 241791 |
while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
const char *zTerm; /* Buffer containing term */
int nTerm; /* Size of zTerm in bytes */
const u8 *pDoclist; /* Pointer to doclist for this term */
int nDoclist; /* Size of doclist in bytes */
/* Get the term and doclist for this entry. */
| | < | 244073 244074 244075 244076 244077 244078 244079 244080 244081 244082 244083 244084 244085 244086 244087 |
while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
const char *zTerm; /* Buffer containing term */
int nTerm; /* Size of zTerm in bytes */
const u8 *pDoclist; /* Pointer to doclist for this term */
int nDoclist; /* Size of doclist in bytes */
/* Get the term and doclist for this entry. */
sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist);
if( bSecureDelete==0 ){
fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
if( p->rc!=SQLITE_OK ) break;
assert( writer.bFirstRowidInPage==0 );
}
if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
|
| ︙ | ︙ | |||
241816 241817 241818 241819 241820 241821 241822 |
/* If in secure delete mode, and if this entry in the poslist is
** in fact a delete, then edit the existing segments directly
** using fts5FlushSecureDelete(). */
if( bSecureDelete ){
if( eDetail==FTS5_DETAIL_NONE ){
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
| | | | 244103 244104 244105 244106 244107 244108 244109 244110 244111 244112 244113 244114 244115 244116 244117 244118 244119 244120 244121 244122 244123 244124 244125 244126 244127 |
/* If in secure delete mode, and if this entry in the poslist is
** in fact a delete, then edit the existing segments directly
** using fts5FlushSecureDelete(). */
if( bSecureDelete ){
if( eDetail==FTS5_DETAIL_NONE ){
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
iOff++;
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
iOff++;
nDoclist = 0;
}else{
continue;
}
}
}else if( (pDoclist[iOff] & 0x01) ){
fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
iOff++;
continue;
}
}
}
|
| ︙ | ︙ | |||
241952 241953 241954 241955 241956 241957 241958 241959 241960 241961 241962 241963 241964 241965 241966 241967 241968 241969 241970 241971 241972 241973 |
}
/*
** Flush any data stored in the in-memory hash tables to the database.
*/
static void fts5IndexFlush(Fts5Index *p){
/* Unless it is empty, flush the hash table to disk */
if( p->nPendingData || p->nContentlessDelete ){
assert( p->pHash );
fts5FlushOneHash(p);
if( p->rc==SQLITE_OK ){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
p->nContentlessDelete = 0;
}
}
}
static Fts5Structure *fts5IndexOptimizeStruct(
Fts5Index *p,
Fts5Structure *pStruct
| > > > > > > | 244239 244240 244241 244242 244243 244244 244245 244246 244247 244248 244249 244250 244251 244252 244253 244254 244255 244256 244257 244258 244259 244260 244261 244262 244263 244264 244265 244266 |
}
/*
** Flush any data stored in the in-memory hash tables to the database.
*/
static void fts5IndexFlush(Fts5Index *p){
/* Unless it is empty, flush the hash table to disk */
if( p->flushRc ){
p->rc = p->flushRc;
return;
}
if( p->nPendingData || p->nContentlessDelete ){
assert( p->pHash );
fts5FlushOneHash(p);
if( p->rc==SQLITE_OK ){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
p->nContentlessDelete = 0;
}else if( p->nPendingData || p->nContentlessDelete ){
p->flushRc = p->rc;
}
}
}
static Fts5Structure *fts5IndexOptimizeStruct(
Fts5Index *p,
Fts5Structure *pStruct
|
| ︙ | ︙ | |||
242446 242447 242448 242449 242450 242451 242452 | static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ int bDesc, /* True for "ORDER BY rowid DESC" */ int iIdx, /* Index to scan for data */ u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset, /* Restrict matches to these columns */ | | > | > > > > > > | 244739 244740 244741 244742 244743 244744 244745 244746 244747 244748 244749 244750 244751 244752 244753 244754 244755 244756 244757 244758 244759 244760 244761 244762 244763 244764 244765 244766 244767 244768 244769 244770 244771 244772 244773 244774 244775 244776 244777 244778 244779 244780 244781 244782 244783 244784 244785 244786 244787 244788 244789 244790 244791 244792 244793 |
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
int iIdx, /* Index to scan for data */
u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
Fts5Buffer *aBuf;
int nBuf = 32;
int nMerge = 1;
void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
xMerge = fts5MergeRowidLists;
xAppend = fts5AppendRowid;
}else{
nMerge = FTS5_MERGE_NLIST-1;
nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
xMerge = fts5MergePrefixLists;
xAppend = fts5AppendPoslist;
}
aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
pStruct = fts5StructureRead(p);
assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );
if( p->rc==SQLITE_OK ){
const int flags = FTS5INDEX_QUERY_SCAN
| FTS5INDEX_QUERY_SKIPEMPTY
| FTS5INDEX_QUERY_NOOUTPUT;
int i;
i64 iLastRowid = 0;
Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
Fts5Data *pData;
Fts5Buffer doclist;
int bNewTerm = 1;
memset(&doclist, 0, sizeof(doclist));
/* If iIdx is non-zero, then it is the number of a prefix-index for
** prefixes 1 character longer than the prefix being queried for. That
** index contains all the doclists required, except for the one
** corresponding to the prefix itself. That one is extracted from the
** main term index here. */
if( iIdx!=0 ){
int dummy = 0;
const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
pToken[0] = FTS5_MAIN_PREFIX;
fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for(;
|
| ︙ | ︙ | |||
242503 242504 242505 242506 242507 242508 242509 242510 242511 242512 242513 242514 242515 242516 242517 242518 242519 242520 242521 242522 242523 242524 |
}
fts5MultiIterFree(p1);
}
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
){
Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
int nTerm = pSeg->term.n;
const u8 *pTerm = pSeg->term.p;
p1->xSetOutputs(p1, pSeg);
assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
if( bNewTerm ){
if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
}
if( p1->base.nData==0 ) continue;
| > < | 244803 244804 244805 244806 244807 244808 244809 244810 244811 244812 244813 244814 244815 244816 244817 244818 244819 244820 244821 244822 244823 244824 244825 244826 244827 244828 244829 244830 244831 244832 |
}
fts5MultiIterFree(p1);
}
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
){
Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
int nTerm = pSeg->term.n;
const u8 *pTerm = pSeg->term.p;
p1->xSetOutputs(p1, pSeg);
assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
if( bNewTerm ){
if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
}
if( p1->base.nData==0 ) continue;
if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
int i1 = i*nMerge;
int iStore;
assert( i1+nMerge<=nBuf );
for(iStore=i1; iStore<i1+nMerge; iStore++){
if( aBuf[iStore].n==0 ){
|
| ︙ | ︙ | |||
242557 242558 242559 242560 242561 242562 242563 |
}
for(iFree=i; iFree<i+nMerge; iFree++){
fts5BufferFree(&aBuf[iFree]);
}
}
fts5MultiIterFree(p1);
| | | 244857 244858 244859 244860 244861 244862 244863 244864 244865 244866 244867 244868 244869 244870 244871 |
}
for(iFree=i; iFree<i+nMerge; iFree++){
fts5BufferFree(&aBuf[iFree]);
}
}
fts5MultiIterFree(p1);
pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);
if( pData ){
pData->p = (u8*)&pData[1];
pData->nn = pData->szLeaf = doclist.n;
if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n);
fts5MultiIterNew2(p, pData, bDesc, ppIter);
}
fts5BufferFree(&doclist);
|
| ︙ | ︙ | |||
242700 242701 242702 242703 242704 242705 242706 242707 242708 242709 242710 242711 242712 242713 |
assert( p->pReader==0 );
fts5StructureInvalidate(p);
sqlite3_finalize(p->pWriter);
sqlite3_finalize(p->pDeleter);
sqlite3_finalize(p->pIdxWriter);
sqlite3_finalize(p->pIdxDeleter);
sqlite3_finalize(p->pIdxSelect);
sqlite3_finalize(p->pDataVersion);
sqlite3_finalize(p->pDeleteFromIdx);
sqlite3Fts5HashFree(p->pHash);
sqlite3_free(p->zDataTbl);
sqlite3_free(p);
}
return rc;
| > | 245000 245001 245002 245003 245004 245005 245006 245007 245008 245009 245010 245011 245012 245013 245014 |
assert( p->pReader==0 );
fts5StructureInvalidate(p);
sqlite3_finalize(p->pWriter);
sqlite3_finalize(p->pDeleter);
sqlite3_finalize(p->pIdxWriter);
sqlite3_finalize(p->pIdxDeleter);
sqlite3_finalize(p->pIdxSelect);
sqlite3_finalize(p->pIdxNextSelect);
sqlite3_finalize(p->pDataVersion);
sqlite3_finalize(p->pDeleteFromIdx);
sqlite3Fts5HashFree(p->pHash);
sqlite3_free(p->zDataTbl);
sqlite3_free(p);
}
return rc;
|
| ︙ | ︙ | |||
242794 242795 242796 242797 242798 242799 242800 242801 242802 242803 242804 242805 242806 242807 |
nByte
);
}
}
return rc;
}
/*
** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
*/
static int sqlite3Fts5IndexQuery(
Fts5Index *p, /* FTS index to query */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 245095 245096 245097 245098 245099 245100 245101 245102 245103 245104 245105 245106 245107 245108 245109 245110 245111 245112 245113 245114 245115 245116 245117 245118 245119 245120 245121 245122 245123 245124 245125 245126 245127 245128 245129 245130 245131 245132 245133 245134 245135 245136 245137 245138 245139 245140 245141 245142 245143 245144 245145 245146 245147 245148 245149 245150 245151 245152 245153 245154 245155 245156 245157 245158 245159 245160 245161 245162 245163 245164 245165 245166 245167 245168 245169 245170 245171 245172 245173 245174 245175 245176 245177 245178 245179 245180 245181 245182 245183 245184 245185 245186 245187 245188 245189 245190 245191 245192 245193 245194 245195 245196 245197 245198 245199 245200 245201 245202 245203 245204 245205 245206 245207 245208 245209 245210 245211 245212 245213 245214 245215 245216 245217 245218 245219 245220 245221 245222 245223 245224 245225 245226 245227 245228 245229 245230 245231 245232 245233 245234 245235 245236 245237 245238 245239 245240 245241 245242 245243 245244 245245 245246 245247 245248 245249 245250 245251 245252 245253 245254 245255 245256 245257 245258 245259 245260 245261 245262 245263 245264 245265 245266 245267 245268 245269 245270 245271 245272 245273 245274 245275 245276 245277 245278 245279 245280 245281 245282 245283 245284 245285 245286 245287 245288 245289 245290 245291 245292 245293 245294 245295 245296 245297 245298 245299 245300 245301 245302 245303 245304 245305 245306 245307 245308 245309 245310 245311 245312 245313 245314 245315 245316 245317 245318 245319 245320 245321 245322 245323 245324 245325 245326 245327 245328 245329 245330 245331 245332 245333 245334 245335 245336 245337 245338 245339 245340 245341 245342 245343 245344 245345 245346 245347 245348 245349 245350 245351 245352 245353 245354 245355 245356 245357 245358 245359 245360 245361 245362 245363 245364 245365 245366 245367 245368 245369 245370 245371 245372 245373 245374 245375 245376 245377 245378 245379 245380 245381 245382 245383 245384 245385 245386 245387 245388 245389 245390 245391 245392 245393 245394 245395 245396 245397 245398 245399 245400 245401 245402 245403 245404 245405 245406 245407 245408 245409 245410 245411 245412 245413 245414 245415 245416 245417 245418 245419 245420 245421 245422 245423 245424 245425 245426 245427 245428 245429 245430 245431 245432 245433 245434 245435 245436 245437 245438 245439 245440 245441 245442 245443 245444 245445 245446 245447 245448 245449 245450 245451 245452 245453 245454 245455 245456 245457 245458 245459 245460 245461 245462 245463 245464 245465 245466 245467 245468 245469 245470 245471 245472 245473 245474 245475 245476 245477 245478 245479 245480 245481 245482 245483 245484 245485 245486 245487 245488 245489 245490 245491 245492 245493 245494 245495 245496 245497 245498 245499 245500 245501 245502 245503 245504 245505 245506 245507 245508 245509 245510 245511 245512 245513 245514 245515 245516 245517 245518 245519 245520 245521 245522 245523 245524 245525 245526 245527 245528 245529 245530 245531 245532 245533 245534 245535 245536 245537 245538 245539 245540 245541 245542 245543 245544 245545 245546 245547 245548 245549 245550 245551 245552 245553 245554 245555 245556 |
nByte
);
}
}
return rc;
}
/*
** pToken points to a buffer of size nToken bytes containing a search
** term, including the index number at the start, used on a tokendata=1
** table. This function returns true if the term in buffer pBuf matches
** token pToken/nToken.
*/
static int fts5IsTokendataPrefix(
Fts5Buffer *pBuf,
const u8 *pToken,
int nToken
){
return (
pBuf->n>=nToken
&& 0==memcmp(pBuf->p, pToken, nToken)
&& (pBuf->n==nToken || pBuf->p[nToken]==0x00)
);
}
/*
** Ensure the segment-iterator passed as the only argument points to EOF.
*/
static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
fts5DataRelease(pSeg->pLeaf);
pSeg->pLeaf = 0;
}
/*
** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
** array of these for each row it visits. Or, for an iterator used by an
** "ORDER BY rank" query, it accumulates an array of these for the entire
** query.
**
** Each instance in the array indicates the iterator (and therefore term)
** associated with position iPos of rowid iRowid. This is used by the
** xInstToken() API.
*/
struct Fts5TokenDataMap {
i64 iRowid; /* Row this token is located in */
i64 iPos; /* Position of token */
int iIter; /* Iterator token was read from */
};
/*
** An object used to supplement Fts5Iter for tokendata=1 iterators.
*/
struct Fts5TokenDataIter {
int nIter;
int nIterAlloc;
int nMap;
int nMapAlloc;
Fts5TokenDataMap *aMap;
Fts5PoslistReader *aPoslistReader;
int *aPoslistToIter;
Fts5Iter *apIter[1];
};
/*
** This function appends iterator pAppend to Fts5TokenDataIter pIn and
** returns the result.
*/
static Fts5TokenDataIter *fts5AppendTokendataIter(
Fts5Index *p, /* Index object (for error code) */
Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */
Fts5Iter *pAppend /* Append this iterator */
){
Fts5TokenDataIter *pRet = pIn;
if( p->rc==SQLITE_OK ){
if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
if( pNew==0 ){
p->rc = SQLITE_NOMEM;
}else{
if( pIn==0 ) memset(pNew, 0, nByte);
pRet = pNew;
pNew->nIterAlloc = nAlloc;
}
}
}
if( p->rc ){
sqlite3Fts5IterClose((Fts5IndexIter*)pAppend);
}else{
pRet->apIter[pRet->nIter++] = pAppend;
}
assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
return pRet;
}
/*
** Delete an Fts5TokenDataIter structure and its contents.
*/
static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
if( pSet ){
int ii;
for(ii=0; ii<pSet->nIter; ii++){
fts5MultiIterFree(pSet->apIter[ii]);
}
sqlite3_free(pSet->aPoslistReader);
sqlite3_free(pSet->aMap);
sqlite3_free(pSet);
}
}
/*
** Append a mapping to the token-map belonging to object pT.
*/
static void fts5TokendataIterAppendMap(
Fts5Index *p,
Fts5TokenDataIter *pT,
int iIter,
i64 iRowid,
i64 iPos
){
if( p->rc==SQLITE_OK ){
if( pT->nMap==pT->nMapAlloc ){
int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
int nByte = nNew * sizeof(Fts5TokenDataMap);
Fts5TokenDataMap *aNew;
aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
if( aNew==0 ){
p->rc = SQLITE_NOMEM;
return;
}
pT->aMap = aNew;
pT->nMapAlloc = nNew;
}
pT->aMap[pT->nMap].iRowid = iRowid;
pT->aMap[pT->nMap].iPos = iPos;
pT->aMap[pT->nMap].iIter = iIter;
pT->nMap++;
}
}
/*
** The iterator passed as the only argument must be a tokendata=1 iterator
** (pIter->pTokenDataIter!=0). This function sets the iterator output
** variables (pIter->base.*) according to the contents of the current
** row.
*/
static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
int ii;
int nHit = 0;
i64 iRowid = SMALLEST_INT64;
int iMin = 0;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
pIter->base.nData = 0;
pIter->base.pData = 0;
for(ii=0; ii<pT->nIter; ii++){
Fts5Iter *p = pT->apIter[ii];
if( p->base.bEof==0 ){
if( nHit==0 || p->base.iRowid<iRowid ){
iRowid = p->base.iRowid;
nHit = 1;
pIter->base.pData = p->base.pData;
pIter->base.nData = p->base.nData;
iMin = ii;
}else if( p->base.iRowid==iRowid ){
nHit++;
}
}
}
if( nHit==0 ){
pIter->base.bEof = 1;
}else{
int eDetail = pIter->pIndex->pConfig->eDetail;
pIter->base.bEof = 0;
pIter->base.iRowid = iRowid;
if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1);
}else
if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
int nReader = 0;
int nByte = 0;
i64 iPrev = 0;
/* Allocate array of iterators if they are not already allocated. */
if( pT->aPoslistReader==0 ){
pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero(
&pIter->pIndex->rc,
pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int))
);
if( pT->aPoslistReader==0 ) return;
pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter];
}
/* Populate an iterator for each poslist that will be merged */
for(ii=0; ii<pT->nIter; ii++){
Fts5Iter *p = pT->apIter[ii];
if( iRowid==p->base.iRowid ){
pT->aPoslistToIter[nReader] = ii;
sqlite3Fts5PoslistReaderInit(
p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++]
);
nByte += p->base.nData;
}
}
/* Ensure the output buffer is large enough */
if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){
return;
}
/* Ensure the token-mapping is large enough */
if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
int nNew = (pT->nMapAlloc + nByte) * 2;
Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
pT->aMap, nNew*sizeof(Fts5TokenDataMap)
);
if( aNew==0 ){
pIter->pIndex->rc = SQLITE_NOMEM;
return;
}
pT->aMap = aNew;
pT->nMapAlloc = nNew;
}
pIter->poslist.n = 0;
while( 1 ){
i64 iMinPos = LARGEST_INT64;
/* Find smallest position */
iMin = 0;
for(ii=0; ii<nReader; ii++){
Fts5PoslistReader *pReader = &pT->aPoslistReader[ii];
if( pReader->bEof==0 ){
if( pReader->iPos<iMinPos ){
iMinPos = pReader->iPos;
iMin = ii;
}
}
}
/* If all readers were at EOF, break out of the loop. */
if( iMinPos==LARGEST_INT64 ) break;
sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos);
sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]);
if( eDetail==FTS5_DETAIL_FULL ){
pT->aMap[pT->nMap].iPos = iMinPos;
pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin];
pT->aMap[pT->nMap].iRowid = iRowid;
pT->nMap++;
}
}
pIter->base.pData = pIter->poslist.p;
pIter->base.nData = pIter->poslist.n;
}
}
}
/*
** The iterator passed as the only argument must be a tokendata=1 iterator
** (pIter->pTokenDataIter!=0). This function advances the iterator. If
** argument bFrom is false, then the iterator is advanced to the next
** entry. Or, if bFrom is true, it is advanced to the first entry with
** a rowid of iFrom or greater.
*/
static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
int ii;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
for(ii=0; ii<pT->nIter; ii++){
Fts5Iter *p = pT->apIter[ii];
if( p->base.bEof==0
&& (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
){
fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
while( bFrom && p->base.bEof==0
&& p->base.iRowid<iFrom
&& p->pIndex->rc==SQLITE_OK
){
fts5MultiIterNext(p->pIndex, p, 0, 0);
}
}
}
fts5IterSetOutputsTokendata(pIter);
}
/*
** If the segment-iterator passed as the first argument is at EOF, then
** set pIter->term to a copy of buffer pTerm.
*/
static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){
if( pIter && pIter->aSeg[0].pLeaf==0 ){
fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p);
}
}
/*
** This function sets up an iterator to use for a non-prefix query on a
** tokendata=1 table.
*/
static Fts5Iter *fts5SetupTokendataIter(
Fts5Index *p, /* FTS index to query */
const u8 *pToken, /* Buffer containing query term */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset /* Colset to filter on */
){
Fts5Iter *pRet = 0;
Fts5TokenDataIter *pSet = 0;
Fts5Structure *pStruct = 0;
const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN;
Fts5Buffer bSeek = {0, 0, 0};
Fts5Buffer *pSmall = 0;
fts5IndexFlush(p);
pStruct = fts5StructureRead(p);
while( p->rc==SQLITE_OK ){
Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0;
Fts5Iter *pNew = 0;
Fts5SegIter *pNewIter = 0;
Fts5SegIter *pPrevIter = 0;
int iLvl, iSeg, ii;
pNew = fts5MultiIterAlloc(p, pStruct->nSegment);
if( pSmall ){
fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p);
fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0");
}else{
fts5BufferSet(&p->rc, &bSeek, nToken, pToken);
}
if( p->rc ){
sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
break;
}
pNewIter = &pNew->aSeg[0];
pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0);
for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){
Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
int bDone = 0;
if( pPrevIter ){
if( fts5BufferCompare(pSmall, &pPrevIter->term) ){
memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter));
memset(pPrevIter, 0, sizeof(Fts5SegIter));
bDone = 1;
}else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){
fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter);
bDone = 1;
}
}
if( bDone==0 ){
fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter);
}
if( pPrevIter ){
if( pPrevIter->pTombArray ){
pNewIter->pTombArray = pPrevIter->pTombArray;
pNewIter->pTombArray->nRef++;
}
}else{
fts5SegIterAllocTombstone(p, pNewIter);
}
pNewIter++;
if( pPrevIter ) pPrevIter++;
if( p->rc ) break;
}
}
fts5TokendataSetTermIfEof(pPrev, pSmall);
pNew->bSkipEmpty = 1;
pNew->pColset = pColset;
fts5IterSetOutputCb(&p->rc, pNew);
/* Loop through all segments in the new iterator. Find the smallest
** term that any segment-iterator points to. Iterator pNew will be
** used for this term. Also, set any iterator that points to a term that
** does not match pToken/nToken to point to EOF */
pSmall = 0;
for(ii=0; ii<pNew->nSeg; ii++){
Fts5SegIter *pII = &pNew->aSeg[ii];
if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){
fts5SegIterSetEOF(pII);
}
if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){
pSmall = &pII->term;
}
}
/* If pSmall is still NULL at this point, then the new iterator does
** not point to any terms that match the query. So delete it and break
** out of the loop - all required iterators have been collected. */
if( pSmall==0 ){
sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
break;
}
/* Append this iterator to the set and continue. */
pSet = fts5AppendTokendataIter(p, pSet, pNew);
}
if( p->rc==SQLITE_OK && pSet ){
int ii;
for(ii=0; ii<pSet->nIter; ii++){
Fts5Iter *pIter = pSet->apIter[ii];
int iSeg;
for(iSeg=0; iSeg<pIter->nSeg; iSeg++){
pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM;
}
fts5MultiIterFinishSetup(p, pIter);
}
}
if( p->rc==SQLITE_OK ){
pRet = fts5MultiIterAlloc(p, 0);
}
if( pRet ){
pRet->pTokenDataIter = pSet;
if( pSet ){
fts5IterSetOutputsTokendata(pRet);
}else{
pRet->base.bEof = 1;
}
}else{
fts5TokendataIterDelete(pSet);
}
fts5StructureRelease(pStruct);
fts5BufferFree(&bSeek);
return pRet;
}
/*
** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
*/
static int sqlite3Fts5IndexQuery(
Fts5Index *p, /* FTS index to query */
|
| ︙ | ︙ | |||
242816 242817 242818 242819 242820 242821 242822 242823 242824 242825 242826 242827 242828 242829 242830 |
/* If the QUERY_SCAN flag is set, all other flags must be clear. */
assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
** satisfied by scanning multiple terms in the main index.
**
** If the QUERY_TEST_NOIDX flag was specified, then this must be a
| > > > > > | 245565 245566 245567 245568 245569 245570 245571 245572 245573 245574 245575 245576 245577 245578 245579 245580 245581 245582 245583 245584 |
/* If the QUERY_SCAN flag is set, all other flags must be clear. */
assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
int bTokendata = pConfig->bTokendata;
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
bTokendata = 0;
}
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
** satisfied by scanning multiple terms in the main index.
**
** If the QUERY_TEST_NOIDX flag was specified, then this must be a
|
| ︙ | ︙ | |||
242843 242844 242845 242846 242847 242848 242849 |
for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
int nIdxChar = pConfig->aPrefix[iIdx-1];
if( nIdxChar==nChar ) break;
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
| > > > | | 245597 245598 245599 245600 245601 245602 245603 245604 245605 245606 245607 245608 245609 245610 245611 245612 245613 245614 |
for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
int nIdxChar = pConfig->aPrefix[iIdx-1];
if( nIdxChar==nChar ) break;
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
if( bTokendata && iIdx==0 ){
buf.p[0] = '0';
pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
}else if( iIdx<=pConfig->nPrefix ){
/* Straight index lookup */
Fts5Structure *pStruct = fts5StructureRead(p);
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
if( pStruct ){
fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
pColset, buf.p, nToken+1, -1, 0, &pRet
);
|
| ︙ | ︙ | |||
242890 242891 242892 242893 242894 242895 242896 |
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
| > > > | > | 245647 245648 245649 245650 245651 245652 245653 245654 245655 245656 245657 245658 245659 245660 245661 245662 245663 245664 245665 |
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
if( pIter->pTokenDataIter ){
fts5TokendataIterNext(pIter, 0, 0);
}else{
fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
}
return fts5IndexReturn(pIter->pIndex);
}
/*
** Move to the next matching term/rowid. Used by the fts5vocab module.
*/
static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
|
| ︙ | ︙ | |||
242923 242924 242925 242926 242927 242928 242929 |
/*
** Move to the next matching rowid that occurs at or after iMatch. The
** definition of "at or after" depends on whether this iterator iterates
** in ascending or descending rowid order.
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
| > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 245684 245685 245686 245687 245688 245689 245690 245691 245692 245693 245694 245695 245696 245697 245698 245699 245700 245701 245702 245703 245704 245705 245706 245707 245708 245709 245710 245711 245712 245713 245714 245715 245716 245717 245718 245719 245720 245721 245722 245723 245724 245725 245726 245727 245728 245729 245730 245731 245732 245733 245734 245735 245736 245737 245738 245739 245740 245741 245742 245743 245744 245745 245746 245747 245748 245749 245750 245751 245752 245753 245754 245755 245756 245757 245758 245759 245760 245761 245762 245763 245764 245765 245766 245767 245768 245769 245770 245771 245772 245773 245774 245775 245776 245777 245778 245779 245780 245781 245782 245783 245784 245785 245786 245787 245788 245789 245790 245791 245792 245793 245794 245795 245796 245797 245798 245799 245800 245801 245802 245803 245804 245805 245806 245807 245808 245809 245810 245811 245812 245813 245814 245815 245816 245817 |
/*
** Move to the next matching rowid that occurs at or after iMatch. The
** definition of "at or after" depends on whether this iterator iterates
** in ascending or descending rowid order.
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
if( pIter->pTokenDataIter ){
fts5TokendataIterNext(pIter, 1, iMatch);
}else{
fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
}
return fts5IndexReturn(pIter->pIndex);
}
/*
** Return the current term.
*/
static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
int n;
const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
assert_nc( z || n<=1 );
*pn = n-1;
return (z ? &z[1] : 0);
}
/*
** This is used by xInstToken() to access the token at offset iOff, column
** iCol of row iRowid. The token is returned via output variables *ppOut
** and *pnOut. The iterator passed as the first argument must be a tokendata=1
** iterator (pIter->pTokenDataIter!=0).
*/
static int sqlite3Fts5IterToken(
Fts5IndexIter *pIndexIter,
i64 iRowid,
int iCol,
int iOff,
const char **ppOut, int *pnOut
){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
Fts5TokenDataMap *aMap = pT->aMap;
i64 iPos = (((i64)iCol)<<32) + iOff;
int i1 = 0;
int i2 = pT->nMap;
int iTest = 0;
while( i2>i1 ){
iTest = (i1 + i2) / 2;
if( aMap[iTest].iRowid<iRowid ){
i1 = iTest+1;
}else if( aMap[iTest].iRowid>iRowid ){
i2 = iTest;
}else{
if( aMap[iTest].iPos<iPos ){
if( aMap[iTest].iPos<0 ){
break;
}
i1 = iTest+1;
}else if( aMap[iTest].iPos>iPos ){
i2 = iTest;
}else{
break;
}
}
}
if( i2>i1 ){
Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
*ppOut = (const char*)pMap->aSeg[0].term.p+1;
*pnOut = pMap->aSeg[0].term.n-1;
}
return SQLITE_OK;
}
/*
** Clear any existing entries from the token-map associated with the
** iterator passed as the only argument.
*/
static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
if( pIter && pIter->pTokenDataIter ){
pIter->pTokenDataIter->nMap = 0;
}
}
/*
** Set a token-mapping for the iterator passed as the first argument. This
** is used in detail=column or detail=none mode when a token is requested
** using the xInstToken() API. In this case the caller tokenizers the
** current row and configures the token-mapping via multiple calls to this
** function.
*/
static int sqlite3Fts5IndexIterWriteTokendata(
Fts5IndexIter *pIndexIter,
const char *pToken, int nToken,
i64 iRowid, int iCol, int iOff
){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
Fts5Index *p = pIter->pIndex;
int ii;
assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
assert( pIter->pTokenDataIter );
for(ii=0; ii<pT->nIter; ii++){
Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
}
if( ii<pT->nIter ){
fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff);
}
return fts5IndexReturn(p);
}
/*
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/
static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
if( pIndexIter ){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5Index *pIndex = pIter->pIndex;
fts5TokendataIterDelete(pIter->pTokenDataIter);
fts5MultiIterFree(pIter);
sqlite3Fts5IndexCloseReader(pIndex);
}
}
/*
** Read and decode the "averages" record from the database.
|
| ︙ | ︙ | |||
243452 243453 243454 243455 243456 243457 243458 |
int n, /* Size of index key in bytes */
int flags, /* Flags for Fts5IndexQuery */
u64 *pCksum /* IN/OUT: Checksum value */
){
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
Fts5IndexIter *pIter = 0;
| | > > | 246311 246312 246313 246314 246315 246316 246317 246318 246319 246320 246321 246322 246323 246324 246325 246326 246327 |
int n, /* Size of index key in bytes */
int flags, /* Flags for Fts5IndexQuery */
u64 *pCksum /* IN/OUT: Checksum value */
){
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
Fts5IndexIter *pIter = 0;
int rc = sqlite3Fts5IndexQuery(
p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter
);
while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){
i64 rowid = pIter->iRowid;
if( eDetail==FTS5_DETAIL_NONE ){
cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
}else{
|
| ︙ | ︙ | |||
243619 243620 243621 243622 243623 243624 243625 |
if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
}
fts5DataRelease(pLeaf);
}
}
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
| | | | 246480 246481 246482 246483 246484 246485 246486 246487 246488 246489 246490 246491 246492 246493 246494 246495 246496 246497 246498 246499 246500 246501 246502 246503 |
if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
}
fts5DataRelease(pLeaf);
}
}
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
i64 iTermOff = 0;
int ii;
Fts5Buffer buf1 = {0,0,0};
Fts5Buffer buf2 = {0,0,0};
ii = pLeaf->szLeaf;
while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
int res;
i64 iOff;
int nIncr;
ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
iTermOff += nIncr;
iOff = iTermOff;
if( iOff>=pLeaf->szLeaf ){
|
| ︙ | ︙ | |||
244149 244150 244151 244152 244153 244154 244155 244156 244157 244158 244159 244160 244161 244162 |
}
}
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
}
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_decode().
*/
static void fts5DecodeFunction(
sqlite3_context *pCtx, /* Function call context */
| > > > > > > > > > > > > > > > > > > | 247010 247011 247012 247013 247014 247015 247016 247017 247018 247019 247020 247021 247022 247023 247024 247025 247026 247027 247028 247029 247030 247031 247032 247033 247034 247035 247036 247037 247038 247039 247040 247041 |
}
}
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
}
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){
int ii;
fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1);
if( *pRc==SQLITE_OK ){
for(ii=0; ii<pTerm->n; ii++){
if( pTerm->p[ii]==0x00 ){
pBuf->p[pBuf->n++] = '\\';
pBuf->p[pBuf->n++] = '0';
}else{
pBuf->p[pBuf->n++] = pTerm->p[ii];
}
}
pBuf->p[pBuf->n] = 0x00;
}
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_decode().
*/
static void fts5DecodeFunction(
sqlite3_context *pCtx, /* Function call context */
|
| ︙ | ︙ | |||
244257 244258 244259 244260 244261 244262 244263 |
while( iOff<szLeaf && rc==SQLITE_OK ){
int nAppend;
/* Read the term data for the next term*/
iOff += fts5GetVarint32(&a[iOff], nAppend);
term.n = nKeep;
fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
| | | < | 247136 247137 247138 247139 247140 247141 247142 247143 247144 247145 247146 247147 247148 247149 247150 247151 |
while( iOff<szLeaf && rc==SQLITE_OK ){
int nAppend;
/* Read the term data for the next term*/
iOff += fts5GetVarint32(&a[iOff], nAppend);
term.n = nKeep;
fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
fts5BufferAppendTerm(&rc, &s, &term);
iOff += nAppend;
/* Figure out where the doclist for this term ends */
if( iPgidxOff<n ){
int nIncr;
iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr);
iTermOff += nIncr;
|
| ︙ | ︙ | |||
244367 244368 244369 244370 244371 244372 244373 |
if( iOff+nByte>n ){
rc = FTS5_CORRUPT;
break;
}
fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
iOff += nByte;
| | | < | 247245 247246 247247 247248 247249 247250 247251 247252 247253 247254 247255 247256 247257 247258 247259 247260 |
if( iOff+nByte>n ){
rc = FTS5_CORRUPT;
break;
}
fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
iOff += nByte;
sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
fts5BufferAppendTerm(&rc, &s, &term);
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
}
fts5BufferFree(&term);
}
decode_out:
|
| ︙ | ︙ | |||
244844 244845 244846 244847 244848 244849 244850 |
struct Fts5FullTable {
Fts5Table p; /* Public class members from fts5Int.h */
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
int iSavepoint; /* Successful xSavepoint()+1 */
| | | 247721 247722 247723 247724 247725 247726 247727 247728 247729 247730 247731 247732 247733 247734 247735 |
struct Fts5FullTable {
Fts5Table p; /* Public class members from fts5Int.h */
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
int iSavepoint; /* Successful xSavepoint()+1 */
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
};
struct Fts5MatchPhrase {
Fts5Buffer *pPoslist; /* Pointer to current poslist */
|
| ︙ | ︙ | |||
245382 245383 245384 245385 245386 245387 245388 |
bSeenGt = 1;
}
}
}
}
idxStr[iIdxStr] = '\0';
| | > > > | | 248259 248260 248261 248262 248263 248264 248265 248266 248267 248268 248269 248270 248271 248272 248273 248274 248275 248276 248277 248278 248279 248280 248281 |
bSeenGt = 1;
}
}
}
}
idxStr[iIdxStr] = '\0';
/* Set idxFlags flags for the ORDER BY clause
**
** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC".
*/
if( pInfo->nOrderBy==1 ){
int iSort = pInfo->aOrderBy[0].iColumn;
if( iSort==(pConfig->nCol+1) && bSeenMatch ){
idxFlags |= FTS5_BI_ORDER_RANK;
}else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){
idxFlags |= FTS5_BI_ORDER_ROWID;
}
if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
pInfo->orderByConsumed = 1;
if( pInfo->aOrderBy[0].desc ){
idxFlags |= FTS5_BI_ORDER_DESC;
}
|
| ︙ | ︙ | |||
245638 245639 245640 245641 245642 245643 245644 245645 245646 245647 245648 245649 245650 245651 |
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc;
assert( (pCsr->ePlan<3)==
(pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
);
assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
if( pCsr->ePlan<3 ){
int bSkip = 0;
if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
fts5CsrNewrow(pCsr);
| > > > > > > > > > > | 248518 248519 248520 248521 248522 248523 248524 248525 248526 248527 248528 248529 248530 248531 248532 248533 248534 248535 248536 248537 248538 248539 248540 248541 |
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc;
assert( (pCsr->ePlan<3)==
(pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
);
assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
/* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table,
** clear any token mappings accumulated at the fts5_index.c level. In
** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH,
** we need to retain the mappings for the entire query. */
if( pCsr->ePlan==FTS5_PLAN_MATCH
&& ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata
){
sqlite3Fts5ExprClearTokens(pCsr->pExpr);
}
if( pCsr->ePlan<3 ){
int bSkip = 0;
if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
fts5CsrNewrow(pCsr);
|
| ︙ | ︙ | |||
246299 246300 246301 246302 246303 246304 246305 |
#ifdef SQLITE_DEBUG
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
}else if( 0==sqlite3_stricmp("flush", zCmd) ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
| > > | > | 249189 249190 249191 249192 249193 249194 249195 249196 249197 249198 249199 249200 249201 249202 249203 249204 249205 249206 |
#ifdef SQLITE_DEBUG
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
}else if( 0==sqlite3_stricmp("flush", zCmd) ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
rc = sqlite3Fts5FlushToDisk(&pTab->p);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
}
if( rc==SQLITE_OK ){
if( bError ){
rc = SQLITE_ERROR;
}else{
|
| ︙ | ︙ | |||
246624 246625 246626 246627 246628 246629 246630 |
Fts5Context *pCtx,
int iCol,
const char **pz,
int *pn
){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
| > > > | | 249517 249518 249519 249520 249521 249522 249523 249524 249525 249526 249527 249528 249529 249530 249531 249532 249533 249534 |
Fts5Context *pCtx,
int iCol,
const char **pz,
int *pn
){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
if( iCol<0 || iCol>=pTab->pConfig->nCol ){
rc = SQLITE_RANGE;
}else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
|| pCsr->ePlan==FTS5_PLAN_SPECIAL
){
*pz = 0;
*pn = 0;
}else{
rc = fts5SeekCursor(pCsr, 0);
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
246649 246650 246651 246652 246653 246654 246655 |
const u8 **pa,
int *pn
){
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
int rc = SQLITE_OK;
int bLive = (pCsr->pSorter==0);
| > > | < | 249545 249546 249547 249548 249549 249550 249551 249552 249553 249554 249555 249556 249557 249558 249559 249560 249561 |
const u8 **pa,
int *pn
){
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
int rc = SQLITE_OK;
int bLive = (pCsr->pSorter==0);
if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
rc = SQLITE_RANGE;
}else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
Fts5PoslistPopulator *aPopulator;
int i;
aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
if( aPopulator==0 ) rc = SQLITE_NOMEM;
for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
int n; const char *z;
|
| ︙ | ︙ | |||
246674 246675 246676 246677 246678 246679 246680 |
if( pCsr->pSorter ){
sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
}
}
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
}
| > | | | | | | | | > > > > > | 249571 249572 249573 249574 249575 249576 249577 249578 249579 249580 249581 249582 249583 249584 249585 249586 249587 249588 249589 249590 249591 249592 249593 249594 249595 249596 249597 249598 |
if( pCsr->pSorter ){
sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
}
}
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
}
if( rc==SQLITE_OK ){
if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
Fts5Sorter *pSorter = pCsr->pSorter;
int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
*pn = pSorter->aIdx[iPhrase] - i1;
*pa = &pSorter->aPoslist[i1];
}else{
*pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
}
}else{
*pa = 0;
*pn = 0;
}
return rc;
}
/*
** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated
** correctly for the current view. Return SQLITE_OK if successful, or an
|
| ︙ | ︙ | |||
246789 246790 246791 246792 246793 246794 246795 |
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
int rc = SQLITE_OK;
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
|| SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
){
if( iIdx<0 || iIdx>=pCsr->nInstCount ){
rc = SQLITE_RANGE;
| < < < < < < | 249692 249693 249694 249695 249696 249697 249698 249699 249700 249701 249702 249703 249704 249705 |
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
int rc = SQLITE_OK;
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
|| SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
){
if( iIdx<0 || iIdx>=pCsr->nInstCount ){
rc = SQLITE_RANGE;
}else{
*piPhrase = pCsr->aInst[iIdx*3];
*piCol = pCsr->aInst[iIdx*3 + 1];
*piOff = pCsr->aInst[iIdx*3 + 2];
}
}
return rc;
|
| ︙ | ︙ | |||
247049 247050 247051 247052 247053 247054 247055 247056 247057 247058 247059 247060 247061 |
}
}
}
return rc;
}
static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
);
static const Fts5ExtensionApi sFts5Api = {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 249946 249947 249948 249949 249950 249951 249952 249953 249954 249955 249956 249957 249958 249959 249960 249961 249962 249963 249964 249965 249966 249967 249968 249969 249970 249971 249972 249973 249974 249975 249976 249977 249978 249979 249980 249981 249982 249983 249984 249985 249986 249987 249988 249989 249990 249991 249992 249993 249994 249995 249996 249997 249998 249999 250000 250001 250002 250003 250004 250005 250006 250007 250008 250009 250010 250011 250012 250013 250014 250015 250016 250017 250018 250019 250020 250021 250022 250023 250024 250025 250026 250027 250028 250029 250030 |
}
}
}
return rc;
}
/*
** xQueryToken() API implemenetation.
*/
static int fts5ApiQueryToken(
Fts5Context* pCtx,
int iPhrase,
int iToken,
const char **ppOut,
int *pnOut
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut);
}
/*
** xInstToken() API implemenetation.
*/
static int fts5ApiInstToken(
Fts5Context *pCtx,
int iIdx,
int iToken,
const char **ppOut, int *pnOut
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
int rc = SQLITE_OK;
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
|| SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
){
if( iIdx<0 || iIdx>=pCsr->nInstCount ){
rc = SQLITE_RANGE;
}else{
int iPhrase = pCsr->aInst[iIdx*3];
int iCol = pCsr->aInst[iIdx*3 + 1];
int iOff = pCsr->aInst[iIdx*3 + 2];
i64 iRowid = fts5CursorRowid(pCsr);
rc = sqlite3Fts5ExprInstToken(
pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut
);
}
}
return rc;
}
static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
);
static const Fts5ExtensionApi sFts5Api = {
3, /* iVersion */
fts5ApiUserData,
fts5ApiColumnCount,
fts5ApiRowCount,
fts5ApiColumnTotalSize,
fts5ApiTokenize,
fts5ApiPhraseCount,
fts5ApiPhraseSize,
fts5ApiInstCount,
fts5ApiInst,
fts5ApiRowid,
fts5ApiColumnText,
fts5ApiColumnSize,
fts5ApiQueryPhrase,
fts5ApiSetAuxdata,
fts5ApiGetAuxdata,
fts5ApiPhraseFirst,
fts5ApiPhraseNext,
fts5ApiPhraseFirstColumn,
fts5ApiPhraseNextColumn,
fts5ApiQueryToken,
fts5ApiInstToken
};
/*
** Implementation of API function xQueryPhrase().
*/
static int fts5ApiQueryPhrase(
Fts5Context *pCtx,
|
| ︙ | ︙ | |||
247341 247342 247343 247344 247345 247346 247347 |
*/
static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
int rc;
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
| < < | | < < < < < < < < < < < < | | | < < | 250283 250284 250285 250286 250287 250288 250289 250290 250291 250292 250293 250294 250295 250296 250297 250298 250299 250300 250301 250302 250303 250304 250305 250306 250307 250308 250309 250310 250311 250312 250313 250314 250315 250316 250317 250318 250319 |
*/
static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
int rc;
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
return rc;
}
static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
fts5TripCursors((Fts5FullTable*)pTab);
return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage);
}
/*
** The xSavepoint() method.
**
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint+1;
}
return rc;
}
/*
** The xRelease() method.
**
** This is a no-op.
|
| ︙ | ︙ | |||
247411 247412 247413 247414 247415 247416 247417 |
** Discard the contents of the pending terms table.
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
| < > | 250337 250338 250339 250340 250341 250342 250343 250344 250345 250346 250347 250348 250349 250350 250351 250352 |
** Discard the contents of the pending terms table.
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
if( (iSavepoint+1)<=pTab->iSavepoint ){
pTab->p.pConfig->pgsz = 0;
rc = sqlite3Fts5StorageRollback(pTab->pStorage);
}
return rc;
}
/*
** Register a new auxiliary function with global context pGlobal.
|
| ︙ | ︙ | |||
247617 247618 247619 247620 247621 247622 247623 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
| | | 250543 250544 250545 250546 250547 250548 250549 250550 250551 250552 250553 250554 250555 250556 250557 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
}
/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){
|
| ︙ | ︙ | |||
247640 247641 247642 247643 247644 247645 247646 | } /* ** Run an integrity check on the FTS5 data structures. Return a string ** if anything is found amiss. Return a NULL pointer if everything is ** OK. */ | | < < < > | < < < < < | > | | 250566 250567 250568 250569 250570 250571 250572 250573 250574 250575 250576 250577 250578 250579 250580 250581 250582 250583 250584 250585 250586 250587 250588 250589 250590 250591 250592 250593 250594 250595 250596 250597 250598 250599 250600 250601 250602 |
}
/*
** Run an integrity check on the FTS5 data structures. Return a string
** if anything is found amiss. Return a NULL pointer if everything is
** OK.
*/
static int fts5IntegrityMethod(
sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */
const char *zSchema, /* Name of schema in which this table lives */
const char *zTabname, /* Name of the table itself */
int isQuick, /* True if this is a quick-check */
char **pzErr /* Write error message here */
){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc;
assert( pzErr!=0 && *pzErr==0 );
UNUSED_PARAM(isQuick);
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
zSchema, zTabname);
}else if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS5 table %s.%s: %s",
zSchema, zTabname, sqlite3_errstr(rc));
}
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
return SQLITE_OK;
}
static int fts5Init(sqlite3 *db){
static const sqlite3_module fts5Mod = {
/* iVersion */ 4,
/* xCreate */ fts5CreateMethod,
|
| ︙ | ︙ | |||
247698 247699 247700 247701 247702 247703 247704 |
/* xRollback */ fts5RollbackMethod,
/* xFindFunction */ fts5FindFunctionMethod,
/* xRename */ fts5RenameMethod,
/* xSavepoint */ fts5SavepointMethod,
/* xRelease */ fts5ReleaseMethod,
/* xRollbackTo */ fts5RollbackToMethod,
/* xShadowName */ fts5ShadowName,
| | | 250618 250619 250620 250621 250622 250623 250624 250625 250626 250627 250628 250629 250630 250631 250632 |
/* xRollback */ fts5RollbackMethod,
/* xFindFunction */ fts5FindFunctionMethod,
/* xRename */ fts5RenameMethod,
/* xSavepoint */ fts5SavepointMethod,
/* xRelease */ fts5ReleaseMethod,
/* xRollbackTo */ fts5RollbackToMethod,
/* xShadowName */ fts5ShadowName,
/* xIntegrity */ fts5IntegrityMethod
};
int rc;
Fts5Global *pGlobal = 0;
pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
if( pGlobal==0 ){
|
| ︙ | ︙ | |||
248464 248465 248466 248467 248468 248469 248470 |
ctx.pStorage = p;
rc = sqlite3Fts5StorageDeleteAll(p);
if( rc==SQLITE_OK ){
rc = fts5StorageLoadTotals(p, 1);
}
if( rc==SQLITE_OK ){
| | | 251384 251385 251386 251387 251388 251389 251390 251391 251392 251393 251394 251395 251396 251397 251398 |
ctx.pStorage = p;
rc = sqlite3Fts5StorageDeleteAll(p);
if( rc==SQLITE_OK ){
rc = fts5StorageLoadTotals(p, 1);
}
if( rc==SQLITE_OK ){
rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
i64 iRowid = sqlite3_column_int64(pScan, 0);
sqlite3Fts5BufferZero(&buf);
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
|
| ︙ | ︙ | |||
249250 249251 249252 249253 249254 249255 249256 249257 249258 249259 249260 249261 249262 249263 |
*zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \
*zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \
*zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
} \
}
#endif /* ifndef SQLITE_AMALGAMATION */
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
unsigned char aTokenChar[128]; /* ASCII range token characters */
char *aFold; /* Buffer to fold text into */
int nFold; /* Size of aFold[] in bytes */
int eRemoveDiacritic; /* True if remove_diacritics=1 is set */
| > > > > > > | 252170 252171 252172 252173 252174 252175 252176 252177 252178 252179 252180 252181 252182 252183 252184 252185 252186 252187 252188 252189 |
*zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \
*zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \
*zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
} \
}
#endif /* ifndef SQLITE_AMALGAMATION */
#define FTS5_SKIP_UTF8(zIn) { \
if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \
while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \
} \
}
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
unsigned char aTokenChar[128]; /* ASCII range token characters */
char *aFold; /* Buffer to fold text into */
int nFold; /* Size of aFold[] in bytes */
int eRemoveDiacritic; /* True if remove_diacritics=1 is set */
|
| ︙ | ︙ | |||
250286 250287 250288 250289 250290 250291 250292 250293 250294 250295 250296 250297 250298 250299 |
/**************************************************************************
** Start of trigram implementation.
*/
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
int bFold; /* True to fold to lower-case */
};
/*
** Free a trigram tokenizer.
*/
static void fts5TriDelete(Fts5Tokenizer *p){
sqlite3_free(p);
| > | 253212 253213 253214 253215 253216 253217 253218 253219 253220 253221 253222 253223 253224 253225 253226 |
/**************************************************************************
** Start of trigram implementation.
*/
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
int bFold; /* True to fold to lower-case */
int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */
};
/*
** Free a trigram tokenizer.
*/
static void fts5TriDelete(Fts5Tokenizer *p){
sqlite3_free(p);
|
| ︙ | ︙ | |||
250312 250313 250314 250315 250316 250317 250318 250319 250320 250321 250322 250323 250324 250325 250326 250327 250328 250329 250330 250331 250332 250333 250334 250335 250336 250337 |
TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
UNUSED_PARAM(pUnused);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
int i;
pNew->bFold = 1;
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
rc = SQLITE_ERROR;
}else{
pNew->bFold = (zArg[0]=='0');
}
}else{
rc = SQLITE_ERROR;
}
}
if( rc!=SQLITE_OK ){
fts5TriDelete((Fts5Tokenizer*)pNew);
pNew = 0;
}
}
*ppOut = (Fts5Tokenizer*)pNew;
return rc;
| > > > > > > > > > > > > | 253239 253240 253241 253242 253243 253244 253245 253246 253247 253248 253249 253250 253251 253252 253253 253254 253255 253256 253257 253258 253259 253260 253261 253262 253263 253264 253265 253266 253267 253268 253269 253270 253271 253272 253273 253274 253275 253276 |
TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
UNUSED_PARAM(pUnused);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
int i;
pNew->bFold = 1;
pNew->iFoldParam = 0;
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
rc = SQLITE_ERROR;
}else{
pNew->bFold = (zArg[0]=='0');
}
}else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
rc = SQLITE_ERROR;
}else{
pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
}
}else{
rc = SQLITE_ERROR;
}
}
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
rc = SQLITE_ERROR;
}
if( rc!=SQLITE_OK ){
fts5TriDelete((Fts5Tokenizer*)pNew);
pNew = 0;
}
}
*ppOut = (Fts5Tokenizer*)pNew;
return rc;
|
| ︙ | ︙ | |||
250346 250347 250348 250349 250350 250351 250352 250353 250354 250355 250356 250357 250358 |
int unusedFlags,
const char *pText, int nText,
int (*xToken)(void*, int, const char*, int, int, int)
){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = &zIn[nText];
u32 iCode;
UNUSED_PARAM(unusedFlags);
while( 1 ){
| > > > > > > > > > > > > > > > > > > > > > > > > | | < | > > | | < < | | | > > | < < | | | > | | < | | < < | > | < > | | 253285 253286 253287 253288 253289 253290 253291 253292 253293 253294 253295 253296 253297 253298 253299 253300 253301 253302 253303 253304 253305 253306 253307 253308 253309 253310 253311 253312 253313 253314 253315 253316 253317 253318 253319 253320 253321 253322 253323 253324 253325 253326 253327 253328 253329 253330 253331 253332 253333 253334 253335 253336 253337 253338 253339 253340 253341 253342 253343 253344 253345 253346 253347 253348 253349 253350 253351 253352 253353 253354 |
int unusedFlags,
const char *pText, int nText,
int (*xToken)(void*, int, const char*, int, int, int)
){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
char *zOut = aBuf;
int ii;
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = &zIn[nText];
u32 iCode;
int aStart[3]; /* Input offset of each character in aBuf[] */
UNUSED_PARAM(unusedFlags);
/* Populate aBuf[] with the characters for the first trigram. */
for(ii=0; ii<3; ii++){
do {
aStart[ii] = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) return SQLITE_OK;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
WRITE_UTF8(zOut, iCode);
}
/* At the start of each iteration of this loop:
**
** aBuf: Contains 3 characters. The 3 characters of the next trigram.
** zOut: Points to the byte following the last character in aBuf.
** aStart[3]: Contains the byte offset in the input text corresponding
** to the start of each of the three characters in the buffer.
*/
assert( zIn<=zEof );
while( 1 ){
int iNext; /* Start of character following current tri */
const char *z1;
/* Read characters from the input up until the first non-diacritic */
do {
iNext = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
/* Pass the current trigram back to fts5 */
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
if( iCode==0 || rc!=SQLITE_OK ) break;
/* Remove the first character from buffer aBuf[]. Append the character
** with codepoint iCode. */
z1 = aBuf;
FTS5_SKIP_UTF8(z1);
memmove(aBuf, z1, zOut - z1);
zOut -= (z1 - aBuf);
WRITE_UTF8(zOut, iCode);
/* Update the aStart[] array */
aStart[0] = aStart[1];
aStart[1] = aStart[2];
aStart[2] = iNext;
}
return rc;
}
/*
** Argument xCreate is a pointer to a constructor function for a tokenizer.
|
| ︙ | ︙ | |||
250402 250403 250404 250405 250406 250407 250408 |
*/
static int sqlite3Fts5TokenizerPattern(
int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
Fts5Tokenizer *pTok
){
if( xCreate==fts5TriCreate ){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
| > | > | 253363 253364 253365 253366 253367 253368 253369 253370 253371 253372 253373 253374 253375 253376 253377 253378 253379 |
*/
static int sqlite3Fts5TokenizerPattern(
int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
Fts5Tokenizer *pTok
){
if( xCreate==fts5TriCreate ){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
if( p->iFoldParam==0 ){
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
}
}
return FTS5_PATTERN_NONE;
}
/*
** Register all built-in tokenizers with FTS5.
*/
|
| ︙ | ︙ | |||
252191 252192 252193 252194 252195 252196 252197 |
if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];
if( pEq ){
zTerm = (const char *)sqlite3_value_text(pEq);
nTerm = sqlite3_value_bytes(pEq);
| | | 255154 255155 255156 255157 255158 255159 255160 255161 255162 255163 255164 255165 255166 255167 255168 |
if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];
if( pEq ){
zTerm = (const char *)sqlite3_value_text(pEq);
nTerm = sqlite3_value_bytes(pEq);
f = FTS5INDEX_QUERY_NOTOKENDATA;
}else{
if( pGe ){
zTerm = (const char *)sqlite3_value_text(pGe);
nTerm = sqlite3_value_bytes(pGe);
}
if( pLe ){
const char *zCopy = (const char *)sqlite3_value_text(pLe);
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.h.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.45.1" #define SQLITE_VERSION_NUMBER 3045001 #define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
3950 3951 3952 3953 3954 3955 3956 | ** <li> sqlite3_extended_errcode() ** <li> sqlite3_errmsg() ** <li> sqlite3_errmsg16() ** <li> sqlite3_error_offset() ** </ul> ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language | | > | | > | 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 | ** <li> sqlite3_extended_errcode() ** <li> sqlite3_errmsg() ** <li> sqlite3_errmsg16() ** <li> sqlite3_error_offset() ** </ul> ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively, ** or NULL if no error message is available. ** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** ** ^The sqlite3_errstr(E) interface returns the English-language text ** that describes the [result code] E, as UTF-8, or NULL if E is not an ** result code for which a text error message is available. ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** ** ^If the most recent error references a specific token in the input ** SQL, the sqlite3_error_offset() interface returns the byte offset ** of the start of that token. ^The byte offset returned by ** sqlite3_error_offset() assumes that the input SQL is UTF8. |
| ︙ | ︙ | |||
8033 8034 8035 8036 8037 8038 8039 | ** In such cases, the ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() | | | | > > | 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 | ** In such cases, the ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() ** will always return SQLITE_BUSY. In most cases the SQLite core only uses ** sqlite3_mutex_try() as an optimization, so this is acceptable ** behavior. The exceptions are unix builds that set the ** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working ** sqlite3_mutex_try() is required.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), |
| ︙ | ︙ | |||
8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 | > | 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 |
| ︙ | ︙ | |||
12807 12808 12809 12810 12811 12812 12813 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: | > > > | | > > | | | > | | | | 12812 12813 12814 12815 12816 12817 12818 12819 12820 12821 12822 12823 12824 12825 12826 12827 12828 12829 12830 12831 12832 12833 12834 12835 12836 12837 12838 12839 12840 12841 12842 12843 12844 12845 12846 12847 12848 12849 12850 12851 12852 12853 12854 12855 12856 12857 12858 12859 12860 12861 12862 12863 12864 12865 | ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** This function may be quite inefficient if used with an FTS5 table ** created with the "columnsize=0" option. ** ** xColumnText: ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. ** ** Otherwise, this function attempts to retrieve the text of column iCol of ** the current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values ** of (*pz) and (*pn) are undefined. ** ** xPhraseCount: ** Returns the number of phrases in the current query expression. ** ** xPhraseSize: ** If parameter iCol is less than zero, or greater than or equal to the ** number of phrases in the current query, as returned by xPhraseCount, ** 0 is returned. Otherwise, this function returns the number of tokens in ** phrase iPhrase of the query. Phrases are numbered starting from zero. ** ** xInstCount: ** Set *pnInst to the total number of occurrences of all phrases within ** the query within the current row. Return SQLITE_OK if successful, or ** an error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. If the FTS5 table is created ** with either "detail=none" or "detail=column" and "content=" option ** (i.e. if it is a contentless table), then this API always returns 0. ** ** xInst: ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value ** output by xInstCount(). If iIdx is less than zero or greater than ** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. ** ** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the ** first token of the phrase. SQLITE_OK is returned if successful, or an ** error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xRowid: ** Returns the rowid of the current row. ** |
| ︙ | ︙ | |||
12865 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 12878 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. | > > > > | 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 12892 12893 | ** current query is executed. Any column filter that applies to ** phrase iPhrase of the current query is included in $p. For each ** row visited, the callback function passed as the fourth argument ** is invoked. The context and API objects passed to the callback ** function may be used to access the properties of each matched row. ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** ** If parameter iPhrase is less than zero, or greater than or equal to ** the number of phrases in the query, as returned by xPhraseCount(), ** this function returns SQLITE_RANGE. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. ** Otherwise, the error code is propagated upwards. ** ** If the query runs to completion without incident, SQLITE_OK is returned. |
| ︙ | ︙ | |||
12980 12981 12982 12983 12984 12985 12986 12987 12988 |
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
*/
struct Fts5ExtensionApi {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 12995 12996 12997 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016 13017 13018 13019 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 |
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
**
** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
** This is used to access token iToken of phrase iPhrase of the current
** query. Before returning, output parameter *ppToken is set to point
** to a buffer containing the requested token, and *pnToken to the
** size of this buffer in bytes.
**
** If iPhrase or iToken are less than zero, or if iPhrase is greater than
** or equal to the number of phrases in the query as reported by
** xPhraseCount(), or if iToken is equal to or greater than the number of
** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
are both zeroed.
**
** The output text is not a copy of the query text that specified the
** token. It is the output of the tokenizer module. For tokendata=1
** tables, this includes any embedded 0x00 and trailing data.
**
** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
** This is used to access token iToken of phrase hit iIdx within the
** current row. If iIdx is less than zero or greater than or equal to the
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
** output variable (*ppToken) is set to point to a buffer containing the
** matching document token, and (*pnToken) to the size of that buffer in
** bytes. This API is not available if the specified token matches a
** prefix query term. In that case both output variables are always set
** to 0.
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
int (*xColumnCount)(Fts5Context*);
int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
|
| ︙ | ︙ | |||
13017 13018 13019 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 | void *(*xGetAuxdata)(Fts5Context*, int bClear); int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* | > > > > > > > | 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083 13084 13085 |
void *(*xGetAuxdata)(Fts5Context*, int bClear);
int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
/* Below this point are iVersion>=3 only */
int (*xQueryToken)(Fts5Context*,
int iPhrase, int iToken,
const char **ppToken, int *pnToken
);
int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
|
| ︙ | ︙ |
Changes to skins/default/css.txt.
1 2 3 4 5 6 7 |
/* Overall page style */
body {
margin: 0 auto;
background-color: white;
font-family: sans-serif;
font-size: 14pt;
| < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* Overall page style */
body {
margin: 0 auto;
background-color: white;
font-family: sans-serif;
font-size: 14pt;
}
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
|
| ︙ | ︙ |
Changes to src/ajax.c.
| ︙ | ︙ | |||
390 391 392 393 394 395 396 |
*/
void ajax_route_dispatcher(void){
const char * zName = P("name");
AjaxRoute routeName = {0,0,0,0};
const AjaxRoute * pRoute = 0;
const AjaxRoute routes[] = {
/* Keep these sorted by zName (for bsearch()) */
| | > > > > > > > > > | | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
*/
void ajax_route_dispatcher(void){
const char * zName = P("name");
AjaxRoute routeName = {0,0,0,0};
const AjaxRoute * pRoute = 0;
const AjaxRoute routes[] = {
/* Keep these sorted by zName (for bsearch()) */
{"preview-text", ajax_route_preview_text, 0, 1
/* Note that this does not require write permissions in the repo.
** It should arguably require write permissions but doing means
** that /chat does not work without checkin permissions:
**
** https://fossil-scm.org/forum/forumpost/ed4a762b3a557898
**
** This particular route is used by /fileedit and /chat, whereas
** /wikiedit uses a simpler wiki-specific route.
*/ }
};
if(zName==0 || zName[0]==0){
ajax_route_error(400,"Missing required [route] 'name' parameter.");
return;
}
routeName.zName = zName;
pRoute = (const AjaxRoute *)bsearch(&routeName, routes,
count(routes), sizeof routes[0],
cmp_ajax_route_name);
if(pRoute==0){
ajax_route_error(404,"Ajax route not found.");
return;
}else if(0==ajax_route_bootstrap(pRoute->bWriteMode, pRoute->bPost)){
return;
}
pRoute->xCallback();
}
|
Changes to src/alerts.c.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | @ -- to the USER entry. @ -- @ -- The ssub field is a string where each character indicates a particular @ -- type of event to subscribe to. Choices: @ -- a - Announcements @ -- c - Check-ins @ -- f - Forum posts @ -- n - New forum threads @ -- r - Replies to my own forum posts @ -- t - Ticket changes @ -- w - Wiki changes @ -- x - Edits to forum posts @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email | > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | @ -- to the USER entry. @ -- @ -- The ssub field is a string where each character indicates a particular @ -- type of event to subscribe to. Choices: @ -- a - Announcements @ -- c - Check-ins @ -- f - Forum posts @ -- k - ** Special: Unsubscribed using /oneclickunsub @ -- n - New forum threads @ -- r - Replies to my own forum posts @ -- t - Ticket changes @ -- w - Wiki changes @ -- x - Edits to forum posts @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email |
| ︙ | ︙ | |||
84 85 86 87 88 89 90 | @ -- @ CREATE TABLE repository.pending_alert( @ eventid TEXT PRIMARY KEY, -- Object that changed @ sentSep BOOLEAN DEFAULT false, -- individual alert sent @ sentDigest BOOLEAN DEFAULT false, -- digest alert sent @ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent @ ) WITHOUT ROWID; | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | @ -- @ CREATE TABLE repository.pending_alert( @ eventid TEXT PRIMARY KEY, -- Object that changed @ sentSep BOOLEAN DEFAULT false, -- individual alert sent @ sentDigest BOOLEAN DEFAULT false, -- digest alert sent @ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent @ ) WITHOUT ROWID; @ @ -- Obsolete table. No longer used. @ DROP TABLE IF EXISTS repository.alert_bounce; ; /* ** Return true if the email notification tables exist. */ |
| ︙ | ︙ | |||
873 874 875 876 877 878 879 |
*/
void email_header_to(Blob *pMsg, int *pnTo, char ***pazTo){
int nTo = 0;
char **azTo = 0;
Blob v;
char *z, *zAddr;
int i;
| | | | 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 |
*/
void email_header_to(Blob *pMsg, int *pnTo, char ***pazTo){
int nTo = 0;
char **azTo = 0;
Blob v;
char *z, *zAddr;
int i;
email_header_value(pMsg, "to", &v);
z = blob_str(&v);
for(i=0; z[i]; i++){
if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){
azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) );
azTo[nTo++] = zAddr;
}
}
*pnTo = nTo;
*pazTo = azTo;
}
/*
** Free a list of To addresses obtained from a prior call to
** email_header_to()
*/
void email_header_to_free(int nTo, char **azTo){
int i;
for(i=0; i<nTo; i++) fossil_free(azTo[i]);
fossil_free(azTo);
}
|
| ︙ | ︙ | |||
913 914 915 916 917 918 919 | ** From: ** Date: ** Message-Id: ** Content-Type: ** Content-Transfer-Encoding: ** MIME-Version: ** Sender: | | | | 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 | ** From: ** Date: ** Message-Id: ** Content-Type: ** Content-Transfer-Encoding: ** MIME-Version: ** Sender: ** ** The caller maintains ownership of the input Blobs. This routine will ** read the Blobs and send them onward to the email system, but it will ** not free them. ** ** The Message-Id: field is added if there is not already a Message-Id ** in the pHdr parameter. ** ** If the zFromName argument is not NULL, then it should be a human-readable ** name or handle for the sender. In that case, "From:" becomes a made-up ** email address based on a hash of zFromName and the domain of email-self, ** and an additional "Sender:" field is inserted with the email-self ** address. Downstream software might use the Sender header to set ** the envelope-from address of the email. If zFromName is a NULL pointer, ** then the "From:" is set to the email-self value and Sender is ** omitted. */ void alert_send( AlertSender *p, /* Emailer context */ Blob *pHdr, /* Email header (incomplete) */ Blob *pBody, /* Email body */ |
| ︙ | ︙ | |||
1043 1044 1045 1046 1047 1048 1049 | ** the basename for hyperlinks included in email alert text. ** Omit the trailing "/". If the repository is not intended to be ** a long-running server and will not be sending email notifications, ** then leave this setting blank. */ /* ** SETTING: email-admin width=40 | | | 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 | ** the basename for hyperlinks included in email alert text. ** Omit the trailing "/". If the repository is not intended to be ** a long-running server and will not be sending email notifications, ** then leave this setting blank. */ /* ** SETTING: email-admin width=40 ** This is the email address for the human administrator for the system. ** Abuse and trouble reports and password reset requests are send here. */ /* ** SETTING: email-subname width=16 ** This is a short name used to identifies the repository in the Subject: ** line of email alerts. Traditionally this name is included in square ** brackets. Examples: "[fossil-src]", "[sqlite-src]". |
| ︙ | ︙ | |||
1078 1079 1080 1081 1082 1083 1084 | ** a subscription is less than email-renew-cutoff, then now new emails ** are sent to the subscriber. ** ** email-renew-warning is the time (in days since 1970-01-01) when the ** last batch of "your subscription is about to expire" emails were ** sent out. ** | | | | 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 | ** a subscription is less than email-renew-cutoff, then now new emails ** are sent to the subscriber. ** ** email-renew-warning is the time (in days since 1970-01-01) when the ** last batch of "your subscription is about to expire" emails were ** sent out. ** ** email-renew-cutoff is normally 7 days behind email-renew-warning. */ /* ** SETTING: email-send-method width=5 default=off sensitive ** Determine the method used to send email. Allowed values are ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value ** means no email is ever sent. The "relay" value means emails are sent ** to an Mail Sending Agent using SMTP located at email-send-relayhost. ** The "pipe" value means email messages are piped into a command ** determined by the email-send-command setting. The "dir" value means ** emails are written to individual files in a directory determined ** by the email-send-dir setting. The "db" value means that emails ** are added to an SQLite database named by the* email-send-db setting. ** The "stdout" value writes email text to standard output, for debugging. */ /* |
| ︙ | ︙ | |||
1131 1132 1133 1134 1135 1136 1137 | ** SMTP server configured as a Mail Submission Agent listening on the ** designated host and port and all times. */ /* ** COMMAND: alerts* | | | 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | ** SMTP server configured as a Mail Submission Agent listening on the ** designated host and port and all times. */ /* ** COMMAND: alerts* ** ** Usage: %fossil alerts SUBCOMMAND ARGS... ** ** Subcommands: ** ** pending Show all pending alerts. Useful for debugging. ** ** reset Hard reset of all email notification tables |
| ︙ | ︙ | |||
1457 1458 1459 1460 1461 1462 1463 | /* If we reach this point, all is well */ return 1; } /* ** Text of email message sent in order to confirm a subscription. */ | | | 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 | /* If we reach this point, all is well */ return 1; } /* ** Text of email message sent in order to confirm a subscription. */ static const char zConfirmMsg[] = @ Someone has signed you up for email alerts on the Fossil repository @ at %s. @ @ To confirm your subscription and begin receiving alerts, click on @ the following hyperlink: @ @ %s/alerts/%s |
| ︙ | ︙ | |||
1740 1741 1742 1743 1744 1745 1746 | } /* ** Either shutdown or completely delete a subscription entry given ** by the hex value zName. Then paint a webpage that explains that ** the entry has been removed. */ | | | > > | | | > > > > > > > > | 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 |
}
/*
** Either shutdown or completely delete a subscription entry given
** by the hex value zName. Then paint a webpage that explains that
** the entry has been removed.
*/
static void alert_unsubscribe(int sid, int bTotal){
const char *zEmail = 0;
const char *zLogin = 0;
int uid = 0;
Stmt q;
db_prepare(&q, "SELECT semail, suname FROM subscriber"
" WHERE subscriberId=%d", sid);
if( db_step(&q)==SQLITE_ROW ){
zEmail = db_column_text(&q, 0);
zLogin = db_column_text(&q, 1);
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
}
style_set_current_feature("alerts");
if( zEmail==0 ){
style_header("Unsubscribe Fail");
@ <p>Unable to locate a subscriber with the requested key</p>
}else{
db_unprotect(PROTECT_READONLY);
if( bTotal ){
/* Completely delete the subscriber */
db_multi_exec(
"DELETE FROM subscriber WHERE subscriberId=%d", sid
);
}else{
/* Keep the subscriber, but turn off all notifications */
db_multi_exec(
"UPDATE subscriber SET ssub='k', mtime=now() WHERE subscriberId=%d",
sid
);
}
db_protect_pop();
style_header("Unsubscribed");
@ <p>The "%h(zEmail)" email address has been unsubscribed from all
@ notifications. All subscription records for "%h(zEmail)" have
@ been purged. No further emails will be sent to "%h(zEmail)".</p>
if( uid && g.perm.Admin ){
@ <p>You may also want to
@ <a href="%R/setup_uedit?id=%d(uid)">edit or delete
|
| ︙ | ︙ | |||
1792 1793 1794 1795 1796 1797 1798 | ** email and clicks on the link in the email. When a ** compilete subscriberCode is seen on the name= query parameter, ** that constitutes verification of the email address. ** ** * The sid= query parameter contains an integer subscriberId. ** This only works for the administrator. It allows the ** administrator to edit any subscription. | | | 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 | ** email and clicks on the link in the email. When a ** compilete subscriberCode is seen on the name= query parameter, ** that constitutes verification of the email address. ** ** * The sid= query parameter contains an integer subscriberId. ** This only works for the administrator. It allows the ** administrator to edit any subscription. ** ** * The user is logged into an account other than "nobody" or ** "anonymous". In that case the notification settings ** associated with that account can be edited without needing ** to know the subscriber code. ** ** * The name= query parameter contains a 32-digit prefix of ** subscriber code. (Subscriber codes are normally 64 hex digits |
| ︙ | ︙ | |||
1922 1923 1924 1925 1926 1927 1928 |
}
if( P("delete")!=0 && cgi_csrf_safe(2) ){
if( !PB("dodelete") ){
eErr = 9;
zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
" unsubscribe");
}else{
| | | | 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 |
}
if( P("delete")!=0 && cgi_csrf_safe(2) ){
if( !PB("dodelete") ){
eErr = 9;
zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
" unsubscribe");
}else{
alert_unsubscribe(sid, 1);
db_commit_transaction();
return;
}
}
style_set_current_feature("alerts");
style_header("Update Subscription");
db_prepare(&q,
"SELECT"
" semail," /* 0 */
|
| ︙ | ︙ | |||
2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 |
@ Ticket changes</label><br>
}
if( g.perm.RdWiki ){
@ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
@ Wiki</label>
}
@ </td></tr>
@ <tr>
@ <td class="form_label">Delivery:</td>
@ <td><select size="1" name="sdigest">
@ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
@ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
@ </select></td>
@ </tr>
| > > > > | 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 |
@ Ticket changes</label><br>
}
if( g.perm.RdWiki ){
@ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
@ Wiki</label>
}
@ </td></tr>
if( strchr(ssub,'k')!=0 ){
@ <tr><td></td><td> ↑
@ Note: User did a one-click unsubscribe</td></tr>
}
@ <tr>
@ <td class="form_label">Delivery:</td>
@ <td><select size="1" name="sdigest">
@ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
@ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
@ </select></td>
@ </tr>
|
| ︙ | ︙ | |||
2181 2182 2183 2184 2185 2186 2187 | style_finish_page(); } /* This is the message that gets sent to describe how to change ** or modify a subscription */ | | > > > > | | | > > | 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 |
style_finish_page();
}
/* This is the message that gets sent to describe how to change
** or modify a subscription
*/
static const char zUnsubMsg[] =
@ To changes your subscription settings at %s visit this link:
@
@ %s/alerts/%s
@
@ To completely unsubscribe from %s, visit the following link:
@
@ %s/unsubscribe/%s
;
/*
** WEBPAGE: unsubscribe
** WEBPAGE: oneclickunsub
**
** Users visit this page to be delisted from email alerts.
**
** If a valid subscriber code is supplied in the name= query parameter,
** then that subscriber is delisted.
**
** Otherwise, If the users is logged in, then they are redirected
** to the /alerts page where they have an unsubscribe button.
**
** Non-logged-in users with no name= query parameter are invited to enter
** an email address to which will be sent the unsubscribe link that
** contains the correct subscriber code.
**
** The /unsubscribe page requires comfirmation. The /oneclickunsub
** page unsubscribes immediately without any need to confirm.
*/
void unsubscribe_page(void){
const char *zName = P("name");
char *zErr = 0;
int eErr = 0;
unsigned int uSeed = 0;
const char *zDecoded;
char *zCaptcha = 0;
int dx;
int bSubmit;
const char *zEAddr;
char *zCode = 0;
int sid = 0;
if( zName==0 ) zName = P("scode");
/* If a valid subscriber code is supplied, then either present the user
** with a confirmation, or if already confirmed, unsubscribe immediately.
*/
if( zName
&& (sid = db_int(0, "SELECT subscriberId FROM subscriber"
" WHERE subscriberCode=hextoblob(%Q)", zName))!=0
){
char *zUnsubName = mprintf("confirm%04x", sid);
if( P(zUnsubName)!=0 ){
alert_unsubscribe(sid, 1);
}else if( sqlite3_strglob("*oneclick*",g.zPath)==0 ){
alert_unsubscribe(sid, 0);
}else if( P("manage")!=0 ){
cgi_redirectf("%R/alerts/%s", zName);
}else{
style_header("Unsubscribe");
form_begin(0, "%R/unsubscribe");
@ <input type="hidden" name="scode" value="%h(zName)">
@ <table border="0" cellpadding="10" width="100%%">
|
| ︙ | ︙ | |||
2311 2312 2313 2314 2315 2316 2317 |
}else{
@ <p>An email has been sent to "%h(zEAddr)" that explains how to
@ unsubscribe and/or modify your subscription settings</p>
}
alert_sender_free(pSender);
style_finish_page();
return;
| | | 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 |
}else{
@ <p>An email has been sent to "%h(zEAddr)" that explains how to
@ unsubscribe and/or modify your subscription settings</p>
}
alert_sender_free(pSender);
style_finish_page();
return;
}
/* Non-logged-in users have to enter an email address to which is
** sent a message containing the unsubscribe link.
*/
style_header("Unsubscribe Request");
@ <p>Fill out the form below to request an email message that will
@ explain how to unsubscribe and/or change your subscription settings.</p>
|
| ︙ | ︙ | |||
2540 2541 2542 2543 2544 2545 2546 | } /* ** Compute a string that is appropriate for the EmailEvent.zPriors field ** for a particular forum post. ** ** This string is an encode list of sender names and rids for all ancestors | | | 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 |
}
/*
** Compute a string that is appropriate for the EmailEvent.zPriors field
** for a particular forum post.
**
** This string is an encode list of sender names and rids for all ancestors
** of the fpdi post - the post that fpid answer, the post that that parent
** post answers, and so forth back up to the root post. Duplicates sender
** names are omitted.
**
** The EmailEvent.zPriors field is used to screen events for people who
** only want to see replies to their own posts or to specific posts.
*/
static char *alert_compute_priors(int fpid){
|
| ︙ | ︙ | |||
2720 2721 2722 2723 2724 2725 2726 |
zUuid = db_column_text(&q, 1);
zTitle = db_column_text(&q, 3);
if( p->needMod ){
blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
zSub, zTitle);
}else{
blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
| | | 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 |
zUuid = db_column_text(&q, 1);
zTitle = db_column_text(&q, 3);
if( p->needMod ){
blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
zSub, zTitle);
}else{
blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n",
zUuid, alert_hostname(zFrom));
zIrt = db_column_text(&q, 4);
if( zIrt && zIrt[0] ){
blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
zIrt, alert_hostname(zFrom));
}
}
|
| ︙ | ︙ | |||
3087 3088 3089 3090 3091 3092 3093 |
" ssub," /* 2 */
" fullcap(user.cap)," /* 3 */
" suname" /* 4 */
" FROM subscriber LEFT JOIN user ON (login=suname)"
" WHERE sverified"
" AND NOT sdonotcall"
" AND sdigest IS %s"
| | > | | | 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 |
" ssub," /* 2 */
" fullcap(user.cap)," /* 3 */
" suname" /* 4 */
" FROM subscriber LEFT JOIN user ON (login=suname)"
" WHERE sverified"
" AND NOT sdonotcall"
" AND sdigest IS %s"
" AND coalesce(subscriber.lastContact*86400,subscriber.mtime)>=%d",
zDigest/*safe-for-%s*/,
db_get_int("email-renew-cutoff",0)
);
while( db_step(&q)==SQLITE_ROW ){
const char *zCode = db_column_text(&q, 0);
const char *zSub = db_column_text(&q, 2);
const char *zEmail = db_column_text(&q, 1);
const char *zCap = db_column_text(&q, 3);
const char *zUser = db_column_text(&q, 4);
int nHit = 0;
for(p=pEvents; p; p=p->pNext){
if( strchr(zSub,p->type)==0 ){
if( p->type!='f' ) continue;
if( strchr(zSub,'n')!=0 && (p->zPriors==0 || p->zPriors[0]==0) ){
/* New post: accepted */
}else if( strchr(zSub,'r')!=0 && zUser!=0
&& alert_in_priors(zUser, p->zPriors) ){
/* A follow-up to a post written by the user: accept */
}else{
continue;
}
}
if( p->needMod ){
/* For events that require moderator approval, only send an alert
|
| ︙ | ︙ | |||
3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 |
if( blob_size(&p->hdr)>0 ){
/* This alert should be sent as a separate email */
Blob fhdr, fbody;
blob_init(&fhdr, 0, 0);
blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
zUrl, zCode);
/* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
** zUrl, zCode); */
alert_send(pSender,&fhdr,&fbody,p->zFromName);
nSent++;
blob_reset(&fhdr);
| > > > > | 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 |
if( blob_size(&p->hdr)>0 ){
/* This alert should be sent as a separate email */
Blob fhdr, fbody;
blob_init(&fhdr, 0, 0);
blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
zUrl, zCode);
blob_appendf(&fhdr,
"List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
zUrl, zCode);
/* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
** zUrl, zCode); */
alert_send(pSender,&fhdr,&fbody,p->zFromName);
nSent++;
blob_reset(&fhdr);
|
| ︙ | ︙ | |||
3172 3173 3174 3175 3176 3177 3178 |
}
nHit++;
blob_append(&body, "\n", 1);
blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
}
}
if( nHit==0 ) continue;
| | | 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 |
}
nHit++;
blob_append(&body, "\n", 1);
blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
}
}
if( nHit==0 ) continue;
blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
zUrl, zCode);
blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
zUrl, zCode);
alert_send(pSender,&hdr,&body,0);
nSent++;
blob_truncate(&hdr, 0);
|
| ︙ | ︙ | |||
3195 3196 3197 3198 3199 3200 3201 |
** alerts that have been completely sent.
*/
db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;");
/* Send renewal messages to subscribers whose subscriptions are about
** to expire. Only do this if:
**
| | | 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 |
** alerts that have been completely sent.
*/
db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;");
/* Send renewal messages to subscribers whose subscriptions are about
** to expire. Only do this if:
**
** (1) email-renew-interval is 14 or greater (or in other words if
** subscription expiration is enabled).
**
** (2) The SENDALERT_RENEWAL flag is set
*/
send_alert_expiration_warnings:
if( (flags & SENDALERT_RENEWAL)!=0
&& (iInterval = db_get_int("email-renew-interval",0))>=14
|
| ︙ | ︙ | |||
3224 3225 3226 3227 3228 3229 3230 |
" AND length(sdigest)>0",
iNewWarn, iOldWarn
);
while( db_step(&q)==SQLITE_ROW ){
Blob hdr, body;
blob_init(&hdr, 0, 0);
blob_init(&body, 0, 0);
| | | 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 |
" AND length(sdigest)>0",
iNewWarn, iOldWarn
);
while( db_step(&q)==SQLITE_ROW ){
Blob hdr, body;
blob_init(&hdr, 0, 0);
blob_init(&body, 0, 0);
alert_renewal_msg(&hdr, &body,
db_column_text(&q,0),
db_column_int(&q,1),
db_column_text(&q,2),
db_column_text(&q,3),
zRepoName, zUrl);
alert_send(pSender,&hdr,&body,0);
blob_reset(&hdr);
|
| ︙ | ︙ | |||
3294 3295 3296 3297 3298 3299 3300 |
style_set_current_feature("alerts");
if( zAdminEmail==0 || zAdminEmail[0]==0 ){
style_header("Outbound Email Disabled");
@ <p>Outbound email is disabled on this repository
style_finish_page();
return;
}
| | | 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 |
style_set_current_feature("alerts");
if( zAdminEmail==0 || zAdminEmail[0]==0 ){
style_header("Outbound Email Disabled");
@ <p>Outbound email is disabled on this repository
style_finish_page();
return;
}
if( P("submit")!=0
&& P("subject")!=0
&& P("msg")!=0
&& P("from")!=0
&& cgi_csrf_safe(2)
&& captcha_is_correct(0)
){
Blob hdr, body;
|
| ︙ | ︙ |
Changes to src/allrepo.c.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 |
*/
static void collect_argument(Blob *pExtra,const char *zArg,const char *zShort){
const char *z = find_option(zArg, zShort, 0);
if( z!=0 ){
blob_appendf(pExtra, " %s", z);
}
}
| | > > | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
*/
static void collect_argument(Blob *pExtra,const char *zArg,const char *zShort){
const char *z = find_option(zArg, zShort, 0);
if( z!=0 ){
blob_appendf(pExtra, " %s", z);
}
}
static void collect_argument_value(
Blob *pExtra, const char *zArg, const char *zShort
){
const char *zValue = find_option(zArg, zShort, 1);
if( zValue ){
if( zValue[0] ){
blob_appendf(pExtra, " --%s %$", zArg, zValue);
}else{
blob_appendf(pExtra, " --%s \"\"", zArg);
}
}
|
| ︙ | ︙ | |||
130 131 132 133 134 135 136 | ** ** ui Run the "ui" command on all repositories. Like "server" ** but bind to the loopback TCP address only, enable ** the --localauth option and automatically launch a ** web-browser ** ** whatis Run the "whatis" command on all repositories. Only | | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | ** ** ui Run the "ui" command on all repositories. Like "server" ** but bind to the loopback TCP address only, enable ** the --localauth option and automatically launch a ** web-browser ** ** whatis Run the "whatis" command on all repositories. Only ** show output for repositories that have a match. ** ** ** In addition, the following maintenance operations are supported: ** ** add Add all the repositories named to the set of repositories ** tracked by Fossil. Normally Fossil is able to keep up with ** this list by itself, but sometimes it can benefit from this |
| ︙ | ︙ | |||
208 209 210 211 212 213 214 |
if( file_isdir(zDest, ExtFILE)!=1 ){
fossil_fatal("argument to \"fossil all backup\" must be a directory");
}
blob_appendf(&extra, " %$", zDest);
}else if( fossil_strcmp(zCmd, "clean")==0 ){
zCmd = "clean --chdir";
collect_argument(&extra, "allckouts",0);
| | | | | | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
if( file_isdir(zDest, ExtFILE)!=1 ){
fossil_fatal("argument to \"fossil all backup\" must be a directory");
}
blob_appendf(&extra, " %$", zDest);
}else if( fossil_strcmp(zCmd, "clean")==0 ){
zCmd = "clean --chdir";
collect_argument(&extra, "allckouts",0);
collect_argument_value(&extra, "case-sensitive", 0);
collect_argument_value(&extra, "clean", 0);
collect_argument(&extra, "dirsonly",0);
collect_argument(&extra, "disable-undo",0);
collect_argument(&extra, "dotfiles",0);
collect_argument(&extra, "emptydirs",0);
collect_argument(&extra, "force","f");
collect_argument_value(&extra, "ignore", 0);
collect_argument_value(&extra, "keep", 0);
collect_argument(&extra, "no-prompt",0);
collect_argument(&extra, "temp",0);
collect_argument(&extra, "verbose","v");
collect_argument(&extra, "whatif",0);
useCheckouts = 1;
}else if( fossil_strcmp(zCmd, "config")==0 ){
zCmd = "config -R";
|
| ︙ | ︙ | |||
245 246 247 248 249 250 251 |
}else if( fossil_strcmp(zCmd, "extras")==0 ){
if( showFile ){
zCmd = "extras --chdir";
}else{
zCmd = "extras --header --chdir";
}
collect_argument(&extra, "abs-paths",0);
| | | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
}else if( fossil_strcmp(zCmd, "extras")==0 ){
if( showFile ){
zCmd = "extras --chdir";
}else{
zCmd = "extras --header --chdir";
}
collect_argument(&extra, "abs-paths",0);
collect_argument_value(&extra, "case-sensitive", 0);
collect_argument(&extra, "dotfiles",0);
collect_argument_value(&extra, "ignore", 0);
collect_argument(&extra, "rel-paths",0);
useCheckouts = 1;
stopOnError = 0;
quiet = 1;
}else if( fossil_strcmp(zCmd, "git")==0 ){
if( g.argc<4 ){
usage("git (export|status)");
|
| ︙ | ︙ | |||
274 275 276 277 278 279 280 281 282 283 284 |
collect_argument(&extra, "verbose","v");
}else if( fossil_strcmp(zCmd, "pull")==0 ){
zCmd = "pull -autourl -R";
collect_argument(&extra, "verbose","v");
collect_argument(&extra, "share-links",0);
}else if( fossil_strcmp(zCmd, "rebuild")==0 ){
zCmd = "rebuild";
collect_argument(&extra, "cluster",0);
collect_argument(&extra, "compress",0);
collect_argument(&extra, "compress-only",0);
collect_argument(&extra, "noverify",0);
| > | | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
collect_argument(&extra, "verbose","v");
}else if( fossil_strcmp(zCmd, "pull")==0 ){
zCmd = "pull -autourl -R";
collect_argument(&extra, "verbose","v");
collect_argument(&extra, "share-links",0);
}else if( fossil_strcmp(zCmd, "rebuild")==0 ){
zCmd = "rebuild";
collect_argument(&extra, "analyze",0);
collect_argument(&extra, "cluster",0);
collect_argument(&extra, "compress",0);
collect_argument(&extra, "compress-only",0);
collect_argument(&extra, "noverify",0);
collect_argument_value(&extra, "pagesize", 0);
collect_argument(&extra, "vacuum",0);
collect_argument(&extra, "deanalyze",0); /* Deprecated */
collect_argument(&extra, "analyze",0);
collect_argument(&extra, "wal",0);
collect_argument(&extra, "stats",0);
collect_argument(&extra, "index",0);
collect_argument(&extra, "noindex",0);
collect_argument(&extra, "ifneeded", 0);
}else if( fossil_strcmp(zCmd, "remote")==0 ){
|
| ︙ | ︙ | |||
412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
}else if( fossil_strcmp(zCmd, "cache")==0 ){
zCmd = "cache -R";
showLabel = 1;
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "whatis")==0 ){
zCmd = "whatis -q -R";
quiet = 1;
collect_argv(&extra, 3);
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
"server setting sync ui unset whatis");
}
| > > | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 |
}else if( fossil_strcmp(zCmd, "cache")==0 ){
zCmd = "cache -R";
showLabel = 1;
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "whatis")==0 ){
zCmd = "whatis -q -R";
quiet = 1;
collect_argument(&extra, "file", "f");
collect_argument_value(&extra, "type", 0);
collect_argv(&extra, 3);
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
"server setting sync ui unset whatis");
}
|
| ︙ | ︙ |
Changes to src/attach.c.
| ︙ | ︙ | |||
748 749 750 751 752 753 754 |
if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){
zBody = pWiki->zWiki;
}
if( zBody==0 ){
fossil_fatal("technote [%s] not found",zETime);
}
zTarget = db_text(0,
| | > | 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){
zBody = pWiki->zWiki;
}
if( zBody==0 ){
fossil_fatal("technote [%s] not found",zETime);
}
zTarget = db_text(0,
"SELECT substr(tagname,7) FROM tag "
" WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",
rid
);
zFile = g.argv[3];
}
blob_read_from_file(&content, zFile, ExtFILE);
user_select();
attach_commit(
|
| ︙ | ︙ |
Changes to src/backlink.c.
| ︙ | ︙ | |||
247 248 249 250 251 252 253 |
void *opaque
){
Backlink *p = (Backlink*)opaque;
char *zTarget = blob_buffer(target);
int nTarget = blob_size(target);
backlink_create(p, zTarget, nTarget);
| | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
void *opaque
){
Backlink *p = (Backlink*)opaque;
char *zTarget = blob_buffer(target);
int nTarget = blob_size(target);
backlink_create(p, zTarget, nTarget);
return 1;
}
/* No-op routines for the rendering callbacks that we do not need */
static void mkdn_noop_prolog(Blob *b, void *v){ return; }
static void (*mkdn_noop_epilog)(Blob*, void*) = mkdn_noop_prolog;
static void mkdn_noop_footnotes(Blob *b1, const Blob *b2, void *v){ return; }
static void mkdn_noop_blockcode(Blob *b1, Blob *b2, void *v){ return; }
|
| ︙ | ︙ |
Changes to src/backoffice.c.
| ︙ | ︙ | |||
313 314 315 316 317 318 319 |
** we cannot prove that the process is dead, return true.
*/
static int backofficeProcessExists(sqlite3_uint64 pid){
#if defined(_WIN32)
return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0;
#else
return pid>0 && kill((pid_t)pid, 0)==0;
| | | | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
** we cannot prove that the process is dead, return true.
*/
static int backofficeProcessExists(sqlite3_uint64 pid){
#if defined(_WIN32)
return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0;
#else
return pid>0 && kill((pid_t)pid, 0)==0;
#endif
}
/*
** Check to see if the process identified by pid has finished. If
** we cannot prove that the process is still running, return true.
*/
static int backofficeProcessDone(sqlite3_uint64 pid){
#if defined(_WIN32)
return pid<=0 || backofficeWin32ProcessExists((DWORD)pid)==0;
#else
return pid<=0 || kill((pid_t)pid, 0)!=0;
#endif
}
/*
** Return a process id number for the current process
*/
static sqlite3_uint64 backofficeProcessId(void){
return (sqlite3_uint64)GETPID();
|
| ︙ | ︙ | |||
673 674 675 676 677 678 679 | ** This might be done by a cron job or similar to make sure backoffice ** processing happens periodically. Or, the --poll option can be used ** to run this command as a daemon that will periodically invoke backoffice ** on a collection of repositories. ** ** If only a single repository is named and --poll is omitted, then the ** backoffice work is done in-process. But if there are multiple repositories | | | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 | ** This might be done by a cron job or similar to make sure backoffice ** processing happens periodically. Or, the --poll option can be used ** to run this command as a daemon that will periodically invoke backoffice ** on a collection of repositories. ** ** If only a single repository is named and --poll is omitted, then the ** backoffice work is done in-process. But if there are multiple repositories ** or if --poll is used, a separate sub-process is started for each poll of ** each repository. ** ** Standard options: ** ** --debug Show what this command is doing ** ** --logfile FILE Append a log of backoffice actions onto FILE |
| ︙ | ︙ |
Changes to src/blob.c.
| ︙ | ︙ | |||
1549 1550 1551 1552 1553 1554 1555 |
z[--j] = z[i];
}
}
}
/*
** ASCII (for reference):
| | | | | | | | | | | 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 |
z[--j] = z[i];
}
}
}
/*
** ASCII (for reference):
** x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf
** 0x ^` ^a ^b ^c ^d ^e ^f ^g \b \t \n () \f \r ^n ^o
** 1x ^p ^q ^r ^s ^t ^u ^v ^w ^x ^y ^z ^{ ^| ^} ^~ ^
** 2x () ! " # $ % & ' ( ) * + , - . /
** 3x 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
** 4x @ A B C D E F G H I J K L M N O
** 5x P Q R S T U V W X Y Z [ \ ] ^ _
** 6x ` a b c d e f g h i j k l m n o
** 7x p q r s t u v w x y z { | } ~ ^_
*/
/*
** Meanings for bytes in a filename:
**
** 0 Ordinary character. No encoding required
** 1 Needs to be escaped
|
| ︙ | ︙ | |||
1663 1664 1665 1666 1667 1668 1669 |
blob_token(pBlob, &bad);
fossil_fatal("the [%s] argument to the \"%s\" command contains "
"an illegal UTF-8 character",
zIn, blob_str(&bad));
}
i += x-2;
}
| | | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 |
blob_token(pBlob, &bad);
fossil_fatal("the [%s] argument to the \"%s\" command contains "
"an illegal UTF-8 character",
zIn, blob_str(&bad));
}
i += x-2;
}
}
}
/* Separate from the previous argument by a space */
if( n>0 && !fossil_isspace(z[n-1]) ){
blob_append_char(pBlob, ' ');
}
|
| ︙ | ︙ | |||
1793 1794 1795 1796 1797 1798 1799 |
}
#ifdef _WIN32
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
#else
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
#endif
if( strcmp(zBuf, zArg)!=0 ){
| | | 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 |
}
#ifdef _WIN32
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
#else
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
#endif
if( strcmp(zBuf, zArg)!=0 ){
fossil_fatal("argument disagree: \"%s\" (%s) versus \"%s\"",
zBuf, g.argv[i-1], zArg);
}
continue;
}else if( fossil_strcmp(zArg, "--fuzz")==0 && i+1<g.argc ){
int n = atoi(g.argv[++i]);
int j;
for(j=0; j<n; j++){
|
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
304 305 306 307 308 309 310 |
const char *zUser
){
Blob sql;
blob_init(&sql, 0, 0);
brlist_create_temp_table();
/* Ignore nLimitMRU if no chronological sort requested. */
if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0;
| | < | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
const char *zUser
){
Blob sql;
blob_init(&sql, 0, 0);
brlist_create_temp_table();
/* Ignore nLimitMRU if no chronological sort requested. */
if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0;
/* Negative values for nLimitMRU also mean "no limit". */
if( nLimitMRU<0 ) nLimitMRU = 0;
/* OUTER QUERY */
blob_append_sql(&sql,"SELECT name, isprivate, mergeto,");
if( brFlags & BRL_LIST_USERS ){
blob_append_sql(&sql,
" (SELECT group_concat(user) FROM ("
" SELECT DISTINCT * FROM ("
" SELECT coalesce(euser,user) AS user"
|
| ︙ | ︙ | |||
339 340 341 342 343 344 345 |
blob_append_sql(&sql,
"SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1"
);
break;
}
case BRL_OPEN_ONLY: {
blob_append_sql(&sql,
| | > | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
blob_append_sql(&sql,
"SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1"
);
break;
}
case BRL_OPEN_ONLY: {
blob_append_sql(&sql,
"SELECT name, isprivate, mtime, mergeto FROM tmp_brlist "
" WHERE NOT isclosed"
);
break;
}
}
if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
if( brFlags & BRL_MERGED ) blob_append_sql(&sql, " AND mergeto IS NOT NULL");
if( zBrNameGlob ) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob);
|
| ︙ | ︙ | |||
771 772 773 774 775 776 777 |
int isPriv = db_column_int(&q, 1)==1;
const char *zMergeTo = db_column_text(&q, 2);
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
const char *zUsers = db_column_text(&q, 3);
if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){
continue;
}
| | | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
int isPriv = db_column_int(&q, 1)==1;
const char *zMergeTo = db_column_text(&q, 2);
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
const char *zUsers = db_column_text(&q, 3);
if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){
continue;
}
if( (brFlags & BRL_UNMERGED) && (fossil_strcmp(zCurrent,zMergeTo)==0
|| isCur) ){
continue;
}
blob_appendf(&txt, "%s%s%s",
( (brFlags & BRL_PRIVATE) ? " " : ( isPriv ? "#" : " ") ),
(isCur ? "* " : " "), zBr);
if( nUsers ){
|
| ︙ | ︙ | |||
885 886 887 888 889 890 891 |
}
}
if( zBgClr && zBgClr[0] && show_colors ){
@ <tr style="background-color:%s(zBgClr)">
}else{
@ <tr>
}
| | | 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 |
}
}
if( zBgClr && zBgClr[0] && show_colors ){
@ <tr style="background-color:%s(zBgClr)">
}else{
@ <tr>
}
@ <td>%z(href("%R/timeline?r=%T",zBranch))%h(zBranch)</a><input
@ type="checkbox" disabled="disabled"/></td>
@ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
@ <td>%d(nCkin)</td>
fossil_free(zAge);
@ <td>%s(isClosed?"closed":"")</td>
if( zMergeTo ){
@ <td>merged into
|
| ︙ | ︙ |
Changes to src/browse.c.
| ︙ | ︙ | |||
356 357 358 359 360 361 362 | /* Generate a multi-column table listing the contents of zD[] ** directory. */ mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); if( mxLen<12 ) mxLen = 12; mxLen += (mxLen+9)/10; | | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
/* Generate a multi-column table listing the contents of zD[]
** directory.
*/
mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
if( mxLen<12 ) mxLen = 12;
mxLen += (mxLen+9)/10;
db_prepare(&q,
"SELECT x, u FROM localfiles ORDER BY x COLLATE uintnocase /*scan*/");
@ <div class="columns files" style="columns: %d(mxLen)ex auto">
@ <ul class="browser">
while( db_step(&q)==SQLITE_ROW ){
const char *zFN;
zFN = db_column_text(&q, 0);
if( zFN[0]=='/' ){
|
| ︙ | ︙ | |||
469 470 471 472 473 474 475 | FileTreeNode *pSibling; /* Next element in the same subdirectory */ FileTreeNode *pChild; /* List of child nodes */ FileTreeNode *pLastChild; /* Last child on the pChild list */ char *zName; /* Name of this entry. The "tail" */ char *zFullName; /* Full pathname of this entry */ char *zUuid; /* Artifact hash of this file. May be NULL. */ double mtime; /* Modification time for this entry */ | | > | 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 |
FileTreeNode *pSibling; /* Next element in the same subdirectory */
FileTreeNode *pChild; /* List of child nodes */
FileTreeNode *pLastChild; /* Last child on the pChild list */
char *zName; /* Name of this entry. The "tail" */
char *zFullName; /* Full pathname of this entry */
char *zUuid; /* Artifact hash of this file. May be NULL. */
double mtime; /* Modification time for this entry */
double sortBy; /* Either mtime or size, depending on desired
sort order */
int iSize; /* Size for this entry */
unsigned nFullName; /* Length of zFullName */
unsigned iLevel; /* Levels of parent directories */
};
/*
** A complete file hierarchy
|
| ︙ | ︙ | |||
506 507 508 509 510 511 512 |
const char *zUuid, /* Hash of the file. Might be NULL. */
double mtime, /* Modification time for this entry */
int size, /* Size for this entry */
int sortOrder /* 0: filename, 1: mtime, 2: size */
){
int i;
FileTreeNode *pParent; /* Parent (directory) of the next node to insert */
| | | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
const char *zUuid, /* Hash of the file. Might be NULL. */
double mtime, /* Modification time for this entry */
int size, /* Size for this entry */
int sortOrder /* 0: filename, 1: mtime, 2: size */
){
int i;
FileTreeNode *pParent; /* Parent (directory) of the next node to insert */
/* Make pParent point to the most recent ancestor of zPath, or
** NULL if there are no prior entires that are a container for zPath.
*/
pParent = pTree->pLast;
while( pParent!=0 &&
( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
|| zPath[pParent->nFullName]!='/' )
|
| ︙ | ︙ |
Changes to src/builtin.c.
| ︙ | ︙ | |||
519 520 521 522 523 524 525 | builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; return pCur->iRowid>count(aBuiltinFiles); } /* ** This method is called to "rewind" the builtinVtab_cursor object back ** to the first row of output. This method is always called at least | | | | 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur;
return pCur->iRowid>count(aBuiltinFiles);
}
/*
** This method is called to "rewind" the builtinVtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or
** builtinVtabEof().
*/
static int builtinVtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor;
pCur->iRowid = 1;
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
548 549 550 551 552 553 554 |
){
pIdxInfo->estimatedCost = (double)count(aBuiltinFiles);
pIdxInfo->estimatedRows = count(aBuiltinFiles);
return SQLITE_OK;
}
/*
| | | 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
){
pIdxInfo->estimatedCost = (double)count(aBuiltinFiles);
pIdxInfo->estimatedRows = count(aBuiltinFiles);
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module builtinVtabModule = {
/* iVersion */ 0,
/* xCreate */ 0, /* The builtin vtab is eponymous and read-only */
/* xConnect */ builtinVtabConnect,
/* xBestIndex */ builtinVtabBestIndex,
|
| ︙ | ︙ | |||
575 576 577 578 579 580 581 | /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, | | > | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 |
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindMethod */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0,
/* xIntegrity */ 0
};
/*
** Register the builtin virtual table
*/
int builtin_vtab_register(sqlite3 *db){
|
| ︙ | ︙ | |||
813 814 815 816 817 818 819 | ** per-page basis. In this case, all arguments are ignored! ** ** This function has an internal mapping of the dependencies for each ** of the known fossil.XYZ.js modules and ensures that the ** dependencies also get queued (recursively) and that each module is ** queued only once. ** | | | 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | ** per-page basis. In this case, all arguments are ignored! ** ** This function has an internal mapping of the dependencies for each ** of the known fossil.XYZ.js modules and ensures that the ** dependencies also get queued (recursively) and that each module is ** queued only once. ** ** If passed a name which is not a base fossil module name then it ** will fail fatally! ** ** DO NOT use this for loading fossil.page.*.js: use ** builtin_request_js() for those. ** ** If the current JS delivery mode is *not* JS_BUNDLED then this ** function queues up a request for each given module and its known |
| ︙ | ︙ |
Changes to src/cache.c.
| ︙ | ︙ | |||
311 312 313 314 315 316 317 |
sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0);
sqlite3_close(db);
fossil_print("cache cleared\n");
}else{
fossil_print("nothing to clear; cache does not exist\n");
}
}else if( strncmp(zCmd, "list", nCmd)==0
| | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0);
sqlite3_close(db);
fossil_print("cache cleared\n");
}else{
fossil_print("nothing to clear; cache does not exist\n");
}
}else if( strncmp(zCmd, "list", nCmd)==0
|| strncmp(zCmd, "ls", nCmd)==0
|| strncmp(zCmd, "status", nCmd)==0
){
db = cacheOpen(0);
if( db==0 ){
fossil_print("cache does not exist\n");
}else{
int nEntry = 0;
|
| ︙ | ︙ | |||
430 431 432 433 434 435 436 |
@ hit-count: %d(sqlite3_column_int(pStmt,2))
@ last-access: %s(sqlite3_column_text(pStmt,3)) \
if( zHash ){
@ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
fossil_free(zHash);
}
@ </p></li>
| | | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
@ hit-count: %d(sqlite3_column_int(pStmt,2))
@ last-access: %s(sqlite3_column_text(pStmt,3)) \
if( zHash ){
@ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
fossil_free(zHash);
}
@ </p></li>
}
sqlite3_finalize(pStmt);
@ </ol>
}
zDbName = cacheName();
bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
@ <p>
|
| ︙ | ︙ |
Changes to src/capabilities.c.
| ︙ | ︙ | |||
399 400 401 402 403 404 405 |
@ <th>Unversioned Content</th></tr>
while( db_step(&q)==SQLITE_ROW ){
const char *zId = db_column_text(&q, 0);
const char *zCap = db_column_text(&q, 1);
int n = db_column_int(&q, 3);
int eType;
static const char *const azType[] = { "off", "read", "write" };
| | | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
@ <th>Unversioned Content</th></tr>
while( db_step(&q)==SQLITE_ROW ){
const char *zId = db_column_text(&q, 0);
const char *zCap = db_column_text(&q, 1);
int n = db_column_int(&q, 3);
int eType;
static const char *const azType[] = { "off", "read", "write" };
static const char *const azClass[] =
{ "capsumOff", "capsumRead", "capsumWrite" };
if( n==0 ) continue;
/* Code */
if( db_column_int(&q,2)<10 ){
@ <tr><th align="right"><tt>"%h(zId)"</tt></th>
|
| ︙ | ︙ |
Changes to src/cgi.c.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | ** So, even though the name of this file implies that it only deals with ** CGI, in fact, the code in this file is used to interpret webpage requests ** received by a variety of means, and to generate well-formatted replies ** to those requests. ** ** The code in this file abstracts the web-request so that downstream ** modules that generate the body of the reply (based on the requested page) | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | ** So, even though the name of this file implies that it only deals with ** CGI, in fact, the code in this file is used to interpret webpage requests ** received by a variety of means, and to generate well-formatted replies ** to those requests. ** ** The code in this file abstracts the web-request so that downstream ** modules that generate the body of the reply (based on the requested page) ** do not need to know if the request is coming from CGI, direct HTTP, ** SCGI, or some other means. ** ** This module gathers information about web page request into a key/value ** store. Keys and values come from: ** ** * Query parameters ** * POST parameter |
| ︙ | ︙ | |||
479 480 481 482 483 484 485 |
if( iReplyStatus<=0 ){
iReplyStatus = 200;
zReplyStatus = "OK";
}
if( g.fullHttpReply ){
if( rangeEnd>0
| | | 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
if( iReplyStatus<=0 ){
iReplyStatus = 200;
zReplyStatus = "OK";
}
if( g.fullHttpReply ){
if( rangeEnd>0
&& iReplyStatus==200
&& fossil_strcmp(P("REQUEST_METHOD"),"GET")==0
){
iReplyStatus = 206;
zReplyStatus = "Partial Content";
}
blob_appendf(&hdr, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
blob_appendf(&hdr, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
|
| ︙ | ︙ | |||
560 561 562 563 564 565 566 |
blob_appendf(&hdr, "Content-Encoding: gzip\r\n");
blob_appendf(&hdr, "Vary: Accept-Encoding\r\n");
}
total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
if( iReplyStatus==206 ){
blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n",
rangeStart, rangeEnd-1, total_size);
| | | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
blob_appendf(&hdr, "Content-Encoding: gzip\r\n");
blob_appendf(&hdr, "Vary: Accept-Encoding\r\n");
}
total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
if( iReplyStatus==206 ){
blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n",
rangeStart, rangeEnd-1, total_size);
total_size = rangeEnd - rangeStart;
}
blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
}else{
total_size = 0;
}
blob_appendf(&hdr, "\r\n");
cgi_fwrite(blob_buffer(&hdr), blob_size(&hdr));
|
| ︙ | ︙ | |||
1309 1310 1311 1312 1313 1314 1315 | ** / \ ** https://fossil-scm.org/forum/info/12736b30c072551a?t=c ** \___/ \____________/\____/\____________________/ \_/ ** | | | | | ** | HTTP_HOST | PATH_INFO QUERY_STRING ** | | ** REQUEST_SCHEMA SCRIPT_NAME | | | 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 |
** / \
** https://fossil-scm.org/forum/info/12736b30c072551a?t=c
** \___/ \____________/\____/\____________________/ \_/
** | | | | |
** | HTTP_HOST | PATH_INFO QUERY_STRING
** | |
** REQUEST_SCHEMA SCRIPT_NAME
**
*/
void cgi_init(void){
char *z;
const char *zType;
char *zSemi;
int len;
const char *zRequestUri = cgi_parameter("REQUEST_URI",0);
|
| ︙ | ︙ | |||
1346 1347 1348 1349 1350 1351 1352 |
zScriptName = fossil_strndup(zRequestUri,(int)(z-zRequestUri));
cgi_set_parameter("SCRIPT_NAME", zScriptName);
}
#ifdef _WIN32
/* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
** PATH_INFO for virtually the same purpose. Define REQUEST_URI the same as
| | | 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 |
zScriptName = fossil_strndup(zRequestUri,(int)(z-zRequestUri));
cgi_set_parameter("SCRIPT_NAME", zScriptName);
}
#ifdef _WIN32
/* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
** PATH_INFO for virtually the same purpose. Define REQUEST_URI the same as
** PATH_INFO and redefine PATH_INFO with SCRIPT_NAME removed from the
** beginning. */
if( zServerSoftware && strstr(zServerSoftware, "Microsoft-IIS") ){
int i, j;
cgi_set_parameter("REQUEST_URI", zPathInfo);
for(i=0; zPathInfo[i]==zScriptName[i] && zPathInfo[i]; i++){}
for(j=i; zPathInfo[j] && zPathInfo[j]!='?'; j++){}
zPathInfo = fossil_strndup(zPathInfo+i, j-i);
|
| ︙ | ︙ |
Changes to src/chat.c.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | ** * Chat content lives in a single repository. It is never synced. ** Content expires and is deleted after a set interval (a week or so). ** ** Notification is accomplished using the "hanging GET" or "long poll" design ** in which a GET request is issued but the server does not send a reply until ** new content arrives. Newer Web Sockets and Server Sent Event protocols are ** more elegant, but are not compatible with CGI, and would thus complicate | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ** * Chat content lives in a single repository. It is never synced. ** Content expires and is deleted after a set interval (a week or so). ** ** Notification is accomplished using the "hanging GET" or "long poll" design ** in which a GET request is issued but the server does not send a reply until ** new content arrives. Newer Web Sockets and Server Sent Event protocols are ** more elegant, but are not compatible with CGI, and would thus complicate ** configuration. */ #include "config.h" #include <assert.h> #include "chat.h" /* ** Outputs JS code to initialize a list of chat alert audio files for |
| ︙ | ︙ | |||
317 318 319 320 321 322 323 |
" ORDER BY msgid LIMIT 1");
if( rAge>mxDays ){
msgid = db_int(0, "SELECT msgid FROM chat"
" ORDER BY msgid DESC LIMIT 1 OFFSET %d", mxCnt);
if( msgid>0 ){
Stmt s;
db_multi_exec("PRAGMA secure_delete=ON;");
| | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
" ORDER BY msgid LIMIT 1");
if( rAge>mxDays ){
msgid = db_int(0, "SELECT msgid FROM chat"
" ORDER BY msgid DESC LIMIT 1 OFFSET %d", mxCnt);
if( msgid>0 ){
Stmt s;
db_multi_exec("PRAGMA secure_delete=ON;");
db_prepare(&s,
"DELETE FROM chat WHERE mtime<julianday('now')-:mxage"
" AND msgid<%d", msgid);
db_bind_double(&s, ":mxage", mxDays);
db_step(&s);
db_finalize(&s);
}
}
|
| ︙ | ︙ | |||
691 692 693 694 695 696 697 |
}
sqlite3_sleep(iDelay); nDelay--;
}
} /* Exit by "break" */
db_finalize(&q1);
blob_append(&json, "\n]}", 3);
cgi_set_content(&json);
| | | 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 |
}
sqlite3_sleep(iDelay); nDelay--;
}
} /* Exit by "break" */
db_finalize(&q1);
blob_append(&json, "\n]}", 3);
cgi_set_content(&json);
return;
}
/*
** WEBPAGE: chat-fetch-one hidden loadavg-exempt
**
** /chat-fetch-one/N
**
|
| ︙ | ︙ | |||
724 725 726 727 728 729 730 |
if( !g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
zChatUser = db_get("chat-timeline-user",0);
chat_create_tables();
cgi_set_content_type("application/json");
| | | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
if( !g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
zChatUser = db_get("chat-timeline-user",0);
chat_create_tables();
cgi_set_content_type("application/json");
db_prepare(&q,
"SELECT datetime(mtime), xfrom, xmsg, octet_length(file),"
" fname, fmime, lmtime"
" FROM chat WHERE msgid=%d AND mdel IS NULL",
msgid);
if(SQLITE_ROW==db_step(&q)){
const char *zDate = db_column_text(&q, 0);
const char *zFrom = db_column_text(&q, 1);
|
| ︙ | ︙ | |||
767 768 769 770 771 772 773 |
fossil_free(zMsg);
}
if( nByte==0 ){
blob_appendf(&json, "\"fsize\":0");
}else{
blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
nByte, zFName, zFMime);
| | | 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
fossil_free(zMsg);
}
if( nByte==0 ){
blob_appendf(&json, "\"fsize\":0");
}else{
blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
nByte, zFName, zFMime);
}
blob_append(&json,"}",1);
cgi_set_content(&json);
}else{
ajax_route_error(404,"Chat message #%d not found.", msgid);
}
db_finalize(&q);
}
|
| ︙ | ︙ | |||
955 956 957 958 959 960 961 |
sqlite3_value **argv
){
const char *zType = (const char*)sqlite3_value_text(argv[0]);
int rid = sqlite3_value_int(argv[1]);
const char *zUser = (const char*)sqlite3_value_text(argv[2]);
const char *zMsg = (const char*)sqlite3_value_text(argv[3]);
char *zRes = 0;
| | | 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 |
sqlite3_value **argv
){
const char *zType = (const char*)sqlite3_value_text(argv[0]);
int rid = sqlite3_value_int(argv[1]);
const char *zUser = (const char*)sqlite3_value_text(argv[2]);
const char *zMsg = (const char*)sqlite3_value_text(argv[3]);
char *zRes = 0;
if( zType==0 || zUser==0 || zMsg==0 ) return;
if( zType[0]=='c' ){
/* Check-ins */
char *zBranch;
char *zUuid;
zBranch = db_text(0,
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 |
"#\n%.78c\n"
"# The following diff is excluded from the commit message:\n#\n",
'#'
);
diff_options(&DCfg, 0, 1);
DCfg.diffFlags |= DIFF_VERBOSE;
if( g.aCommitFile ){
FileDirList *diffFiles;
int i;
for(i=0; g.aCommitFile[i]!=0; ++i){}
diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles));
for(i=0; g.aCommitFile[i]!=0; ++i){
| > > > > > > > > | < > > > > > > > | 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 |
"#\n%.78c\n"
"# The following diff is excluded from the commit message:\n#\n",
'#'
);
diff_options(&DCfg, 0, 1);
DCfg.diffFlags |= DIFF_VERBOSE;
if( g.aCommitFile ){
Stmt q;
Blob sql = BLOB_INITIALIZER;
FileDirList *diffFiles;
int i;
for(i=0; g.aCommitFile[i]!=0; ++i){}
diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles));
for(i=0; g.aCommitFile[i]!=0; ++i){
blob_append_sql(&sql,
"SELECT pathname, deleted, rid WHERE id=%d",
g.aCommitFile[i]);
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_reset(&sql);
assert( db_step(&q)==SQLITE_ROW );
diffFiles[i].zName = fossil_strdup(db_column_text(&q, 0));
DCfg.diffFlags &= (~DIFF_FILE_MASK);
if( db_column_int(&q, 1) ){
DCfg.diffFlags |= DIFF_FILE_DELETED;
}else if( db_column_int(&q, 2)==0 ){
DCfg.diffFlags |= DIFF_FILE_ADDED;
}
db_finalize(&q);
if( fossil_strcmp(diffFiles[i].zName, "." )==0 ){
diffFiles[0].zName[0] = '.';
diffFiles[0].zName[1] = 0;
break;
}
diffFiles[i].nName = strlen(diffFiles[i].zName);
diffFiles[i].nUsed = 0;
|
| ︙ | ︙ | |||
2519 2520 2521 2522 2523 2524 2525 |
"use --override-lock",
g.ckinLockFail);
}else{
fossil_fatal("Would fork. \"update\" first or use --branch or "
"--allow-fork.");
}
}
| | | | 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 |
"use --override-lock",
g.ckinLockFail);
}else{
fossil_fatal("Would fork. \"update\" first or use --branch or "
"--allow-fork.");
}
}
/*
** Do not allow a commit against a closed leaf unless the commit
** ends up on a different branch.
*/
if(
/* parent check-in has the "closed" tag... */
leaf_is_closed(vid)
/* ... and the new check-in has no --branch option or the --branch
** option does not actually change the branch */
&& (sCiInfo.zBranch==0
|| db_exists("SELECT 1 FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0"
" AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
){
fossil_fatal("cannot commit against a closed leaf");
}
/* Always exit the loop on the second pass */
if( bRecheck ) break;
/* Get the check-in comment. This might involve prompting the
** user for the check-in comment, in which case we should resync
** to renew the check-in lock and repeat the checks for conflicts.
*/
if( zComment ){
blob_zero(&comment);
blob_append(&comment, zComment, -1);
|
| ︙ | ︙ |
Changes to src/clone.c.
| ︙ | ︙ | |||
202 203 204 205 206 207 208 |
g.argv[2]);
}
zRepo = mprintf("./%s.fossil", zBase);
if( zWorkDir==0 ){
zWorkDir = mprintf("./%s", zBase);
}
fossil_free(zBase);
| | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
g.argv[2]);
}
zRepo = mprintf("./%s.fossil", zBase);
if( zWorkDir==0 ){
zWorkDir = mprintf("./%s", zBase);
}
fossil_free(zBase);
}
if( -1 != file_size(zRepo, ExtFILE) ){
db_open_repository(zRepo);
nResumeSeqno = db_get_int("aux-clone-seqno",0);
db_close(0);
if( !nResumeSeqno ){
fossil_fatal("file already exists: %s", zRepo);
}
|
| ︙ | ︙ |
Changes to src/comformat.c.
| ︙ | ︙ | |||
272 273 274 275 276 277 278 |
if( maxChars<useChars ){
zBuf[iBuf++] = ' ';
break;
}
}else if( wordBreak && fossil_isspace(c) ){
int distUTF8;
int nextIndex = comment_next_space(zLine, index, &distUTF8);
| | | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
if( maxChars<useChars ){
zBuf[iBuf++] = ' ';
break;
}
}else if( wordBreak && fossil_isspace(c) ){
int distUTF8;
int nextIndex = comment_next_space(zLine, index, &distUTF8);
if( nextIndex<=0 || distUTF8>=maxChars ){
break;
}
charCnt++;
}else{
charCnt++;
}
assert( c!='\n' || charCnt==0 );
|
| ︙ | ︙ |
Changes to src/cookies.c.
| ︙ | ︙ | |||
237 238 239 240 241 242 243 |
zDel = mprintf("del%s",zName);
if( P(zDel)!=0 ){
cgi_set_cookie(zName, "", 0, -1);
cgi_redirect("cookies");
}
nCookie++;
@ <li><p><b>%h(zName)</b>: %h(zValue)
| | | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
zDel = mprintf("del%s",zName);
if( P(zDel)!=0 ){
cgi_set_cookie(zName, "", 0, -1);
cgi_redirect("cookies");
}
nCookie++;
@ <li><p><b>%h(zName)</b>: %h(zValue)
@ <input type="submit" name="%h(zDel)" value="Delete">
if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0 && cookies.nParam>0 ){
int j;
@ <ul>
for(j=0; j<cookies.nParam; j++){
@ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)"
}
@ </ul>
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
170 171 172 173 174 175 176 |
void *pAuthArg; /* Argument to the authorizer */
const char *zAuthName; /* Name of the authorizer */
int bProtectTriggers; /* True if protection triggers already exist */
int nProtect; /* Slots of aProtect used */
unsigned aProtect[12]; /* Saved values of protectMask */
} db = {
PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
| | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
void *pAuthArg; /* Argument to the authorizer */
const char *zAuthName; /* Name of the authorizer */
int bProtectTriggers; /* True if protection triggers already exist */
int nProtect; /* Slots of aProtect used */
unsigned aProtect[12]; /* Saved values of protectMask */
} db = {
PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
if( zFilename==0 ) return;
|
| ︙ | ︙ | |||
455 456 457 458 459 460 461 |
** be compromised by an attack.
*/
void db_protect_only(unsigned flags){
if( db.nProtect>=count(db.aProtect)-2 ){
fossil_panic("too many db_protect() calls");
}
db.aProtect[db.nProtect++] = db.protectMask;
| | | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
** be compromised by an attack.
*/
void db_protect_only(unsigned flags){
if( db.nProtect>=count(db.aProtect)-2 ){
fossil_panic("too many db_protect() calls");
}
db.aProtect[db.nProtect++] = db.protectMask;
if( (flags & PROTECT_SENSITIVE)!=0
&& db.bProtectTriggers==0
&& g.repositoryOpen
){
/* Create the triggers needed to protect sensitive settings from
** being created or modified the first time that PROTECT_SENSITIVE
** is enabled. Deleting a sensitive setting is harmless, so there
** is not trigger to block deletes. After being created once, the
|
| ︙ | ︙ | |||
1555 1556 1557 1558 1559 1560 1561 |
sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0,
db_protected_setting_func, 0, 0);
sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
db_win_reserved_func,0,0);
sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
url_nouser_func,0,0);
sqlite3_create_function(db, "chat_msg_from_event", 4,
| | | | 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 |
sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0,
db_protected_setting_func, 0, 0);
sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
db_win_reserved_func,0,0);
sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
url_nouser_func,0,0);
sqlite3_create_function(db, "chat_msg_from_event", 4,
SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
chat_msg_from_event, 0, 0);
}
#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
static char *zSavedKey = 0;
|
| ︙ | ︙ | |||
2487 2488 2489 2490 2491 2492 2493 |
db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
}
if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
}
}
| | | 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 |
db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
}
if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
}
}
/* The design of the check-out database changed on 2019-01-19 adding the mhash
** column to vfile and vmerge and changing the UNIQUE index on vmerge into
** a PRIMARY KEY that includes the new mhash column. However, we must have
** the repository database at hand in order to do the migration, so that
** step is deferred. */
return 1;
}
|
| ︙ | ︙ | |||
2604 2605 2606 2607 2608 2609 2610 | sqlite3_stmt *pStmt = 0; sz = file_size(zDbName, ExtFILE); if( sz<16834 ) return 0; db = db_open(zDbName); if( !db ) return 0; if( !g.zVfsName && sz%512 ) return 0; | | | 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 |
sqlite3_stmt *pStmt = 0;
sz = file_size(zDbName, ExtFILE);
if( sz<16834 ) return 0;
db = db_open(zDbName);
if( !db ) return 0;
if( !g.zVfsName && sz%512 ) return 0;
rc = sqlite3_prepare_v2(db,
"SELECT count(*) FROM sqlite_schema"
" WHERE name COLLATE nocase IN"
"('blob','delta','rcvfrom','user','config','mlink','plink');",
-1, &pStmt, 0);
if( rc ) goto is_repo_end;
rc = sqlite3_step(pStmt);
if( rc!=SQLITE_ROW ) goto is_repo_end;
|
| ︙ | ︙ | |||
3714 3715 3716 3717 3718 3719 3720 |
z = fossil_strdup(pSetting->def);
}else{
z = fossil_strdup(zDefault);
}
}
return z;
}
| | > | 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 |
z = fossil_strdup(pSetting->def);
}else{
z = fossil_strdup(zDefault);
}
}
return z;
}
char *db_get_mtime(const char *zName, const char *zFormat,
const char *zDefault){
char *z = 0;
if( g.repositoryOpen ){
z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
}
if( z==0 ){
z = fossil_strdup(zDefault);
}else if( zFormat!=0 ){
|
| ︙ | ︙ | |||
4019 4020 4021 4022 4023 4024 4025 |
if( !g.localOpen ) return;
zName = db_repository_filename();
}
file_canonical_name(zName, &full, 0);
(void)filename_collation(); /* Initialize before connection swap */
db_swap_connections();
zRepoSetting = mprintf("repo:%q", blob_str(&full));
| | | 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 |
if( !g.localOpen ) return;
zName = db_repository_filename();
}
file_canonical_name(zName, &full, 0);
(void)filename_collation(); /* Initialize before connection swap */
db_swap_connections();
zRepoSetting = mprintf("repo:%q", blob_str(&full));
db_unprotect(PROTECT_CONFIG);
db_multi_exec(
"DELETE FROM global_config WHERE name %s = %Q;",
filename_collation(), zRepoSetting
);
db_multi_exec(
"INSERT OR IGNORE INTO global_config(name,value)"
|
| ︙ | ︙ | |||
4096 4097 4098 4099 4100 4101 4102 | ** "new-name.fossil". ** ** Options: ** --empty Initialize check-out as being empty, but still connected ** with the local repository. If you commit this check-out, ** it will become a new "initial" commit in the repository. ** -f|--force Continue with the open even if the working directory is | | < < | 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 | ** "new-name.fossil". ** ** Options: ** --empty Initialize check-out as being empty, but still connected ** with the local repository. If you commit this check-out, ** it will become a new "initial" commit in the repository. ** -f|--force Continue with the open even if the working directory is ** not empty, or if auto-sync fails. ** --force-missing Force opening a repository with missing content ** -k|--keep Only modify the manifest file(s) ** --nested Allow opening a repository inside an opened check-out ** --nosync Do not auto-sync the repository prior to opening even ** if the autosync setting is on. ** --repodir DIR If REPOSITORY is a URI that will be cloned, store ** the clone in DIR rather than in "." ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last check-in which modified ** them). ** --verbose If passed a URI then this flag is passed on to the clone ** operation, otherwise it has no effect ** --workdir DIR Use DIR as the working directory instead of ".". The DIR ** directory is created if it does not exist. ** ** See also: [[close]], [[clone]] */ |
| ︙ | ︙ | |||
4185 4186 4187 4188 4189 4190 4191 |
if( keepFlag==0
&& bForce==0
&& (nLocal = file_directory_size(".", 0, 1))>0
&& (nLocal>1 || isUri || !file_in_cwd(zRepo))
){
fossil_fatal("directory %s is not empty\n"
"use the -f (--force) option to override\n"
| | | 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 |
if( keepFlag==0
&& bForce==0
&& (nLocal = file_directory_size(".", 0, 1))>0
&& (nLocal>1 || isUri || !file_in_cwd(zRepo))
){
fossil_fatal("directory %s is not empty\n"
"use the -f (--force) option to override\n"
"or the -k (--keep) option to keep local files unchanged",
file_getcwd(0,0));
}
if( db_open_local_v2(0, allowNested) ){
fossil_fatal("there is already an open tree at %s", g.zLocalRoot);
}
|
| ︙ | ︙ | |||
4394 4395 4396 4397 4398 4399 4400 | ** ** When the admin-log setting is enabled, configuration changes are recorded ** in the "admin_log" table of the repository. */ /* ** SETTING: allow-symlinks boolean default=off sensitive ** | | | 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 | ** ** When the admin-log setting is enabled, configuration changes are recorded ** in the "admin_log" table of the repository. */ /* ** SETTING: allow-symlinks boolean default=off sensitive ** ** When allow-symlinks is OFF, Fossil does not see symbolic links ** (a.k.a "symlinks") on disk as a separate class of object. Instead Fossil ** sees the object that the symlink points to. Fossil will only manage files ** and directories, not symlinks. When a symlink is added to a repository, ** the object that the symlink points to is added, not the symlink itself. ** ** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate ** object class that is distinct from files and directories. When a symlink |
| ︙ | ︙ | |||
4452 4453 4454 4455 4456 4457 4458 | ** When the auto-hyperlink setting is 1, the javascript that runs to set ** the href= attributes of hyperlinks delays by this many milliseconds ** after the page load. Suggested values: 50 to 200. */ /* ** SETTING: auto-hyperlink-mouseover boolean default=off ** | | | 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 | ** When the auto-hyperlink setting is 1, the javascript that runs to set ** the href= attributes of hyperlinks delays by this many milliseconds ** after the page load. Suggested values: 50 to 200. */ /* ** SETTING: auto-hyperlink-mouseover boolean default=off ** ** When the auto-hyperlink setting is 1 and this setting is on, the ** javascript that runs to set the href= attributes of hyperlinks waits ** until either a mousedown or mousemove event is seen. This helps ** to distinguish real users from robots. For maximum robot defense, ** the recommended setting is ON. */ /* ** SETTING: auto-shun boolean default=on |
| ︙ | ︙ | |||
4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 | ** off,commit=pullonly Do not autosync, except do a pull before each ** "commit", presumably to avoid undesirable ** forks. ** ** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries. ** A plain VALUE entry is the default that is used if no COMMAND matches. ** Otherwise, the VALUE of the matching command is used. */ /* ** SETTING: autosync-tries width=16 default=1 ** If autosync is enabled setting this to a value greater ** than zero will cause autosync to try no more than this ** number of attempts if there is a sync failure. */ | > > > | 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 | ** off,commit=pullonly Do not autosync, except do a pull before each ** "commit", presumably to avoid undesirable ** forks. ** ** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries. ** A plain VALUE entry is the default that is used if no COMMAND matches. ** Otherwise, the VALUE of the matching command is used. ** ** The "all" value is special in that it applies to the "sync" command in ** addition to "commit", "merge", "open", and "update". */ /* ** SETTING: autosync-tries width=16 default=1 ** If autosync is enabled setting this to a value greater ** than zero will cause autosync to try no more than this ** number of attempts if there is a sync failure. */ |
| ︙ | ︙ | |||
4673 4674 4675 4676 4677 4678 4679 | ** Note that /fileedit cannot edit binary files, so the list should not ** contain any globs for, e.g., images or PDFs. */ /* ** SETTING: forbid-delta-manifests boolean default=off ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts | | | 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 | ** Note that /fileedit cannot edit binary files, so the list should not ** contain any globs for, e.g., images or PDFs. */ /* ** SETTING: forbid-delta-manifests boolean default=off ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will ** send the "pragma avoid-delta-manifests" statement in its reply, ** which will cause the client to avoid generating a delta ** manifest. */ /* ** SETTING: forum-close-policy boolean default=off ** If true, forum moderators may close/re-open forum posts, and reply |
| ︙ | ︙ | |||
5014 5015 5016 5017 5018 5019 5020 | ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. */ /* ** SETTING: large-file-size width=10 default=200000000 ** Fossil considers any file whose size is greater than this value ** to be a "large file". Fossil might issue warnings if you try to | | | 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 | ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. */ /* ** SETTING: large-file-size width=10 default=200000000 ** Fossil considers any file whose size is greater than this value ** to be a "large file". Fossil might issue warnings if you try to ** "add" or "commit" a "large file". Set this value to 0 or less ** to disable all such warnings. */ /* ** Look up a control setting by its name. Return a pointer to the Setting ** object, or NULL if there is no such setting. ** |
| ︙ | ︙ | |||
5239 5240 5241 5242 5243 5244 5245 | ** optimization. FILENAME can also be the configuration database file ** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file. ** ** The purpose of this command is for testing the WITHOUT ROWID capabilities ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil. ** ** Options: | | | 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 |
** optimization. FILENAME can also be the configuration database file
** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file.
**
** The purpose of this command is for testing the WITHOUT ROWID capabilities
** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil.
**
** Options:
** -n|--dry-run No changes. Just print what would happen.
*/
void test_without_rowid(void){
int i, j;
Stmt q;
Blob allSql;
int dryRun = find_option("dry-run", "n", 0)!=0;
for(i=2; i<g.argc; i++){
|
| ︙ | ︙ |
Changes to src/deltafunc.c.
| ︙ | ︙ | |||
484 485 486 487 488 489 490 | /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, | | > | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindMethod */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0,
/* xIntegrity */ 0
};
/*
** Invoke this routine to register the various delta functions.
*/
int deltafunc_init(sqlite3 *db){
int rc = SQLITE_OK;
|
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#define DIFF_BROWSER 0x00008000 /* The --browser option */
#define DIFF_JSON 0x00010000 /* JSON output */
#define DIFF_DEBUG 0x00020000 /* Debugging diff output */
#define DIFF_RAW 0x00040000 /* Raw triples - for debugging */
#define DIFF_TCL 0x00080000 /* For the --tk option */
#define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */
#define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */
/*
** These error messages are shared in multiple locations. They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
"cannot compute difference between binary files\n"
| > > > > > > > > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#define DIFF_BROWSER 0x00008000 /* The --browser option */
#define DIFF_JSON 0x00010000 /* JSON output */
#define DIFF_DEBUG 0x00020000 /* Debugging diff output */
#define DIFF_RAW 0x00040000 /* Raw triples - for debugging */
#define DIFF_TCL 0x00080000 /* For the --tk option */
#define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */
#define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */
#define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */
/*
** Per file information that may influence output.
*/
#define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */
#define DIFF_FILE_DELETED 0x80000000 /* Deleted or rename source */
#define DIFF_FILE_MASK 0xc0000000 /* Used for clearing file flags */
/*
** These error messages are shared in multiple locations. They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
"cannot compute difference between binary files\n"
|
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | ** Conceptually, this object is as an encoding of the command-line options ** for the "fossil diff" command. That is not a precise description, though, ** because not all diff operations are started from the command-line. But ** the idea is sound. ** ** Information encoded by this object includes but is not limited to: ** | | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
** Conceptually, this object is as an encoding of the command-line options
** for the "fossil diff" command. That is not a precise description, though,
** because not all diff operations are started from the command-line. But
** the idea is sound.
**
** Information encoded by this object includes but is not limited to:
**
** * The desired output format (unified vs. side-by-side,
** TCL, JSON, HTML vs. plain-text).
**
** * Number of lines of context surrounding each difference block
**
** * Width of output columns for text side-by-side diffop
*/
struct DiffConfig {
u64 diffFlags; /* Diff flags */
int nContext; /* Number of lines of context */
int wColumn; /* Column width in -y mode */
u32 nFile; /* Number of files diffed so far */
const char *zDiffCmd; /* External diff command to use instead of builtin */
|
| ︙ | ︙ | |||
420 421 422 423 424 425 426 |
A = p->aFrom;
B = p->aTo;
R = p->aEdit;
mxr = p->nEdit;
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
for(r=0; r<mxr; r += 3*nr){
/* Figure out how many triples to show in a single block */
| | | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
A = p->aFrom;
B = p->aTo;
R = p->aEdit;
mxr = p->nEdit;
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
for(r=0; r<mxr; r += 3*nr){
/* Figure out how many triples to show in a single block */
for(nr=1; 3*nr<mxr && R[r+nr*3]>0 && R[r+nr*3]<(int)nContext*2; nr++){}
/* printf("r=%d nr=%d\n", r, nr); */
/* For the current block comprising nr triples, figure out
** how many lines of A and B are to be displayed
*/
if( R[r]>nContext ){
na = nb = nContext;
|
| ︙ | ︙ | |||
905 906 907 908 909 910 911 | /* ** This is an abstract superclass for an object that accepts difference ** lines and formats them for display. Subclasses of this object format ** the diff output in different ways. ** ** To subclass, create an instance of the DiffBuilder object and fill ** in appropriate method implementations. | | | 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 |
/*
** This is an abstract superclass for an object that accepts difference
** lines and formats them for display. Subclasses of this object format
** the diff output in different ways.
**
** To subclass, create an instance of the DiffBuilder object and fill
** in appropriate method implementations.
*/
typedef struct DiffBuilder DiffBuilder;
struct DiffBuilder {
void (*xSkip)(DiffBuilder*, unsigned int, int);
void (*xCommon)(DiffBuilder*,const DLine*);
void (*xInsert)(DiffBuilder*,const DLine*);
void (*xDelete)(DiffBuilder*,const DLine*);
void (*xReplace)(DiffBuilder*,const DLine*,const DLine*);
|
| ︙ | ︙ | |||
1090 1091 1092 1093 1094 1095 1096 |
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ' ');
| | | 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 |
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut,
pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
}
if( x<pX->n ){
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, pX->n - x);
}
blob_append_char(p->pOut, '\n');
|
| ︙ | ︙ | |||
1176 1177 1178 1179 1180 1181 1182 |
}
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ',');
| | | 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 |
}
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut,
pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
}
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, pX->n - x);
blob_append(p->pOut, "],\n",3);
}
static void dfjsonEnd(DiffBuilder *p){
|
| ︙ | ︙ | |||
1258 1259 1260 1261 1262 1263 1264 | /* "+" marks for the separator on inserted lines */ for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2); /* Text of the inserted lines */ blob_append(&p->aCol[2], "<ins>", 5); blob_append_xfer(&p->aCol[2], &p->aCol[4]); blob_append(&p->aCol[2], "</ins>", 6); | | | 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 |
/* "+" marks for the separator on inserted lines */
for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2);
/* Text of the inserted lines */
blob_append(&p->aCol[2], "<ins>", 5);
blob_append_xfer(&p->aCol[2], &p->aCol[4]);
blob_append(&p->aCol[2], "</ins>", 6);
p->nPending = 0;
}
static void dfunifiedFinishRow(DiffBuilder *p){
dfunifiedFinishDelete(p);
dfunifiedFinishInsert(p);
if( blob_size(&p->aCol[0])==0 ) return;
blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n", -1);
|
| ︙ | ︙ | |||
1995 1996 1997 1998 1999 2000 2001 |
aBig = aRight;
nBig = nRight;
}
iDivBig = nBig/2;
iDivSmall = nSmall/2;
if( pCfg->diffFlags & DIFF_DEBUG ){
| | | 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 |
aBig = aRight;
nBig = nRight;
}
iDivBig = nBig/2;
iDivSmall = nSmall/2;
if( pCfg->diffFlags & DIFF_DEBUG ){
fossil_print(" Divide at [%.*s]\n",
aBig[iDivBig].n, aBig[iDivBig].z);
}
bestScore = 10000;
for(i=0; i<nSmall; i++){
score = match_dline(aBig+iDivBig, aSmall+i) + abs(i-nSmall/2)*2;
if( score<bestScore ){
|
| ︙ | ︙ | |||
2221 2222 2223 2224 2225 2226 2227 |
B = p->aTo;
R = p->aEdit;
mxr = p->nEdit;
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
for(r=0; r<mxr; r += 3*nr){
/* Figure out how many triples to show in a single block */
| | | 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 |
B = p->aTo;
R = p->aEdit;
mxr = p->nEdit;
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
for(r=0; r<mxr; r += 3*nr){
/* Figure out how many triples to show in a single block */
for(nr=1; 3*nr<mxr && R[r+nr*3]>0 && R[r+nr*3]<(int)nContext*2; nr++){}
/* If there is a regex, skip this block (generate no diff output)
** if the regex matches or does not match both insert and delete.
** Only display the block if one side matches but the other side does
** not.
*/
if( pCfg->pRe ){
|
| ︙ | ︙ | |||
3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 |
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
pCfg->wColumn = f;
}
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
if( find_option("internal","i",0)==0
&& (diffFlags & (DIFF_HTML|DIFF_TCL|DIFF_DEBUG|DIFF_JSON))==0
){
pCfg->zDiffCmd = find_option("command", 0, 1);
if( pCfg->zDiffCmd==0 ) pCfg->zDiffCmd = diff_command_external(isGDiff);
| > | 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 |
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
pCfg->wColumn = f;
}
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE;
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
if( find_option("internal","i",0)==0
&& (diffFlags & (DIFF_HTML|DIFF_TCL|DIFF_DEBUG|DIFF_JSON))==0
){
pCfg->zDiffCmd = find_option("command", 0, 1);
if( pCfg->zDiffCmd==0 ) pCfg->zDiffCmd = diff_command_external(isGDiff);
|
| ︙ | ︙ | |||
3482 3483 3484 3485 3486 3487 3488 |
}
p->nVers++;
cnt++;
}
if( p->nVers==0 ){
if( zRevision ){
| | > | 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 |
}
p->nVers++;
cnt++;
}
if( p->nVers==0 ){
if( zRevision ){
fossil_fatal("file %s does not exist in check-in %s",
zFilename, zRevision);
}else{
fossil_fatal("no history for file: %s", zFilename);
}
}
db_finalize(&q);
db_end_transaction(0);
|
| ︙ | ︙ |
Changes to src/diff.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 |
# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
# to this file, then runs this file using "tclsh" in order to display the
# graphical diff in a separate window. A typical "set fossilcmd" line
# looks like this:
#
# set fossilcmd {| "./fossil" diff --html -y -i -v}
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
set prog {
package require Tk
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
# to this file, then runs this file using "tclsh" in order to display the
# graphical diff in a separate window. A typical "set fossilcmd" line
# looks like this:
#
# set fossilcmd {| "./fossil" diff --html -y -i -v}
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
set prog {
package require Tk
array set CFG_light {
TITLE {Fossil Diff}
LN_COL_BG #dddddd
LN_COL_FG #444444
TXT_COL_BG #ffffff
TXT_COL_FG #000000
MKR_COL_BG #444444
MKR_COL_FG #dddddd
|
| ︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
ERR_FG #ee0000
PADX 5
WIDTH 80
HEIGHT 45
LB_HEIGHT 25
}
if {![namespace exists ttk]} {
interp alias {} ::ttk::scrollbar {} ::scrollbar
interp alias {} ::ttk::menubutton {} ::menubutton
}
proc dehtml {x} {
set x [regsub -all {<[^>]*>} $x {}]
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
ERR_FG #ee0000
PADX 5
WIDTH 80
HEIGHT 45
LB_HEIGHT 25
}
array set CFG_dark {
TITLE {Fossil Diff}
LN_COL_BG #dddddd
LN_COL_FG #444444
TXT_COL_BG #3f3f3f
TXT_COL_FG #dcdccc
MKR_COL_BG #444444
MKR_COL_FG #dddddd
CHNG_BG #6a6afc
ADD_BG #57934c
RM_BG #ef6767
HR_FG #444444
HR_PAD_TOP 4
HR_PAD_BTM 8
FN_BG #5e5e5e
FN_FG #ffffff
FN_PAD 5
ERR_FG #ee0000
PADX 5
WIDTH 80
HEIGHT 45
LB_HEIGHT 25
}
array set CFG_arr {
0 CFG_light
1 CFG_dark
}
array set CFG [array get $CFG_arr($darkmode)]
if {![namespace exists ttk]} {
interp alias {} ::ttk::scrollbar {} ::scrollbar
interp alias {} ::ttk::menubutton {} ::menubutton
}
proc dehtml {x} {
set x [regsub -all {<[^>]*>} $x {}]
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
111 112 113 114 115 116 117 | } return 0; } /* ** Print details about the compared versions - possibly the working directory ** or the undo buffer. For check-ins, show hash and commit time. | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
}
return 0;
}
/*
** Print details about the compared versions - possibly the working directory
** or the undo buffer. For check-ins, show hash and commit time.
**
** This is intended primarily to go into the "header garbage" that is ignored
** by patch(1).
**
** zFrom and zTo are interpreted as symbolic version names, unless they
** start with '(', in which case they are printed directly.
*/
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
void diff_print_filenames(
const char *zLeft, /* Name of the left file */
const char *zRight, /* Name of the right file */
DiffConfig *pCfg, /* Diff configuration */
Blob *pOut /* Write to this blob, or stdout of this is NULL */
){
u64 diffFlags = pCfg->diffFlags;
if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
/* no-op */
}else if( diffFlags & DIFF_DEBUG ){
blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
}else if( diffFlags & DIFF_WEBPAGE ){
if( fossil_strcmp(zLeft,zRight)==0 ){
blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);
| > > > | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
void diff_print_filenames(
const char *zLeft, /* Name of the left file */
const char *zRight, /* Name of the right file */
DiffConfig *pCfg, /* Diff configuration */
Blob *pOut /* Write to this blob, or stdout of this is NULL */
){
u64 diffFlags = pCfg->diffFlags;
/* Standardize on /dev/null, regardless of platform. */
if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
/* no-op */
}else if( diffFlags & DIFF_DEBUG ){
blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
}else if( diffFlags & DIFF_WEBPAGE ){
if( fossil_strcmp(zLeft,zRight)==0 ){
blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);
|
| ︙ | ︙ | |||
211 212 213 214 215 216 217 |
}else{
blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight);
}
}
/*
| | | | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
}else{
blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight);
}
}
/*
** Default header texts for diff with --webpage
*/
static const char zWebpageHdr[] =
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ body {
@ background-color: white;
|
| ︙ | ︙ | |||
308 309 310 311 312 313 314 |
@ font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
@ font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
@ @media (prefers-color-scheme: dark) {
@ body {
@ background-color: #353535;
@ color: #ffffff;
@ }
@ td.diffln ins {
@ background-color: #559855;
@ color: #000000;
@ }
@ td.diffln del {
@ background-color: #cc5555;
@ color: #000000;
@ }
@ td.difftxt del {
@ background-color: #f9cfcf;
@ color: #000000;
@ }
@ td.difftxt del > del {
@ background-color: #cc5555;
@ color: #000000;
@ }
@ td.difftxt ins {
@ background-color: #a2dbb2;
@ color: #000000;
@ }
@ td.difftxt ins > ins {
@ background-color: #559855;
@ }
@ }
@
@ </style>
@ </head>
@ <body>
;
static const char zWebpageHdrDark[] =
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ body {
@ background-color: #353535;
@ color: #ffffff;
@ }
@ h1 {
@ font-size: 150%;
@ }
@
@ table.diff {
@ width: 100%;
@ border-spacing: 0;
@ border: 1px solid black;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ table.diff td {
@ vertical-align: top;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
@ width: 1px;
@ text-align: right;
@ padding: 0 1em 0 0;
@ }
@ td.difflne {
@ padding-bottom: 0.4em;
@ }
@ td.diffsep {
@ width: 1px;
@ padding: 0 0.3em 0 1em;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffsep pre {
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.difftxt pre {
@ overflow-x: auto;
@ }
@ td.diffln ins {
@ background-color: #559855;
@ color: #000000;
@ text-decoration: none;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln del {
@ background-color: #cc5555;
@ color: #000000;
@ text-decoration: none;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.difftxt del {
@ background-color: #f9cfcf;
@ color: #000000;
@ text-decoration: none;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.difftxt del > del {
@ background-color: #cc5555;
@ color: #000000;
@ text-decoration: none;
@ font-weight: bold;
@ }
@ td.difftxt del > del.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
@ td.difftxt ins {
@ background-color: #a2dbb2;
@ color: #000000;
@ text-decoration: none;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.difftxt ins > ins {
@ background-color: #559855;
@ text-decoration: none;
@ font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
@
@ </style>
@ </head>
@ <body>
;
const char zWebpageEnd[] =
@ </body>
@ </html>
;
/*
** State variables used by the --browser option for diff. These must
** be static variables, not elements of DiffConfig, since they are
|
| ︙ | ︙ | |||
376 377 378 379 380 381 382 |
#ifndef _WIN32
signal(SIGINT, diff_www_interrupt);
#else
SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
#endif
}
if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
| > | | | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
#ifndef _WIN32
signal(SIGINT, diff_www_interrupt);
#else
SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
#endif
}
if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
fossil_print("%s",(pCfg->diffFlags & DIFF_DARKMODE)!=0 ? zWebpageHdrDark :
zWebpageHdr);
fflush(stdout);
}
}
/* Do any final output required by a diff and complete the diff
** process.
**
** For --browser and --webpage, output any javascript required by
** the diff. (Currently JS is only needed for side-by-side diffs).
**
** For --browser, close the connection to the temporary file, then
** launch a web browser to view the file. After a delay
** of FOSSIL_BROWSER_DIFF_DELAY milliseconds, delete the temp file.
*/
void diff_end(DiffConfig *pCfg, int nErr){
|
| ︙ | ︙ | |||
436 437 438 439 440 441 442 |
if( pCfg->zDiffCmd==0 ){
Blob out; /* Diff output text */
Blob file2; /* Content of zFile2 */
const char *zName2; /* Name of zFile2 for display */
/* Read content of zFile2 into memory */
blob_zero(&file2);
| | | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
if( pCfg->zDiffCmd==0 ){
Blob out; /* Diff output text */
Blob file2; /* Content of zFile2 */
const char *zName2; /* Name of zFile2 for display */
/* Read content of zFile2 into memory */
blob_zero(&file2);
if( pCfg->diffFlags & DIFF_FILE_DELETED || file_size(zFile2, ExtFILE)<0 ){
zName2 = NULL_DEVICE;
}else{
blob_read_from_file(&file2, zFile2, ExtFILE);
zName2 = zName;
}
/* Compute and output the differences */
|
| ︙ | ︙ | |||
467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
}
/* Release memory resources */
blob_reset(&file2);
}else{
Blob nameFile1; /* Name of temporary file to old pFile1 content */
Blob cmd; /* Text of command to run */
if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
Blob file2;
if( looks_like_binary(pFile1) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
return;
}
| > | 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 |
}
/* Release memory resources */
blob_reset(&file2);
}else{
Blob nameFile1; /* Name of temporary file to old pFile1 content */
Blob cmd; /* Text of command to run */
int useTempfile = 1;
if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
Blob file2;
if( looks_like_binary(pFile1) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
return;
}
|
| ︙ | ︙ | |||
498 499 500 501 502 503 504 |
}
blob_reset(&file2);
}
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
file_tempname(&nameFile1, zFile2, "orig");
| > > > > > > > > > | | | 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
}
blob_reset(&file2);
}
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
file_tempname(&nameFile1, zFile2, "orig");
#if !defined(_WIN32)
/* On Unix, use /dev/null for added or deleted files. */
if( pCfg->diffFlags & DIFF_FILE_ADDED ){
blob_init(&nameFile1, NULL_DEVICE, -1);
useTempfile = 0;
}else if( pCfg->diffFlags & DIFF_FILE_DELETED ){
zFile2 = NULL_DEVICE;
}
#endif
if( useTempfile ) blob_write_to_file(pFile1, blob_str(&nameFile1));
/* Construct the external diff command */
blob_zero(&cmd);
blob_append(&cmd, pCfg->zDiffCmd, -1);
if( pCfg->diffFlags & DIFF_INVERT ){
blob_append_escaped_arg(&cmd, zFile2, 1);
blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
}else{
blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
blob_append_escaped_arg(&cmd, zFile2, 1);
}
/* Run the external diff command */
fossil_system(blob_str(&cmd));
/* Delete the temporary file and clean up memory used */
if( useTempfile ) file_delete(blob_str(&nameFile1));
blob_reset(&nameFile1);
blob_reset(&cmd);
}
}
/*
** Show the difference between two files, both in memory.
|
| ︙ | ︙ | |||
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
Blob temp1;
Blob temp2;
if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
return;
}
if( pCfg->zBinGlob ){
Glob *pBinary = glob_create(pCfg->zBinGlob);
if( glob_match(pBinary, zName) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
glob_free(pBinary);
return;
}
glob_free(pBinary);
}
}
| > > | > > > > > > > > > > | | | | | 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 |
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
Blob temp1;
Blob temp2;
int useTempfile1 = 1;
int useTempfile2 = 1;
if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
return;
}
if( pCfg->zBinGlob ){
Glob *pBinary = glob_create(pCfg->zBinGlob);
if( glob_match(pBinary, zName) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
glob_free(pBinary);
return;
}
glob_free(pBinary);
}
}
/* Construct temporary file names */
file_tempname(&temp1, zName, "before");
file_tempname(&temp2, zName, "after");
#if !defined(_WIN32)
/* On Unix, use /dev/null for added or deleted files. */
if( pCfg->diffFlags & DIFF_FILE_ADDED ){
useTempfile1 = 0;
blob_init(&temp1, NULL_DEVICE, -1);
}else if( pCfg->diffFlags & DIFF_FILE_DELETED ){
useTempfile2 = 0;
blob_init(&temp2, NULL_DEVICE, -1);
}
#endif
if( useTempfile1 ) blob_write_to_file(pFile1, blob_str(&temp1));
if( useTempfile2 ) blob_write_to_file(pFile2, blob_str(&temp2));
/* Construct the external diff command */
blob_zero(&cmd);
blob_append(&cmd, pCfg->zDiffCmd, -1);
blob_append_escaped_arg(&cmd, blob_str(&temp1), 1);
blob_append_escaped_arg(&cmd, blob_str(&temp2), 1);
/* Run the external diff command */
fossil_system(blob_str(&cmd));
/* Delete the temporary file and clean up memory used */
if( useTempfile1 ) file_delete(blob_str(&temp1));
if( useTempfile2 ) file_delete(blob_str(&temp2));
blob_reset(&temp1);
blob_reset(&temp2);
blob_reset(&cmd);
}
}
|
| ︙ | ︙ | |||
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 |
blob_zero(&fname);
file_relative_name(zPathname, &fname, 1);
}else{
blob_set(&fname, g.zLocalRoot);
blob_append(&fname, zPathname, -1);
}
zFullName = blob_str(&fname);
if( isDeleted ){
if( !isNumStat ){ fossil_print("DELETED %s\n", zPathname); }
if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; }
}else if( file_access(zFullName, F_OK) ){
if( !isNumStat ){ fossil_print("MISSING %s\n", zPathname); }
if( !asNewFile ){ showDiff = 0; }
}else if( isNew ){
if( !isNumStat ){ fossil_print("ADDED %s\n", zPathname); }
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}else if( isChnged==3 ){
if( !isNumStat ){ fossil_print("ADDED_BY_MERGE %s\n", zPathname); }
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}else if( isChnged==5 ){
if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}
if( showDiff ){
Blob content;
if( !isLink != !file_islink(zFullName) ){
diff_print_index(zPathname, pCfg, 0);
diff_print_filenames(zPathname, zPathname, pCfg, 0);
fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
continue;
}
if( srcid>0 ){
content_get(srcid, &content);
}else{
blob_zero(&content);
}
| > > > > > > > | > | 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 |
blob_zero(&fname);
file_relative_name(zPathname, &fname, 1);
}else{
blob_set(&fname, g.zLocalRoot);
blob_append(&fname, zPathname, -1);
}
zFullName = blob_str(&fname);
pCfg->diffFlags &= (~DIFF_FILE_MASK);
if( isDeleted ){
if( !isNumStat ){ fossil_print("DELETED %s\n", zPathname); }
pCfg->diffFlags |= DIFF_FILE_DELETED;
if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; }
}else if( file_access(zFullName, F_OK) ){
if( !isNumStat ){ fossil_print("MISSING %s\n", zPathname); }
if( !asNewFile ){ showDiff = 0; }
}else if( isNew ){
if( !isNumStat ){ fossil_print("ADDED %s\n", zPathname); }
pCfg->diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}else if( isChnged==3 ){
if( !isNumStat ){ fossil_print("ADDED_BY_MERGE %s\n", zPathname); }
pCfg->diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}else if( isChnged==5 ){
if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
pCfg->diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}
if( showDiff ){
Blob content;
if( !isLink != !file_islink(zFullName) ){
diff_print_index(zPathname, pCfg, 0);
diff_print_filenames(zPathname, zPathname, pCfg, 0);
fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
continue;
}
if( srcid>0 ){
content_get(srcid, &content);
}else{
blob_zero(&content);
}
if( isChnged==0
|| pCfg->diffFlags & DIFF_FILE_DELETED
|| !file_same_as_blob(&content, zFullName)
){
diff_print_index(zPathname, pCfg, pOut);
diff_file(&content, zFullName, zPathname, pCfg, pOut);
}
blob_reset(&content);
}
blob_reset(&fname);
}
|
| ︙ | ︙ | |||
776 777 778 779 780 781 782 |
){
Stmt q;
Blob content;
db_prepare(&q, "SELECT pathname, content FROM undo");
blob_init(&content, 0, 0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions("(undo)", "(workdir)", pCfg);
| | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 |
){
Stmt q;
Blob content;
db_prepare(&q, "SELECT pathname, content FROM undo");
blob_init(&content, 0, 0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions("(undo)", "(workdir)", pCfg);
}
while( db_step(&q)==SQLITE_ROW ){
char *zFullName;
const char *zFile = (const char*)db_column_text(&q, 0);
if( !file_dir_match(pFileDir, zFile) ) continue;
zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
db_column_blob(&q, 1, &content);
diff_file(&content, zFullName, zFile, pCfg, 0);
|
| ︙ | ︙ | |||
863 864 865 866 867 868 869 |
manifest_file_rewind(pFrom);
pFromFile = manifest_file_next(pFrom,0);
pTo = manifest_get_by_name(zTo, 0);
manifest_file_rewind(pTo);
pToFile = manifest_file_next(pTo,0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions(zFrom, zTo, pCfg);
| | > > > | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 |
manifest_file_rewind(pFrom);
pFromFile = manifest_file_next(pFrom,0);
pTo = manifest_get_by_name(zTo, 0);
manifest_file_rewind(pTo);
pToFile = manifest_file_next(pTo,0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions(zFrom, zTo, pCfg);
}
while( pFromFile || pToFile ){
int cmp;
if( pFromFile==0 ){
cmp = +1;
}else if( pToFile==0 ){
cmp = -1;
}else{
cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
}
pCfg->diffFlags &= (~DIFF_FILE_MASK);
if( cmp<0 ){
if( file_dir_match(pFileDir, pFromFile->zName) ){
if( (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_HTML))==0 ){
fossil_print("DELETED %s\n", pFromFile->zName);
}
pCfg->diffFlags |= DIFF_FILE_DELETED;
if( asNewFlag ){
diff_manifest_entry(pFromFile, 0, pCfg);
}
}
pFromFile = manifest_file_next(pFrom,0);
}else if( cmp>0 ){
if( file_dir_match(pFileDir, pToFile->zName) ){
if( (pCfg->diffFlags &
(DIFF_NUMSTAT|DIFF_HTML|DIFF_TCL|DIFF_JSON))==0 ){
fossil_print("ADDED %s\n", pToFile->zName);
}
pCfg->diffFlags |= DIFF_FILE_ADDED;
if( asNewFlag ){
diff_manifest_entry(0, pToFile, pCfg);
}
}
pToFile = manifest_file_next(pTo,0);
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
/* No changes */
|
| ︙ | ︙ | |||
954 955 956 957 958 959 960 961 962 963 964 965 966 967 |
*/
void diff_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
blob_zero(&script);
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
g.nameOfExe, zSubCmd);
find_option("tcl",0,0);
find_option("html",0,0);
find_option("side-by-side","y",0);
find_option("internal","i",0);
| > | 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 |
*/
void diff_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
int bDarkMode = find_option("dark",0,0)!=0;
blob_zero(&script);
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
g.nameOfExe, zSubCmd);
find_option("tcl",0,0);
find_option("html",0,0);
find_option("side-by-side","y",0);
find_option("internal","i",0);
|
| ︙ | ︙ | |||
980 981 982 983 984 985 986 |
blob_appendf(&script, " {%/}", z);
}else{
int j;
blob_append(&script, " ", 1);
for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
}
}
| > | | 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 |
blob_appendf(&script, " {%/}", z);
}else{
int j;
blob_append(&script, " ", 1);
for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
}
}
blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
|
| ︙ | ︙ | |||
1033 1034 1035 1036 1037 1038 1039 | ** out. Or if the FILE arguments are omitted, show all unsaved changes ** currently in the working check-out. ** ** The default output format is a "unified patch" (the same as the ** output of "diff -u" on most unix systems). Many alternative formats ** are available. A few of the more useful alternatives: ** | | | 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | ** out. Or if the FILE arguments are omitted, show all unsaved changes ** currently in the working check-out. ** ** The default output format is a "unified patch" (the same as the ** output of "diff -u" on most unix systems). Many alternative formats ** are available. A few of the more useful alternatives: ** ** --tk Pop up a Tcl/Tk-based GUI to show the diff ** --by Show a side-by-side diff in the default web browser ** -b Show a linear diff in the default web browser ** -y Show a text side-by-side diff ** --webpage Format output as HTML ** --webpage -y HTML output in the side-by-side format ** ** The "--from VERSION" option is used to specify the source check-in |
| ︙ | ︙ | |||
1074 1075 1076 1077 1078 1079 1080 | ** as binary ** --branch BRANCH Show diff of all changes on BRANCH ** --brief Show filenames only ** -b|--browser Show the diff output in a web-browser ** --by Shorthand for "--browser -y" ** -ci|--checkin VERSION Show diff of all changes in VERSION ** --command PROG External diff program. Overrides "diff-command" | | | > > > | | | 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 | ** as binary ** --branch BRANCH Show diff of all changes on BRANCH ** --brief Show filenames only ** -b|--browser Show the diff output in a web-browser ** --by Shorthand for "--browser -y" ** -ci|--checkin VERSION Show diff of all changes in VERSION ** --command PROG External diff program. Overrides "diff-command" ** -c|--context N Show N lines of context around each change, ** with negative N meaning show all content ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML ** --diff-binary BOOL Include binary files with external commands ** --exec-abs-paths Force absolute path names on external commands ** --exec-rel-paths Force relative path names on external commands ** -r|--from VERSION Select VERSION as source for the diff ** -w|--ignore-all-space Ignore white space when comparing lines ** -i|--internal Use internal diff logic ** --invert Invert the diff ** --json Output formatted as JSON ** -n|--linenum Show line numbers ** -N|--new-file Alias for --verbose ** --numstat Show only the number of added and deleted lines ** -y|--side-by-side Side-by-side diff ** --strip-trailing-cr Strip trailing CR ** --tcl Tcl-formated output used internally by --tk ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION Select VERSION as target for the diff ** --undo Diff against the "undo" buffer ** --unified Unified diff ** -v|--verbose Output complete text of added or deleted files ** -h|--versions Show compared versions in the diff header ** --webpage Format output as a stand-alone HTML webpage |
| ︙ | ︙ | |||
1204 1205 1206 1207 1208 1209 1210 |
}
fossil_free(pFileDir[i].zName);
}
fossil_free(pFileDir);
}
diff_end(&DCfg, 0);
if ( DCfg.diffFlags & DIFF_NUMSTAT ){
| | | 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 |
}
fossil_free(pFileDir[i].zName);
}
fossil_free(pFileDir);
}
diff_end(&DCfg, 0);
if ( DCfg.diffFlags & DIFF_NUMSTAT ){
fossil_print("%10d %10d TOTAL over %d changed files\n",
g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
}
}
/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
|
| ︙ | ︙ |
Changes to src/dispatch.c.
| ︙ | ︙ | |||
451 452 453 454 455 456 457 |
aIndent[iLevel] = nIndent;
azEnd[iLevel] = zEndUL;
if( wantP ){
blob_append(pHtml,"<p>", 3);
wantP = 0;
}
blob_append(pHtml, "<ul>\n", 5);
| | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
aIndent[iLevel] = nIndent;
azEnd[iLevel] = zEndUL;
if( wantP ){
blob_append(pHtml,"<p>", 3);
wantP = 0;
}
blob_append(pHtml, "<ul>\n", 5);
}else if( isDT
|| zHelp[nIndent]=='-'
|| hasGap(zHelp+nIndent,i-nIndent) ){
iLevel++;
aIndent[iLevel] = nIndent;
azEnd[iLevel] = zEndDL;
wantP = 0;
blob_append(pHtml, "<blockquote><dl>\n", -1);
|
| ︙ | ︙ | |||
545 546 547 548 549 550 551 |
if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){
if( i>0 ) blob_append(pText, zHelp, i);
zHelp += i+2;
blob_append(pText, zHelp, x-3);
zHelp += x-1;
i = -1;
continue;
| | | | 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){
if( i>0 ) blob_append(pText, zHelp, i);
zHelp += i+2;
blob_append(pText, zHelp, x-3);
zHelp += x-1;
i = -1;
continue;
}
}
if( i>0 ){
blob_append(pText, zHelp, i);
}
}
/*
** Display help for all commands based on provided flags.
*/
static void display_all_help(int mask, int useHtml, int rawOut){
int i;
|
| ︙ | ︙ | |||
633 634 635 636 637 638 639 | ** ** Show help text for commands and pages. Useful for proof-reading. ** Defaults to just the CLI commands. Specify --www to see only the ** web pages, or --everything to see both commands and pages. ** ** Options: ** -a|--aliases Show aliases | | | 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | ** ** Show help text for commands and pages. Useful for proof-reading. ** Defaults to just the CLI commands. Specify --www to see only the ** web pages, or --everything to see both commands and pages. ** ** Options: ** -a|--aliases Show aliases ** -e|--everything Show all commands and pages. Omit aliases to ** avoid duplicates. ** -h|--html Transform output to HTML ** -o|--options Show global options ** -r|--raw No output formatting ** -s|--settings Show settings ** -t|--test Include test- commands ** -w|--www Show WWW pages |
| ︙ | ︙ | |||
659 660 661 662 663 664 665 |
CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST;
}
if( find_option("settings","s",0) ){
mask = CMDFLAG_SETTING;
}
if( find_option("aliases","a",0) ){
mask = CMDFLAG_ALIAS;
| | | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 |
CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST;
}
if( find_option("settings","s",0) ){
mask = CMDFLAG_SETTING;
}
if( find_option("aliases","a",0) ){
mask = CMDFLAG_ALIAS;
}
if( find_option("test","t",0) ){
mask |= CMDFLAG_TEST;
}
display_all_help(mask, useHtml, rawOut);
}
/*
|
| ︙ | ︙ | |||
766 767 768 769 770 771 772 |
iLast = FOSSIL_FIRST_CMD-1;
}else{
iFirst = FOSSIL_FIRST_CMD;
iLast = MX_COMMAND-1;
}
while( n<nArray ){
| | | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 |
iLast = FOSSIL_FIRST_CMD-1;
}else{
iFirst = FOSSIL_FIRST_CMD;
iLast = MX_COMMAND-1;
}
while( n<nArray ){
bestScore = mxScore;
for(i=iFirst; i<=iLast; i++){
m = edit_distance(zIn, aCommand[i].zName);
if( m<mnScore ) continue;
if( m==mnScore ){
azArray[n++] = aCommand[i].zName;
if( n>=nArray ) return n;
}else if( m<bestScore ){
|
| ︙ | ︙ | |||
895 896 897 898 899 900 901 |
@ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
/* Output aliases */
if( occHelp[aCommand[i].iHelp] > 1 ){
int j;
int aliases[MX_HELP_DUP], nAliases=0;
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
if( bktHelp[aCommand[i].iHelp][j] != i ){
| | > | 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 |
@ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
/* Output aliases */
if( occHelp[aCommand[i].iHelp] > 1 ){
int j;
int aliases[MX_HELP_DUP], nAliases=0;
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
if( bktHelp[aCommand[i].iHelp][j] != i ){
if( aCommand[bktHelp[aCommand[i].iHelp][j]].eCmdFlags
& CMDFLAG_ALIAS ){
aliases[nAliases++] = bktHelp[aCommand[i].iHelp][j];
}
}
}
if( nAliases>0 ){
int k;
@(\
|
| ︙ | ︙ | |||
985 986 987 988 989 990 991 |
style_set_current_feature("test");
style_header("All Help Text");
@ <dl>
/* Fill in help string buckets */
for(i=0; i<MX_COMMAND; i++){
if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i;
| | | 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 |
style_set_current_feature("test");
style_header("All Help Text");
@ <dl>
/* Fill in help string buckets */
for(i=0; i<MX_COMMAND; i++){
if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i;
}
for(i=0; i<MX_COMMAND; i++){
const char *zDesc;
unsigned int e = aCommand[i].eCmdFlags;
if( e & CMDFLAG_1ST_TIER ){
zDesc = "1st tier command";
}else if( e & CMDFLAG_2ND_TIER ){
zDesc = "2nd tier command";
|
| ︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 |
}else if( e & CMDFLAG_WEBPAGE ){
if( e & CMDFLAG_RAWCONTENT ){
zDesc = "raw-content web page";
}else{
zDesc = "web page";
}
}
| | | 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 |
}else if( e & CMDFLAG_WEBPAGE ){
if( e & CMDFLAG_RAWCONTENT ){
zDesc = "raw-content web page";
}else{
zDesc = "web page";
}
}
@ <dt><big><b>%s(aCommand[bktHelp[aCommand[i].iHelp][j]].zName)</b>
@</big> (%s(zDesc))</dt>
}
@ <p><dd>
help_to_html(aCommand[i].zHelp, cgi_output_blob());
@ </dd><p>
occHelp[aCommand[i].iHelp] = 0;
|
| ︙ | ︙ | |||
1116 1117 1118 1119 1120 1121 1122 | /* ** Documentation on universal command-line options. */ /* @-comment: # */ static const char zOptions[] = @ Command-line options common to all commands: | | | | | 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 | /* ** Documentation on universal command-line options. */ /* @-comment: # */ static const char zOptions[] = @ Command-line options common to all commands: @ @ --args FILENAME Read additional arguments and options from FILENAME @ --case-sensitive BOOL Set case sensitivity for file names @ --cgitrace Active CGI tracing @ --chdir PATH Change to PATH before performing any operations @ --comfmtflags VALUE Set comment formatting flags to VALUE @ --comment-format VALUE Alias for --comfmtflags @ --errorlog FILENAME Log errors to FILENAME @ --help Show help on the command rather than running it @ --httptrace Trace outbound HTTP requests @ --localtime Display times using the local timezone @ --nocgi Do not act as CGI @ --no-th-hook Do not run TH1 hooks @ --quiet Reduce the amount of output @ --sqlstats Show SQL usage statistics when done |
| ︙ | ︙ | |||
1485 1486 1487 1488 1489 1490 1491 | helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur; return pCur->iRowid>=MX_COMMAND; } /* ** This method is called to "rewind" the helptextVtab_cursor object back ** to the first row of output. This method is always called at least | | | | 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 |
helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur;
return pCur->iRowid>=MX_COMMAND;
}
/*
** This method is called to "rewind" the helptextVtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to helptextVtabColumn() or helptextVtabRowid() or
** helptextVtabEof().
*/
static int helptextVtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
helptextVtab_cursor *pCur = (helptextVtab_cursor *)pVtabCursor;
pCur->iRowid = 1;
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
1514 1515 1516 1517 1518 1519 1520 |
){
pIdxInfo->estimatedCost = (double)MX_COMMAND;
pIdxInfo->estimatedRows = MX_COMMAND;
return SQLITE_OK;
}
/*
| | | 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 |
){
pIdxInfo->estimatedCost = (double)MX_COMMAND;
pIdxInfo->estimatedRows = MX_COMMAND;
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module helptextVtabModule = {
/* iVersion */ 0,
/* xCreate */ 0, /* Helptext is eponymous and read-only */
/* xConnect */ helptextVtabConnect,
/* xBestIndex */ helptextVtabBestIndex,
|
| ︙ | ︙ | |||
1541 1542 1543 1544 1545 1546 1547 | /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, | | > | 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 |
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindMethod */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0,
/* xIntegrity */ 0
};
/*
** Register the helptext virtual table
*/
int helptext_vtab_register(sqlite3 *db){
int rc = sqlite3_create_module(db, "helptext", &helptextVtabModule, 0);
return rc;
}
/* End of the helptext virtual table
******************************************************************************/
|
Changes to src/doc.c.
| ︙ | ︙ | |||
339 340 341 342 343 344 345 |
static char * zList = 0;
static char const * zEnd = 0;
static int once = 0;
char * z;
int tokenizerState /* 0=expecting a key, 1=skip next token,
** 2=accept next token */;
if(once==0){
| | | 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
static char * zList = 0;
static char const * zEnd = 0;
static int once = 0;
char * z;
int tokenizerState /* 0=expecting a key, 1=skip next token,
** 2=accept next token */;
if(once==0){
once = 1;
zList = db_get("mimetypes",0);
if(zList==0){
return 0;
}
/* Transform zList to simplify the main loop:
replace non-newline spaces with NUL bytes. */
zEnd = zList + strlen(zList);
|
| ︙ | ︙ | |||
727 728 729 730 731 732 733 | ** Transfer content to the output. During the transfer, when text of ** the following form is seen: ** ** href="$ROOT/..." ** action="$ROOT/..." ** href=".../doc/$CURRENT/..." ** | | | | 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 |
** Transfer content to the output. During the transfer, when text of
** the following form is seen:
**
** href="$ROOT/..."
** action="$ROOT/..."
** href=".../doc/$CURRENT/..."
**
** Convert $ROOT to the root URI of the repository, and $CURRENT to the
** version number of the /doc/ document currently being displayed (if any).
** Allow ' in place of " and any case for href or action.
**
** Efforts are made to limit this translation to cases where the text is
** fully contained with an HTML markup element.
*/
void convert_href_and_output(Blob *pIn){
int i, base;
int n = blob_size(pIn);
|
| ︙ | ︙ | |||
1209 1210 1211 1212 1213 1214 1215 | ** ** The intended use case here is to supply an icon for the "fossil ui" ** command. For a permanent website, the recommended process is for ** the admin to set up a project-specific icon and reference that icon ** in the HTML header using a line like: ** ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/> | | | 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 |
**
** The intended use case here is to supply an icon for the "fossil ui"
** command. For a permanent website, the recommended process is for
** the admin to set up a project-specific icon and reference that icon
** in the HTML header using a line like:
**
** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/>
**
*/
void favicon_page(void){
Blob icon;
char *zMime;
etag_check(ETAG_CONFIG, 0);
zMime = db_get("icon-mimetype", "image/gif");
|
| ︙ | ︙ |
Changes to src/etag.c.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 | char zBuf[50]; assert( zETag[0]==0 ); /* Only call this routine once! */ if( etagCancelled ) return; /* By default, ETagged URLs never expire since the ETag will change * when the content changes. Approximate this policy as 10 years. */ | | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
char zBuf[50];
assert( zETag[0]==0 ); /* Only call this routine once! */
if( etagCancelled ) return;
/* By default, ETagged URLs never expire since the ETag will change
* when the content changes. Approximate this policy as 10 years. */
iMaxAge = 10 * 365 * 24 * 60 * 60;
md5sum_init();
/* Always include the executable ID as part of the hash */
md5sum_step_text("exe-id: ", -1);
md5sum_step_text(fossil_exe_id(), -1);
md5sum_step_text("\n", 1);
if( (eFlags & ETAG_HASH)!=0 && zHash ){
md5sum_step_text("hash: ", -1);
md5sum_step_text(zHash, -1);
md5sum_step_text("\n", 1);
iMaxAge = 0;
}
if( eFlags & ETAG_DATA ){
|
| ︙ | ︙ | |||
208 209 210 211 212 213 214 |
/* Check to see the If-Modified-Since constraint is satisfied */
zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE");
if( zIfModifiedSince==0 ) return;
x = cgi_rfc822_parsedate(zIfModifiedSince);
if( x<mtime ) return;
| | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
/* Check to see the If-Modified-Since constraint is satisfied */
zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE");
if( zIfModifiedSince==0 ) return;
x = cgi_rfc822_parsedate(zIfModifiedSince);
if( x<mtime ) return;
#if 0
/* If the Fossil executable is more recent than If-Modified-Since,
** go ahead and regenerate the resource. */
if( file_mtime(g.nameOfExe, ExtFILE)>x ) return;
#endif
/* If we reach this point, it means that the resource has not changed
** and that we should generate a 304 Not Modified reply */
|
| ︙ | ︙ | |||
242 243 244 245 246 247 248 |
/* Return the last-modified time in seconds since 1970. Or return 0 if
** there is no last-modified time.
*/
sqlite3_int64 etag_mtime(void){
return iEtagMtime;
}
| | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
/* Return the last-modified time in seconds since 1970. Or return 0 if
** there is no last-modified time.
*/
sqlite3_int64 etag_mtime(void){
return iEtagMtime;
}
/*
** COMMAND: test-etag
**
** Usage: fossil test-etag -key KEY-NUMBER -hash HASH
**
** Generate an etag given a KEY-NUMBER and/or a HASH.
**
** KEY-NUMBER is some combination of:
|
| ︙ | ︙ |
Changes to src/export.c.
| ︙ | ︙ | |||
447 448 449 450 451 452 453 |
}while( (rid = bag_next(vers, rid))!=0 );
}
}
}
/* This is the original header command (and hence documentation) for
** the "fossil export" command:
| | | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
}while( (rid = bag_next(vers, rid))!=0 );
}
}
}
/* This is the original header command (and hence documentation) for
** the "fossil export" command:
**
** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY?
**
** Write an export of all check-ins to standard output. The export is
** written in the git-fast-export file format assuming the --git option is
** provided. The git-fast-export format is currently the only VCS
** interchange format supported, though other formats may be added in
** the future.
|
| ︙ | ︙ | |||
1002 1003 1004 1005 1006 1007 1008 |
db_bind_int(&sIns, ":isfile", isFile!=0);
db_step(&sIns);
db_reset(&sIns);
return mprintf(":%d", db_last_insert_rowid());
}
/* This is the SHA3-256 hash of an empty file */
| | | 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 |
db_bind_int(&sIns, ":isfile", isFile!=0);
db_step(&sIns);
db_reset(&sIns);
return mprintf(":%d", db_last_insert_rowid());
}
/* This is the SHA3-256 hash of an empty file */
static const char zEmptySha3[] =
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a";
/*
** Export a single file named by zUuid.
**
** Return 0 on success and non-zero on any failure.
**
|
| ︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 |
}else{
rc = content_get(rid, &data);
if( rc==0 ){
if( bPhantomOk ){
blob_init(&data, 0, 0);
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
zUuid = zEmptySha3;
| | | 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 |
}else{
rc = content_get(rid, &data);
if( rc==0 ){
if( bPhantomOk ){
blob_init(&data, 0, 0);
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
zUuid = zEmptySha3;
}else{
return 1;
}
}
}
zMark = gitmirror_find_mark(zUuid, 1, 1);
if( zMark[0]==':' ){
fprintf(xCmd, "blob\nmark %s\ndata %d\n", zMark, blob_size(&data));
|
| ︙ | ︙ | |||
1348 1349 1350 1351 1352 1353 1354 |
int i;
zCmd = "git symbolic-ref --short HEAD";
gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
xCmd = popen(zCmd, "r");
if( xCmd==0 ){
fossil_fatal("git command failed: %s", zCmd);
}
| | | | 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 |
int i;
zCmd = "git symbolic-ref --short HEAD";
gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
xCmd = popen(zCmd, "r");
if( xCmd==0 ){
fossil_fatal("git command failed: %s", zCmd);
}
z = fgets(zLine, sizeof(zLine), xCmd);
pclose(xCmd);
if( z==0 ){
fossil_fatal("no output from \"%s\"", zCmd);
}
for(i=0; z[i] && !fossil_isspace(z[i]); i++){}
z[i] = 0;
zMainBr = fossil_strdup(z);
}
return zMainBr;
}
/*
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
const char *zLimit; /* Text of the --limit flag */
int nLimit = 0x7fffffff; /* Numeric value of the --limit flag */
|
| ︙ | ︙ | |||
1434 1435 1436 1437 1438 1439 1440 |
/* Make sure GIT has been initialized */
z = mprintf("%s/.git", zMirror);
if( !file_isdir(z, ExtFILE) ){
zMainBr = gitmirror_init(zMirror, zMainBr);
bNeedRepack = 1;
}
fossil_free(z);
| | | 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 |
/* Make sure GIT has been initialized */
z = mprintf("%s/.git", zMirror);
if( !file_isdir(z, ExtFILE) ){
zMainBr = gitmirror_init(zMirror, zMainBr);
bNeedRepack = 1;
}
fossil_free(z);
/* Make sure the .mirror_state subdirectory exists */
z = mprintf("%s/.mirror_state", zMirror);
rc = file_mkdir(z, ExtFILE, 0);
if( rc ) fossil_fatal("cannot create directory \"%s\"", z);
fossil_free(z);
/* Attach the .mirror_state/db database */
|
| ︙ | ︙ | |||
1741 1742 1743 1744 1745 1746 1747 |
char *zSql;
int bQuiet = 0;
int bByAll = 0; /* Undocumented option meaning this command was invoked
** from "fossil all" and should modify output accordingly */
db_find_and_open_repository(0, 0);
bQuiet = find_option("quiet","q",0)!=0;
| | | 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 |
char *zSql;
int bQuiet = 0;
int bByAll = 0; /* Undocumented option meaning this command was invoked
** from "fossil all" and should modify output accordingly */
db_find_and_open_repository(0, 0);
bQuiet = find_option("quiet","q",0)!=0;
bByAll = find_option("by-all",0,0)!=0;
verify_all_options();
zMirror = db_get("last-git-export-repo", 0);
if( zMirror==0 ){
if( bQuiet ) return;
if( bByAll ) return;
fossil_print("Git mirror: none\n");
return;
|
| ︙ | ︙ | |||
1854 1855 1856 1857 1858 1859 1860 | ** mapped into this name. "master" is used if ** this option is omitted. ** -q|--quiet Reduce output. Repeat for even less output. ** -v|--verbose More output ** ** > fossil git import MIRROR ** | | | 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 | ** mapped into this name. "master" is used if ** this option is omitted. ** -q|--quiet Reduce output. Repeat for even less output. ** -v|--verbose More output ** ** > fossil git import MIRROR ** ** TBD... ** ** > fossil git status ** ** Show the status of the current Git mirror, if there is one. ** ** -q|--quiet No output if there is nothing to report */ |
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
2243 2244 2245 2246 2247 2248 2249 |
/*
** Return non-NULL if zFilename contains pathname elements that
** are reserved on Windows. The returned string is the disallowed
** path element.
*/
const char *file_is_win_reserved(const char *zPath){
| | | 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 |
/*
** Return non-NULL if zFilename contains pathname elements that
** are reserved on Windows. The returned string is the disallowed
** path element.
*/
const char *file_is_win_reserved(const char *zPath){
static const char *const azRes[] = { "CON","PRN","AUX","NUL","COM","LPT" };
static char zReturn[5];
int i;
while( zPath[0] ){
for(i=0; i<count(azRes); i++){
if( sqlite3_strnicmp(zPath, azRes[i], 3)==0
&& ((i>=4 && fossil_isdigit(zPath[3])
&& (zPath[4]=='/' || zPath[4]=='.' || zPath[4]==0))
|
| ︙ | ︙ |
Changes to src/fileedit.c.
| ︙ | ︙ | |||
434 435 436 437 438 439 440 | ** pCI's ownership is not modified. ** ** This function validates pCI's state and fails if any validation ** fails. ** ** On error, returns false (0) and, if pErr is not NULL, writes a ** diagnostic message there. | | | 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | ** pCI's ownership is not modified. ** ** This function validates pCI's state and fails if any validation ** fails. ** ** On error, returns false (0) and, if pErr is not NULL, writes a ** diagnostic message there. ** ** Returns true on success. If pRid is not NULL, the RID of the ** resulting manifest is written to *pRid. ** ** The check-in process is largely influenced by pCI->flags, and that ** must be populated before calling this. See the fossil_cimini_flags ** enum for the docs for each flag. */ |
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
&& blob_size(&pCI->fileContent)>0
){
/* Convert to the requested EOL style. Note that this inherently
** runs a risk of breaking content, e.g. string literals which
** contain embedded newlines. Note that HTML5 specifies that
** form-submitted TEXTAREA content gets normalized to CRLF-style:
**
| | | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
&& blob_size(&pCI->fileContent)>0
){
/* Convert to the requested EOL style. Note that this inherently
** runs a risk of breaking content, e.g. string literals which
** contain embedded newlines. Note that HTML5 specifies that
** form-submitted TEXTAREA content gets normalized to CRLF-style:
**
** https://html.spec.whatwg.org/#the-textarea-element
*/
const int pseudoBinary = LOOK_LONG | LOOK_NUL;
const int lookFlags = LOOK_CRLF | LOOK_LONE_LF | pseudoBinary;
const int lookNew = looks_like_utf8( &pCI->fileContent, lookFlags );
if(!(pseudoBinary & lookNew)){
int rehash = 0;
/*fossil_print("lookNew=%08x\n",lookNew);*/
|
| ︙ | ︙ | |||
979 980 981 982 983 984 985 |
char ** zRevUuid,
int * pVid,
const char * zFilename,
int * frid){
char * zFileUuid = 0; /* file content UUID */
const int checkFile = zFilename!=0 || frid!=0;
int vid = 0;
| | | 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 |
char ** zRevUuid,
int * pVid,
const char * zFilename,
int * frid){
char * zFileUuid = 0; /* file content UUID */
const int checkFile = zFilename!=0 || frid!=0;
int vid = 0;
if(checkFile && !fileedit_ajax_check_filename(zFilename)){
return 0;
}
vid = symbolic_name_to_rid(zRev, "ci");
if(0==vid){
ajax_route_error(404,"Cannot resolve name as a check-in: %s",
zRev);
|
| ︙ | ︙ | |||
1174 1175 1176 1177 1178 1179 1180 |
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
int * bIsMissingArg){
char * zFileUuid = 0; /* UUID of file content */
const char * zFlag; /* generic flag */
| | | 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 |
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
int * bIsMissingArg){
char * zFileUuid = 0; /* UUID of file content */
const char * zFlag; /* generic flag */
int rc = 0, vid = 0, frid = 0; /* result code, check-in/file rids */
#define fail(EXPR) blob_appendf EXPR; goto end_fail
zFlag = PD("filename",P("fn"));
if(zFlag==0 || !*zFlag){
rc = 400;
if(bIsMissingArg){
*bIsMissingArg = 1;
|
| ︙ | ︙ | |||
1369 1370 1371 1372 1373 1374 1375 |
if(i++){
CX(",");
}
CX("%!j", zFilename);
}
}
db_finalize(&q);
| | | 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 |
if(i++){
CX(",");
}
CX("%!j", zFilename);
}
}
db_finalize(&q);
CX("]}");
}
/*
** AJAX route /fileedit?ajax=filelist
**
** Fetches a JSON-format list of leaves and/or filenames for use in
** creating a file selection list in /fileedit. It has different modes
|
| ︙ | ︙ | |||
1425 1426 1427 1428 1429 1430 1431 | } } /* ** AJAX route /fileedit?ajax=commit ** ** Required query parameters: | | | | 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 | } } /* ** AJAX route /fileedit?ajax=commit ** ** Required query parameters: ** ** filename=FILENAME ** checkin=Parent check-in UUID ** content=text ** comment=non-empty text ** ** Optional query parameters: ** ** comment_mimetype=text (NOT currently honored) ** ** dry_run=int (1 or 0) ** ** include_manifest=int (1 or 0), whether to include ** the generated manifest in the response. ** ** ** User must have Write permissions to use this page. ** ** Responds with JSON (with some state repeated ** from the input in order to avoid certain race conditions ** client-side): ** |
| ︙ | ︙ | |||
1575 1576 1577 1578 1579 1580 1581 | ** use of the name parameter. ** ** Which additional parameters are used by each distinct ajax route ** is an internal implementation detail and may change with any ** given build of this code. An unknown "name" value triggers an ** error, as documented for ajax_route_error(). */ | | | 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 |
** use of the name parameter.
**
** Which additional parameters are used by each distinct ajax route
** is an internal implementation detail and may change with any
** given build of this code. An unknown "name" value triggers an
** error, as documented for ajax_route_error().
*/
/* Allow no access to this page without check-in privilege */
login_check_credentials();
if( !g.perm.Write ){
if(zAjax!=0){
ajax_route_error(403, "Write permissions required.");
}else{
login_needed(g.anon.Write);
|
| ︙ | ︙ | |||
1668 1669 1670 1671 1672 1673 1674 |
** have a common, page-specific container we can filter our CSS
** selectors, but we do have the BODY, which we can decorate with
** whatever CSS we wish...
*/
style_script_begin(__FILE__,__LINE__);
CX("document.body.classList.add('fileedit');\n");
style_script_end();
| | | 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 |
** have a common, page-specific container we can filter our CSS
** selectors, but we do have the BODY, which we can decorate with
** whatever CSS we wish...
*/
style_script_begin(__FILE__,__LINE__);
CX("document.body.classList.add('fileedit');\n");
style_script_end();
/* Status bar */
CX("<div id='fossil-status-bar' "
"title='Status message area. Double-click to clear them.'>"
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
CX("<div id='fileedit-edit-status'>"
|
| ︙ | ︙ | |||
1696 1697 1698 1699 1700 1701 1702 |
"data-tab-parent='fileedit-tabs' "
"data-tab-label='File Selection' "
"class='hidden'"
">");
CX("<div id='fileedit-file-selector'></div>");
CX("</div>"/*#fileedit-tab-fileselect*/);
}
| | > | > | 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 |
"data-tab-parent='fileedit-tabs' "
"data-tab-label='File Selection' "
"class='hidden'"
">");
CX("<div id='fileedit-file-selector'></div>");
CX("</div>"/*#fileedit-tab-fileselect*/);
}
/******* Content tab *******/
{
CX("<div id='fileedit-tab-content' "
"data-tab-parent='fileedit-tabs' "
"data-tab-label='File Content' "
"class='hidden'"
">");
CX("<div class='fileedit-options flex-container "
"flex-row child-gap-small'>");
CX("<div class='input-with-label'>"
"<button class='fileedit-content-reload confirmer' "
">Discard & Reload</button>"
"<div class='help-buttonlet'>"
"Reload the file from the server, discarding "
"any local edits. To help avoid accidental loss of "
"edits, it requires confirmation (a second click) within "
"a few seconds or it will not reload."
"</div>"
"</div>");
style_select_list_int("select-font-size",
"editor_font_size", "Editor font size",
NULL/*tooltip*/,
100,
"100%", 100, "125%", 125,
"150%", 150, "175%", 175,
"200%", 200, NULL);
wikiedit_emit_toggle_preview();
CX("</div>");
CX("<div class='flex-container flex-column stretch'>");
CX("<textarea name='content' id='fileedit-content-editor' "
"class='fileedit' rows='25'>");
CX("</textarea>");
CX("</div>"/*textarea wrapper*/);
CX("</div>"/*#tab-file-content*/);
|
| ︙ | ︙ | |||
1935 1936 1937 1938 1939 1940 1941 |
*/
style_select_list_str("comment-mimetype", "comment_mimetype",
"Comment style:",
"Specify how fossil will interpret the "
"comment string.",
NULL,
"Fossil", "text/x-fossil-wiki",
| | | 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 |
*/
style_select_list_str("comment-mimetype", "comment_mimetype",
"Comment style:",
"Specify how fossil will interpret the "
"comment string.",
NULL,
"Fossil", "text/x-fossil-wiki",
"Markdown", "text/x-markdown",
"Plain text", "text/plain",
NULL);
CX("</div>\n");
}
CX("<div class='fileedit-hint flex-container flex-row'>"
"(Warning: switching from multi- to single-line mode will "
"strip out all newlines!)</div>");
|
| ︙ | ︙ |
Changes to src/foci.c.
| ︙ | ︙ | |||
266 267 268 269 270 271 272 |
0, /* xCommit */
0, /* xRollback */
0, /* xFindFunction */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
| | > | 266 267 268 269 270 271 272 273 274 275 276 277 278 |
0, /* xCommit */
0, /* xRollback */
0, /* xFindFunction */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
sqlite3_create_module(db, "files_of_checkin", &foci_module, 0);
return SQLITE_OK;
}
|
Changes to src/fossil.page.chat.js.
1 2 | /** This file contains the client-side implementation of fossil's /chat | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/**
This file contains the client-side implementation of fossil's /chat
application.
*/
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
const E1 = function(selector){
const e = document.querySelector(selector);
if(!e) throw new Error("missing required DOM element: "+selector);
return e;
};
/**
Returns true if e is entirely within the bounds of the window's viewport.
*/
const isEntirelyInViewport = function(e) {
const rect = e.getBoundingClientRect();
return (
rect.top >= 0 &&
|
| ︙ | ︙ | |||
394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
ctrl-enter both send them. */
"edit-ctrl-send": false,
/* When on, the edit field starts as a single line and
expands as the user types, and the relevant buttons are
laid out in a compact form. When off, the edit field and
buttons are larger. */
"edit-compact-mode": true,
/* When on, sets the font-family on messages and the edit
field to monospace. */
"monospace-messages": false,
/* When on, non-chat UI elements (page header/footer) are
hidden */
"chat-only-mode": false,
/* When set to a URI, it is assumed to be an audio file,
| > > > > > | 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
ctrl-enter both send them. */
"edit-ctrl-send": false,
/* When on, the edit field starts as a single line and
expands as the user types, and the relevant buttons are
laid out in a compact form. When off, the edit field and
buttons are larger. */
"edit-compact-mode": true,
/* See notes for this setting in fossil.page.wikiedit.js.
Both /wikiedit and /fileedit share this persistent config
option under the same storage key. */
"edit-shift-enter-preview":
F.storage.getBool('edit-shift-enter-preview', true),
/* When on, sets the font-family on messages and the edit
field to monospace. */
"monospace-messages": false,
/* When on, non-chat UI elements (page header/footer) are
hidden */
"chat-only-mode": false,
/* When set to a URI, it is assumed to be an audio file,
|
| ︙ | ︙ | |||
1497 1498 1499 1500 1501 1502 1503 |
/* Shift-enter will run preview mode UNLESS preview mode is
active AND the input field is empty, in which case it will
switch back to message view. */
if(Chat.e.currentView===Chat.e.viewPreview && !text){
Chat.setCurrentView(Chat.e.viewMessages);
}else if(!text){
f.$toggleCompact(compactMode);
| | | | | 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 |
/* Shift-enter will run preview mode UNLESS preview mode is
active AND the input field is empty, in which case it will
switch back to message view. */
if(Chat.e.currentView===Chat.e.viewPreview && !text){
Chat.setCurrentView(Chat.e.viewMessages);
}else if(!text){
f.$toggleCompact(compactMode);
}else if(Chat.settings.getBool('edit-shift-enter-preview', true)){
Chat.e.btnPreview.click();
}
return false;
}
if(ev.ctrlKey && !text && !BlobXferState.blob){
/* Ctrl-enter on empty input field(s) toggles Enter/Ctrl-enter mode */
ev.preventDefault();
ev.stopPropagation();
f.$toggleCtrl(ctrlMode);
return false;
}
if(!ctrlMode && ev.ctrlKey && text){
//console.debug("!ctrlMode && ev.ctrlKey && text.");
/* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
newline, but that is not happening, for unknown reasons
(possibly related to this element being a contenteditable DIV
instead of a textarea). Forcibly appending a newline do the
input area does not work, also for unknown reasons, and would
only be suitable when we're at the end of the input.
Strangely, this approach DOES work for shift-enter, but we
need shift-enter as a hotkey for preview mode.
*/
//return;
// return here "should" cause newline to be added, but that doesn't work
}
if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey/* && ctrlMode*/)){
/* Ship it! */
ev.preventDefault();
ev.stopPropagation();
Chat.submitMessage();
return false;
}
};
Chat.e.inputFields.forEach(
(e)=>e.addEventListener('keydown', inputWidgetKeydown, false)
);
Chat.e.btnSubmit.addEventListener('click',(e)=>{
e.preventDefault();
Chat.submitMessage();
return false;
|
| ︙ | ︙ | |||
1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 |
boolValue: 'edit-widget-x',
hint: [
"When enabled, chat input uses a so-called 'contenteditable' ",
"field. Though generally more comfortable and modern than ",
"plain-text input fields, browser-specific quirks and bugs ",
"may lead to frustration. Ideal for mobile devices."
].join('')
}]
},{
label: "Appearance Options...",
children:[{
label: "Left-align my posts",
hint: "Default alignment of your own messages is selected "
+ "based window width/height ratio.",
| > > > > > > > | 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 |
boolValue: 'edit-widget-x',
hint: [
"When enabled, chat input uses a so-called 'contenteditable' ",
"field. Though generally more comfortable and modern than ",
"plain-text input fields, browser-specific quirks and bugs ",
"may lead to frustration. Ideal for mobile devices."
].join('')
},{
label: "Shift-enter to preview",
hint: ["Use shift-enter to preview being-edited messages. ",
"This is normally desirable but some software-mode ",
"keyboards misinteract with this, in which cases it can be ",
"disabled."],
boolValue: 'edit-shift-enter-preview'
}]
},{
label: "Appearance Options...",
children:[{
label: "Left-align my posts",
hint: "Default alignment of your own messages is selected "
+ "based window width/height ratio.",
|
| ︙ | ︙ | |||
1963 1964 1965 1966 1967 1968 1969 |
D.enable(elemsToEnable);
}
});
return false;
};
btnPreview.addEventListener('click', submit, false);
})()/*message preview setup*/;
| | | 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 |
D.enable(elemsToEnable);
}
});
return false;
};
btnPreview.addEventListener('click', submit, false);
})()/*message preview setup*/;
/** Callback for poll() to inject new content into the page. jx ==
the response from /chat-poll. If atEnd is true, the message is
appended to the end of the chat list (for loading older
messages), else the beginning (the default). */
const newcontent = function f(jx,atEnd){
if(!f.processPost){
/** Processes chat message m, placing it either the start (if atEnd
|
| ︙ | ︙ |
Changes to src/fossil.page.fileedit.js.
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
);
*/
const E = (s)=>document.querySelector(s),
D = F.dom,
P = F.page;
P.config = {
| | > > > > > > | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
);
*/
const E = (s)=>document.querySelector(s),
D = F.dom,
P = F.page;
P.config = {
defaultMaxStashSize: 7,
/**
See notes for this setting in fossil.page.wikiedit.js. Both
/wikiedit and /fileedit share this persistent config option
under the same storage key.
*/
shiftEnterPreview: F.storage.getBool('edit-shift-enter-preview', true)
};
/**
$stash is an internal-use-only object for managing "stashed"
local edits, to help avoid that users accidentally lose content
by switching tabs or following links or some such. The basic
theory of operation is...
|
| ︙ | ︙ | |||
568 569 570 571 572 573 574 |
opt._finfo = finfo;
if(0===f.compare(currentFinfo, finfo)){
D.attr(opt, 'selected', true);
}
});
}
}/*P.stashWidget*/;
| | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
opt._finfo = finfo;
if(0===f.compare(currentFinfo, finfo)){
D.attr(opt, 'selected', true);
}
});
}
}/*P.stashWidget*/;
/**
Internal workaround to select the current preview mode
and fire a change event if the value actually changes
or if forceEvent is truthy.
*/
P.selectPreviewMode = function(modeValue, forceEvent){
const s = this.e.selectPreviewMode;
|
| ︙ | ︙ | |||
722 723 724 725 726 727 728 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
| | | 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
if(P.config.shiftEnterPreview && ev.shiftKey && 13===ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
P.e.taEditor.blur(/*force change event, if needed*/);
P.tabs.switchToTab(P.e.tabs.preview);
if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
P.preview();
}
|
| ︙ | ︙ | |||
843 844 845 846 847 848 849 850 851 852 853 854 855 856 |
}
);
P.fileSelectWidget.init();
P.stashWidget.init(
P.e.tabs.content.lastElementChild
);
}/*F.onPageLoad()*/);
/**
Getter (if called with no args) or setter (if passed an arg) for
the current file content.
The setter form sets the content, dispatches a
| > > > > > > > | 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 |
}
);
P.fileSelectWidget.init();
P.stashWidget.init(
P.e.tabs.content.lastElementChild
);
const cbEditPreview = E('#edit-shift-enter-preview');
cbEditPreview.addEventListener('change', function(e){
F.storage.set('edit-shift-enter-preview',
P.config.shiftEnterPreview = e.target.checked);
}, false);
cbEditPreview.checked = P.config.shiftEnterPreview;
}/*F.onPageLoad()*/);
/**
Getter (if called with no args) or setter (if passed an arg) for
the current file content.
The setter form sets the content, dispatches a
|
| ︙ | ︙ | |||
1159 1160 1161 1162 1163 1164 1165 |
const target = this.e.previewTarget;
D.clearElement(target);
if('string'===typeof c) D.parseHtml(target,c);
if(F.pikchr){
F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
}
};
| | | 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 |
const target = this.e.previewTarget;
D.clearElement(target);
if('string'===typeof c) D.parseHtml(target,c);
if(F.pikchr){
F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
}
};
/**
Callback for use with F.connectPagePreviewers()
*/
P._postPreview = function(content,callback){
if(!affirmHasFile()) return this;
if(!content){
callback(content);
|
| ︙ | ︙ |
Changes to src/fossil.page.whistory.js.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* This script adds interactivity for wiki-history webpages.
*
* The main code is within the 'on-click' handler of the "diff" links.
* Instead of standard redirection it fills-in two hidden inputs with
* the appropriate values and submits the corresponding form.
* A special care should be taken if some intermediate edits are hidden.
*
* For the sake of compatibility with ascetic browsers the code tries
* to avoid modern API and ECMAScript constructs. This makes it less
* readable and may be reconsidered in the future.
*/
window.addEventListener( 'load', function() {
| | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* This script adds interactivity for wiki-history webpages.
*
* The main code is within the 'on-click' handler of the "diff" links.
* Instead of standard redirection it fills-in two hidden inputs with
* the appropriate values and submits the corresponding form.
* A special care should be taken if some intermediate edits are hidden.
*
* For the sake of compatibility with ascetic browsers the code tries
* to avoid modern API and ECMAScript constructs. This makes it less
* readable and may be reconsidered in the future.
*/
window.addEventListener( 'load', function() {
var form = document.getElementById("wh-form");
form.method = "GET";
var csrf = form.querySelector("input[name='csrf']");
if( csrf ) form.removeChild( csrf );
var wh_id = document.getElementById("wh-id" );
var wh_pid = document.getElementById("wh-pid");
var wh_cleaner = document.getElementById("wh-cleaner");
var wh_collapser = document.getElementById("wh-collapser");
var wh_radios = []; // user-visible controls for baseline selection
|
| ︙ | ︙ |
Changes to src/fossil.page.wikiedit.js.
| ︙ | ︙ | |||
73 74 75 76 77 78 79 |
useConfirmerButtons:{
/* If true during fossil.page setup, certain buttons will use a
"confirmer" step, else they will not. The confirmer topic has
been the source of much contention in the forum. */
save: false,
reload: true,
discardStash: true
| > > > > > > > | > > > > > > | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
useConfirmerButtons:{
/* If true during fossil.page setup, certain buttons will use a
"confirmer" step, else they will not. The confirmer topic has
been the source of much contention in the forum. */
save: false,
reload: true,
discardStash: true
},
/**
If true, a keyboard combo of shift-enter (from the editor)
toggles between preview and edit modes. This is normally
desired but at least one software keyboard is known to
misinteract with this, treating an Enter after
automatically-capitalized letters as a shift-enter:
https://fossil-scm.org/forum/forumpost/dbd5b68366147ce8
Maintenance note: /fileedit also uses this same key for the
same purpose.
*/
shiftEnterPreview: F.storage.getBool('edit-shift-enter-preview', true)
};
/**
$stash is an internal-use-only object for managing "stashed"
local edits, to help avoid that users accidentally lose content
by switching tabs or following links or some such. The basic
theory of operation is...
|
| ︙ | ︙ | |||
452 453 454 455 456 457 458 |
opt.dataset.isDeleted = true;
}
self._refreshStashMarks(opt);
});
D.enable(sel);
if(P.winfo) sel.value = P.winfo.name;
},
| | | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
opt.dataset.isDeleted = true;
}
self._refreshStashMarks(opt);
});
D.enable(sel);
if(P.winfo) sel.value = P.winfo.name;
},
/** Loads the page list and populates the selection list. */
loadList: function callee(){
if(!callee.onload){
const self = this;
callee.onload = function(list){
self.cache.pageList = list;
self._rebuildList();
|
| ︙ | ︙ | |||
649 650 651 652 653 654 655 |
}, false);
D.append(
parentElem,
D.append(D.addClass(D.div(), 'fieldset-wrapper'),
fsFilter, fsNewPage, fsLegend)
);
| | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
}, false);
D.append(
parentElem,
D.append(D.addClass(D.div(), 'fieldset-wrapper'),
fsFilter, fsNewPage, fsLegend)
);
D.append(parentElem, btn);
btn.addEventListener('click', ()=>this.loadList(), false);
this.loadList();
const onSelect = (e)=>P.loadPage(e.target.value);
sel.addEventListener('change', onSelect, false);
sel.addEventListener('dblclick', onSelect, false);
F.page.addEventListener('wiki-stash-updated', ()=>{
|
| ︙ | ︙ | |||
672 673 674 675 676 677 678 679 |
if(page.isEmpty) opt.dataset.isDeleted = true;
else delete opt.dataset.isDeleted;
self._refreshStashMarks(opt);
}else if('sandbox'!==page.type){
F.error("BUG: internal mis-handling of page object: missing OPTION for page "+page.name);
}
});
delete this.init;
| > > > > > > > | | 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 |
if(page.isEmpty) opt.dataset.isDeleted = true;
else delete opt.dataset.isDeleted;
self._refreshStashMarks(opt);
}else if('sandbox'!==page.type){
F.error("BUG: internal mis-handling of page object: missing OPTION for page "+page.name);
}
});
const cbEditPreview = E('#edit-shift-enter-preview');
cbEditPreview.addEventListener('change', function(e){
F.storage.set('edit-shift-enter-preview',
P.config.shiftEnterPreview = e.target.checked);
}, false);
cbEditPreview.checked = P.config.shiftEnterPreview;
delete this.init;
}/*init()*/
};
/**
Widget for listing and selecting $stash entries.
*/
P.stashWidget = {
e:{/*DOM element(s)*/},
|
| ︙ | ︙ | |||
912 913 914 915 916 917 918 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
| | | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 |
}
}
);
////////////////////////////////////////////////////////////
// Trigger preview on Ctrl-Enter. This only works on the built-in
// editor widget, not a client-provided one.
P.e.taEditor.addEventListener('keydown',function(ev){
if(P.config.shiftEnterPreview && ev.shiftKey && 13===ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
P.e.taEditor.blur(/*force change event, if needed*/);
P.tabs.switchToTab(P.e.tabs.preview);
if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
P.preview();
}
|
| ︙ | ︙ | |||
1459 1460 1461 1462 1463 1464 1465 |
const target = this.e.previewTarget;
D.clearElement(target);
if('string'===typeof c) D.parseHtml(target,c);
if(F.pikchr){
F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
}
};
| | | 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 |
const target = this.e.previewTarget;
D.clearElement(target);
if('string'===typeof c) D.parseHtml(target,c);
if(F.pikchr){
F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
}
};
/**
Callback for use with F.connectPagePreviewers()
*/
P._postPreview = function(content,callback){
if(!affirmPageLoaded()) return this;
if(!content){
callback(content);
|
| ︙ | ︙ |
Changes to src/graph.c.
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
dist = i - iNearto;
if( dist<0 ) dist = -dist;
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
| | | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
dist = i - iNearto;
if( dist<0 ) dist = -dist;
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
/* If no match, consider all possible rails */
if( iBestDist>1000 ){
for(i=0; i<=p->mxRail+1; i++){
int dist;
if( inUseMask & BIT(i) ) continue;
if( iNearto<=0 ){
iBest = i;
|
| ︙ | ︙ | |||
537 538 539 540 541 542 543 |
** the aParent[] array.
*/
if( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
GraphRow *pParent = hashFind(p, pRow->aParent[i]);
if( pParent==0 ){
| | | | 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
** the aParent[] array.
*/
if( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
GraphRow *pParent = hashFind(p, pRow->aParent[i]);
if( pParent==0 ){
memmove(pRow->aParent+i, pRow->aParent+i+1,
sizeof(pRow->aParent[0])*(pRow->nParent-i-1));
pRow->nParent--;
if( i<pRow->nNonCherrypick ){
pRow->nNonCherrypick--;
}else{
pRow->nCherrypick--;
}
i--;
}
}
}
}
/* Put the deepest (earliest) merge parent first in the list.
** An off-screen merge parent is considered deepest.
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext ){
if( pRow->nParent<=1 ) continue;
for(i=1; i<pRow->nParent; i++){
GraphRow *pParent = hashFind(p, pRow->aParent[i]);
|
| ︙ | ︙ | |||
938 939 940 941 942 943 944 |
/* The parent branch from which this branch emerges is on the
** same rail as pRow. Do not shift as that would stack a child
** branch directly above its parent. */
continue;
}
/* All clear. Make the translation
| | | 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 |
/* The parent branch from which this branch emerges is on the
** same rail as pRow. Do not shift as that would stack a child
** branch directly above its parent. */
continue;
}
/* All clear. Make the translation
*/
for(pLoop=pRow; pLoop && pLoop->idx<=pBottom->idx; pLoop=pLoop->pNext){
if( pLoop->iRail==iFrom ){
pLoop->iRail = iTo;
pLoop->aiRiser[iTo] = pLoop->aiRiser[iFrom];
pLoop->aiRiser[iFrom] = -1;
}
}
|
| ︙ | ︙ |
Changes to src/graph.js.
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
function hideGraphTooltip(){ /* Hide the tooltip */
document.removeEventListener('keydown',onKeyDown,/* useCapture == */true);
stopCloseTimer();
tooltipObj.style.display = "none";
tooltipInfo.ixActive = -1;
tooltipInfo.idNodeActive = 0;
}
| | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
function hideGraphTooltip(){ /* Hide the tooltip */
document.removeEventListener('keydown',onKeyDown,/* useCapture == */true);
stopCloseTimer();
tooltipObj.style.display = "none";
tooltipInfo.ixActive = -1;
tooltipInfo.idNodeActive = 0;
}
window.onpagehide = hideGraphTooltip;
function stopDwellTimer(){
if(tooltipInfo.idTimer!=0){
clearTimeout(tooltipInfo.idTimer);
tooltipInfo.idTimer = 0;
}
}
function resumeCloseTimer(){
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 | ** sha1_shared_secret()), not the original password. So convert the ** password to its SHA1 encoding if it isn't already a SHA1 hash. ** ** We assume that a hexadecimal string of exactly 40 characters is a ** SHA1 hash, not an original password. If a user has a password which ** just happens to be a 40-character hex string, then this routine won't ** be able to distinguish it from a hash, the translation will not be | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
** sha1_shared_secret()), not the original password. So convert the
** password to its SHA1 encoding if it isn't already a SHA1 hash.
**
** We assume that a hexadecimal string of exactly 40 characters is a
** SHA1 hash, not an original password. If a user has a password which
** just happens to be a 40-character hex string, then this routine won't
** be able to distinguish it from a hash, the translation will not be
** performed, and the sync won't work.
*/
if( zPw && zPw[0] && (strlen(zPw)!=40 || !validate16(zPw,40)) ){
const char *zProjectCode = 0;
if( g.url.flags & URL_USE_PARENT ){
zProjectCode = db_get("parent-project-code", 0);
}else{
zProjectCode = db_get("project-code", 0);
|
| ︙ | ︙ | |||
256 257 258 259 260 261 262 |
blob_write_to_file(pSend, zUplink);
if( g.fHttpTrace ){
fossil_print("RUN %s\n", zCmd);
}
rc = fossil_system(zCmd);
if( rc ){
fossil_warning("Transport command failed: %s\n", zCmd);
| | | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
blob_write_to_file(pSend, zUplink);
if( g.fHttpTrace ){
fossil_print("RUN %s\n", zCmd);
}
rc = fossil_system(zCmd);
if( rc ){
fossil_warning("Transport command failed: %s\n", zCmd);
}
fossil_free(zCmd);
file_delete(zUplink);
if( file_size(zDownlink, ExtFILE)<0 ){
blob_zero(pReply);
}else{
blob_read_from_file(pReply, zDownlink, ExtFILE);
file_delete(zDownlink);
}
return rc;
}
/*
** Sign the content in pSend, compress it, and send it to the server
** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
** in pRecv. pRecv is assumed to be uninitialized when
** this routine is called - this routine will initialize it.
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 | } sException; static int sslNoCertVerify = 0; /* Do not verify SSL certs */ /* This is a self-signed cert in the PEM format that can be used when ** no other certs are available. */ | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | } sException; static int sslNoCertVerify = 0; /* Do not verify SSL certs */ /* This is a self-signed cert in the PEM format that can be used when ** no other certs are available. */ static const char sslSelfCert[] = "-----BEGIN CERTIFICATE-----\n" "MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n" "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n" "EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n" "MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n" "QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n" "VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n" |
| ︙ | ︙ | |||
81 82 83 84 85 86 87 | "G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n" "pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n" "s/TsrXk=\n" "-----END CERTIFICATE-----\n"; /* This is the private-key corresponding to the cert above */ | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | "G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n" "pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n" "s/TsrXk=\n" "-----END CERTIFICATE-----\n"; /* This is the private-key corresponding to the cert above */ static const char sslSelfPKey[] = "-----BEGIN PRIVATE KEY-----\n" "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n" "q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n" "glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n" "GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n" "Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n" "U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n" |
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
"or the ssl-identity setting.");
return 0; /* no cert available */
}
/*
** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp.
**
| | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
"or the ssl-identity setting.");
return 0; /* no cert available */
}
/*
** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp.
**
** Per RFC 5280, ASN1 timestamps in X.509 certificates must
** be in UTC (Zulu timezone) with no fractional seconds.
**
** If showUtc==1, add " UTC" at the end of the returned string. This is
** not ISO8601-compliant, but makes the displayed value more user-friendly.
*/
static const char *ssl_asn1time_to_iso8601(ASN1_TIME *asn1_time,
int showUtc){
|
| ︙ | ︙ | |||
410 411 412 413 414 415 416 |
** Invoke this routine to disable SSL cert verification. After
** this call is made, any SSL cert that the server provides will
** be accepted. Communication will still be encrypted, but the
** client has no way of knowing whether it is talking to the
** real server or a man-in-the-middle imposter.
*/
void ssl_disable_cert_verification(void){
| | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
** Invoke this routine to disable SSL cert verification. After
** this call is made, any SSL cert that the server provides will
** be accepted. Communication will still be encrypted, but the
** client has no way of knowing whether it is talking to the
** real server or a man-in-the-middle imposter.
*/
void ssl_disable_cert_verification(void){
sslNoCertVerify = 1;
}
/*
** Open an SSL connection as a client that is to connect to the server
** identified by pUrlData.
**
* The identify of the server is determined as follows:
|
| ︙ | ︙ | |||
563 564 565 566 567 568 569 |
X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
BIO_printf(mem, "\n notBefore: %s",
ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1));
BIO_printf(mem, "\n notAfter: %s",
ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1));
BIO_printf(mem, "\n sha256: %s", zHash);
desclen = BIO_get_mem_data(mem, &desc);
| | | | 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
BIO_printf(mem, "\n notBefore: %s",
ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1));
BIO_printf(mem, "\n notAfter: %s",
ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1));
BIO_printf(mem, "\n sha256: %s", zHash);
desclen = BIO_get_mem_data(mem, &desc);
prompt = mprintf("Unable to verify SSL cert from %s\n%.*s\n"
"accept this cert and continue (y/N/fingerprint)? ",
pUrlData->name, desclen, desc);
BIO_free(mem);
prompt_user(prompt, &ans);
free(prompt);
cReply = blob_str(&ans)[0];
if( cReply!='y' && cReply!='Y'
&& fossil_stricmp(blob_str(&ans),zHash)!=0
){
X509_free(cert);
|
| ︙ | ︙ | |||
1183 1184 1185 1186 1187 1188 1189 |
/*
** Return the OpenSSL version number being used. Space to hold
** this name is obtained from fossil_malloc() and should be
** freed by the caller.
*/
char *fossil_openssl_version(void){
| | | 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 |
/*
** Return the OpenSSL version number being used. Space to hold
** this name is obtained from fossil_malloc() and should be
** freed by the caller.
*/
char *fossil_openssl_version(void){
#if defined(FOSSIL_ENABLE_SSL)
return mprintf("%s (0x%09x)\n",
SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
#else
return mprintf("none");
#endif
}
|
Changes to src/http_transport.c.
| ︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
}else{
blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
}
if( !is_safe_fossil_command(pUrlData->fossil) ){
fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
"the server.", pUrlData->fossil);
}
blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
blob_append(&zCmd, " test-http", 10);
if( pUrlData->path && pUrlData->path[0] ){
blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
}else{
fossil_fatal("ssh:// URI does not specify a path to the repository");
}
| > | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
}else{
blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
}
if( !is_safe_fossil_command(pUrlData->fossil) ){
fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
"the server.", pUrlData->fossil);
}
blob_append_escaped_arg(&zCmd, "PATH=$HOME/bin:$PATH", 1);
blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
blob_append(&zCmd, " test-http", 10);
if( pUrlData->path && pUrlData->path[0] ){
blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
}else{
fossil_fatal("ssh:// URI does not specify a path to the repository");
}
|
| ︙ | ︙ |
Changes to src/import.c.
| ︙ | ︙ | |||
815 816 817 818 819 820 821 |
gg.fromLoaded = 1;
}else
if( strncmp(zLine, "N ", 2)==0 ){
/* No-op */
}else
if( strncmp(zLine, "property branch-nick ", 21)==0 ){
/* Breezy uses this property to store the branch name.
| | | 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 |
gg.fromLoaded = 1;
}else
if( strncmp(zLine, "N ", 2)==0 ){
/* No-op */
}else
if( strncmp(zLine, "property branch-nick ", 21)==0 ){
/* Breezy uses this property to store the branch name.
** It has two values. Integer branch number, then the
** user-readable branch name. */
z = &zLine[21];
next_token(&z);
fossil_free(gg.zBranch);
gg.zBranch = fossil_strdup(next_token(&z));
}else
if( strncmp(zLine, "property rebase-of ", 19)==0 ){
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
2500 2501 2502 2503 2504 2505 2506 |
) ){
if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
page_tree();
return;
}
/* No directory found, look for an historic version of the file
** that was subsequently deleted. */
| | | 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 |
) ){
if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
page_tree();
return;
}
/* No directory found, look for an historic version of the file
** that was subsequently deleted. */
db_prepare(&q,
"SELECT fid, uuid FROM mlink, filename, event, blob"
" WHERE filename.name=%Q"
" AND mlink.fnid=filename.fnid AND mlink.fid>0"
" AND event.objid=mlink.mid"
" AND blob.rid=mlink.mid"
" ORDER BY event.mtime DESC",
zName
|
| ︙ | ︙ | |||
2798 2799 2800 2801 2802 2803 2804 |
}
}
if( strcmp(zModAction,"approve")==0 ){
moderation_approve('t', rid);
}
}
zTktTitle = db_table_has_column("repository", "ticket", "title" )
| | | 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 |
}
}
if( strcmp(zModAction,"approve")==0 ){
moderation_approve('t', rid);
}
}
zTktTitle = db_table_has_column("repository", "ticket", "title" )
? db_text("(No title)",
"SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
: 0;
style_set_current_feature("tinfo");
style_header("Ticket Change Details");
style_submenu_element("Raw", "%R/artifact/%s", zUuid);
style_submenu_element("History", "%R/tkthistory/%s#%S", zTktName,zUuid);
style_submenu_element("Page", "%R/tktview/%t", zTktName);
|
| ︙ | ︙ | |||
3545 3546 3547 3548 3549 3550 3551 | Blob ctrl; Blob comment; char *zNow; int nTags, nCancels; int i; Stmt q; | < | 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 |
Blob ctrl;
Blob comment;
char *zNow;
int nTags, nCancels;
int i;
Stmt q;
fEditComment = find_option("edit-comment","e",0)!=0;
zNewComment = find_option("comment","m",1);
zComFile = find_option("message-file","M",1);
zNewBranch = find_option("branch",0,1);
zNewColor = find_option("bgcolor",0,1);
zNewBrColor = find_option("branchcolor",0,1);
if( zNewBrColor ){
|
| ︙ | ︙ | |||
3831 3832 3833 3834 3835 3836 3837 | ** If no VERSION is provided, describe the currently checked-out version. ** ** If VERSION and the found ancestor refer to the same commit, the last two ** components are omitted, unless --long is provided. When no fitting tagged ** ancestor is found, show only the short hash of VERSION. ** ** Options: | | | 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 |
** If no VERSION is provided, describe the currently checked-out version.
**
** If VERSION and the found ancestor refer to the same commit, the last two
** components are omitted, unless --long is provided. When no fitting tagged
** ancestor is found, show only the short hash of VERSION.
**
** Options:
** --digits Display so many hex digits of the hash
** (default: the larger of 6 and the 'hash-digit' setting)
** -d|--dirty Show whether there are changes to be committed
** --long Always show all three components
** --match GLOB Consider only non-propagating tags matching GLOB
*/
void describe_cmd(void){
const char *zName;
|
| ︙ | ︙ |
Changes to src/interwiki.c.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 |
**
** {
** "base": Base URL for the remote site.
** "hash": Append this to "base" for Hash targets.
** "wiki": Append this to "base" for Wiki targets.
** }
**
| | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
**
** {
** "base": Base URL for the remote site.
** "hash": Append this to "base" for Hash targets.
** "wiki": Append this to "base" for Wiki targets.
** }
**
** If the remote wiki is Fossil, then the correct value for "hash"
** is "/info/" and the correct value for "wiki" is "/wiki?name=".
** If (for example) Wikipedia is the remote, then "hash" should be
** omitted and the correct value for "wiki" is "/wiki/".
**
** PageName is link name of the target wiki. Several different forms
** of PageName are recognized.
**
** Path If PageName is empty or begins with a "/" character, then
** it is a pathname that is appended to "base".
**
|
| ︙ | ︙ | |||
80 81 82 83 84 85 86 |
static Stmt q;
for(i=0; fossil_isalnum(zTarget[i]); i++){}
if( zTarget[i]!=':' ) return 0;
nCode = i;
if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
zPage = zTarget + nCode + 1;
nPage = (int)strlen(zPage);
| | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
static Stmt q;
for(i=0; fossil_isalnum(zTarget[i]); i++){}
if( zTarget[i]!=':' ) return 0;
nCode = i;
if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
zPage = zTarget + nCode + 1;
nPage = (int)strlen(zPage);
db_static_prepare(&q,
"SELECT value->>'base', value->>'hash', value->>'wiki'"
" FROM config WHERE name=lower($name) AND json_valid(value)"
);
zName = mprintf("interwiki:%.*s", nCode, zTarget);
db_bind_text(&q, "$name", zName);
while( db_step(&q)==SQLITE_ROW ){
const char *zBase = db_column_text(&q,0);
|
| ︙ | ︙ | |||
220 221 222 223 224 225 226 |
verify_all_options();
if( g.argc<4 ) usage("delete ID ...");
db_begin_write();
db_unprotect(PROTECT_CONFIG);
for(i=3; i<g.argc; i++){
const char *zName = g.argv[i];
db_multi_exec(
| | | 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
verify_all_options();
if( g.argc<4 ) usage("delete ID ...");
db_begin_write();
db_unprotect(PROTECT_CONFIG);
for(i=3; i<g.argc; i++){
const char *zName = g.argv[i];
db_multi_exec(
"DELETE FROM config WHERE name='interwiki:%q'",
zName
);
}
setup_incr_cfgcnt();
db_protect_pop();
db_commit_transaction();
}else
|
| ︙ | ︙ |
Changes to src/json.c.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 | ** The JSON API's public interface is documented at: ** ** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md ** ** Notes for hackers... ** ** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or | | | > | > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
** The JSON API's public interface is documented at:
**
** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md
**
** Notes for hackers...
**
** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions
** then dispatch to a JSON-mode-specific command/page handler with the type
** fossil_json_f().
** See the API docs for that typedef (below) for the semantics of the callbacks.
**
**
*/
#include "VERSION.h"
#include "config.h"
#include "json.h"
#include <assert.h>
#include <time.h>
#if INTERFACE
#include "json_detail.h" /* workaround for apparent enum limitation
in makeheaders */
#endif
const FossilJsonKeys_ FossilJsonKeys = {
"anonymousSeed" /*anonymousSeed*/,
"authToken" /*authToken*/,
"COMMAND_PATH" /*commandPath*/,
"mtime" /*mtime*/,
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 | return 0; } /* ** Convenience wrapper around cson_output() which appends the output ** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used. */ | | > | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
return 0;
}
/*
** Convenience wrapper around cson_output() which appends the output
** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used.
*/
int cson_output_Blob( cson_value const * pVal, Blob * pDest,
cson_output_opt const * pOpt ){
return cson_output( pVal, cson_data_dest_Blob,
pDest, pOpt ? pOpt : &g.json.outOpt );
}
/*
** Convenience wrapper around cson_parse() which reads its input
** from pSrc. pSrc is rewound before parsing.
|
| ︙ | ︙ | |||
705 706 707 708 709 710 711 |
login_cookie_name(), there is(?) a potential(?) login hijacking
window here. We may need to change the JSON auth token to be in
the form: login_cookie_name()=...
Then again, the hardened cookie value helps ensure that
only a proper key/value match is valid.
*/
| | > | 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
login_cookie_name(), there is(?) a potential(?) login hijacking
window here. We may need to change the JSON auth token to be in
the form: login_cookie_name()=...
Then again, the hardened cookie value helps ensure that
only a proper key/value match is valid.
*/
cgi_replace_parameter( login_cookie_name(),
cson_value_get_cstr(g.json.authToken) );
}else if( g.isHTTP ){
/* try fossil's conventional cookie. */
/* Reminder: chicken/egg scenario regarding db access in CLI
mode because login_cookie_name() needs the db. CLI
mode does not use any authentication, so we don't need
to support it here.
*/
|
| ︙ | ︙ | |||
902 903 904 905 906 907 908 |
assert( head != p );
zPart = (char*)fossil_malloc(len+1);
memcpy(zPart, head, len);
zPart[len] = 0;
if(doDeHttp){
dehttpize(zPart);
}
| > | | 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 |
assert( head != p );
zPart = (char*)fossil_malloc(len+1);
memcpy(zPart, head, len);
zPart[len] = 0;
if(doDeHttp){
dehttpize(zPart);
}
if( *zPart ){
/* should only fail if someone manages to url-encoded a NUL byte */
part = cson_value_new_string(zPart, strlen(zPart));
if( 0 != cson_array_append( target, part ) ){
cson_value_free(part);
rc = -rc;
break;
}
}else{
|
| ︙ | ︙ | |||
1084 1085 1086 1087 1088 1089 1090 |
break;
}
/* g.json.reqPayload exists only to simplify some of our access to
the request payload. We currently only use this in the context of
Object payloads, not Arrays, strings, etc.
*/
| | | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 |
break;
}
/* g.json.reqPayload exists only to simplify some of our access to
the request payload. We currently only use this in the context of
Object payloads, not Arrays, strings, etc.
*/
g.json.reqPayload.v = cson_object_get( g.json.post.o,FossilJsonKeys.payload );
if( g.json.reqPayload.v ){
g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
/* g.json.reqPayload.o may legally be NULL, which means only that
g.json.reqPayload.v is-not-a Object.
*/;
}
|
| ︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 |
}
if(!g.json.jsonp){
g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
}
if(!g.isHTTP){
| | | 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 |
}
if(!g.json.jsonp){
g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
}
if(!g.isHTTP){
g.json.errorDetailParanoia = 0;/*disable error code dumb-down for CLI mode*/
}
{/* set up JSON output formatting options. */
int indent = -1;
indent = json_find_option_int("indent",NULL,"I",-1);
g.json.outOpt.indentation = (0>indent)
? (g.isHTTP ? 0 : 1)
|
| ︙ | ︙ | |||
1164 1165 1166 1167 1168 1169 1170 |
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
cson_array * ar = g.json.cmd.a;
assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
| | | 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 |
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
cson_array * ar = g.json.cmd.a;
assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
assert((g.argc>1) &&"Internal error - we never should have gotten this far.");
if( g.json.cmd.offset < 0 ){
/* first-time setup. */
short i = 0;
#define NEXT cson_string_cstr( \
cson_value_get_string( \
cson_array_get(ar,i) \
))
|
| ︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 |
}
}
#undef NEXT
if(g.json.cmd.offset < 0){
return NULL;
}else{
ndx = g.json.cmd.offset + ndx;
| | > | > | 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 |
}
}
#undef NEXT
if(g.json.cmd.offset < 0){
return NULL;
}else{
ndx = g.json.cmd.offset + ndx;
return cson_string_cstr(cson_value_get_string(
cson_array_get( ar, g.json.cmd.offset + ndx )));
}
}
/* Returns the C-string form of json_auth_token(), or NULL
** if json_auth_token() returns NULL.
*/
char const * json_auth_token_cstr(){
return cson_value_get_cstr( json_auth_token() );
}
/*
** Returns the JsonPageDef with the given name, or NULL if no match is
** found.
**
** head must be a pointer to an array of JsonPageDefs in which the
** last entry has a NULL name.
*/
JsonPageDef const * json_handler_for_name( char const * name,
JsonPageDef const * head ){
JsonPageDef const * pageDef = head;
assert( head != NULL );
if(name && *name) for( ; pageDef->name; ++pageDef ){
if( 0 == strcmp(name, pageDef->name) ){
return pageDef;
}
}
|
| ︙ | ︙ | |||
1290 1291 1292 1293 1294 1295 1296 |
*/
static cson_value * json_response_command_path(){
if(!g.json.cmd.a){
return NULL;
}else{
cson_value * rc = NULL;
Blob path = empty_blob;
| | > | > | 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 |
*/
static cson_value * json_response_command_path(){
if(!g.json.cmd.a){
return NULL;
}else{
cson_value * rc = NULL;
Blob path = empty_blob;
unsigned int aLen = g.json.dispatchDepth+1;
/*cson_array_length_get(g.json.cmd.a);*/
unsigned int i = 1;
for( ; i < aLen; ++i ){
char const * part = cson_string_cstr(cson_value_get_string(
cson_array_get(g.json.cmd.a, i)));
if(!part){
#if 1
fossil_warning("Iterating further than expected in %s.",
__FILE__);
#endif
break;
}
|
| ︙ | ︙ | |||
1327 1328 1329 1330 1331 1332 1333 |
*/
cson_value * json_g_to_json(){
cson_object * o = NULL;
cson_object * pay = NULL;
pay = o = cson_new_object();
#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
| | > | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 |
*/
cson_value * json_g_to_json(){
cson_object * o = NULL;
cson_object * pay = NULL;
pay = o = cson_new_object();
#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
#define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) \
: cson_value_null())
#define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
VAL(capabilities, json_cap_value());
INT(g, argc);
INT(g, isConst);
CSTR(g, zConfigDbName);
INT(g, repositoryOpen);
INT(g, localOpen);
|
| ︙ | ︙ | |||
1811 1812 1813 1814 1815 1816 1817 |
cson_string * kDesc;
cson_array_reserve( list, 35 );
kRC = cson_new_string("resultCode",10);
kSymbol = cson_new_string("cSymbol",7);
kNumber = cson_new_string("number",6);
kDesc = cson_new_string("description",11);
#define C(K) obj = cson_new_object(); \
| | | | | > | 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 |
cson_string * kDesc;
cson_array_reserve( list, 35 );
kRC = cson_new_string("resultCode",10);
kSymbol = cson_new_string("cSymbol",7);
kNumber = cson_new_string("number",6);
kDesc = cson_new_string("description",11);
#define C(K) obj = cson_new_object(); \
cson_object_set_s(obj, kRC,json_new_string(json_rc_cstr(FSL_JSON_E_##K))); \
cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); \
cson_object_set_s(obj, kNumber, cson_value_new_integer(FSL_JSON_E_##K) ); \
cson_object_set_s(obj, kDesc, \
json_new_string(json_err_cstr(FSL_JSON_E_##K))); \
cson_array_append( list, cson_object_value(obj) ); obj = NULL;
C(GENERIC);
C(INVALID_REQUEST);
C(UNKNOWN_COMMAND);
C(UNKNOWN);
C(TIMEOUT);
|
| ︙ | ︙ | |||
2004 2005 2006 2007 2008 2009 2010 |
if( !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'o' permissions.");
return NULL;
}
full = json_find_option_bool("full",NULL,"f",
json_find_option_bool("verbose",NULL,"v",0));
| | > | | 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 |
if( !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'o' permissions.");
return NULL;
}
full = json_find_option_bool("full",NULL,"f",
json_find_option_bool("verbose",NULL,"v",0));
#define SETBUF(O,K) cson_object_set(O, K, \
cson_value_new_string(zBuf, strlen(zBuf)));
jv = cson_value_new_object();
jo = cson_value_get_object(jv);
zTmp = db_get("project-name",NULL);
cson_object_set(jo, "projectName", json_new_string(zTmp));
fossil_free(zTmp);
zTmp = db_get("project-description",NULL);
cson_object_set(jo, "projectDescription", json_new_string(zTmp));
fossil_free(zTmp);
zTmp = NULL;
fsize = file_size(g.zRepositoryName, ExtFILE);
cson_object_set(jo, "repositorySize",
cson_value_new_integer((cson_int_t)fsize));
if(full){
n = db_int(0, "SELECT count(*) FROM blob");
m = db_int(0, "SELECT count(*) FROM delta");
cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
|
| ︙ | ︙ | |||
2066 2067 2068 2069 2070 2071 2072 |
}/*full*/
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
SETBUF(jo, "projectCode");
| > | | | | > | > | > | | > | > | 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 |
}/*full*/
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
SETBUF(jo, "projectCode");
cson_object_set(jo, "compiler",
cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
jv2 = cson_value_new_object();
jo2 = cson_value_get_object(jv2);
cson_object_set(jo, "sqlite", jv2);
sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)", sqlite3_sourceid(),
&sqlite3_sourceid()[20], sqlite3_libversion());
SETBUF(jo2, "version");
cson_object_set(jo2, "pageCount", cson_value_new_integer(
(cson_int_t)db_int(0, "PRAGMA repository.page_count")));
cson_object_set(jo2, "pageSize", cson_value_new_integer(
(cson_int_t)db_int(0, "PRAGMA repository.page_size")));
cson_object_set(jo2, "freeList", cson_value_new_integer(
(cson_int_t)db_int(0, "PRAGMA repository.freelist_count")));
sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0,"PRAGMA repository.encoding"));
SETBUF(jo2, "encoding");
sqlite3_snprintf(BufLen, zBuf, "%s",
db_text(0, "PRAGMA repository.journal_mode"));
cson_object_set(jo2, "journalMode", *zBuf ?
cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
return jv;
#undef SETBUF
}
|
| ︙ | ︙ | |||
2235 2236 2237 2238 2239 2240 2241 |
cson_value * json_page_status(void);
/*
** Mapping of names to JSON pages/commands. Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
| | > | 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 |
cson_value * json_page_status(void);
/*
** Mapping of names to JSON pages/commands. Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
/* please keep alphabetically sorted (case-insensitive)
for maintenance reasons. */
{"anonymousPassword", json_page_anon_password, 0},
{"artifact", json_page_artifact, 0},
{"branch", json_page_branch,0},
{"cap", json_page_cap, 0},
{"config", json_page_config, 0 },
{"diff", json_page_diff, 0},
{"dir", json_page_dir, 0},
|
| ︙ | ︙ |
Changes to src/json_artifact.c.
| ︙ | ︙ | |||
209 210 211 212 213 214 215 |
}
/*
** Sub-impl of /json/artifact for check-ins.
*/
static cson_value * json_artifact_ci( cson_object * zParent, int rid ){
if(!g.perm.Read){
| | > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
}
/*
** Sub-impl of /json/artifact for check-ins.
*/
static cson_value * json_artifact_ci( cson_object * zParent, int rid ){
if(!g.perm.Read){
json_set_err( FSL_JSON_E_DENIED,
"Viewing check-ins requires 'o' privileges." );
return NULL;
}else{
cson_value * artV = json_artifact_for_ci(rid, 1);
cson_object * art = cson_value_get_object(artV);
if(art){
cson_object_merge( zParent, art, CSON_MERGE_REPLACE );
cson_free_object(art);
|
| ︙ | ︙ | |||
248 249 250 251 252 253 254 |
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/
static int json_artifact_get_content_format_flag(void){
enum { MagicValue = -9 };
int contentFormat = json_wiki_get_content_format_flag(MagicValue);
if(MagicValue == contentFormat){
| | > | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/
static int json_artifact_get_content_format_flag(void){
enum { MagicValue = -9 };
int contentFormat = json_wiki_get_content_format_flag(MagicValue);
if(MagicValue == contentFormat){
contentFormat = json_find_option_bool("includeContent",
"content","c",0) /* deprecated */ ? -1 : 0;
}
return contentFormat;
}
extern int json_wiki_get_content_format_flag(int defaultValue) /* json_wiki.c*/;
cson_value * json_artifact_wiki(cson_object * zParent, int rid){
if( ! g.perm.RdWiki ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'j' privileges.");
return NULL;
}else{
|
| ︙ | ︙ | |||
378 379 380 381 382 383 384 |
);
/* TODO: add a "state" flag for the file in each check-in,
e.g. "modified", "new", "deleted".
*/
checkin_arr = cson_new_array();
cson_object_set(pay, "checkins", cson_array_value(checkin_arr));
while( (SQLITE_ROW==db_step(&q) ) ){
| | > | | | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
);
/* TODO: add a "state" flag for the file in each check-in,
e.g. "modified", "new", "deleted".
*/
checkin_arr = cson_new_array();
cson_object_set(pay, "checkins", cson_array_value(checkin_arr));
while( (SQLITE_ROW==db_step(&q) ) ){
cson_object * row = cson_value_get_object(
cson_sqlite3_row_to_object(q.pStmt));
/* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */
char const isNew = cson_value_get_bool(cson_object_get(row,"isNew"));
char const isDel = cson_value_get_bool(cson_object_get(row,"isDel"));
cson_object_set(row, "isNew", NULL);
cson_object_set(row, "isDel", NULL);
cson_object_set(row, "state", json_new_string(
json_artifact_status_to_string(isNew, isDel)));
cson_array_append( checkin_arr, cson_object_value(row) );
}
db_finalize(&q);
return cson_object_value(pay);
}
/*
|
| ︙ | ︙ |
Changes to src/json_branch.c.
| ︙ | ︙ | |||
200 201 202 203 204 205 206 |
Manifest *pParent; /* Parsed parent manifest */
Blob mcksum; /* Self-checksum on the manifest */
int bAutoColor = 0; /* Value of "--bgcolor" is "auto" */
if( fossil_strncmp(zColor, "auto", 4)==0 ) {
bAutoColor = 1;
zColor = 0;
| | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
Manifest *pParent; /* Parsed parent manifest */
Blob mcksum; /* Self-checksum on the manifest */
int bAutoColor = 0; /* Value of "--bgcolor" is "auto" */
if( fossil_strncmp(zColor, "auto", 4)==0 ) {
bAutoColor = 1;
zColor = 0;
}
/* fossil branch new name */
if( zBranch==0 || zBranch[0]==0 ){
zOpt->rcErrMsg = "Branch name may not be null/empty.";
return FSL_JSON_E_INVALID_ARGS;
}
if( db_exists(
"SELECT 1 FROM tagxref"
|
| ︙ | ︙ | |||
333 334 335 336 337 338 339 |
}
if(!opt.zName){
opt.zName = json_command_arg(g.json.dispatchDepth+1);
}
if(!opt.zName){
| | > | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
}
if(!opt.zName){
opt.zName = json_command_arg(g.json.dispatchDepth+1);
}
if(!opt.zName){
json_set_err(FSL_JSON_E_MISSING_ARGS,
"'name' parameter was not specified." );
return NULL;
}
opt.zColor = json_find_option_cstr("bgColor","bgcolor",NULL);
opt.zBasis = json_find_option_cstr("basis",NULL,NULL);
if(!opt.zBasis && !g.isHTTP){
opt.zBasis = json_command_arg(g.json.dispatchDepth+2);
|
| ︙ | ︙ |
Changes to src/json_config.c.
| ︙ | ︙ | |||
255 256 257 258 259 260 261 |
}
for(i=0; i<nSetting; ++i){
const Setting *pSet = &aSetting[i];
cson_object * jSet;
cson_value * pVal = 0, * pSrc = 0;
jSet = cson_new_object();
cson_object_set(pay, pSet->name, cson_object_value(jSet));
| | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
}
for(i=0; i<nSetting; ++i){
const Setting *pSet = &aSetting[i];
cson_object * jSet;
cson_value * pVal = 0, * pSrc = 0;
jSet = cson_new_object();
cson_object_set(pay, pSet->name, cson_object_value(jSet));
cson_object_set(jSet, "versionable",cson_value_new_bool(pSet->versionable));
cson_object_set(jSet, "sensitive", cson_value_new_bool(pSet->sensitive));
cson_object_set(jSet, "defaultValue", (pSet->def && pSet->def[0])
? json_new_string(pSet->def)
: cson_value_null());
if( 0==pSet->sensitive || 0!=g.perm.Setup ){
if( pSet->versionable ){
/* Check to see if this is overridden by a versionable
|
| ︙ | ︙ | |||
290 291 292 293 294 295 296 |
Blob versionedPathname;
blob_zero(&versionedPathname);
blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
g.zLocalRoot, pSet->name);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
Blob content;
blob_zero(&content);
| | | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
Blob versionedPathname;
blob_zero(&versionedPathname);
blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
g.zLocalRoot, pSet->name);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
Blob content;
blob_zero(&content);
blob_read_from_file(&content, blob_str(&versionedPathname),ExtFILE);
pSrc = json_new_string("versioned");
pVal = json_new_string(blob_str(&content));
blob_reset(&content);
}
blob_reset(&versionedPathname);
}
}
|
| ︙ | ︙ |
Changes to src/json_finfo.c.
| ︙ | ︙ | |||
34 35 36 37 38 39 40 | Blob sql = empty_blob; Stmt q = empty_Stmt; char const * zAfter = NULL; char const * zBefore = NULL; int limit = -1; int currentRow = 0; char const * zCheckin = NULL; | | > | | | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
Blob sql = empty_blob;
Stmt q = empty_Stmt;
char const * zAfter = NULL;
char const * zBefore = NULL;
int limit = -1;
int currentRow = 0;
char const * zCheckin = NULL;
signed char sort = -1;
if(!g.perm.Read){
json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges.");
return NULL;
}
json_warn( FSL_JSON_W_UNKNOWN,
"Achtung: the output of the finfo command is up for change.");
/* For the "name" argument we have to jump through some hoops to make sure
that we don't get the fossil-internally-assigned "name" option.
*/
zFilename = json_find_option_cstr2("name",NULL,NULL, g.json.dispatchDepth+1);
if(!zFilename || !*zFilename){
json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' parameter.");
return NULL;
}
if(0==db_int(0,"SELECT 1 FROM filename WHERE name=%Q",zFilename)){
json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "File entry not found.");
return NULL;
}
zBefore = json_find_option_cstr("before",NULL,"b");
zAfter = json_find_option_cstr("after",NULL,"a");
limit = json_find_option_int("limit",NULL,"n", -1);
zCheckin = json_find_option_cstr("checkin",NULL,"ci");
blob_append_sql(&sql,
/*0*/ "SELECT b.uuid,"
/*1*/ " ci.uuid,"
/*2*/ " (SELECT uuid FROM blob WHERE rid=mlink.fid),"/* Current file uuid */
/*3*/ " cast(strftime('%%s',event.mtime) AS INTEGER),"
/*4*/ " coalesce(event.euser, event.user),"
/*5*/ " coalesce(event.ecomment, event.comment),"
/*6*/ " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
/*7*/ " event.bgcolor,"
/*8*/ " b.size,"
/*9*/ " (mlink.pid==0) AS isNew,"
|
| ︙ | ︙ | |||
86 87 88 89 90 91 92 |
);
if( zCheckin && *zCheckin ){
char * zU = NULL;
int rc = name_to_uuid2( zCheckin, "ci", &zU );
/*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
if(rc<=0){
| | > | > | > | > | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
);
if( zCheckin && *zCheckin ){
char * zU = NULL;
int rc = name_to_uuid2( zCheckin, "ci", &zU );
/*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
if(rc<=0){
json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID :
FSL_JSON_E_RESOURCE_NOT_FOUND,
"Check-in hash %s.", (rc<0) ? "is ambiguous" : "not found");
blob_reset(&sql);
return NULL;
}
blob_append_sql(&sql, " AND ci.uuid='%q'", zU);
free(zU);
}else{
if( zAfter && *zAfter ){
blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zAfter);
sort = 1;
}else if( zBefore && *zBefore ){
blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zBefore);
}
}
blob_append_sql(&sql," ORDER BY event.mtime %s /*sort*/",
(sort>0 ? "ASC" : "DESC"));
/*printf("SQL=\n%s\n",blob_str(&sql));*/
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_reset(&sql);
pay = cson_new_object();
cson_object_set(pay, "name", json_new_string(zFilename));
if( limit > 0 ){
cson_object_set(pay, "limit", json_new_int(limit));
}
checkins = cson_new_array();
cson_object_set(pay, "checkins", cson_array_value(checkins));
while( db_step(&q)==SQLITE_ROW ){
cson_object * row = cson_new_object();
int const isNew = db_column_int(&q,9);
int const isDel = db_column_int(&q,10);
cson_array_append( checkins, cson_object_value(row) );
cson_object_set(row, "checkin", json_new_string( db_column_text(&q,1) ));
cson_object_set(row, "uuid", json_new_string( db_column_text(&q,2) ));
/*cson_object_set(row, "parentArtifact",
json_new_string( db_column_text(&q,6) ));*/
cson_object_set(row, "timestamp", json_new_int( db_column_int64(&q,3) ));
cson_object_set(row, "user", json_new_string( db_column_text(&q,4) ));
cson_object_set(row, "comment", json_new_string( db_column_text(&q,5) ));
/*cson_object_set(row, "bgColor",
json_new_string( db_column_text(&q,7) ));*/
cson_object_set(row, "size", json_new_int( db_column_int64(&q,8) ));
cson_object_set(row, "state", json_new_string(
json_artifact_status_to_string(isNew, isDel)));
if( (0 < limit) && (++currentRow >= limit) ){
break;
}
}
db_finalize(&q);
return pay ? cson_object_value(pay) : NULL;
}
#endif /* FOSSIL_ENABLE_JSON */
|
Changes to src/json_login.c.
| ︙ | ︙ | |||
153 154 155 156 157 158 159 |
}
payload = cson_value_new_object();
po = cson_value_get_object(payload);
cson_object_set(po, "authToken", json_new_string(cookie));
free(cookie);
cson_object_set(po, "name", json_new_string(name));
cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name);
| | > | > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
}
payload = cson_value_new_object();
po = cson_value_get_object(payload);
cson_object_set(po, "authToken", json_new_string(cookie));
free(cookie);
cson_object_set(po, "name", json_new_string(name));
cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name);
cson_object_set(po, "capabilities",
cap ? json_new_string(cap) : cson_value_null() );
free(cap);
cson_object_set(po, "loginCookieName",
json_new_string( login_cookie_name() ) );
/* TODO: add loginExpiryTime to the payload. To do this properly
we "should" add an ([unsigned] int *) to
login_set_user_cookie() and login_set_anon_cookie(), to which
the expiry time is assigned. (Remember that JSON doesn't do
unsigned int.)
For non-anonymous users we could also simply query the
|
| ︙ | ︙ |
Changes to src/json_tag.c.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 |
cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
{
Blob uu = empty_blob;
int rc;
blob_append(&uu, zName, -1);
rc = name_to_uuid(&uu, 9, "*");
if(0!=rc){
| | > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
{
Blob uu = empty_blob;
int rc;
blob_append(&uu, zName, -1);
rc = name_to_uuid(&uu, 9, "*");
if(0!=rc){
json_set_err(FSL_JSON_E_UNKNOWN,
"Could not convert name back to artifact hash!");
blob_reset(&uu);
goto error;
}
cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
blob_reset(&uu);
}
|
| ︙ | ︙ |
Changes to src/json_timeline.c.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 | ** ** If payload is not NULL then on success its "tag" or "branch" ** property is set to the tag/branch name found in the request. ** ** Only one of "tag" or "branch" modes will work at a time, and if ** both are specified, which one takes precedence is unspecified. */ | | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
**
** If payload is not NULL then on success its "tag" or "branch"
** property is set to the tag/branch name found in the request.
**
** Only one of "tag" or "branch" modes will work at a time, and if
** both are specified, which one takes precedence is unspecified.
*/
static signed char json_timeline_add_tag_branch_clause(Blob *pSql,
cson_object * pPayload){
char const * zTag = NULL;
char const * zBranch = NULL;
char const * zMiOnly = NULL;
char const * zUnhide = NULL;
int tagid = 0;
if(! g.perm.Read ){
return 0;
|
| ︙ | ︙ | |||
167 168 169 170 171 172 173 |
zUnhide = json_find_option_cstr("unhide",NULL,NULL);
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
zTag);
if(tagid<=0){
return -1;
}
if(pPayload){
| | > | > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
zUnhide = json_find_option_cstr("unhide",NULL,NULL);
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
zTag);
if(tagid<=0){
return -1;
}
if(pPayload){
cson_object_set( pPayload, zBranch ? "branch" : "tag",
json_new_string(zTag) );
}
blob_appendf(pSql,
" AND ("
" EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
tagid);
if(!zUnhide){
blob_appendf(pSql,
" AND NOT EXISTS(SELECT 1 FROM plink "
" JOIN tagxref ON rid=blob.rid"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
TAG_HIDDEN);
}
if(zBranch){
/* from "r" flag code in page_timeline().*/
blob_appendf(pSql,
" OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
|
| ︙ | ︙ | |||
218 219 220 221 222 223 224 |
** of the "after" ("a") or "before" ("b") environment parameters.
** This function gives "after" precedence over "before", and only
** applies one of them.
**
** Returns -1 if it adds a "before" clause, 1 if it adds
** an "after" clause, and 0 if adds only an order-by clause.
*/
| | | 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
** of the "after" ("a") or "before" ("b") environment parameters.
** This function gives "after" precedence over "before", and only
** applies one of them.
**
** Returns -1 if it adds a "before" clause, 1 if it adds
** an "after" clause, and 0 if adds only an order-by clause.
*/
static signed char json_timeline_add_time_clause(Blob *pSql){
char const * zAfter = NULL;
char const * zBefore = NULL;
int rc = 0;
zAfter = json_find_option_cstr("after",NULL,"a");
zBefore = zAfter ? NULL : json_find_option_cstr("before",NULL,"b");
if(zAfter&&*zAfter){
|
| ︙ | ︙ | |||
350 351 352 353 354 355 356 |
cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3)));
if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){
cson_object_set(row, "parent", json_new_string(db_column_text(&q,4)));
}
cson_object_set(row, "size", json_new_int(db_column_int(&q,5)));
cson_object_set(row, "state",
| | | > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3)));
if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){
cson_object_set(row, "parent", json_new_string(db_column_text(&q,4)));
}
cson_object_set(row, "size", json_new_int(db_column_int(&q,5)));
cson_object_set(row, "state",
json_new_string(json_artifact_status_to_string(isNew,isDel)));
zDownload = mprintf("/raw/%s?name=%s",
/* reminder: g.zBaseURL is of course not set
for CLI mode. */
db_column_text(&q,2),
db_column_text(&q,3));
cson_object_set(row, "downloadPath", json_new_string(zDownload));
free(zDownload);
}
db_finalize(&q);
return rowsV;
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 |
int const rid = db_column_int(&q,0);
cson_value * rowV = json_artifact_for_ci(rid, verboseFlag);
cson_object * row = cson_value_get_object(rowV);
if(!row){
if( !warnRowToJsonFailed ){
warnRowToJsonFailed = 1;
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
| | | 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
int const rid = db_column_int(&q,0);
cson_value * rowV = json_artifact_for_ci(rid, verboseFlag);
cson_object * row = cson_value_get_object(rowV);
if(!row){
if( !warnRowToJsonFailed ){
warnRowToJsonFailed = 1;
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
"Could not convert at least one timeline result row to JSON." );
}
continue;
}
cson_array_append(list, rowV);
}
#undef SET
goto ok;
|
| ︙ | ︙ | |||
546 547 548 549 550 551 552 |
if(check){
json_set_err(check, "Query initialization failed.");
goto error;
}
#if 0
/* only for testing! */
| | > | > > | 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
if(check){
json_set_err(check, "Query initialization failed.");
goto error;
}
#if 0
/* only for testing! */
cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),
strlen(blob_buffer(&sql))));
#endif
db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
blob_reset(&sql);
db_prepare(&q, "SELECT"
/* For events, the name is generally more useful than
the uuid, but the uuid is unambiguous and can be used
with commands like 'artifact'. */
" substr((SELECT tagname FROM tag AS tn "
" WHERE tn.tagid=json_timeline.tagId "
" AND tagname LIKE 'event-%%'),7) AS name,"
" uuid as uuid,"
" mtime AS timestamp,"
" comment AS comment, "
" user AS user,"
" eventType AS eventType"
" FROM json_timeline"
" ORDER BY rowid");
|
| ︙ | ︙ | |||
589 590 591 592 593 594 595 |
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdWiki && !g.perm.Read ){
| | > | > | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdWiki && !g.perm.Read ){
json_set_err( FSL_JSON_E_DENIED,
"Wiki timeline requires 'o' or 'j' access.");
return NULL;
}
payV = cson_value_new_object();
pay = cson_value_get_object(payV);
check = json_timeline_setup_sql( "w", &sql, pay );
if(check){
json_set_err(check, "Query initialization failed.");
goto error;
}
#if 0
/* only for testing! */
cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),
strlen(blob_buffer(&sql))));
#endif
db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
blob_reset(&sql);
db_prepare(&q, "SELECT"
" uuid AS uuid,"
" mtime AS timestamp,"
#if 0
|
| ︙ | ︙ | |||
652 653 654 655 656 657 658 |
cson_value * tmp = NULL;
cson_value * listV = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdTkt && !g.perm.Read ){
| | > | 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 |
cson_value * tmp = NULL;
cson_value * listV = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdTkt && !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED,
"Ticket timeline requires 'o' or 'r' access.");
return NULL;
}
payV = cson_value_new_object();
pay = cson_value_get_object(payV);
check = json_timeline_setup_sql( "t", &sql, pay );
if(check){
json_set_err(check, "Query initialization failed.");
|
| ︙ | ︙ | |||
724 725 726 727 728 729 730 |
}
rowV = cson_sqlite3_row_to_object(q.pStmt);
row = cson_value_get_object(rowV);
if(!row){
manifest_destroy(pMan);
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
| | | 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
}
rowV = cson_sqlite3_row_to_object(q.pStmt);
row = cson_value_get_object(rowV);
if(!row){
manifest_destroy(pMan);
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
"Could not convert at least one timeline result row to JSON." );
continue;
}
/* FIXME: certainly there's a more efficient way for use to get
the ticket UUIDs?
*/
cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid));
manifest_destroy(pMan);
|
| ︙ | ︙ |
Changes to src/json_user.c.
| ︙ | ︙ | |||
168 169 170 171 172 173 174 |
** Requires either Admin, Setup, or Password access. Non-admin/setup
** users can only change their own information. Non-setup users may
** not modify the 's' permission. Admin users without setup
** permissions may not edit any other user who has the 's' permission.
**
*/
int json_user_update_from_json( cson_object * pUser ){
| | > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
** Requires either Admin, Setup, or Password access. Non-admin/setup
** users can only change their own information. Non-setup users may
** not modify the 's' permission. Admin users without setup
** permissions may not edit any other user who has the 's' permission.
**
*/
int json_user_update_from_json( cson_object * pUser ){
#define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, \
X ) ))
char const * zName = CSTR("name");
char const * zNameNew = zName;
char * zNameFree = NULL;
char const * zInfo = CSTR("info");
char const * zCap = CSTR("capabilities");
char const * zPW = CSTR("password");
cson_value const * forceLogout = cson_object_get(pUser, "forceLogout");
|
| ︙ | ︙ |
Changes to src/json_wiki.c.
| ︙ | ︙ | |||
161 162 163 164 165 166 167 | } /* ** Searches for the latest version of a wiki page with the given ** name. If found it behaves like json_get_wiki_page_by_rid(theRid, ** contentFormat), else it returns NULL. */ | | > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
}
/*
** Searches for the latest version of a wiki page with the given
** name. If found it behaves like json_get_wiki_page_by_rid(theRid,
** contentFormat), else it returns NULL.
*/
cson_value * json_get_wiki_page_by_name(char const * zPageName,
int contentFormat){
int rid;
rid = db_int(0,
"SELECT x.rid FROM tag t, tagxref x, blob b"
" WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' "
" AND b.rid=x.rid"
" ORDER BY x.mtime DESC LIMIT 1",
zPageName
|
| ︙ | ︙ | |||
257 258 259 260 261 262 263 |
}
zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);
zSymName = json_find_option_cstr("uuid",NULL,"u");
if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){
json_set_err(FSL_JSON_E_MISSING_ARGS,
| | | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
}
zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);
zSymName = json_find_option_cstr("uuid",NULL,"u");
if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){
json_set_err(FSL_JSON_E_MISSING_ARGS,
"At least one of the 'name' or 'uuid' arguments must be provided.");
return NULL;
}
/* TODO: see if we have a page named zPageName. If not, try to resolve
zPageName as a UUID.
*/
|
| ︙ | ︙ | |||
295 296 297 298 299 300 301 |
zMime = cson_value_get_cstr(cson_object_get(g.json.reqPayload.o,
"mimetype"));
}else{
sContent = cson_value_get_string(g.json.reqPayload.v);
}
if(!sContent) {
json_set_err(FSL_JSON_E_MISSING_ARGS,
| | | | | | > | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
zMime = cson_value_get_cstr(cson_object_get(g.json.reqPayload.o,
"mimetype"));
}else{
sContent = cson_value_get_string(g.json.reqPayload.v);
}
if(!sContent) {
json_set_err(FSL_JSON_E_MISSING_ARGS,
"The 'payload' property must be either a string containing the "
"Fossil wiki code to preview or an object with body + mimetype "
"properties.");
return NULL;
}
zContent = cson_string_cstr(sContent);
blob_append( &contentOrig, zContent, (int)cson_string_length_bytes(sContent));
zMime = wiki_filter_mimetypes(zMime);
if( 0==fossil_strcmp(zMime, "text/x-markdown") ){
markdown_to_html(&contentOrig, 0, &contentHtml);
}else if( 0==fossil_strcmp(zMime, "text/plain") ){
blob_append(&contentHtml, "<pre class='textPlain'>", -1);
blob_append(&contentHtml, blob_str(&contentOrig), blob_size(&contentOrig));
blob_append(&contentHtml, "</pre>", -1);
}else{
wiki_convert( &contentOrig, &contentHtml, 0 );
}
blob_reset( &contentOrig );
pay = cson_value_new_string( blob_str(&contentHtml),
(unsigned int)blob_size(&contentHtml));
blob_reset( &contentHtml );
return pay;
}
/*
** Internal impl of /wiki/save and /wiki/create. If createMode is 0
|
| ︙ | ︙ | |||
344 345 346 347 348 349 350 |
char allowCreateIfNotExists){
Blob content = empty_blob; /* wiki page content */
cson_value * nameV; /* wiki page name */
char const * zPageName; /* cstr form of page name */
cson_value * contentV; /* passed-in content */
cson_value * emptyContent = NULL; /* placeholder for empty content. */
cson_value * payV = NULL; /* payload/return value */
| | > | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
char allowCreateIfNotExists){
Blob content = empty_blob; /* wiki page content */
cson_value * nameV; /* wiki page name */
char const * zPageName; /* cstr form of page name */
cson_value * contentV; /* passed-in content */
cson_value * emptyContent = NULL; /* placeholder for empty content. */
cson_value * payV = NULL; /* payload/return value */
cson_string const * jstr = NULL; /* temp for cson_value-to-cson_string
conversions. */
char const * zMimeType = 0;
unsigned int contentLen = 0;
int rid;
if( (createMode && !g.perm.NewWiki)
|| (!createMode && !g.perm.WrWiki)){
json_set_err(FSL_JSON_E_DENIED,
"Requires '%c' permissions.",
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
52 53 54 55 56 57 58 |
#include <time.h>
/*
** Compute an appropriate Anti-CSRF token into g.zCsrfToken[].
*/
static void login_create_csrf_secret(const char *zSeed){
unsigned char zResult[20];
| | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#include <time.h>
/*
** Compute an appropriate Anti-CSRF token into g.zCsrfToken[].
*/
static void login_create_csrf_secret(const char *zSeed){
unsigned char zResult[20];
unsigned int i;
sha1sum_binary(zSeed, zResult);
for(i=0; i<sizeof(g.zCsrfToken)-1; i++){
g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789-/"[zResult[i]%64];
}
|
| ︙ | ︙ | |||
252 253 254 255 256 257 258 |
const char *zLogin = db_column_text(&q,0);
if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){
*pzUsername = fossil_strdup(zLogin);
break;
}
}
db_finalize(&q);
| | | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
const char *zLogin = db_column_text(&q,0);
if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){
*pzUsername = fossil_strdup(zLogin);
break;
}
}
db_finalize(&q);
}
free(zSha1Pw);
return uid;
}
/*
** Generates a login cookie value for a non-anonymous user.
**
|
| ︙ | ︙ | |||
773 774 775 776 777 778 779 |
}else{
zAnonPw = 0;
}
@ <table class="login_out">
if( P("HTTPS")==0 ){
@ <tr><td class="form_label">Warning:</td>
@ <td><span class='securityWarning'>
| | | 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 |
}else{
zAnonPw = 0;
}
@ <table class="login_out">
if( P("HTTPS")==0 ){
@ <tr><td class="form_label">Warning:</td>
@ <td><span class='securityWarning'>
@ Login information, including the password,
@ will be sent in the clear over an unencrypted connection.
if( !g.sslNotAvailable ){
@ Consider logging in at
@ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
}
@ </span></td></tr>
}
|
| ︙ | ︙ | |||
822 823 824 825 826 827 828 |
@ </tr>
}
@ </table>
if( zAnonPw && !noAnon ){
const char *zDecoded = captcha_decode(uSeed);
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
char *zCaptcha = captcha_render(zDecoded);
| | | 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 |
@ </tr>
}
@ </table>
if( zAnonPw && !noAnon ){
const char *zDecoded = captcha_decode(uSeed);
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
char *zCaptcha = captcha_render(zDecoded);
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
@ Visitors may enter <b>anonymous</b> as the user-ID with
@ the 8-character hexadecimal password shown below:</p>
@ <div class="captcha"><table class="captcha"><tr><td>\
@ <pre class="captcha">
@ %h(zCaptcha)
@ </pre></td></tr></table>
|
| ︙ | ︙ | |||
1027 1028 1029 1030 1031 1032 1033 |
uid = login_resetpw_suffix_is_valid(zName);
if( uid==0 ){
@ <p><span class="loginError">
@ This password-reset URL is invalid, probably because it has expired.
@ Password-reset URLs have a short lifespan.
@ </span></p>
style_finish_page();
| | | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 |
uid = login_resetpw_suffix_is_valid(zName);
if( uid==0 ){
@ <p><span class="loginError">
@ This password-reset URL is invalid, probably because it has expired.
@ Password-reset URLs have a short lifespan.
@ </span></p>
style_finish_page();
sleep(1); /* Introduce a small delay on an invalid suffix as an
** extra defense against search attacks */
return;
}
fossil_redirect_to_https_if_needed(1);
login_set_uid(uid, 0);
if( g.perm.Setup || g.perm.Admin || !g.perm.Password || g.zLogin==0 ){
@ <p><span class="loginError">
|
| ︙ | ︙ | |||
1161 1162 1163 1164 1165 1166 1167 |
pStmt = 0;
rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
db_unprotect(PROTECT_USER);
db_multi_exec(
"UPDATE user SET cookie=%Q, cexpire=%.17g"
" WHERE login=%Q",
| | | 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 |
pStmt = 0;
rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
db_unprotect(PROTECT_USER);
db_multi_exec(
"UPDATE user SET cookie=%Q, cexpire=%.17g"
" WHERE login=%Q",
zHash,
sqlite3_column_double(pStmt, 0), zLogin
);
db_protect_pop();
nXfer++;
}
sqlite3_finalize(pStmt);
}
|
| ︙ | ︙ | |||
1578 1579 1580 1581 1582 1583 1584 |
case 'a': p->Admin = p->RdTkt = p->WrTkt = p->Zip =
p->RdWiki = p->WrWiki = p->NewWiki =
p->ApndWiki = p->Hyperlink = p->Clone =
p->NewTkt = p->Password = p->RdAddr =
p->TktFmt = p->Attach = p->ApndTkt =
p->ModWiki = p->ModTkt =
p->RdForum = p->WrForum = p->ModForum =
| | | 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 |
case 'a': p->Admin = p->RdTkt = p->WrTkt = p->Zip =
p->RdWiki = p->WrWiki = p->NewWiki =
p->ApndWiki = p->Hyperlink = p->Clone =
p->NewTkt = p->Password = p->RdAddr =
p->TktFmt = p->Attach = p->ApndTkt =
p->ModWiki = p->ModTkt =
p->RdForum = p->WrForum = p->ModForum =
p->WrTForum = p->AdminForum = p->Chat =
p->EmailAlert = p->Announce = p->Debug = 1;
/* Fall thru into Read/Write */
case 'i': p->Read = p->Write = 1; break;
case 'o': p->Read = 1; break;
case 'z': p->Zip = 1; break;
case 'h': p->Hyperlink = 1; break;
|
| ︙ | ︙ | |||
1825 1826 1827 1828 1829 1830 1831 |
*/
void login_insert_csrf_secret(void){
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}
/*
** Check to see if the candidate username zUserID is already used.
| | | 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 |
*/
void login_insert_csrf_secret(void){
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}
/*
** Check to see if the candidate username zUserID is already used.
** Return 1 if it is already in use. Return 0 if the name is
** available for a self-registeration.
*/
static int login_self_choosen_userid_already_exists(const char *zUserID){
int rc = db_exists(
"SELECT 1 FROM user WHERE login=%Q "
"UNION ALL "
"SELECT 1 FROM event WHERE user=%Q OR euser=%Q",
|
| ︙ | ︙ | |||
1847 1848 1849 1850 1851 1852 1853 |
** searches for a user or subscriber that has that email address. If the
** email address is used no-where in the system, return 0. If the email
** address is assigned to a particular user return the UID for that user.
** If the email address is used, but not by a particular user, return -1.
*/
static int email_address_in_use(const char *zEMail){
int uid;
| | | 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 |
** searches for a user or subscriber that has that email address. If the
** email address is used no-where in the system, return 0. If the email
** address is assigned to a particular user return the UID for that user.
** If the email address is used, but not by a particular user, return -1.
*/
static int email_address_in_use(const char *zEMail){
int uid;
uid = db_int(0,
"SELECT uid FROM user"
" WHERE info LIKE '%%<%q>%%'", zEMail);
if( uid>0 ){
if( db_exists("SELECT 1 FROM user WHERE uid=%d AND ("
" cap GLOB '*[as]*' OR"
" find_emailaddr(info)<>%Q COLLATE nocase)",
uid, zEMail) ){
|
| ︙ | ︙ | |||
1876 1877 1878 1879 1880 1881 1882 | } return uid; } /* ** COMMAND: test-email-used ** Usage: fossil test-email-used EMAIL ... | | | 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 |
}
return uid;
}
/*
** COMMAND: test-email-used
** Usage: fossil test-email-used EMAIL ...
**
** Given a list of email addresses, show the UID and LOGIN associated
** with each one.
*/
void test_email_used(void){
int i;
db_find_and_open_repository(0, 0);
verify_all_options();
|
| ︙ | ︙ | |||
1901 1902 1903 1904 1905 1906 1907 |
}else{
char *zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
fossil_print("%s: UID %d (%s)\n", zEMail, uid, zLogin);
fossil_free(zLogin);
}
}
}
| | | 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 |
}else{
char *zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
fossil_print("%s: UID %d (%s)\n", zEMail, uid, zLogin);
fossil_free(zLogin);
}
}
}
/*
** Check an email address and confirm that it is valid for self-registration.
** The email address is known already to be well-formed. Return true
** if the email address is on the allowed list.
**
** The default behavior is that any valid email address is accepted.
|
| ︙ | ︙ | |||
1993 1994 1995 1996 1997 1998 1999 |
zErr = "Incorrect CAPTCHA";
}else if( strlen(zUserID)<6 ){
iErrLine = 1;
zErr = "User ID too short. Must be at least 6 characters.";
}else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){
iErrLine = 1;
zErr = "User ID may not contain spaces or special characters.";
| | | 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 |
zErr = "Incorrect CAPTCHA";
}else if( strlen(zUserID)<6 ){
iErrLine = 1;
zErr = "User ID too short. Must be at least 6 characters.";
}else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){
iErrLine = 1;
zErr = "User ID may not contain spaces or special characters.";
}else if( sqlite3_strlike("anonymous%", zUserID, 0)==0
|| sqlite3_strlike("nobody%", zUserID, 0)==0
|| sqlite3_strlike("reader%", zUserID, 0)==0
|| sqlite3_strlike("developer%", zUserID, 0)==0
){
iErrLine = 1;
zErr = "This User ID is reserved. Choose something different.";
}else if( zDName[0]==0 ){
|
| ︙ | ︙ |
Changes to src/lookslike.c.
| ︙ | ︙ | |||
268 269 270 271 272 273 274 |
const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent);
unsigned int n = blob_size(pContent);
int j, c, flags = LOOK_NONE; /* Assume UTF-16 text, prove otherwise */
if( n%sizeof(WCHAR_T) ){
flags |= LOOK_ODD; /* Odd number of bytes -> binary (UTF-8?) */
}
| | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent);
unsigned int n = blob_size(pContent);
int j, c, flags = LOOK_NONE; /* Assume UTF-16 text, prove otherwise */
if( n%sizeof(WCHAR_T) ){
flags |= LOOK_ODD; /* Odd number of bytes -> binary (UTF-8?) */
}
if( n<sizeof(WCHAR_T) ) return flags;/* Zero or One byte -> binary (UTF-8?) */
c = *z;
if( bReverse ){
c = UTF16_SWAP(c);
}
if( c==0 ){
flags |= LOOK_NUL; /* NUL character in a file -> binary */
}else if( c=='\r' ){
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
850 851 852 853 854 855 856 |
zNewArgv[0] = g.argv[0];
zNewArgv[1] = "ui";
zNewArgv[2] = g.argv[1];
zNewArgv[3] = 0;
g.argc = 3;
g.argv = zNewArgv;
#endif
| | | 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 |
zNewArgv[0] = g.argv[0];
zNewArgv[1] = "ui";
zNewArgv[2] = g.argv[1];
zNewArgv[3] = 0;
g.argc = 3;
g.argv = zNewArgv;
#endif
}
zCmdName = g.argv[1];
}
#ifndef _WIN32
/* There is a bug in stunnel4 in which it sometimes starts up client
** processes without first opening file descriptor 2 (standard error).
** If this happens, and a subsequent open() of a database returns file
** descriptor 2, and then an assert() fires and writes on fd 2, that
|
| ︙ | ︙ | |||
1412 1413 1414 1415 1416 1417 1418 |
/* Remove trailing ":443" from the HOST, if any */
if( i>4 && z[i-1]=='3' && z[i-2]=='4' && z[i-3]=='4' && z[i-4]==':' ){
i -= 4;
}
}else{
/* Remove trailing ":80" from the HOST */
if( i>3 && z[i-1]=='0' && z[i-2]=='8' && z[i-3]==':' ) i -= 3;
| | | 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 |
/* Remove trailing ":443" from the HOST, if any */
if( i>4 && z[i-1]=='3' && z[i-2]=='4' && z[i-3]=='4' && z[i-4]==':' ){
i -= 4;
}
}else{
/* Remove trailing ":80" from the HOST */
if( i>3 && z[i-1]=='0' && z[i-2]=='8' && z[i-3]==':' ) i -= 3;
}
if( i && z[i-1]=='.' ) i--;
z[i] = 0;
zCur = PD("SCRIPT_NAME","/");
i = strlen(zCur);
while( i>0 && zCur[i-1]=='/' ) i--;
if( fossil_stricmp(zMode,"on")==0 ){
g.zBaseURL = mprintf("https://%s%.*s", z, i, zCur);
|
| ︙ | ︙ | |||
1612 1613 1614 1615 1616 1617 1618 |
if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
if( P("HTTPS")!=0 ) return 0;
return 1;
}
/*
** Redirect to the equivalent HTTPS request if the current connection is
| | | 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 |
if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
if( P("HTTPS")!=0 ) return 0;
return 1;
}
/*
** Redirect to the equivalent HTTPS request if the current connection is
** insecure and if the redirect-to-https flag greater than or equal to
** iLevel. iLevel is 1 for /login pages and 2 for every other page.
*/
int fossil_redirect_to_https_if_needed(int iLevel){
if( fossil_wants_https(iLevel) ){
const char *zQS = P("QUERY_STRING");
char *zURL;
if( zQS==0 || zQS[0]==0 ){
|
| ︙ | ︙ | |||
1967 1968 1969 1970 1971 1972 1973 |
zPathInfo += 7;
g.nExtraURL += 7;
cgi_replace_parameter("PATH_INFO", zPathInfo);
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
etag_cancel();
}
| | | 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 |
zPathInfo += 7;
g.nExtraURL += 7;
cgi_replace_parameter("PATH_INFO", zPathInfo);
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
etag_cancel();
}
/* If the content type is application/x-fossil or
** application/x-fossil-debug, then a sync/push/pull/clone is
** desired, so default the PATH_INFO to /xfer
*/
if( g.zContentType &&
strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
/* Special case: If the content mimetype shows that it is "fossil sync"
** payload, then pretend that the PATH_INFO is /xfer so that we always
|
| ︙ | ︙ | |||
2783 2784 2785 2786 2787 2788 2789 | ** --nocompress Do not compress HTTP replies ** --nodelay Omit backoffice processing if it would delay ** process exit ** --nojail Drop root privilege but do not enter the chroot jail ** --nossl Do not do http: to https: redirects, regardless of ** the redirect-to-https setting. ** --notfound URL Use URL as the "HTTP 404, object not found" page | | | 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 |
** --nocompress Do not compress HTTP replies
** --nodelay Omit backoffice processing if it would delay
** process exit
** --nojail Drop root privilege but do not enter the chroot jail
** --nossl Do not do http: to https: redirects, regardless of
** the redirect-to-https setting.
** --notfound URL Use URL as the "HTTP 404, object not found" page
** --out FILE Write the HTTP reply to FILE instead of to
** standard output
** --pkey FILE Read the private key used for TLS from FILE
** --repolist If REPOSITORY is directory, URL "/" lists all repos
** --scgi Interpret input as SCGI rather than HTTP
** --skin LABEL Use override skin LABEL. Use an empty string ("")
** to force use of the current local skin config.
** --th-trace Trace TH1 execution (for debugging purposes)
|
| ︙ | ︙ | |||
3173 3174 3175 3176 3177 3178 3179 | int fCreate = 0; /* The --create flag */ int fNoBrowser = 0; /* Do not auto-launch web-browser */ const char *zInitPage = 0; /* Start on this page. --page option */ int findServerArg = 2; /* argv index for find_server_repository() */ char *zRemote = 0; /* Remote host on which to run "fossil ui" */ const char *zJsMode; /* The --jsmode parameter */ const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ | | | 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 | int fCreate = 0; /* The --create flag */ int fNoBrowser = 0; /* Do not auto-launch web-browser */ const char *zInitPage = 0; /* Start on this page. --page option */ int findServerArg = 2; /* argv index for find_server_repository() */ char *zRemote = 0; /* Remote host on which to run "fossil ui" */ const char *zJsMode; /* The --jsmode parameter */ const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ #if USE_SEE db_setup_for_saved_encryption_key(); #endif #if defined(_WIN32) const char *zStopperFile; /* Name of file used to terminate server */ |
| ︙ | ︙ | |||
3345 3346 3347 3348 3349 3350 3351 |
** tunnel from the local machine to the remote. */
FILE *sshIn;
Blob ssh;
char zLine[1000];
blob_init(&ssh, 0, 0);
transport_ssh_command(&ssh);
db_close_config();
| | | 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 |
** tunnel from the local machine to the remote. */
FILE *sshIn;
Blob ssh;
char zLine[1000];
blob_init(&ssh, 0, 0);
transport_ssh_command(&ssh);
db_close_config();
blob_appendf(&ssh,
" -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
iPort, iPort, zRemote
);
if( zFossilCmd==0 ){
blob_appendf(&ssh, " %$ fossil", "PATH=$HOME/bin:$PATH");
}else{
blob_appendf(&ssh, " %$", zFossilCmd);
|
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
1224 1225 1226 1227 1228 1229 1230 | ** control artifact. Make a copy, and run it through the official ** artifact parser. This is the slow path, but it is rarely taken. */ blob_init(©, 0, 0); blob_init(&errmsg, 0, 0); blob_append(©, zIn, nIn); pManifest = manifest_parse(©, 0, &errmsg); | | | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 | ** control artifact. Make a copy, and run it through the official ** artifact parser. This is the slow path, but it is rarely taken. */ blob_init(©, 0, 0); blob_init(&errmsg, 0, 0); blob_append(©, zIn, nIn); pManifest = manifest_parse(©, 0, &errmsg); iRes = pManifest!=0; manifest_destroy(pManifest); blob_reset(&errmsg); return iRes; } /* ** COMMAND: test-parse-manifest |
| ︙ | ︙ | |||
1336 1337 1338 1339 1340 1341 1342 |
id, blob_str(&err));
nErr++;
}else if( !isWF && p!=0 ){
fossil_print("%d ERROR: manifest_is_well_formed() reported false "
"but manifest_parse() found nothing wrong.\n", id);
nErr++;
}
| | | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 |
id, blob_str(&err));
nErr++;
}else if( !isWF && p!=0 ){
fossil_print("%d ERROR: manifest_is_well_formed() reported false "
"but manifest_parse() found nothing wrong.\n", id);
nErr++;
}
}else{
p = manifest_get(id, CFTYPE_ANY, &err);
if( p==0 ){
fossil_print("%d ERROR: %s\n", id, blob_str(&err));
nErr++;
}
}
blob_reset(&err);
|
| ︙ | ︙ | |||
2111 2112 2113 2114 2115 2116 2117 |
** Activate EVENT triggers if they do not already exist.
*/
void manifest_create_event_triggers(void){
if( manifest_event_triggers_are_enabled ){
return; /* Triggers already exists. No-op. */
}
alert_create_trigger();
| | | 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 |
** Activate EVENT triggers if they do not already exist.
*/
void manifest_create_event_triggers(void){
if( manifest_event_triggers_are_enabled ){
return; /* Triggers already exists. No-op. */
}
alert_create_trigger();
manifest_event_triggers_are_enabled = 1;
}
/*
** Disable manifest event triggers. Drop them if they exist, but mark
** them has having been created so that they won't be recreated. This
** is used during "rebuild" to prevent triggers from firing then.
*/
|
| ︙ | ︙ |
Changes to src/markdown.c.
| ︙ | ︙ | |||
62 63 64 65 66 67 68 |
void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque);
void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows,
void *opaque);
void (*table_cell)(struct Blob *ob, struct Blob *text, int flags,
void *opaque);
void (*table_row)(struct Blob *ob, struct Blob *cells, int flags,
void *opaque);
| | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque);
void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows,
void *opaque);
void (*table_cell)(struct Blob *ob, struct Blob *text, int flags,
void *opaque);
void (*table_row)(struct Blob *ob, struct Blob *cells, int flags,
void *opaque);
void (*footnote_item)(struct Blob *ob, const struct Blob *text,
int index, int nUsed, void *opaque);
/* span level callbacks - NULL or return 0 prints the span verbatim */
int (*autolink)(struct Blob *ob, struct Blob *link,
enum mkd_autolink type, void *opaque);
int (*codespan)(struct Blob *ob, struct Blob *text, int nSep, void *opaque);
int (*double_emphasis)(struct Blob *ob, struct Blob *text,
|
| ︙ | ︙ | |||
380 381 382 383 384 385 386 |
/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
if( !buf ) return;
rndr->iDepth--;
blob_reset(buf);
| > | | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
if( !buf ) return;
rndr->iDepth--;
blob_reset(buf);
if( rndr->nBlobCache <
(int)(sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0])) ){
rndr->aBlobCache[rndr->nBlobCache++] = buf;
}else{
fossil_free(buf);
}
}
|
| ︙ | ︙ | |||
1615 1616 1617 1618 1619 1620 1621 |
/* parse_blockquote -- handles parsing of a blockquote fragment */
static size_t parse_blockquote(
struct Blob *ob,
struct render *rndr,
char *data,
size_t size
){
| | > > > > > > > > > > > > > > > > > > > > > > > > > | | 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 |
/* parse_blockquote -- handles parsing of a blockquote fragment */
static size_t parse_blockquote(
struct Blob *ob,
struct render *rndr,
char *data,
size_t size
){
size_t beg, end = 0, pre, work_size = 0, nb, endFence = 0;
char *work_data = 0;
struct Blob *out = new_work_buffer(rndr);
/* Check to see if this is a quote of a fenced code block, because
** if it is, then blank lines do not terminated the quoted text. Ex:
**
** > ~~~~
** First line
**
** Line after blank
** ~~~~
**
** If this is a quoted fenced block, then set endFence to be the
** offset of the end of the fenced block.
*/
pre = prefix_quote(data,size);
pre += is_empty(data+pre,size-pre);
nb = prefix_fencedcode(data+pre,size-pre);
if( nb ){
size_t i = 0;
char delim = data[pre];
for(end=pre+nb; end<size && i<nb; end++){
if( data[end]==delim ) i++; else i = 0;
}
if( i>=nb ) endFence = end;
}
beg = 0;
while( beg<size ){
for(end=beg+1; end<size && data[end-1]!='\n'; end++);
pre = prefix_quote(data+beg, end-beg);
if( pre ){
beg += pre; /* skipping prefix */
}else if( is_empty(data+beg, end-beg)
&& (end>=size
|| (end>endFence
&& prefix_quote(data+end, size-end)==0
&& !is_empty(data+end, size-end)))
){
/* empty line followed by non-quote line */
break;
}
if( beg<end ){ /* copy into the in-place working buffer */
if( !work_data ){
|
| ︙ | ︙ | |||
2343 2344 2345 2346 2347 2348 2349 |
beg += parse_blockcode(ob, rndr, txt_data, end);
}else if( prefix_uli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, 0);
}else if( prefix_oli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
}else if( has_table && is_tableline(txt_data, end) ){
beg += parse_table(ob, rndr, txt_data, end);
| | | 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 |
beg += parse_blockcode(ob, rndr, txt_data, end);
}else if( prefix_uli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, 0);
}else if( prefix_oli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
}else if( has_table && is_tableline(txt_data, end) ){
beg += parse_table(ob, rndr, txt_data, end);
}else if( prefix_fencedcode(txt_data, end)
&& (i = char_codespan(ob, rndr, txt_data, 0, end))!=0
){
beg += i;
}else{
beg += parse_paragraph(ob, rndr, txt_data, end);
}
}
|
| ︙ | ︙ |
Changes to src/markdown_html.c.
| ︙ | ︙ | |||
778 779 780 781 782 783 784 |
){
char *zLink = blob_buffer(link);
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
char zClose[20];
if( zLink==0 || zLink[0]==0 ){
zClose[0] = 0;
| | | | 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 |
){
char *zLink = blob_buffer(link);
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
char zClose[20];
if( zLink==0 || zLink[0]==0 ){
zClose[0] = 0;
}else{
static const int flags =
WIKI_NOBADLINKS |
WIKI_MARKDOWNLINKS
;
wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
}
if( blob_size(content)==0 ){
if( link ) blob_appendb(ob, link);
|
| ︙ | ︙ |
Changes to src/merge.c.
| ︙ | ︙ | |||
134 135 136 137 138 139 140 | /* ** Add an entry to the FV table for all files renamed between ** version N and the version specified by vid. */ static void add_renames( const char *zFnCol, /* The FV column for the filename in vid */ int vid, /* The desired version's- RID */ | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
/*
** Add an entry to the FV table for all files renamed between
** version N and the version specified by vid.
*/
static void add_renames(
const char *zFnCol, /* The FV column for the filename in vid */
int vid, /* The desired version's- RID */
int nid, /* The check-in rid for the name pivot */
int revOK, /* OK to move backwards (child->parent) if true */
const char *zDebug /* Generate trace output if not NULL */
){
int nChng; /* Number of file name changes */
int *aChng; /* An array of file name changes */
int i; /* Loop counter */
find_filename_changes(nid, vid, revOK, &nChng, &aChng, zDebug);
|
| ︙ | ︙ | |||
266 267 268 269 270 271 272 |
*/
void test_show_vfile_cmd(void){
if( g.argc!=2 ){
fossil_fatal("unknown arguments to the %s command\n", g.argv[1]);
}
verify_all_options();
db_must_be_within_tree();
| | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
*/
void test_show_vfile_cmd(void){
if( g.argc!=2 ){
fossil_fatal("unknown arguments to the %s command\n", g.argv[1]);
}
verify_all_options();
db_must_be_within_tree();
debug_show_vfile();
}
/*
** COMMAND: merge
** COMMAND: cherry-pick
**
|
| ︙ | ︙ | |||
373 374 375 376 377 378 379 | /* Undocumented --debug and --show-vfile options: ** ** When included on the command-line, --debug causes lots of state ** information to be displayed. This option is undocumented as it ** might change or be eliminated in future releases. ** | | | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
/* Undocumented --debug and --show-vfile options:
**
** When included on the command-line, --debug causes lots of state
** information to be displayed. This option is undocumented as it
** might change or be eliminated in future releases.
**
** The --show-vfile flag does a dump of the VFILE table for reference.
**
** Hints:
** * Combine --debug and --verbose for still more output.
** * The --dry-run option is also useful in combination with --debug.
*/
debugFlag = find_option("debug",0,0)!=0;
if( debugFlag && verboseFlag ) debugFlag = 2;
|
| ︙ | ︙ |
Changes to src/merge3.c.
| ︙ | ︙ | |||
209 210 211 212 213 214 215 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ int nConflict = 0; /* Number of merge conflicts seen so far */ int useCrLf = 0; int ln1, ln2, lnPivot; /* Line numbers for all files */ DiffConfig DCfg; blob_zero(pOut); /* Merge results stored in pOut */ | | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
int limit1, limit2; /* Sizes of aC1[] and aC2[] */
int nConflict = 0; /* Number of merge conflicts seen so far */
int useCrLf = 0;
int ln1, ln2, lnPivot; /* Line numbers for all files */
DiffConfig DCfg;
blob_zero(pOut); /* Merge results stored in pOut */
/* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM),
** keep it in the output. This should be secure enough not to cause
** unintended changes to the merged file and consistent with what
** users are using in their source files.
*/
if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){
blob_append(pOut, (char*)get_utf8_bom(0), -1);
|
| ︙ | ︙ |
Changes to src/name.c.
| ︙ | ︙ | |||
511 512 513 514 515 516 517 |
return start_of_branch(rid, 0);
}
/* start:BR -> The first check-in on branch named BR */
if( strncmp(zTag, "start:", 6)==0 ){
rid = symbolic_name_to_rid(zTag+6, zType);
return start_of_branch(rid, 1);
| | | | | 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
return start_of_branch(rid, 0);
}
/* start:BR -> The first check-in on branch named BR */
if( strncmp(zTag, "start:", 6)==0 ){
rid = symbolic_name_to_rid(zTag+6, zType);
return start_of_branch(rid, 1);
}
/* merge-in:BR -> Most recent merge-in for the branch named BR */
if( strncmp(zTag, "merge-in:", 9)==0 ){
rid = symbolic_name_to_rid(zTag+9, zType);
return start_of_branch(rid, 2);
}
/* symbolic-name ":" date-time */
nTag = strlen(zTag);
for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
if( zTag[i]==':'
&& (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0)
){
char *zDate = mprintf("%s", &zTag[i+1]);
char *zTagBase = mprintf("%.*s", i, zTag);
char *zXDate;
int nDate = strlen(zDate);
if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
|
| ︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 |
" coalesce(euser,user), coalesce(ecomment,comment)"
" FROM mlink, filename, blob, event"
" WHERE mlink.fid=%d"
" AND filename.fnid=mlink.fnid"
" AND event.objid=mlink.mid"
" AND blob.rid=mlink.mid"
" ORDER BY event.mtime %s /*sort*/",
| | | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 |
" coalesce(euser,user), coalesce(ecomment,comment)"
" FROM mlink, filename, blob, event"
" WHERE mlink.fid=%d"
" AND filename.fnid=mlink.fnid"
" AND event.objid=mlink.mid"
" AND blob.rid=mlink.mid"
" ORDER BY event.mtime %s /*sort*/",
rid,
(flags & WHATIS_BRIEF) ? "LIMIT 1" : "DESC");
while( db_step(&q)==SQLITE_ROW ){
if( flags & WHATIS_BRIEF ){
fossil_print("mtime: %s\n", db_column_text(&q,2));
}
fossil_print("file: %s\n", db_column_text(&q,0));
fossil_print(" part of [%S] by %s on %s\n",
|
| ︙ | ︙ | |||
1161 1162 1163 1164 1165 1166 1167 |
*/
void whatis_artifact(
const char *zName, /* Symbolic name or full hash */
const char *zFileName,/* Optional: original filename (in file mode) */
const char *zType, /* Artifact type filter */
int mFlags /* WHATIS_* flags */
){
| < < < < < > > > | > > > > > > | | 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 |
*/
void whatis_artifact(
const char *zName, /* Symbolic name or full hash */
const char *zFileName,/* Optional: original filename (in file mode) */
const char *zType, /* Artifact type filter */
int mFlags /* WHATIS_* flags */
){
int rid = symbolic_name_to_rid(zName, zType);
if( rid<0 ){
Stmt q;
int cnt = 0;
if( mFlags & WHATIS_REPO ){
fossil_print("\nrepository: %s\n", g.zRepositoryName);
}
if( zFileName ){
fossil_print("%-12s%s\n", "name:", zFileName);
}
fossil_print("%-12s%s (ambiguous)\n", "hash:", zName);
db_prepare(&q,
"SELECT rid FROM blob WHERE uuid>=lower(%Q) AND uuid<(lower(%Q)||'z')",
zName, zName
);
while( db_step(&q)==SQLITE_ROW ){
if( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt);
whatis_rid(db_column_int(&q, 0), mFlags);
}
db_finalize(&q);
}else if( rid==0 ){
if( (mFlags & WHATIS_OMIT_UNK)==0 ){
/* 0123456789 12 */
if( zFileName ){
fossil_print("%-12s%s\n", "name:", zFileName);
}
fossil_print("unknown: %s\n", zName);
}
}else{
if( mFlags & WHATIS_REPO ){
fossil_print("\nrepository: %s\n", g.zRepositoryName);
}
if( zFileName ){
zName = zFileName;
}
fossil_print("%-12s%s\n", "name:", zName);
whatis_rid(rid, mFlags);
}
}
/*
** COMMAND: whatis*
**
|
| ︙ | ︙ |
Changes to src/patch.c.
| ︙ | ︙ | |||
69 70 71 72 73 74 75 | } /* ** mkdelta(X,Y) ** ** X is an numeric artifact id. Y is a filename. ** | | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
}
/*
** mkdelta(X,Y)
**
** X is an numeric artifact id. Y is a filename.
**
** Compute a compressed delta that carries X into Y. Or return
** and zero-length blob if X is equal to Y.
*/
static void mkdeltaFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
"PRAGMA patch.page_size=512;\n"
"CREATE TABLE patch.chng(\n"
" pathname TEXT,\n" /* Filename */
" origname TEXT,\n" /* Name before rename. NULL if not renamed */
" hash TEXT,\n" /* Baseline hash. NULL for new files. */
" isexe BOOL,\n" /* True if executable */
" islink BOOL,\n" /* True if is a symbolic link */
| | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
"PRAGMA patch.page_size=512;\n"
"CREATE TABLE patch.chng(\n"
" pathname TEXT,\n" /* Filename */
" origname TEXT,\n" /* Name before rename. NULL if not renamed */
" hash TEXT,\n" /* Baseline hash. NULL for new files. */
" isexe BOOL,\n" /* True if executable */
" islink BOOL,\n" /* True if is a symbolic link */
" delta BLOB\n" /* compressed delta. NULL if deleted.
** length 0 if unchanged */
");"
"CREATE TABLE patch.cfg(\n"
" key TEXT,\n"
" value ANY\n"
");"
);
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 |
";", vid, g.zLocalRoot, g.zRepositoryName, g.zLogin);
z = fossil_hostname();
if( z ){
db_multi_exec(
"INSERT INTO patch.cfg(key,value)VALUES('hostname',%Q)", z);
fossil_free(z);
}
| | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
";", vid, g.zLocalRoot, g.zRepositoryName, g.zLogin);
z = fossil_hostname();
if( z ){
db_multi_exec(
"INSERT INTO patch.cfg(key,value)VALUES('hostname',%Q)", z);
fossil_free(z);
}
/* New files */
db_multi_exec(
"INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)"
" SELECT pathname, NULL, isexe, islink,"
" compress(read_co_file(%Q||pathname))"
" FROM vfile WHERE rid==0;",
g.zLocalRoot
|
| ︙ | ︙ | |||
247 248 249 250 251 252 253 |
fossil_fatal("out of memory");
}
#ifdef _WIN32
fflush(out);
_setmode(_fileno(out), _O_BINARY);
#endif
fwrite(pData, sz, 1, out);
| | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
fossil_fatal("out of memory");
}
#ifdef _WIN32
fflush(out);
_setmode(_fileno(out), _O_BINARY);
#endif
fwrite(pData, sz, 1, out);
sqlite3_free(pData);
fflush(out);
}
}
/*
** Attempt to load and validate a patchfile identified by the first
** argument.
|
| ︙ | ︙ | |||
299 300 301 302 303 304 305 |
}
/*
** Show a summary of the content of a patch on standard output
*/
void patch_view(unsigned mFlags){
Stmt q;
| | | | 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
}
/*
** Show a summary of the content of a patch on standard output
*/
void patch_view(unsigned mFlags){
Stmt q;
db_prepare(&q,
"WITH nmap(nkey,nm) AS (VALUES"
"('baseline','BASELINE'),"
"('project-name','PROJECT-NAME'))"
"SELECT nm, value FROM nmap, patch.cfg WHERE nkey=key;"
);
while( db_step(&q)==SQLITE_ROW ){
fossil_print("%-12s %s\n", db_column_text(&q,0), db_column_text(&q,1));
}
db_finalize(&q);
if( mFlags & PATCH_VERBOSE ){
db_prepare(&q,
"WITH nmap(nkey,nm,isDate) AS (VALUES"
"('project-code','PROJECT-CODE',0),"
"('date','TIMESTAMP',1),"
"('user','USER',0),"
"('hostname','HOSTNAME',0),"
"('ckout','CHECKOUT',0),"
"('repo','REPOSITORY',0))"
|
| ︙ | ︙ | |||
431 432 433 434 435 436 437 |
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
if( strcmp(zType,"merge")==0 ){
blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
}else{
blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
}
if( mFlags & PATCH_VERBOSE ){
| | | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
if( strcmp(zType,"merge")==0 ){
blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
}else{
blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
}
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", db_column_text(&q,2),
db_column_text(&q,0));
}
}
db_finalize(&q);
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s", blob_str(&cmd));
}else{
|
| ︙ | ︙ | |||
559 560 561 562 563 564 565 |
}else{
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " add %$\n", zPathname);
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", "NEW", zPathname);
}
}
| | | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
}else{
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " add %$\n", zPathname);
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", "NEW", zPathname);
}
}
if( (mFlags & PATCH_DRYRUN)==0 ){
if( isLink ){
symlink_create(blob_str(&data), zPathname);
}else{
blob_write_to_file(&data, zPathname);
}
file_setexe(zPathname, isExe);
blob_reset(&data);
|
| ︙ | ︙ | |||
695 696 697 698 699 700 701 |
blob_appendf(&cmd, " -T");
blob_append_escaped_arg(&cmd, zRemote, 0);
blob_init(&remote, 0, 0);
if( zFossilCmd==0 ){
blob_append_escaped_arg(&cmd, "PATH=$HOME/bin:$PATH", 0);
zFossilCmd = "fossil";
}
| | | 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 |
blob_appendf(&cmd, " -T");
blob_append_escaped_arg(&cmd, zRemote, 0);
blob_init(&remote, 0, 0);
if( zFossilCmd==0 ){
blob_append_escaped_arg(&cmd, "PATH=$HOME/bin:$PATH", 0);
zFossilCmd = "fossil";
}
blob_appendf(&remote, "%$ patch %s%s --dir64 %z -",
zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1));
blob_append_escaped_arg(&cmd, blob_str(&remote), 0);
blob_reset(&remote);
}
fossil_print("%s\n", blob_str(&cmd));
fflush(stdout);
f = popen(blob_str(&cmd), zRW);
|
| ︙ | ︙ | |||
775 776 777 778 779 780 781 |
" FROM patch.chng"
" ORDER BY pathname"
);
while( db_step(&q)==SQLITE_ROW ){
int rid;
const char *zName;
Blob a, b;
| | | 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 |
" FROM patch.chng"
" ORDER BY pathname"
);
while( db_step(&q)==SQLITE_ROW ){
int rid;
const char *zName;
Blob a, b;
if( db_column_type(&q,0)!=SQLITE_INTEGER
&& db_column_type(&q,4)==SQLITE_TEXT
){
char *zUuid = fossil_strdup(db_column_text(&q,4));
char *zName = fossil_strdup(db_column_text(&q,1));
if( mFlags & PATCH_FORCE ){
fossil_print("ERROR cannot find base artifact %S for file \"%s\"\n",
|
| ︙ | ︙ | |||
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 |
fossil_fatal("base artifact %S for file \"%s\" not found",
zUuid, zName);
}
}
zName = db_column_text(&q, 1);
rid = db_column_int(&q, 0);
if( db_column_type(&q,3)==SQLITE_NULL ){
if( !bWebpage ) fossil_print("DELETE %s\n", zName);
diff_print_index(zName, pCfg, 0);
content_get(rid, &a);
diff_file_mem(&a, &empty, zName, pCfg);
}else if( rid==0 ){
db_ephemeral_blob(&q, 3, &a);
blob_uncompress(&a, &a);
if( !bWebpage ) fossil_print("ADDED %s\n", zName);
diff_print_index(zName, pCfg, 0);
diff_file_mem(&empty, &a, zName, pCfg);
blob_reset(&a);
}else if( db_column_bytes(&q, 3)>0 ){
Blob delta;
db_ephemeral_blob(&q, 3, &delta);
blob_uncompress(&delta, &delta);
| > > > | 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 |
fossil_fatal("base artifact %S for file \"%s\" not found",
zUuid, zName);
}
}
zName = db_column_text(&q, 1);
rid = db_column_int(&q, 0);
pCfg->diffFlags &= (~DIFF_FILE_MASK);
if( db_column_type(&q,3)==SQLITE_NULL ){
if( !bWebpage ) fossil_print("DELETE %s\n", zName);
pCfg->diffFlags |= DIFF_FILE_DELETED;
diff_print_index(zName, pCfg, 0);
content_get(rid, &a);
diff_file_mem(&a, &empty, zName, pCfg);
}else if( rid==0 ){
db_ephemeral_blob(&q, 3, &a);
blob_uncompress(&a, &a);
if( !bWebpage ) fossil_print("ADDED %s\n", zName);
pCfg->diffFlags |= DIFF_FILE_ADDED;
diff_print_index(zName, pCfg, 0);
diff_file_mem(&empty, &a, zName, pCfg);
blob_reset(&a);
}else if( db_column_bytes(&q, 3)>0 ){
Blob delta;
db_ephemeral_blob(&q, 3, &delta);
blob_uncompress(&delta, &delta);
|
| ︙ | ︙ | |||
894 895 896 897 898 899 900 | ** ** Command-line options: ** ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved ** changes will be reverted and then the patch is ** applied. | | | 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 | ** ** Command-line options: ** ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved ** changes will be reverted and then the patch is ** applied. ** --fossilcmd EXE Name of the "fossil" executable on the remote ** -n|--dry-run Do nothing, but print what would have happened ** -v|--verbose Extra output explaining what happens ** ** ** > fossil patch pull REMOTE-CHECKOUT ** ** Like "fossil patch push" except that the transfer is from remote |
| ︙ | ︙ | |||
971 972 973 974 975 976 977 |
unsigned flags = 0;
const char *zFossilCmd = find_option("fossilcmd",0,1);
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
db_must_be_within_tree();
verify_all_options();
| | | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 |
unsigned flags = 0;
const char *zFossilCmd = find_option("fossilcmd",0,1);
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
db_must_be_within_tree();
verify_all_options();
pIn = patch_remote_command(flags & (~PATCH_FORCE),
"pull", "create", zFossilCmd, "r");
if( pIn ){
patch_attach(0, pIn);
pclose(pIn);
patch_apply(flags);
}
}else
|
| ︙ | ︙ | |||
1009 1010 1011 1012 1013 1014 1015 |
zIn = g.argv[3];
if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
patch_attach(zIn, stdin);
patch_view(flags);
}else
{
goto patch_usage;
| | | 1012 1013 1014 1015 1016 1017 1018 1019 1020 |
zIn = g.argv[3];
if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
patch_attach(zIn, stdin);
patch_view(flags);
}else
{
goto patch_usage;
}
}
|
Changes to src/pikchrshow.c.
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
) & pikFlags){
pikFlags |= PIKCHR_PROCESS_DIV;
}
if(!(PIKCHR_PROCESS_TH1 & pikFlags)
/* If any TH1_xxx flags are set, set TH1 */
&& (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
pikFlags |= PIKCHR_PROCESS_TH1;
| | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
) & pikFlags){
pikFlags |= PIKCHR_PROCESS_DIV;
}
if(!(PIKCHR_PROCESS_TH1 & pikFlags)
/* If any TH1_xxx flags are set, set TH1 */
&& (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
pikFlags |= PIKCHR_PROCESS_TH1;
}
if(zNonce){
blob_appendf(pOut, "%s\n", zNonce);
}
if(PIKCHR_PROCESS_TH1 & pikFlags){
Blob out = empty_blob;
isErr = Th_RenderToBlob(zIn, &out, thFlags)
? 1 : 0;
|
| ︙ | ︙ | |||
544 545 546 547 548 549 550 | ** ** -div-source Set the 'source' CSS class on the div, which tells ** CSS to hide the SVG and reveal the source by default. ** ** -src Store the input pikchr's source code in the output as ** a separate element adjacent to the SVG one. Implied ** by -div-source. | | | 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | ** ** -div-source Set the 'source' CSS class on the div, which tells ** CSS to hide the SVG and reveal the source by default. ** ** -src Store the input pikchr's source code in the output as ** a separate element adjacent to the SVG one. Implied ** by -div-source. ** ** ** -th Process the input using TH1 before passing it to pikchr ** ** -th-novar Disable $var and $<var> TH1 processing. Use this if the ** pikchr script uses '$' for its own purposes and that ** causes issues. This only affects parsing of '$' outside ** of TH1 script blocks. Code in such blocks is unaffected. |
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
654 655 656 657 658 659 660 | ** executable in a way that changes the database schema. ** ** Options: ** --analyze Run ANALYZE on the database after rebuilding ** --cluster Compute clusters for unclustered artifacts ** --compress Strive to make the database as small as possible ** --compress-only Skip the rebuilding step. Do --compress only | < | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 |
** executable in a way that changes the database schema.
**
** Options:
** --analyze Run ANALYZE on the database after rebuilding
** --cluster Compute clusters for unclustered artifacts
** --compress Strive to make the database as small as possible
** --compress-only Skip the rebuilding step. Do --compress only
** --force Force the rebuild to complete even if errors are seen
** --ifneeded Only do the rebuild if it would change the schema version
** --index Always add in the full-text search index
** --noverify Skip the verification of changes to the BLOB table
** --noindex Always omit the full-text search index
** --pagesize N Set the database pagesize to N (512..65536, power of 2)
** --quiet Only show output if there are errors
** --stats Show artifact statistics after rebuilding
** --vacuum Run VACUUM on the database after rebuilding
** --wal Set Write-Ahead-Log journalling mode on the database
*/
void rebuild_database(void){
int forceFlag;
|
| ︙ | ︙ | |||
689 690 691 692 693 694 695 |
int optIfNeeded;
int compressOnlyFlag;
omitVerify = find_option("noverify",0,0)!=0;
forceFlag = find_option("force","f",0)!=0;
doClustering = find_option("cluster", 0, 0)!=0;
runVacuum = find_option("vacuum",0,0)!=0;
| | | 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 |
int optIfNeeded;
int compressOnlyFlag;
omitVerify = find_option("noverify",0,0)!=0;
forceFlag = find_option("force","f",0)!=0;
doClustering = find_option("cluster", 0, 0)!=0;
runVacuum = find_option("vacuum",0,0)!=0;
runDeanalyze = find_option("deanalyze",0,0)!=0; /* Deprecated */
runAnalyze = find_option("analyze",0,0)!=0;
runCompress = find_option("compress",0,0)!=0;
zPagesize = find_option("pagesize",0,1);
showStats = find_option("stats",0,0)!=0;
optIndex = find_option("index",0,0)!=0;
optNoIndex = find_option("noindex",0,0)!=0;
optIfNeeded = find_option("ifneeded",0,0)!=0;
|
| ︙ | ︙ | |||
1394 1395 1396 1397 1398 1399 1400 |
*/
verify_cancel();
db_end_transaction(0);
fossil_print("project-id: %s\n", db_get("project-code", 0));
fossil_print("server-id: %s\n", db_get("server-code", 0));
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
| | > | 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 |
*/
verify_cancel();
db_end_transaction(0);
fossil_print("project-id: %s\n", db_get("project-code", 0));
fossil_print("server-id: %s\n", db_get("server-code", 0));
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin,
zPassword);
hash_user_password(g.zLogin);
}
/*
** COMMAND: deconstruct*
**
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
|
| ︙ | ︙ |
Changes to src/report.c.
| ︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 | char *zClrKey; char *zDesc; char *zMimetype; int tabs; Stmt q; char *zErr1 = 0; char *zErr2 = 0; | | | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 |
char *zClrKey;
char *zDesc;
char *zMimetype;
int tabs;
Stmt q;
char *zErr1 = 0;
char *zErr2 = 0;
login_check_credentials();
if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
report_update_reportfmt_table();
rn = report_number();
tabs = P("tablist")!=0;
db_prepare(&q,
"SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
|
| ︙ | ︙ | |||
1366 1367 1368 1369 1370 1371 1372 | Stmt q; char *zSql; char *zErr1 = 0; char *zErr2 = 0; int count = 0; int rn; | | > | 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 |
Stmt q;
char *zSql;
char *zErr1 = 0;
char *zErr2 = 0;
int count = 0;
int rn;
if( !zRep || !strcmp(zRep,zFullTicketRptRn)
|| !strcmp(zRep,zFullTicketRptTitle) ){
zSql = "SELECT * FROM ticket";
}else{
rn = atoi(zRep);
if( rn ){
db_prepare(&q,
"SELECT sqlcode FROM reportfmt WHERE rn=%d", rn);
}else{
|
| ︙ | ︙ |
Changes to src/rss.c.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
blob_append_sql( &bSQL, " ORDER BY event.mtime DESC" );
cgi_set_content_type("application/rss+xml");
zProjectName = db_get("project-name", 0);
if( zProjectName==0 ){
| | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
blob_append_sql( &bSQL, " ORDER BY event.mtime DESC" );
cgi_set_content_type("application/rss+xml");
zProjectName = db_get("project-name", 0);
if( zProjectName==0 ){
zFreeProjectName = zProjectName =
mprintf("Fossil source repository for: %s", g.zBaseURL);
}
zProjectDescr = db_get("project-description", 0);
if( zProjectDescr==0 ){
zProjectDescr = zProjectName;
}
zPubDate = cgi_rfc822_datestamp(time(NULL));
|
| ︙ | ︙ | |||
256 257 258 259 260 261 262 |
** The default is "URL-PLACEHOLDER" (without quotes).
*/
void cmd_timeline_rss(void){
Stmt q;
int nLine=0;
char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
Blob bSQL;
| | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
** The default is "URL-PLACEHOLDER" (without quotes).
*/
void cmd_timeline_rss(void){
Stmt q;
int nLine=0;
char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
Blob bSQL;
const char *zType = find_option("type","y",1); /* Type of events;All if NULL*/
const char *zTicketUuid = find_option("tkt",NULL,1);
const char *zTag = find_option("tag",NULL,1);
const char *zFilename = find_option("name",NULL,1);
const char *zWiki = find_option("wiki",NULL,1);
const char *zLimit = find_option("limit", "n",1);
const char *zBaseURL = find_option("url", NULL, 1);
int nLimit = atoi( (zLimit && *zLimit) ? zLimit : "20" );
|
| ︙ | ︙ | |||
330 331 332 333 334 335 336 |
}else if( nTagId!=0 ){
blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
}
if( zFilename ){
blob_append_sql(&bSQL,
| | > | | | > | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
}else if( nTagId!=0 ){
blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
}
if( zFilename ){
blob_append_sql(&bSQL,
" AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) "
" IN (SELECT fnid FROM filename WHERE name=%Q %s)",
zFilename, filename_collation()
);
}
blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );
zProjectName = db_get("project-name", 0);
if( zProjectName==0 ){
zFreeProjectName = zProjectName =
mprintf("Fossil source repository for: %s", zBaseURL);
}
zProjectDescr = db_get("project-description", 0);
if( zProjectDescr==0 ){
zProjectDescr = zProjectName;
}
zPubDate = cgi_rfc822_datestamp(time(NULL));
fossil_print("<?xml version=\"1.0\"?>");
fossil_print("<rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
" version=\"2.0\">");
fossil_print("<channel>\n");
fossil_print("<title>%h</title>\n", zProjectName);
fossil_print("<link>%s</link>\n", zBaseURL);
fossil_print("<description>%h</description>\n", zProjectDescr);
fossil_print("<pubDate>%s</pubDate>\n", zPubDate);
fossil_print("<generator>Fossil version %s %s</generator>\n",
MANIFEST_VERSION, MANIFEST_DATE);
|
| ︙ | ︙ |
Changes to src/search.c.
| ︙ | ︙ | |||
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
** option can be used to output all matches, regardless of their search
** score. The -limit option can be used to limit the number of entries
** returned. The -width option can be used to set the output width used
** when printing matches.
**
** Options:
** -a|--all Output all matches, not just best matches
** -n|--limit N Limit output to N matches
** -W|--width WIDTH Set display width to WIDTH columns, 0 for
** unlimited. Defaults the terminal's width.
*/
void search_cmd(void){
Blob pattern;
int i;
Blob sql = empty_blob;
Stmt q;
int iBest;
| > > > > | < < > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | > | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
** option can be used to output all matches, regardless of their search
** score. The -limit option can be used to limit the number of entries
** returned. The -width option can be used to set the output width used
** when printing matches.
**
** Options:
** -a|--all Output all matches, not just best matches
** --debug Show additional debug content on --fts search
** --fts Use the full-text search mechanism (testing only)
** -n|--limit N Limit output to N matches
** --scope SCOPE Scope of search. Valid for --fts only. One or
** more of: all, c, d, e, f, t, w. Defaults to all.
** -W|--width WIDTH Set display width to WIDTH columns, 0 for
** unlimited. Defaults the terminal's width.
*/
void search_cmd(void){
Blob pattern;
int i;
Blob sql = empty_blob;
Stmt q;
int iBest;
char fAll = NULL != find_option("all", "a", 0);
const char *zLimit = find_option("limit","n",1);
const char *zWidth = find_option("width","W",1);
const char *zScope = find_option("scope",0,1);
int bDebug = find_option("debug",0,0)!=0;
int nLimit = zLimit ? atoi(zLimit) : -1000;
int width;
int bFts = find_option("fts",0,0)!=0;
if( zWidth ){
width = atoi(zWidth);
if( (width!=0) && (width<=20) ){
fossil_fatal("-W|--width value must be >20 or 0");
}
}else{
width = -1;
}
db_find_and_open_repository(0, 0);
if( g.argc<3 ) return;
blob_init(&pattern, g.argv[2], -1);
for(i=3; i<g.argc; i++){
blob_appendf(&pattern, " %s", g.argv[i]);
}
if( bFts ){
/* Search using FTS */
Blob com;
Blob snip;
const char *zPattern = blob_str(&pattern);
int srchFlags;
unsigned int j;
if( zScope==0 ){
srchFlags = SRCH_ALL;
}else{
srchFlags = 0;
for(i=0; zScope[i]; i++){
switch( zScope[i] ){
case 'a': srchFlags = SRCH_ALL; break;
case 'c': srchFlags |= SRCH_CKIN; break;
case 'd': srchFlags |= SRCH_DOC; break;
case 'e': srchFlags |= SRCH_TECHNOTE; break;
case 'f': srchFlags |= SRCH_FORUM; break;
case 't': srchFlags |= SRCH_TKT; break;
case 'w': srchFlags |= SRCH_WIKI; break;
}
}
}
search_sql_setup(g.db);
add_content_sql_commands(g.db);
db_multi_exec(
"CREATE TEMP TABLE x(label,url,score,id,date,snip);"
);
if( !search_index_exists() ){
search_fullscan(zPattern, srchFlags); /* Full-scan search */
}else{
search_update_index(srchFlags); /* Update the index */
search_indexed(zPattern, srchFlags); /* Indexed search */
}
db_prepare(&q, "SELECT snip, label, score, id, date"
" FROM x"
" ORDER BY score DESC, date DESC;");
blob_init(&com, 0, 0);
blob_init(&snip, 0, 0);
if( width<0 ) width = 80;
while( db_step(&q)==SQLITE_ROW ){
const char *zSnippet = db_column_text(&q, 0);
const char *zLabel = db_column_text(&q, 1);
const char *zDate = db_column_text(&q, 4);
const char *zScore = db_column_text(&q, 2);
const char *zId = db_column_text(&q, 3);
blob_appendf(&snip, "%s", zSnippet);
for(j=0; j<snip.nUsed; j++){
if( snip.aData[j]=='\n' ){
if( j>0 && snip.aData[j-1]=='\r' ) snip.aData[j-1] = ' ';
snip.aData[j] = ' ';
}
}
blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
if( bDebug ){
blob_appendf(&com," score: %s id: %s", zScore, zId);
}
comment_print(blob_str(&com), 0, 5, width,
COMMENT_PRINT_TRIM_CRLF |
COMMENT_PRINT_WORD_BREAK |
COMMENT_PRINT_TRIM_SPACE);
blob_reset(&com);
blob_reset(&snip);
if( nLimit>=1 ){
nLimit--;
if( nLimit==0 ) break;
}
}
db_finalize(&q);
blob_reset(&pattern);
}else{
/* Legacy timeline search (the default) */
(void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC);
blob_reset(&pattern);
search_sql_setup(g.db);
db_multi_exec(
"CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
"CREATE INDEX srch_idx1 ON srch(x);"
"INSERT INTO srch(rid,uuid,date,comment,x)"
" SELECT blob.rid, uuid, datetime(event.mtime,toLocal()),"
" coalesce(ecomment,comment),"
" search_score()"
" FROM event, blob"
" WHERE blob.rid=event.objid"
" AND search_match(coalesce(ecomment,comment));"
);
iBest = db_int(0, "SELECT max(x) FROM srch");
blob_append(&sql,
"SELECT rid, uuid, date, comment, 0, 0 FROM srch "
"WHERE 1 ", -1);
if(!fAll){
blob_append_sql(&sql,"AND x>%d ", iBest/3);
}
blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_reset(&sql);
print_timeline(&q, nLimit, width, 0, 0);
db_finalize(&q);
}
}
#if INTERFACE
/* What to search for */
#define SRCH_CKIN 0x0001 /* Search over check-in comments */
#define SRCH_DOC 0x0002 /* Search over embedded documents */
#define SRCH_TKT 0x0004 /* Search over tickets */
|
| ︙ | ︙ | |||
704 705 706 707 708 709 710 | ** snip: A snippet for the match ** ** And the srchFlags parameter has been validated. This routine ** fills the X table with search results using a full-scan search. ** ** The companion indexed search routine is search_indexed(). */ | | | 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 |
** snip: A snippet for the match
**
** And the srchFlags parameter has been validated. This routine
** fills the X table with search results using a full-scan search.
**
** The companion indexed search routine is search_indexed().
*/
LOCAL void search_fullscan(
const char *zPattern, /* The query pattern */
unsigned int srchFlags /* What to search over */
){
search_init(zPattern, "<mark>", "</mark>", " ... ",
SRCHFLG_STATIC|SRCHFLG_HTML);
if( (srchFlags & SRCH_DOC)!=0 ){
char *zDocGlob = db_get("doc-glob","");
|
| ︙ | ︙ | |||
912 913 914 915 916 917 918 | ** snip: A snippet for the match ** ** And the srchFlags parameter has been validated. This routine ** fills the X table with search results using FTS indexed search. ** ** The companion full-scan search routine is search_fullscan(). */ | | | 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 |
** snip: A snippet for the match
**
** And the srchFlags parameter has been validated. This routine
** fills the X table with search results using FTS indexed search.
**
** The companion full-scan search routine is search_fullscan().
*/
LOCAL void search_indexed(
const char *zPattern, /* The query pattern */
unsigned int srchFlags /* What to search over */
){
Blob sql;
char *zPat = mprintf("%s",zPattern);
int i;
static const char *zSnippetCall;
|
| ︙ | ︙ | |||
1255 1256 1257 1258 1259 1260 1261 |
if( zMimetype==0 ) zMimetype = "text/plain";
if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
if( blob_size(&title) ){
wiki_convert(pIn, &html, 0);
}else{
Blob tail;
blob_init(&tail, 0, 0);
| | > | | > > > | > > | 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 |
if( zMimetype==0 ) zMimetype = "text/plain";
if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
if( blob_size(&title) ){
wiki_convert(pIn, &html, 0);
}else{
Blob tail;
blob_init(&tail, 0, 0);
if( wiki_find_title(pIn, &title, &tail) ){
blob_appendf(pOut, "%s\n", blob_str(&title));
wiki_convert(&tail, &html, 0);
blob_reset(&tail);
}else{
blob_append(pOut, "\n", 1);
wiki_convert(pIn, &html, 0);
}
}
html_to_plaintext(blob_str(&html), pOut);
}else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
}else if( fossil_strcmp(zMimetype,"text/html")==0 ){
if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
pHtml = pIn;
}
blob_appendf(pOut, "%s\n", blob_str(&title));
|
| ︙ | ︙ | |||
2294 2295 2296 2297 2298 2299 2300 | return rc; } /* ** Argument f should be a flag accepted by matchinfo() (a valid character | | | 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 |
return rc;
}
/*
** Argument f should be a flag accepted by matchinfo() (a valid character
** in the string passed as the second argument). If it is not, -1 is
** returned. Otherwise, if f is a valid matchinfo flag, the value returned
** is the number of 32-bit integers added to the output array if the
** table has nCol columns and the query nPhrase phrases.
*/
static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){
int ret = -1;
switch( f ){
|
| ︙ | ︙ |
Changes to src/security_audit.c.
| ︙ | ︙ | |||
334 335 336 337 338 339 340 |
}
/* Anonymous users probably should not be allowed act as moderators
** for wiki or tickets.
*/
if( hasAnyCap(zAnonCap, "lq5") ){
@ <li><p><b>WARNING:</b>
| | | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
}
/* Anonymous users probably should not be allowed act as moderators
** for wiki or tickets.
*/
if( hasAnyCap(zAnonCap, "lq5") ){
@ <li><p><b>WARNING:</b>
@ Anonymous users can act as moderators for wiki, tickets, or
@ forum posts. This defeats the whole purpose of moderation.
@ Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum"
@ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5")
@ from users "anonymous" and "nobody"
@ on the <a href="setup_ulist">User Configuration</a> page.
}
/* Check to see if any TH1 scripts are configured to run on a sync
*/
if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'"
" AND length(value)>0") ){
@ <li><p><b>WARNING:</b>
@ TH1 scripts might be configured to run on any sync, push, pull, or
@ clone operation. See the the <a href="%R/xfersetup">/xfersetup</a>
@ page for more information. These TH1 scripts are a potential
@ security concern and so should be carefully audited by a human.
}
/* The strict-manifest-syntax setting should be on. */
if( db_get_boolean("strict-manifest-syntax",1)==0 ){
@ <li><p><b>WARNING:</b>
@ The "strict-manifest-syntax" flag is off. This is a security
@ risk. Turn this setting on (its default) to protect the users
@ of this repository.
|
| ︙ | ︙ | |||
580 581 582 583 584 585 586 |
}else {
double r = atof(db_get("max-loadavg", 0));
if( r<=0.0 ){
@ <li><p>
@ Load average limiting is turned off. This can cause the server
@ to bog down if many requests for expensive services (such as
@ large diffs or tarballs) arrive at about the same time.
| | | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
}else {
double r = atof(db_get("max-loadavg", 0));
if( r<=0.0 ){
@ <li><p>
@ Load average limiting is turned off. This can cause the server
@ to bog down if many requests for expensive services (such as
@ large diffs or tarballs) arrive at about the same time.
@ To fix this, set the
@ <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on the
@ <a href='%R/setup_access'>Access Control</a> page to the approximate
@ the number of available cores on your server, or maybe just a little
@ less.
}else if( r>=8.0 ){
@ <li><p>
@ The <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on
|
| ︙ | ︙ | |||
602 603 604 605 606 607 608 |
@ <li><p>
@ The server error log is disabled.
@ To set up an error log,
if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
@ make an entry like "errorlog: <i>FILENAME</i>" in the
@ CGI script at %h(P("SCRIPT_FILENAME")).
}else{
| | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
@ <li><p>
@ The server error log is disabled.
@ To set up an error log,
if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
@ make an entry like "errorlog: <i>FILENAME</i>" in the
@ CGI script at %h(P("SCRIPT_FILENAME")).
}else{
@ add the "--errorlog <i>FILENAME</i>" option to the
@ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server.
}
}else{
FILE *pTest = fossil_fopen(g.zErrlog,"a");
if( pTest==0 ){
@ <li><p>
@ <b>Error:</b>
|
| ︙ | ︙ | |||
633 634 635 636 637 638 639 |
@ <li><p> CGI Extensions are enabled with a document root
@ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
@ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
}
if( fileedit_glob()!=0 ){
@ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled
| | | | 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 |
@ <li><p> CGI Extensions are enabled with a document root
@ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
@ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
}
if( fileedit_glob()!=0 ){
@ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled
@ for this repository. Clear the
@ <a href='%R/setup_settings'>"fileedit-glob" setting</a> to
@ disable online editing.</p>
}
@ <li><p> User capability summary:
capability_summary();
azCSP = parse_content_security_policy();
if( azCSP==0 ){
@ <li><p> WARNING: No Content Security Policy (CSP) is specified in the
@ header. Though not required, a strong CSP is recommended. Fossil will
@ automatically insert an appropriate CSP if you let it generate the
@ HTML <tt><head></tt> element by omitting <tt><body></tt>
@ from the header configuration in your customized skin.
@
}else{
int ii;
@ <li><p> Content Security Policy:
@ <ol type="a">
for(ii=0; azCSP[ii]; ii++){
@ <li>%h(azCSP[ii])
}
|
| ︙ | ︙ | |||
785 786 787 788 789 790 791 |
@ <li><p>
@ If the server is running as CGI, then create a line in the CGI file
@ like this:
@ <blockquote><pre>
@ errorlog: <i>FILENAME</i>
@ </pre></blockquote>
@ <li><p>
| | | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 |
@ <li><p>
@ If the server is running as CGI, then create a line in the CGI file
@ like this:
@ <blockquote><pre>
@ errorlog: <i>FILENAME</i>
@ </pre></blockquote>
@ <li><p>
@ If the server is running using one of
@ the "fossil http" or "fossil server" commands then add
@ a command-line option "--errorlog <i>FILENAME</i>" to that
@ command.
@ </ol>
style_finish_page();
return;
}
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
586 587 588 589 590 591 592 |
@ for users who are not logged in. (Property: "require-captcha")</p>
@ <hr>
entry_attribute("Public pages", 30, "public-pages",
"pubpage", "", 0);
@ <p>A comma-separated list of glob patterns for pages that are accessible
@ without needing a login and using the privileges given by the
| | | 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
@ for users who are not logged in. (Property: "require-captcha")</p>
@ <hr>
entry_attribute("Public pages", 30, "public-pages",
"pubpage", "", 0);
@ <p>A comma-separated list of glob patterns for pages that are accessible
@ without needing a login and using the privileges given by the
@ "Default privileges" setting below.
@
@ <p>Example use case: Set this field to "/doc/trunk/www/*" and set
@ the "Default privileges" to include the "o" privilege
@ to give anonymous users read-only permission to the
@ latest version of the embedded documentation in the www/ folder without
@ allowing them to see the rest of the source code.
@ (Property: "public-pages")
|
| ︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 |
@ choices (such as the hamburger button) to the menu that are not shown
@ on this list. (Property: mainmenu)
@ <p>
if(P("resetMenu")!=0){
db_unset("mainmenu", 0);
cgi_delete_parameter("mmenu");
}
| | | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 |
@ choices (such as the hamburger button) to the menu that are not shown
@ on this list. (Property: mainmenu)
@ <p>
if(P("resetMenu")!=0){
db_unset("mainmenu", 0);
cgi_delete_parameter("mmenu");
}
textarea_attribute("Main Menu", 12, 80,
"mainmenu", "mmenu", style_default_mainmenu(), 0);
@ </p>
@ <p><input type='checkbox' id='cbResetMenu' name='resetMenu' value='1'>
@ <label for='cbResetMenu'>Reset menu to default value</label>
@ </p>
@ <hr>
@ <p>Extra links to appear on the <a href="%R/sitemap">/sitemap</a> page,
|
| ︙ | ︙ | |||
1227 1228 1229 1230 1231 1232 1233 | @ If capexpr evaluates to true, then the entry is shown. If not, @ the entry is omitted. "*" is always true. @ </ol> @ @ <p>The default value is blank, meaning no added entries. @ (Property: sitemap-extra) @ <p> | | | 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 |
@ If capexpr evaluates to true, then the entry is shown. If not,
@ the entry is omitted. "*" is always true.
@ </ol>
@
@ <p>The default value is blank, meaning no added entries.
@ (Property: sitemap-extra)
@ <p>
textarea_attribute("Custom Sitemap Entries", 8, 80,
"sitemap-extra", "smextra", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ |
Changes to src/setupuser.c.
| ︙ | ︙ | |||
808 809 810 811 812 813 814 | @ subscript suffix @ indicates the privileges of <span class="usertype">anonymous</span> that @ are inherited by all logged-in users. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" | | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 | @ subscript suffix @ indicates the privileges of <span class="usertype">anonymous</span> that @ are inherited by all logged-in users. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" @ subscript suffix indicates the privileges of @ <span class="usertype">developer</span> that @ are inherited by all users with the @ <span class="capability">Developer</span> privilege. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix |
| ︙ | ︙ |
Changes to src/sha1.c.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | ** ** Downloaded on 2017-03-01 then repackaged to work with Fossil ** and makeheaders. */ #if FOSSIL_HARDENED_SHA1 #if INTERFACE | | > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
**
** Downloaded on 2017-03-01 then repackaged to work with Fossil
** and makeheaders.
*/
#if FOSSIL_HARDENED_SHA1
#if INTERFACE
typedef void(*collision_block_callback)(uint64_t, const uint32_t*,
const uint32_t*, const uint32_t*, const uint32_t*);
struct SHA1_CTX {
uint64_t total;
uint32_t ihv[5];
unsigned char buffer[64];
int bigendian;
int found_collision;
int safe_hash;
|
| ︙ | ︙ |
Changes to src/sha1hard.c.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
void sha1_message_expansion(uint32_t W[80]);
void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]);
void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]);
extern sha1_recompression_type sha1_recompression_step[80];
typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
typedef struct {
| | | | | | | | | | | | | | | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
void sha1_message_expansion(uint32_t W[80]);
void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]);
void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]);
extern sha1_recompression_type sha1_recompression_step[80];
typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
typedef struct {
uint64_t total;
uint32_t ihv[5];
unsigned char buffer[64];
int bigendian;
int found_collision;
int safe_hash;
int detect_coll;
int ubc_check;
int reduced_round_coll;
collision_block_callback callback;
uint32_t ihv1[5];
uint32_t ihv2[5];
uint32_t m1[80];
uint32_t m2[80];
uint32_t states[80][5];
} SHA1_CTX;
/******************** File: lib/ubc_check.c **************************/
/***
* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
* Distributed under the MIT Software License.
* See accompanying file LICENSE.txt or copy at
|
| ︙ | ︙ |
Changes to src/sha3.c.
| ︙ | ︙ | |||
414 415 416 417 418 419 420 |
static void SHA3Update(
SHA3Context *p,
const unsigned char *aData,
unsigned int nData
){
unsigned int i = 0;
#if SHA3_BYTEORDER==1234
| | | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
static void SHA3Update(
SHA3Context *p,
const unsigned char *aData,
unsigned int nData
){
unsigned int i = 0;
#if SHA3_BYTEORDER==1234
if( (p->nLoaded % 8)==0 && (((intptr_t)aData)&7)==0 ){
for(; i+7<nData; i+=8){
p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i];
p->nLoaded += 8;
if( p->nLoaded>=p->nRate ){
KeccakF1600Step(p);
p->nLoaded = 0;
}
|
| ︙ | ︙ |
Changes to src/shun.c.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
void shun_page(void){
Stmt q;
int cnt = 0;
const char *zUuid = P("uuid");
const char *zShun = P("shun");
const char *zAccept = P("accept");
const char *zRcvid = P("rcvid");
int nRcvid = 0;
int numRows = 3;
char *zCanonical = 0;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
| > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
void shun_page(void){
Stmt q;
int cnt = 0;
const char *zUuid = P("uuid");
const char *zShun = P("shun");
const char *zAccept = P("accept");
const char *zRcvid = P("rcvid");
int reviewList = P("review")!=0;
int nRcvid = 0;
int numRows = 3;
char *zCanonical = 0;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
|
| ︙ | ︙ | |||
83 84 85 86 87 88 89 |
}
i++;
}
zCanonical[j+1] = zCanonical[j] = 0;
p = zCanonical;
while( *p ){
int nUuid = strlen(p);
| | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
}
i++;
}
zCanonical[j+1] = zCanonical[j] = 0;
p = zCanonical;
while( *p ){
int nUuid = strlen(p);
if( !(reviewList || hname_validate(p, nUuid)) ){
@ <p class="generalError">Error: Bad artifact IDs.</p>
fossil_free(zCanonical);
zCanonical = 0;
break;
}else{
canonical16(p, nUuid);
p += nUuid+1;
|
| ︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ <a href="%R/artifact/%s(p)">%s(p)</a><br>
}
@ have been shunned. They will no longer be pushed.
@ They will be removed from the repository the next time the repository
@ is rebuilt using the <b>fossil rebuild</b> command-line</p>
}
if( zRcvid ){
nRcvid = atoi(zRcvid);
numRows = db_int(0, "SELECT min(count(), 10) FROM blob WHERE rcvid=%d",
nRcvid);
}
@ <p>A shunned artifact will not be pushed nor accepted in a pull and the
@ artifact content will be purged from the repository the next time the
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ <a href="%R/artifact/%s(p)">%s(p)</a><br>
}
@ have been shunned. They will no longer be pushed.
@ They will be removed from the repository the next time the repository
@ is rebuilt using the <b>fossil rebuild</b> command-line</p>
}
if( zUuid && reviewList ){
const char *p;
int nTotal = 0;
int nOk = 0;
@ <table class="shun-review"><tbody><tr><td>
for( p = zUuid ; *p ; p += strlen(p)+1 ){
int rid = symbolic_name_to_rid(p, 0);
nTotal++;
if( rid < 0 ){
@ Ambiguous<br>
}else if( rid == 0 ){
if( !hname_validate(p, strlen(p)) ){
@ Bad artifact<br>
}else if(db_int(0, "SELECT 1 FROM shun WHERE uuid=%Q", p)){
@ Already shunned<br>
}else{
@ Unknown<br>
}
}else{
char *zCmpUuid = db_text(0,
"SELECT uuid"
" FROM blob, rcvfrom"
" WHERE rid=%d"
" AND rcvfrom.rcvid=blob.rcvid",
rid);
if( fossil_strcmp(p, zCmpUuid)==0 ){
nOk++;
@ OK</br>
}else{
@ Abbreviated<br>
}
}
}
@ </td><td>
for( p = zUuid ; *p ; p += strlen(p)+1 ){
int rid = symbolic_name_to_rid(p, 0);
if( rid > 0 ){
@ <a href="%R/artifact/%s(p)">%s(p)</a><br>
}else{
@ %s(p)<br>
}
}
@ </td></tr></tbody></table>
@ <p class="shunned">
if( nOk < nTotal){
@ <b>Warning:</b> Not all artifacts
}else if( nTotal==1 ){
@ The artifact is present and
}else{
@ All %i(nOk) artifacts are present and
}
@ can be shunned with its hash above.</p>
}
if( zRcvid ){
nRcvid = atoi(zRcvid);
numRows = db_int(0, "SELECT min(count(), 10) FROM blob WHERE rcvid=%d",
nRcvid);
}
@ <p>A shunned artifact will not be pushed nor accepted in a pull and the
@ artifact content will be purged from the repository the next time the
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
}else if( nRcvid ){
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
while( db_step(&q)==SQLITE_ROW ){
@ %s(db_column_text(&q, 0))
}
db_finalize(&q);
}
}
@ </textarea>
@ <input type="submit" name="add" value="Shun">
@ </div></form>
@ </blockquote>
@
@ <a name="delshun"></a>
@ <p>Enter the UUIDs of previously shunned artifacts to cause them to be
@ accepted again in the repository. The artifacts content is not
@ restored because the content is unknown. The only change is that
| > > > > > > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
}else if( nRcvid ){
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
while( db_step(&q)==SQLITE_ROW ){
@ %s(db_column_text(&q, 0))
}
db_finalize(&q);
}
}else if( zUuid && reviewList ){
const char *p;
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ %s(p)
}
}
@ </textarea>
@ <input type="submit" name="add" value="Shun">
@ <input type="submit" name="review" value="Review">
@ </div></form>
@ </blockquote>
@
@ <a name="delshun"></a>
@ <p>Enter the UUIDs of previously shunned artifacts to cause them to be
@ accepted again in the repository. The artifacts content is not
@ restored because the content is unknown. The only change is that
|
| ︙ | ︙ |
Changes to src/sitemap.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 |
g.jsHref = 0;
}
srchFlags = search_restrict(SRCH_ALL);
if( !isPopup ){
style_header("Site Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
| | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
g.jsHref = 0;
}
srchFlags = search_restrict(SRCH_ALL);
if( !isPopup ){
style_header("Site Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
@ <ul id="sitemap" class="columns" style="column-width:20em">
if( (e&1)==0 ){
@ <li>%z(href("%R/home"))Home Page</a>
}
#if 0 /* Removed 2021-01-26 */
for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){
|
| ︙ | ︙ | |||
150 151 152 153 154 155 156 |
}
@ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
}
#endif
if( inSublist ){
@ </ul>
| | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
}
@ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
}
#endif
if( inSublist ){
@ </ul>
inSublist = 0;
}
@ </li>
if( g.perm.Read ){
const char *zEditGlob = db_get("fileedit-glob","");
@ <li>%z(href("%R/tree"))File Browser</a>
@ <ul>
@ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
|
| ︙ | ︙ |
Changes to src/skins.c.
| ︙ | ︙ | |||
883 884 885 886 887 888 889 |
DiffConfig DCfg;
construct_diff_flags(1, &DCfg);
DCfg.diffFlags |= DIFF_STRIP_EOLCR;
if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE;
blob_init(&to, zContent, -1);
blob_init(&from, skin_file_content(zBasis, zFile), -1);
blob_zero(&out);
| | | 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 |
DiffConfig DCfg;
construct_diff_flags(1, &DCfg);
DCfg.diffFlags |= DIFF_STRIP_EOLCR;
if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE;
blob_init(&to, zContent, -1);
blob_init(&from, skin_file_content(zBasis, zFile), -1);
blob_zero(&out);
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
text_diff(&from, &to, &out, &DCfg);
@ %s(blob_str(&out))
}else{
DCfg.diffFlags |= DIFF_LINENO;
text_diff(&from, &to, &out, &DCfg);
@ <pre class="udiff">
|
| ︙ | ︙ | |||
1204 1205 1206 1207 1208 1209 1210 |
}else if( pAltSkin ){
char *zPattern = mprintf("*/skn_%s", pAltSkin->zLabel);
if( sqlite3_strglob(zPattern, zBase)==0 ){
nBase -= strlen(zPattern)-1;
zBase[nBase] = 0;
}
fossil_free(zPattern);
| | | 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 |
}else if( pAltSkin ){
char *zPattern = mprintf("*/skn_%s", pAltSkin->zLabel);
if( sqlite3_strglob(zPattern, zBase)==0 ){
nBase -= strlen(zPattern)-1;
zBase[nBase] = 0;
}
fossil_free(zPattern);
}
login_check_credentials();
style_header("Skins");
if( iDraftSkin || nSkinRank<=1 ){
@ <p class="warning">Warning:
if( iDraftSkin>0 ){
@ you are using a draft skin,
}else{
|
| ︙ | ︙ |
Changes to src/smtp.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** Implementation of SMTP (Simple Mail Transport Protocol) according ** to RFC 5321. */ #include "config.h" #include "smtp.h" #include <assert.h> | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
**
** Implementation of SMTP (Simple Mail Transport Protocol) according
** to RFC 5321.
*/
#include "config.h"
#include "smtp.h"
#include <assert.h>
#if (HAVE_DN_EXPAND || HAVE___NS_NAME_UNCOMPRESS || HAVE_NS_NAME_UNCOMPRESS) \
&& (HAVE_NS_PARSERR || HAVE___NS_PARSERR) && !defined(FOSSIL_OMIT_DNS)
# include <sys/types.h>
# include <netinet/in.h>
# if defined(HAVE_BIND_RESOLV_H)
# include <bind/resolv.h>
# include <bind/arpa/nameser_compat.h>
# else
# include <arpa/nameser.h>
|
| ︙ | ︙ |
Changes to src/sqlcmd.c.
| ︙ | ︙ | |||
382 383 384 385 386 387 388 |
** files_of_checkin(X) A table-valued function that returns info on
** all files contained in check-in X. Example:
**
** SELECT * FROM files_of_checkin('trunk');
**
** helptext A virtual table with one row for each command,
** webpage, and setting together with the built-in
| | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
** files_of_checkin(X) A table-valued function that returns info on
** all files contained in check-in X. Example:
**
** SELECT * FROM files_of_checkin('trunk');
**
** helptext A virtual table with one row for each command,
** webpage, and setting together with the built-in
** help text.
**
** now() Return the number of seconds since 1970.
**
** obscure(T) Obfuscate the text password T so that its
** original value is not readily visible. Fossil
** uses this same algorithm when storing passwords
** of remote URLs.
|
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
int rid = db_column_int(&q, 0);
int isRemoved = db_column_int(&q, 1);
int isLink = db_column_int(&q, 3);
const char *zOrig = db_column_text(&q, 4);
const char *zNew = db_column_text(&q, 5);
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
Blob a, b;
if( rid==0 ){
db_ephemeral_blob(&q, 6, &a);
if( !bWebpage ) fossil_print("ADDED %s\n", zNew);
diff_print_index(zNew, pCfg, 0);
diff_file_mem(&empty, &a, zNew, pCfg);
}else if( isRemoved ){
if( !bWebpage) fossil_print("DELETE %s\n", zOrig);
diff_print_index(zNew, pCfg, 0);
if( fBaseline ){
content_get(rid, &a);
diff_file_mem(&a, &empty, zOrig, pCfg);
}
}else{
Blob delta;
| > > > | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
int rid = db_column_int(&q, 0);
int isRemoved = db_column_int(&q, 1);
int isLink = db_column_int(&q, 3);
const char *zOrig = db_column_text(&q, 4);
const char *zNew = db_column_text(&q, 5);
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
Blob a, b;
pCfg->diffFlags &= (~DIFF_FILE_MASK);
if( rid==0 ){
db_ephemeral_blob(&q, 6, &a);
if( !bWebpage ) fossil_print("ADDED %s\n", zNew);
pCfg->diffFlags |= DIFF_FILE_ADDED;
diff_print_index(zNew, pCfg, 0);
diff_file_mem(&empty, &a, zNew, pCfg);
}else if( isRemoved ){
if( !bWebpage) fossil_print("DELETE %s\n", zOrig);
pCfg->diffFlags |= DIFF_FILE_DELETED;
diff_print_index(zNew, pCfg, 0);
if( fBaseline ){
content_get(rid, &a);
diff_file_mem(&a, &empty, zOrig, pCfg);
}
}else{
Blob delta;
|
| ︙ | ︙ | |||
567 568 569 570 571 572 573 |
stash_tables_exist_and_current();
if( g.argc<=2 ){
zCmd = "save";
}else{
zCmd = g.argv[2];
}
nCmd = strlen(zCmd);
| | | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
stash_tables_exist_and_current();
if( g.argc<=2 ){
zCmd = "save";
}else{
zCmd = g.argv[2];
}
nCmd = strlen(zCmd);
if( strncmp(zCmd, "save", nCmd)==0 ){
if( unsaved_changes(0)==0 ){
fossil_fatal("nothing to stash");
}
stashid = stash_create();
undo_disable();
if( g.argc>=2 ){
int nFile = db_int(0, "SELECT count(*) FROM stashfile WHERE stashid=%d",
|
| ︙ | ︙ | |||
598 599 600 601 602 603 604 |
** we have a copy of the changes before deleting them. */
db_commit_transaction();
g.argv[1] = "revert";
revert_cmd();
fossil_print("stash %d saved\n", stashid);
return;
}else
| | | | 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 |
** we have a copy of the changes before deleting them. */
db_commit_transaction();
g.argv[1] = "revert";
revert_cmd();
fossil_print("stash %d saved\n", stashid);
return;
}else
if( strncmp(zCmd, "snapshot", nCmd)==0 ){
stash_create();
}else
if( strncmp(zCmd, "list", nCmd)==0 || strncmp(zCmd, "ls", nCmd)==0 ){
Stmt q, q2;
int n = 0, width;
int verboseFlag = find_option("verbose","v",0)!=0;
const char *zWidth = find_option("width","W",1);
if( zWidth ){
width = atoi(zWidth);
|
| ︙ | ︙ | |||
665 666 667 668 669 670 671 |
db_reset(&q2);
}
}
db_finalize(&q);
if( verboseFlag ) db_finalize(&q2);
if( n==0 ) fossil_print("empty stash\n");
}else
| | | 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 |
db_reset(&q2);
}
}
db_finalize(&q);
if( verboseFlag ) db_finalize(&q2);
if( n==0 ) fossil_print("empty stash\n");
}else
if( strncmp(zCmd, "drop", nCmd)==0 || strncmp(zCmd, "rm", nCmd)==0 ){
int allFlag = find_option("all", "a", 0)!=0;
if( allFlag ){
Blob ans;
char cReply;
prompt_user("This action is not undoable. Continue (y/N)? ", &ans);
cReply = blob_str(&ans)[0];
if( cReply=='y' || cReply=='Y' ){
|
| ︙ | ︙ | |||
691 692 693 694 695 696 697 |
}else{
undo_begin();
undo_save_stash(0);
stash_drop(stashid);
undo_finish();
}
}else
| | | 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 |
}else{
undo_begin();
undo_save_stash(0);
stash_drop(stashid);
undo_finish();
}
}else
if( strncmp(zCmd, "pop", nCmd)==0 || strncmp(zCmd, "apply", nCmd)==0 ){
char *zCom = 0, *zDate = 0, *zHash = 0;
int popped = *zCmd=='p';
if( popped ){
if( g.argc>3 ) usage("pop");
stashid = stash_get_id(0);
}else{
if( g.argc>4 ) usage("apply STASHID");
|
| ︙ | ︙ | |||
720 721 722 723 724 725 726 |
}
fossil_free(zCom);
fossil_free(zDate);
fossil_free(zHash);
undo_finish();
if( popped ) stash_drop(stashid);
}else
| | | | | | | | | | 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 |
}
fossil_free(zCom);
fossil_free(zDate);
fossil_free(zHash);
undo_finish();
if( popped ) stash_drop(stashid);
}else
if( strncmp(zCmd, "goto", nCmd)==0 ){
int nConflict;
int vid;
if( g.argc>4 ) usage("apply STASHID");
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
undo_begin();
vid = db_int(0, "SELECT blob.rid FROM stash,blob"
" WHERE stashid=%d AND blob.uuid=stash.hash", stashid);
nConflict = update_to(vid);
stash_apply(stashid, nConflict);
db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
"(SELECT origname FROM stashfile WHERE stashid=%d)",
stashid);
undo_finish();
}else
if( strncmp(zCmd, "diff", nCmd)==0
|| strncmp(zCmd, "gdiff", nCmd)==0
|| strncmp(zCmd, "show", nCmd)==0
|| strncmp(zCmd, "gshow", nCmd)==0
|| strncmp(zCmd, "cat", nCmd)==0
|| strncmp(zCmd, "gcat", nCmd)==0
){
int fBaseline = 0;
DiffConfig DCfg;
if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
fBaseline = 1;
}
if( find_option("tk",0,0)!=0 ){
db_close(0);
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
return;
}
diff_options(&DCfg, zCmd[0]=='g', 0);
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
stash_diff(stashid, fBaseline, &DCfg);
}else
if( strncmp(zCmd, "help", nCmd)==0 ){
g.argv[1] = "help";
g.argv[2] = "stash";
g.argc = 3;
help_cmd();
}else
{
usage("SUBCOMMAND ARGS...");
}
db_end_transaction(0);
}
|
Changes to src/stat.c.
| ︙ | ︙ | |||
555 556 557 558 559 560 561 |
}else{
@ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
@ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
}
cnt++;
}
db_finalize(&q);
| | | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
}else{
@ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
@ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
}
cnt++;
}
db_finalize(&q);
if( nOmitted ){
@ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a>
}
if( cnt ){
@ </table>
total += cnt;
}
|
| ︙ | ︙ | |||
712 713 714 715 716 717 718 719 720 721 722 723 724 725 |
*/
void repo_schema_page(void){
Stmt q;
Blob sql;
const char *zArg = P("n");
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
style_set_current_feature("stat");
style_header("Repository Schema");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Stat", "stat");
style_submenu_element("URLs", "urllist");
if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){
| > > > > > > > > > > > > > > > > | 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 |
*/
void repo_schema_page(void){
Stmt q;
Blob sql;
const char *zArg = P("n");
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
if( zArg!=0
&& db_table_exists("repository",zArg)
&& cgi_csrf_safe(1)
){
if( P("analyze")!=0 ){
db_multi_exec("ANALYZE \"%w\"", zArg);
}else if( P("analyze200")!=0 ){
db_multi_exec("PRAGMA analysis_limit=200; ANALYZE \"%w\"", zArg);
}else if( P("deanalyze")!=0 ){
db_unprotect(PROTECT_ALL);
db_multi_exec("DELETE FROM repository.sqlite_stat1"
" WHERE tbl LIKE %Q", zArg);
db_protect_pop();
}
}
style_set_current_feature("stat");
style_header("Repository Schema");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Stat", "stat");
style_submenu_element("URLs", "urllist");
if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){
|
| ︙ | ︙ | |||
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
}
@ </pre>
db_finalize(&q);
}else{
style_submenu_element("Stat1","repo_stat1");
}
}
style_finish_page();
}
/*
** WEBPAGE: repo_stat1
**
** Show the sqlite_stat1 table for the repository schema
*/
void repo_stat1_page(void){
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
style_set_current_feature("stat");
style_header("Repository STAT1 Table");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Stat", "stat");
style_submenu_element("Schema", "repo_schema");
if( db_table_exists("repository","sqlite_stat1") ){
Stmt q;
db_prepare(&q,
"SELECT tbl, idx, stat FROM repository.sqlite_stat1"
" ORDER BY tbl, idx");
| > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | | > > > > | > > > > > > > > > > > | 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 |
}
@ </pre>
db_finalize(&q);
}else{
style_submenu_element("Stat1","repo_stat1");
}
}
@ <hr><form method="POST">
@ <input type="submit" name="analyze" value="Run ANALYZE"><br />
@ <input type="submit" name="analyze200"\
@ value="Run ANALYZE with limit=200"><br />
@ <input type="submit" name="deanalyze" value="De-ANALYZE">
@ </form>
style_finish_page();
}
/*
** WEBPAGE: repo_stat1
**
** Show the sqlite_stat1 table for the repository schema
*/
void repo_stat1_page(void){
int bTabular;
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
bTabular = PB("tabular");
if( P("analyze")!=0 && cgi_csrf_safe(1) ){
db_multi_exec("ANALYZE");
}else if( P("analyze200")!=0 && cgi_csrf_safe(1) ){
db_multi_exec("PRAGMA analysis_limit=200; ANALYZE;");
}else if( P("deanalyze")!=0 && cgi_csrf_safe(1) ){
db_unprotect(PROTECT_ALL);
db_multi_exec("DELETE FROM repository.sqlite_stat1;");
db_protect_pop();
}
style_set_current_feature("stat");
style_header("Repository STAT1 Table");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Stat", "stat");
style_submenu_element("Schema", "repo_schema");
style_submenu_checkbox("tabular", "Tabular", 0, 0);
if( db_table_exists("repository","sqlite_stat1") ){
Stmt q;
db_prepare(&q,
"SELECT tbl, idx, stat FROM repository.sqlite_stat1"
" ORDER BY tbl, idx");
if( bTabular ){
@ <table border="1" cellpadding="0" cellspacing="0">
@ <tr><th>Table<th>Index<th>Stat
}else{
@ <pre>
}
while( db_step(&q)==SQLITE_ROW ){
const char *zTab = db_column_text(&q,0);
const char *zIdx = db_column_text(&q,1);
const char *zStat = db_column_text(&q,2);
char *zUrl = href("%R/repo_schema?n=%t",zTab);
if( bTabular ){
@ <tr><td>%z(zUrl)%h(zTab)</a><td>%h(zIdx)<td>%h(zStat)
}else{
@ INSERT INTO sqlite_stat1 \
@ VALUES('%z(zUrl)%h(zTab)</a>','%h(zIdx)','%h(zStat)');
}
}
if( bTabular ){
@ </table>
}else{
@ </pre>
}
db_finalize(&q);
}
@ <p><form method="POST">
if( bTabular ){
@ <input type="hidden" name="tabular" value="1">
}
@ <input type="submit" name="analyze" value="Run ANALYZE"><br />
@ <input type="submit" name="analyze200"\
@ value="Run ANALYZE with limit=200"><br>
@ <input type="submit" name="deanalyze"\
@ value="De-ANALYZE">
@ </form>
style_finish_page();
}
/*
** WEBPAGE: repo-tabsize
**
** Show relative sizes of tables in the repository database.
|
| ︙ | ︙ | |||
873 874 875 876 877 878 879 |
/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
void gather_artifact_stats(int bWithTypes){
| | | | | | | | | | | | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 |
/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
void gather_artifact_stats(int bWithTypes){
static const char zSql[] =
@ CREATE TEMP TABLE artstat(
@ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID
@ atype TEXT, -- 'data', 'manifest', 'tag', 'wiki', etc.
@ isDelta BOOLEAN, -- true if stored as a delta
@ szExp, -- expanded, uncompressed size
@ szCmpr -- size as stored on disk
@ );
@ INSERT INTO artstat(id,atype,isDelta,szExp,szCmpr)
@ SELECT blob.rid, NULL,
@ delta.rid IS NOT NULL,
@ size, octet_length(content)
@ FROM blob LEFT JOIN delta ON blob.rid=delta.rid
@ WHERE content IS NOT NULL;
;
static const char zSql2[] =
@ UPDATE artstat SET atype='file'
@ WHERE +id IN (SELECT fid FROM mlink);
@ UPDATE artstat SET atype='manifest'
@ WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL;
@ UPDATE artstat SET atype='forum'
@ WHERE id IN (SELECT objid FROM event WHERE type='f') AND atype IS NULL;
@ UPDATE artstat SET atype='cluster'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid=(SELECT tagid FROM tag
@ WHERE tagname='cluster'));
@ UPDATE artstat SET atype='ticket'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid IN (SELECT tagid FROM tag
@ WHERE tagname GLOB 'tkt-*'));
@ UPDATE artstat SET atype='wiki'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid IN (SELECT tagid FROM tag
@ WHERE tagname GLOB 'wiki-*'));
@ UPDATE artstat SET atype='technote'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid IN (SELECT tagid FROM tag
@ WHERE tagname GLOB 'event-*'));
@ UPDATE artstat SET atype='attachment'
@ WHERE atype IS NULL
@ AND id IN (SELECT attachid FROM attachment UNION
@ SELECT blob.rid FROM attachment JOIN blob ON uuid=src);
@ UPDATE artstat SET atype='tag'
@ WHERE atype IS NULL
@ AND id IN (SELECT srcid FROM tagxref);
@ UPDATE artstat SET atype='tag'
@ WHERE atype IS NULL
@ AND id IN (SELECT objid FROM event WHERE type='g');
@ UPDATE artstat SET atype='unused' WHERE atype IS NULL;
;
db_multi_exec("%s", zSql/*safe-for-%s*/);
if( bWithTypes ){
db_multi_exec("%s", zSql2/*safe-for-%s*/);
}
|
| ︙ | ︙ |
Changes to src/statrep.c.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
const char *zNot = rc=='n' ? "NOT" : "";
statsReportTimelineYFlag = "ci";
db_multi_exec(
"CREATE TEMP VIEW v_reports AS "
"SELECT * FROM event WHERE type='ci' AND %s"
" AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)",
zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/
| | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
const char *zNot = rc=='n' ? "NOT" : "";
statsReportTimelineYFlag = "ci";
db_multi_exec(
"CREATE TEMP VIEW v_reports AS "
"SELECT * FROM event WHERE type='ci' AND %s"
" AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)",
zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/
);
}
return statsReportType = rc;
}
/*
** Returns a string suitable (for a given value of suitable) for
** use in a label with the header of the /reports pages, dependent
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
450 451 452 453 454 455 456 |
** or after any updates to the CSS files
*/
blob_appendf(&url, "?id=%x", skin_id("css"));
if( P("once")!=0 && P("skin")!=0 ){
blob_appendf(&url, "&skin=%s&once", skin_in_use());
}
| | | | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 |
** or after any updates to the CSS files
*/
blob_appendf(&url, "?id=%x", skin_id("css"));
if( P("once")!=0 && P("skin")!=0 ){
blob_appendf(&url, "&skin=%s&once", skin_in_use());
}
/* Generate the CSS URL variable */
Th_Store("stylesheet_url", blob_str(&url));
blob_reset(&url);
}
/*
** Create a TH1 variable containing the URL for the specified image.
** The resulting variable name will be of the form $[zImageName]_image_url.
** The value will be a URL that includes an id= query parameter that
** changes if the underlying resource changes or if a different skin
** is selected.
*/
static void image_url_var(const char *zImageName){
char *zVarName; /* Name of the new TH1 variable */
char *zResource; /* Name of CONFIG entry holding content */
char *zUrl; /* The URL */
zResource = mprintf("%s-image", zImageName);
zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource));
free(zResource);
zVarName = mprintf("%s_image_url", zImageName);
Th_Store(zVarName, zUrl);
free(zVarName);
free(zUrl);
}
/*
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
|
| ︙ | ︙ | |||
595 596 597 598 599 600 601 |
** The text '$nonce' is replaced by style_nonce() if and whereever it
** occurs in the input string.
**
** The string returned is obtained from fossil_malloc() and
** should be released by the caller.
*/
char *style_csp(int toHeader){
| | | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
** The text '$nonce' is replaced by style_nonce() if and whereever it
** occurs in the input string.
**
** The string returned is obtained from fossil_malloc() and
** should be released by the caller.
*/
char *style_csp(int toHeader){
static const char zBackupCSP[] =
"default-src 'self' data:; "
"script-src 'self' 'nonce-$nonce'; "
"style-src 'self' 'unsafe-inline'; "
"img-src * data:";
const char *zFormat;
Blob csp;
char *zNonce;
|
| ︙ | ︙ | |||
631 632 633 634 635 636 637 | return zCsp; } /* ** Disable content security policy for the current page. ** WARNING: Do not do this lightly! ** | | | | 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 |
return zCsp;
}
/*
** Disable content security policy for the current page.
** WARNING: Do not do this lightly!
**
** This routine must be called before the CSP is sued by
** style_header().
*/
void style_disable_csp(void){
disableCSP = 1;
}
/*
** Default HTML page header text through <body>. If the repository-specific
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static const char zDfltHeader[] =
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <base href="$baseurl/$current_page">
@ <meta http-equiv="Content-Security-Policy" content="$default_csp">
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
|
| ︙ | ︙ | |||
668 669 670 671 672 673 674 |
const char *get_default_header(){
return zDfltHeader;
}
/*
** The default TCL list that defines the main menu.
*/
| | | 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 |
const char *get_default_header(){
return zDfltHeader;
}
/*
** The default TCL list that defines the main menu.
*/
static const char zDfltMainMenu[] =
@ Home /home * {}
@ Timeline /timeline {o r j} {}
@ Files /dir?ci=tip oh desktoponly
@ Branches /brlist o wideonly
@ Tags /taglist o wideonly
@ Forum /forum {@2 3 4 5 6} wideonly
@ Chat /chat C wideonly
|
| ︙ | ︙ | |||
793 794 795 796 797 798 799 |
if( !login_is_nobody() ){
Th_Store("login", g.zLogin);
}
Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) );
if( g.ftntsIssues[0] || g.ftntsIssues[1] ||
g.ftntsIssues[2] || g.ftntsIssues[3] ){
char buf[80];
| | | | 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 |
if( !login_is_nobody() ){
Th_Store("login", g.zLogin);
}
Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) );
if( g.ftntsIssues[0] || g.ftntsIssues[1] ||
g.ftntsIssues[2] || g.ftntsIssues[3] ){
char buf[80];
sqlite3_snprintf(sizeof(buf), buf, "%i %i %i %i", g.ftntsIssues[0],
g.ftntsIssues[1], g.ftntsIssues[2], g.ftntsIssues[3]);
Th_Store("footnotes_issues_counters", buf);
}
}
/*
** Draw the header.
*/
|
| ︙ | ︙ | |||
1283 1284 1285 1286 1287 1288 1289 | ** * $basename ** * $secureurl ** * $home ** * $logo ** * $background ** ** The output from TH1 becomes the style sheet. Fossil always reports | | | 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 |
** * $basename
** * $secureurl
** * $home
** * $logo
** * $background
**
** The output from TH1 becomes the style sheet. Fossil always reports
** that the style sheet is cacheable.
*/
void page_style_css(void){
Blob css = empty_blob;
int i;
const char * zDefaults;
const char *zSkin;
|
| ︙ | ︙ | |||
1323 1324 1325 1326 1327 1328 1329 | /* Tell CGI that the content returned by this page is considered cacheable */ g.isConst = 1; } /* ** All possible capabilities */ | | | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 |
/* Tell CGI that the content returned by this page is considered cacheable */
g.isConst = 1;
}
/*
** All possible capabilities
*/
static const char allCap[] =
"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL";
/*
** Compute the current login capabilities
*/
static char *find_capabilities(char *zCap){
int i, j;
|
| ︙ | ︙ | |||
1477 1478 1479 1480 1481 1482 1483 |
break;
}
default: {
@ CSRF safety = unsafe<br>
break;
}
}
| | | 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 |
break;
}
default: {
@ CSRF safety = unsafe<br>
break;
}
}
@ fossil_exe_id() = %h(fossil_exe_id())<br>
if( g.perm.Admin ){
int k;
for(k=0; g.argvOrig[k]; k++){
Blob t;
blob_init(&t, 0, 0);
blob_append_escaped_arg(&t, g.argvOrig[k], 0);
|
| ︙ | ︙ | |||
1649 1650 1651 1652 1653 1654 1655 |
** Example:
**
** style_select_list_int("my-grapes", "my_grapes", "Grapes",
** "Select the number of grapes",
** atoi(PD("my_field","0")),
** "", 1, "2", 2, "Three", 3,
** NULL);
| | | 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 |
** Example:
**
** style_select_list_int("my-grapes", "my_grapes", "Grapes",
** "Select the number of grapes",
** atoi(PD("my_field","0")),
** "", 1, "2", 2, "Three", 3,
** NULL);
**
*/
void style_select_list_int(const char * zWrapperId,
const char *zFieldName, const char * zLabel,
const char * zToolTip, int selectedVal,
... ){
char * zLabelID = style_next_input_id();
va_list vargs;
|
| ︙ | ︙ | |||
1773 1774 1775 1776 1777 1778 1779 |
if( z[0]=='/' || z[0]=='\\' ){
zOrigin = z+1;
}
}
CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine);
}
| | | 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 |
if( z[0]=='/' || z[0]=='\\' ){
zOrigin = z+1;
}
}
CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine);
}
/* Generate the closing </script> tag
*/
void style_script_end(void){
CX("</script>\n");
}
/*
** Emits a NOSCRIPT tag with an error message stating that JS is
|
| ︙ | ︙ |
Changes to src/style.fileedit.css.
| ︙ | ︙ | |||
74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
overflow: auto;
}
body.fileedit #fileedit-tab-preview-wrapper > pre {
margin: 0;
}
body.fileedit #fileedit-tab-fileselect > h1 {
margin: 0;
}
body.fileedit .fileedit-options.commit-message > div {
display: flex;
flex-direction: column;
align-items: stretch;
font-family: monospace;
}
| > > > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
overflow: auto;
}
body.fileedit #fileedit-tab-preview-wrapper > pre {
margin: 0;
}
body.fileedit #fileedit-tab-fileselect > h1 {
margin: 0;
}
body.fileedit .fileedit-options > div > * {
margin: 0.25em;
}
body.fileedit .fileedit-options.commit-message > div {
display: flex;
flex-direction: column;
align-items: stretch;
font-family: monospace;
}
|
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > input {
vertical-align: middle;
margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > .input-with-label {
| < | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > input {
vertical-align: middle;
margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > .input-with-label {
margin: 0 0.5em 0.25em 0.5em;
}
body.fileedit .fileedit-options > div > * {
margin: 0.25em;
}
body.fileedit .fileedit-options.flex-container.flex-row {
align-items: first baseline;
}
|
| ︙ | ︙ |
Changes to src/style.wikiedit.css.
| ︙ | ︙ | |||
41 42 43 44 45 46 47 |
margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input {
vertical-align: middle;
margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > .input-with-label {
| < | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input {
vertical-align: middle;
margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > .input-with-label {
margin: 0 0.5em 0.25em 0.5em;
}
body.wikiedit label {
display: inline; /* some skins set label display to block! */
}
body.wikiedit .wikiedit-options > div > * {
margin: 0.25em;
|
| ︙ | ︙ |
Changes to src/sync.c.
| ︙ | ︙ | |||
126 127 128 129 130 131 132 |
int configSync = 0; /* configuration changes transferred */
if( g.fNoSync ){
return 0;
}
zAutosync = db_get_for_subsystem("autosync", zSubsys);
if( zAutosync==0 ) zAutosync = "on"; /* defend against misconfig */
if( is_false(zAutosync) ) return 0;
| | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
int configSync = 0; /* configuration changes transferred */
if( g.fNoSync ){
return 0;
}
zAutosync = db_get_for_subsystem("autosync", zSubsys);
if( zAutosync==0 ) zAutosync = "on"; /* defend against misconfig */
if( is_false(zAutosync) ) return 0;
if( db_get_boolean("dont-push",0)
|| sqlite3_strglob("*pull*", zAutosync)==0
){
flags &= ~SYNC_CKIN_LOCK;
if( flags & SYNC_PUSH ) return 0;
}
if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
url_parse(0, URL_REMEMBER|URL_USE_CONFIG);
|
| ︙ | ︙ | |||
301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
if( g.url.protocol==0 ){
if( urlOptional ) fossil_exit(0);
usage("URL");
}
user_select();
url_enable_proxy("via proxy: ");
*pConfigFlags |= configSync;
}
/*
** COMMAND: pull
**
** Usage: %fossil pull ?URL? ?options?
| > > > > > > | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
if( g.url.protocol==0 ){
if( urlOptional ) fossil_exit(0);
usage("URL");
}
user_select();
url_enable_proxy("via proxy: ");
*pConfigFlags |= configSync;
if( (*pSyncFlags & SYNC_ALLURL)==0 && zUrl==0 ){
const char *zAutosync = db_get_for_subsystem("autosync", "sync");
if( sqlite3_strglob("*all*", zAutosync)==0 ){
*pSyncFlags |= SYNC_ALLURL;
}
}
}
/*
** COMMAND: pull
**
** Usage: %fossil pull ?URL? ?options?
|
| ︙ | ︙ | |||
525 526 527 528 529 530 531 | ** ** > fossil remote list|ls ** ** Show all remote repository URLs. ** ** > fossil remote off ** | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | ** ** > fossil remote list|ls ** ** Show all remote repository URLs. ** ** > fossil remote off ** ** Forget the default URL. This disables autosync. ** ** This is a convenient way to enter "airplane mode". To enter ** airplane mode, first save the current default URL, then turn the ** default off. Perhaps like this: ** ** fossil remote add main default ** fossil remote off |
| ︙ | ︙ | |||
587 588 589 590 591 592 593 | ** ** The last-sync-url is called "default" for the display list. ** ** The last-sync-url might be duplicated into one of the sync-url:NAME ** entries. Thus, when doing a "fossil sync --all" or an autosync with ** autosync=all, each sync-url:NAME entry is checked to see if it is the ** same as last-sync-url and if it is then that entry is skipped. | | | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 |
**
** The last-sync-url is called "default" for the display list.
**
** The last-sync-url might be duplicated into one of the sync-url:NAME
** entries. Thus, when doing a "fossil sync --all" or an autosync with
** autosync=all, each sync-url:NAME entry is checked to see if it is the
** same as last-sync-url and if it is then that entry is skipped.
*/
if( g.argc==2 ){
/* "fossil remote" with no arguments: Show the last sync URL. */
zUrl = db_get("last-sync-url", 0);
if( zUrl==0 ){
fossil_print("off\n");
}else{
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
638 639 640 641 642 643 644 |
const char *zTagPrefix = find_option("prefix","",1);
int nTagType = fRaw ? -1 : 0;
if( zTagType!=0 ){
int l = strlen(zTagType);
if( strncmp(zTagType,"cancel",l)==0 ){
nTagType = 0;
| | | | 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 |
const char *zTagPrefix = find_option("prefix","",1);
int nTagType = fRaw ? -1 : 0;
if( zTagType!=0 ){
int l = strlen(zTagType);
if( strncmp(zTagType,"cancel",l)==0 ){
nTagType = 0;
}else if( strncmp(zTagType,"singleton",l)==0 ){
nTagType = 1;
}else if( strncmp(zTagType,"propagated",l)==0 ){
nTagType = 2;
}else{
fossil_fatal("unrecognized tag type");
}
}
if( g.argc==3 ){
const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0;
|
| ︙ | ︙ |
Changes to src/tar.c.
| ︙ | ︙ | |||
242 243 244 245 246 247 248 |
n /= 10;
}
/* adding the length extended the length field? */
if(blen > next10){
blen++;
}
/* build the string */
| | > | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
n /= 10;
}
/* adding the length extended the length field? */
if(blen > next10){
blen++;
}
/* build the string */
blob_appendf(&tball.pax, "%d %s=%*.*s\n",
blen, zField, nValue, nValue, zValue);
/* this _must_ be right */
if((int)blob_size(&tball.pax) != blen){
fossil_panic("internal error: PAX tar header has bad length");
}
}
|
| ︙ | ︙ |
Changes to src/terminal.c.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include "config.h" #include "terminal.h" #include <assert.h> #ifdef _WIN32 # include <windows.h> #else #include <sys/ioctl.h> #include <stdio.h> #include <unistd.h> #endif | > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include "config.h" #include "terminal.h" #include <assert.h> #ifdef _WIN32 # include <windows.h> #else #ifdef __EXTENSIONS__ #include <termio.h> #endif #include <sys/ioctl.h> #include <stdio.h> #include <unistd.h> #endif |
| ︙ | ︙ |
Changes to src/th.c.
| ︙ | ︙ | |||
2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 |
/*
** Set the result of the interpreter to the th1 representation of
** the integer iVal and return TH_OK.
*/
int Th_SetResultInt(Th_Interp *interp, int iVal){
int isNegative = 0;
char zBuf[32];
char *z = &zBuf[32];
if( iVal<0 ){
isNegative = 1;
| > | | | | | 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 |
/*
** Set the result of the interpreter to the th1 representation of
** the integer iVal and return TH_OK.
*/
int Th_SetResultInt(Th_Interp *interp, int iVal){
int isNegative = 0;
unsigned int uVal = iVal;
char zBuf[32];
char *z = &zBuf[32];
if( iVal<0 ){
isNegative = 1;
uVal = iVal * -1;
}
*(--z) = '\0';
*(--z) = (char)(48+(uVal%10));
while( (uVal = (uVal/10))>0 ){
*(--z) = (char)(48+(uVal%10));
assert(z>zBuf);
}
if( isNegative ){
*(--z) = '-';
}
return Th_SetResult(interp, z, -1);
|
| ︙ | ︙ |
Changes to src/th_main.c.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | */ #define TH_INIT_NONE ((u32)0x00000000) /* No flags. */ #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */ #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */ #define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */ #define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */ #define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */ | | > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
*/
#define TH_INIT_NONE ((u32)0x00000000) /* No flags. */
#define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
#define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */
#define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
#define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
#define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */
#define TH_INIT_NO_ENCODE ((u32)0x00000020) /* Do not html-encode sendText()*/
/* output. */
#define TH_INIT_MASK ((u32)0x0000003F) /* All possible init flags. */
/*
** Useful and/or "well-known" combinations of flag values.
*/
#define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */
#define TH_INIT_HOOK (TH_INIT_NEED_CONFIG | TH_INIT_FORCE_SETUP)
|
| ︙ | ︙ |
Changes to src/th_tcl.c.
| ︙ | ︙ | |||
1162 1163 1164 1165 1166 1167 1168 |
Tcl_DeleteInterp(tclInterp); /* TODO: Redundant? */
tclInterp = 0;
return TH_ERROR;
}
tclContext->interp = tclInterp;
if( Tcl_Init(tclInterp)!=TCL_OK ){
Th_ErrorMessage(interp,
| | > | > | 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 |
Tcl_DeleteInterp(tclInterp); /* TODO: Redundant? */
tclInterp = 0;
return TH_ERROR;
}
tclContext->interp = tclInterp;
if( Tcl_Init(tclInterp)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl initialization error:",
Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
if( setTclArguments(tclInterp, argc, argv)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl error setting arguments:",
Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
/*
** Determine (and cache) if an objProc can be called directly for a Tcl
** command invoked via the tclInvoke TH1 command.
|
| ︙ | ︙ | |||
1192 1193 1194 1195 1196 1197 1198 |
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
/* If necessary, evaluate the custom Tcl setup script. */
setup = tclContext->setup;
if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
Th_ErrorMessage(interp,
| | > | 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 |
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
/* If necessary, evaluate the custom Tcl setup script. */
setup = tclContext->setup;
if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl setup script error:",
Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
return TH_OK;
}
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
#define TIMELINE_FMT_ONELINE \
"%h %c"
#define TIMELINE_FMT_MEDIUM \
"Commit: %h%nDate: %d%nAuthor: %a%nComment: %c"
#define TIMELINE_FMT_FULL \
"Commit: %H%nDate: %d%nAuthor: %a%nComment: %c%n"\
| | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#define TIMELINE_FMT_ONELINE \
"%h %c"
#define TIMELINE_FMT_MEDIUM \
"Commit: %h%nDate: %d%nAuthor: %a%nComment: %c"
#define TIMELINE_FMT_FULL \
"Commit: %H%nDate: %d%nAuthor: %a%nComment: %c%n"\
"Branch: %b%nTags: %t%nPhase: %p"
/*
** Add an appropriate tag to the output if "rid" is unpublished (private)
*/
#define UNPUB_TAG "<em>(unpublished)</em>"
void tag_private_status(int rid){
if( content_is_private(rid) ){
cgi_printf(" %s", UNPUB_TAG);
|
| ︙ | ︙ | |||
151 152 153 154 155 156 157 | db_bind_int(&q, "$rid", rid); res = db_step(&q)==SQLITE_ROW; db_reset(&q); return res; } /* | | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
db_bind_int(&q, "$rid", rid);
res = db_step(&q)==SQLITE_ROW;
db_reset(&q);
return res;
}
/*
** Return the text of the unformatted
** forum post given by the RID in the argument.
*/
static void forum_post_content_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
|
| ︙ | ︙ | |||
364 365 366 367 368 369 370 |
int isClosed = 0;
if( is_ticket(zTktid, &isClosed) && isClosed ){
zExtraClass = " tktTlClosed";
}else{
zExtraClass = " tktTlOpen";
}
fossil_free(zTktid);
| | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
int isClosed = 0;
if( is_ticket(zTktid, &isClosed) && isClosed ){
zExtraClass = " tktTlClosed";
}else{
zExtraClass = " tktTlOpen";
}
fossil_free(zTktid);
}
}
if( zType[0]=='e' && tagid ){
if( bTimestampLinksToInfo ){
char *zId;
zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
tagid);
zDateLink = href("%R/technote/%s",zId);
|
| ︙ | ︙ | |||
674 675 676 677 678 679 680 |
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
| | | 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 |
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
cgi_printf(" id: %z%d←%d</a>",
href("%R/deltachain/%d",rid), rid, srcId);
}else{
cgi_printf(" id: %z%d</a>",
href("%R/deltachain/%d",rid), rid);
}
}
tag_private_status(rid);
|
| ︙ | ︙ | |||
2220 2221 2222 2223 2224 2225 2226 |
}
blob_appendf(&desc, " of %z%h</a>",
href("%R/info?name=%h", zCiName), zCiName);
if( ridBackTo ){
if( np==0 ){
blob_reset(&desc);
| | | | 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 |
}
blob_appendf(&desc, " of %z%h</a>",
href("%R/info?name=%h", zCiName), zCiName);
if( ridBackTo ){
if( np==0 ){
blob_reset(&desc);
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
href("%R/info?name=%h",zCiName), zCiName,
href("%R/info?name=%h",zBackTo), zBackTo);
}else{
blob_appendf(&desc, " back to %z%h</a>",
href("%R/info?name=%h",zBackTo), zBackTo);
if( ridFwdTo && zFwdTo ){
blob_appendf(&desc, " and up to %z%h</a>",
href("%R/info?name=%h",zFwdTo), zFwdTo);
}
}
}else if( ridFwdTo ){
if( nd==0 ){
blob_reset(&desc);
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> is not an descendant)",
href("%R/info?name=%h",zCiName), zCiName,
href("%R/info?name=%h",zFwdTo), zFwdTo);
}else{
blob_appendf(&desc, " up to %z%h</a>",
href("%R/info?name=%h",zFwdTo), zFwdTo);
}
|
| ︙ | ︙ | |||
3015 3016 3017 3018 3019 3020 3021 | ** 6. mtime ** 7. branch ** 8. event-type: 'ci', 'w', 't', 'f', and so forth. ** 9. comment ** 10. user ** 11. tags */ | | > > | | > | | 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 |
** 6. mtime
** 7. branch
** 8. event-type: 'ci', 'w', 't', 'f', and so forth.
** 9. comment
** 10. user
** 11. tags
*/
void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat,
int verboseFlag){
int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
int nLine = 0;
int nEntry = 0;
char zPrevDate[20];
const char *zCurrentUuid = 0;
int fchngQueryInit = 0; /* True if fchngQuery is initialized */
Stmt fchngQuery; /* Query for file changes on check-ins */
int rc;
/* True: separate entries with a newline after file listing */
int bVerboseNL = (zFormat &&
(fossil_strcmp(zFormat, TIMELINE_FMT_ONELINE)!=0));
/* True: separate entries with a newline even with no file listing */
int bNoVerboseNL = (zFormat &&
(fossil_strcmp(zFormat, TIMELINE_FMT_MEDIUM)==0 ||
fossil_strcmp(zFormat, TIMELINE_FMT_FULL)==0));
zPrevDate[0] = 0;
if( g.localOpen ){
int rid = db_lget_int("checkout", 0);
zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
}
|
| ︙ | ︙ | |||
3120 3121 3122 3123 3124 3125 3126 |
if( zFormat ){
char *zEntry;
int nEntryLine = 0;
if( nChild==0 ){
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
}
| | | > | 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 |
if( zFormat ){
char *zEntry;
int nEntryLine = 0;
if( nChild==0 ){
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
}
zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate,
zUserShort, zComShort, zBranch, zTags,
zPrefix);
nLine += nEntryLine;
fossil_print("%s\n", zEntry);
fossil_free(zEntry);
}
else{
/* record another X lines */
nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
|
| ︙ | ︙ | |||
3165 3166 3167 3168 3169 3170 3171 |
nLine++; /* record another line */
}
db_reset(&fchngQuery);
if( bVerboseNL ) fossil_print("\n");
}else{
if( bNoVerboseNL ) fossil_print("\n");
}
| | | 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 |
nLine++; /* record another line */
}
db_reset(&fchngQuery);
if( bVerboseNL ) fossil_print("\n");
}else{
if( bNoVerboseNL ) fossil_print("\n");
}
nEntry++; /* record another complete entry */
}
if( rc==SQLITE_DONE ){
/* Did the underlying query actually have all entries? */
if( nAbsLimit==0 ){
fossil_print("+++ end of timeline (%d) +++\n", nEntry);
}else{
|
| ︙ | ︙ | |||
3209 3210 3211 3212 3213 3214 3215 |
@ event.type
@ , coalesce(ecomment,comment) AS comment0
@ , coalesce(euser,user,'?') AS user0
@ , (SELECT case when length(x)>0 then x else '' end
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
@ FROM tag, tagxref
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
| | | 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 |
@ event.type
@ , coalesce(ecomment,comment) AS comment0
@ , coalesce(euser,user,'?') AS user0
@ , (SELECT case when length(x)>0 then x else '' end
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
@ FROM tag, tagxref
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
@ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) AS tags
@ FROM tag CROSS JOIN event CROSS JOIN blob
@ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
@ AND tagxref.tagtype>0
@ AND tagxref.rid=blob.rid
@ WHERE blob.rid=event.objid
@ AND tag.tagname='branch'
;
|
| ︙ | ︙ | |||
3270 3271 3272 3273 3274 3275 3276 | ** means UTC. ** ** ** Options: ** -b|--branch BRANCH Show only items on the branch named BRANCH ** -c|--current-branch Show only items on the current branch ** -F|--format Entry format. Values "oneline", "medium", and "full" | | | | 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 | ** means UTC. ** ** ** Options: ** -b|--branch BRANCH Show only items on the branch named BRANCH ** -c|--current-branch Show only items on the current branch ** -F|--format Entry format. Values "oneline", "medium", and "full" ** get mapped to the full options below. Otherwise a ** string which can contain these placeholders: ** %n newline ** %% a raw % ** %H commit hash ** %h abbreviated commit hash ** %a author name ** %d date ** %c comment (NL, TAB replaced by space, LF erased) ** %b branch ** %t tags ** %p phase: zero or more of *CURRENT*, *MERGE*, ** *FORK*, *UNPUBLISHED*, *LEAF*, *BRANCH* ** --oneline Show only short hash and comment for each entry ** --medium Medium-verbose entry formatting ** --full Extra verbose entry formatting |
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
555 556 557 558 559 560 561 |
case SQLITE_CREATE_VIEW:
case SQLITE_CREATE_TABLE: {
if( sqlite3_stricmp(z2,"main")!=0
&& sqlite3_stricmp(z2,"repository")!=0
){
goto ticket_schema_error;
}
| | | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
case SQLITE_CREATE_VIEW:
case SQLITE_CREATE_TABLE: {
if( sqlite3_stricmp(z2,"main")!=0
&& sqlite3_stricmp(z2,"repository")!=0
){
goto ticket_schema_error;
}
if( sqlite3_strnicmp(z0,"ticket",6)!=0
&& sqlite3_strnicmp(z0,"fx_",3)!=0
){
goto ticket_schema_error;
}
break;
}
case SQLITE_DROP_INDEX:
|
| ︙ | ︙ | |||
1211 1212 1213 1214 1215 1216 1217 | } /* ** WEBPAGE: tkttimeline ** URL: /tkttimeline/TICKETUUID ** ** Show the change history for a single ticket in timeline format. | | | 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 |
}
/*
** WEBPAGE: tkttimeline
** URL: /tkttimeline/TICKETUUID
**
** Show the change history for a single ticket in timeline format.
**
** Query parameters:
**
** y=ci Show only check-ins associated with the ticket
*/
void tkttimeline_page(void){
char *zTitle;
const char *zUuid;
|
| ︙ | ︙ |
Changes to src/unicode.c.
| ︙ | ︙ | |||
238 239 240 241 242 243 244 |
iLo = iTest+1;
}else{
iHi = iTest-1;
}
}
assert( key>=aDia[iRes] );
if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
| | > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
iLo = iTest+1;
}else{
iHi = iTest-1;
}
}
assert( key>=aDia[iRes] );
if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c :
((int)aChar[iRes] & 0x7F);
}
/*
** Return true if the argument interpreted as a unicode codepoint
** is a diacritical modifier character.
*/
|
| ︙ | ︙ |
Changes to src/unversioned.c.
| ︙ | ︙ | |||
306 307 308 309 310 311 312 |
nCmd = (int)strlen(zCmd);
if( zMtime==0 ){
mtime = time(0);
}else{
mtime = db_int(0, "SELECT strftime('%%s',%Q)", zMtime);
if( mtime<=0 ) fossil_fatal("bad timestamp: %Q", zMtime);
}
| | | 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
nCmd = (int)strlen(zCmd);
if( zMtime==0 ){
mtime = time(0);
}else{
mtime = db_int(0, "SELECT strftime('%%s',%Q)", zMtime);
if( mtime<=0 ) fossil_fatal("bad timestamp: %Q", zMtime);
}
if( strncmp(zCmd, "add", nCmd)==0 ){
const char *zError = 0;
const char *zIn;
const char *zAs;
Blob file;
int i;
zAs = find_option("as",0,1);
|
| ︙ | ︙ | |||
338 339 340 341 342 343 344 |
}
blob_init(&file,0,0);
blob_read_from_file(&file, g.argv[i], ExtFILE);
unversioned_write(zIn, &file, mtime);
blob_reset(&file);
}
db_end_transaction(0);
| | | | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
}
blob_init(&file,0,0);
blob_read_from_file(&file, g.argv[i], ExtFILE);
unversioned_write(zIn, &file, mtime);
blob_reset(&file);
}
db_end_transaction(0);
}else if( strncmp(zCmd, "cat", nCmd)==0 ){
int i;
verify_all_options();
db_begin_transaction();
for(i=3; i<g.argc; i++){
Blob content;
if( unversioned_content(g.argv[i], &content)!=0 ){
blob_write_to_file(&content, "-");
}
blob_reset(&content);
}
db_end_transaction(0);
}else if( strncmp(zCmd, "edit", nCmd)==0 ){
const char *zEditor; /* Name of the text-editor command */
const char *zTFile; /* Temporary file */
const char *zUVFile; /* Name of the unversioned file */
char *zCmd; /* Command to run the text editor */
Blob content; /* Content of the unversioned file */
verify_all_options();
|
| ︙ | ︙ | |||
393 394 395 396 397 398 399 |
blob_to_lf_only(&content);
#endif
file_delete(zTFile);
if( zMtime==0 ) mtime = time(0);
unversioned_write(zUVFile, &content, mtime);
db_end_transaction(0);
blob_reset(&content);
| | | | | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 |
blob_to_lf_only(&content);
#endif
file_delete(zTFile);
if( zMtime==0 ) mtime = time(0);
unversioned_write(zUVFile, &content, mtime);
db_end_transaction(0);
blob_reset(&content);
}else if( strncmp(zCmd, "export", nCmd)==0 ){
Blob content;
verify_all_options();
if( g.argc!=5 ) usage("export UVFILE OUTPUT");
if( unversioned_content(g.argv[3], &content)==0 ){
fossil_fatal("no such uv-file: %Q", g.argv[3]);
}
blob_write_to_file(&content, g.argv[4]);
blob_reset(&content);
}else if( strncmp(zCmd, "hash", nCmd)==0 ){ /* undocumented */
/* Show the hash value used during uv sync */
int debugFlag = find_option("debug",0,0)!=0;
fossil_print("%s\n", unversioned_content_hash(debugFlag));
}else if( strncmp(zCmd, "list", nCmd)==0 || strncmp(zCmd, "ls", nCmd)==0 ){
Stmt q;
int allFlag = find_option("all","a",0)!=0;
int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i');
char *zPattern = sqlite3_mprintf("true");
const char *zGlob;
zGlob = find_option("glob",0,1);
if( zGlob ){
|
| ︙ | ︙ | |||
462 463 464 465 466 467 468 |
db_column_text(&q,4),
zNoContent
);
}
}
db_finalize(&q);
sqlite3_free(zPattern);
| | | | | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
db_column_text(&q,4),
zNoContent
);
}
}
db_finalize(&q);
sqlite3_free(zPattern);
}else if( strncmp(zCmd, "revert", nCmd)==0 ){
unsigned syncFlags =
unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
g.argv[1] = "sync";
g.argv[2] = "--uv-noop";
sync_unversioned(syncFlags);
}else if( strncmp(zCmd, "remove", nCmd)==0 || strncmp(zCmd, "rm", nCmd)==0
|| strncmp(zCmd, "delete", nCmd)==0 ){
int i;
const char *zGlob;
db_begin_transaction();
while( (zGlob = find_option("glob",0,1))!=0 ){
db_multi_exec(
"UPDATE unversioned"
" SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q",
|
| ︙ | ︙ | |||
497 498 499 500 501 502 503 |
"UPDATE unversioned"
" SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q",
mtime, g.argv[i]
);
}
db_unset("uv-hash", 0);
db_end_transaction(0);
| | | | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
"UPDATE unversioned"
" SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q",
mtime, g.argv[i]
);
}
db_unset("uv-hash", 0);
db_end_transaction(0);
}else if( strncmp(zCmd,"sync",nCmd)==0 ){
unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED);
g.argv[1] = "sync";
g.argv[2] = "--uv-noop";
sync_unversioned(syncFlags);
}else if( strncmp(zCmd, "touch", nCmd)==0 ){
int i;
verify_all_options();
db_begin_transaction();
for(i=3; i<g.argc; i++){
db_multi_exec(
"UPDATE unversioned SET mtime=%lld WHERE name=%Q",
mtime, g.argv[i]
|
| ︙ | ︙ | |||
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 |
);
iNow = db_int64(0, "SELECT strftime('%%s','now');");
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
sqlite3_int64 mtime = db_column_int(&q, 1);
const char *zHash = db_column_text(&q, 2);
int isDeleted = zHash==0;
int fullSize = db_column_int(&q, 3);
char *zAge = human_readable_age((iNow - mtime)/86400.0);
const char *zLogin = db_column_text(&q, 4);
int rcvid = db_column_int(&q,5);
if( zLogin==0 ) zLogin = "";
if( (n++)==0 ){
style_table_sorter();
@ <div class="uvlist">
@ <table cellpadding="2" cellspacing="0" border="1" class='sortable' \
@ data-column-types='tkKttn' data-init-sort='1'>
@ <thead><tr>
@ <th> Name
@ <th> Age
@ <th> Size
@ <th> User
@ <th> Hash
if( g.perm.Admin ){
@ <th> rcvid
}
@ </tr></thead>
@ <tbody>
}
@ <tr>
| > > > > | 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
);
iNow = db_int64(0, "SELECT strftime('%%s','now');");
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
sqlite3_int64 mtime = db_column_int(&q, 1);
const char *zHash = db_column_text(&q, 2);
int isDeleted = zHash==0;
const char *zAlgo;
int fullSize = db_column_int(&q, 3);
char *zAge = human_readable_age((iNow - mtime)/86400.0);
const char *zLogin = db_column_text(&q, 4);
int rcvid = db_column_int(&q,5);
if( isDeleted ) zAlgo = "deleted";
else zAlgo = hname_alg(strlen(zHash));
if( zLogin==0 ) zLogin = "";
if( (n++)==0 ){
style_table_sorter();
@ <div class="uvlist">
@ <table cellpadding="2" cellspacing="0" border="1" class='sortable' \
@ data-column-types='tkKttn' data-init-sort='1'>
@ <thead><tr>
@ <th> Name
@ <th> Age
@ <th> Size
@ <th> User
@ <th> Hash
@ <th> Algo
if( g.perm.Admin ){
@ <th> rcvid
}
@ </tr></thead>
@ <tbody>
}
@ <tr>
|
| ︙ | ︙ | |||
606 607 608 609 610 611 612 |
iTotalSz += fullSize;
cnt++;
@ <td> <a href='%R/uv/%T(zName)'>%h(zName)</a> </td>
}
@ <td data-sortkey='%016llx(-mtime)'> %s(zAge) </td>
@ <td data-sortkey='%08x(fullSize)'> %s(zSzName) </td>
@ <td> %h(zLogin) </td>
| | > > | 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
iTotalSz += fullSize;
cnt++;
@ <td> <a href='%R/uv/%T(zName)'>%h(zName)</a> </td>
}
@ <td data-sortkey='%016llx(-mtime)'> %s(zAge) </td>
@ <td data-sortkey='%08x(fullSize)'> %s(zSzName) </td>
@ <td> %h(zLogin) </td>
@ <td><code> %h(zHash) </code></td>
@ <td> %s(zAlgo) </td>
if( g.perm.Admin ){
if( rcvid ){
@ <td> <a href="%R/rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a>
}else{
@ <td>
}
}
@ </tr>
fossil_free(zAge);
}
db_finalize(&q);
if( n ){
approxSizeName(sizeof(zSzName), zSzName, iTotalSz);
@ </tbody>
@ <tfoot><tr><td><b>Total for %d(cnt) files</b><td><td>%s(zSzName)
@ <td><td>
if( g.perm.Admin ){
@ <td>
}
@ <td>
@ </tfoot>
@ </table></div>
}else{
@ No unversioned files on this server.
}
style_finish_page();
}
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
565 566 567 568 569 570 571 |
db_finalize(&q);
db_finalize(&mtimeXfer);
fossil_print("%.79c\n",'-');
if( nUpdate==0 ){
show_common_info(tid, "checkout:", 1, 0);
fossil_print("%-13s None. Already up-to-date\n", "changes:");
}else{
| | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
db_finalize(&q);
db_finalize(&mtimeXfer);
fossil_print("%.79c\n",'-');
if( nUpdate==0 ){
show_common_info(tid, "checkout:", 1, 0);
fossil_print("%-13s None. Already up-to-date\n", "changes:");
}else{
fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid),
db_text("", "SELECT datetime(mtime) || ' UTC' FROM event "
" WHERE objid=%d", vid));
show_common_info(tid, "updated-to:", 1, 0);
fossil_print("%-13s %d file%s modified.\n", "changes:",
nUpdate, nUpdate>1 ? "s" : "");
}
|
| ︙ | ︙ |
Changes to src/url.c.
| ︙ | ︙ | |||
87 88 89 90 91 92 93 | ** path Path name for HTTP or HTTPS. ** user Userid. ** passwd Password. ** hostname HOST:PORT or just HOST if port is the default. ** canonical The URL in canonical form, omitting the password ** ** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | ** path Path name for HTTP or HTTPS. ** user Userid. ** passwd Password. ** hostname HOST:PORT or just HOST if port is the default. ** canonical The URL in canonical form, omitting the password ** ** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the ** URL stored in last-sync-url and last-sync-pw of the CONFIG table. Or if ** URL_USE_PARENT is also set, then use parent-project-url and ** parent-project-pw from the CONFIG table instead of last-sync-url ** and last-sync-pw. ** ** If URL_USE_CONFIG is set and zUrl is a symbolic name, then look up ** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where ** %Q is the symbolic name. |
| ︙ | ︙ | |||
315 316 317 318 319 320 321 |
free(zFile);
zFile = 0;
pUrlData->protocol = "file";
pUrlData->path = mprintf("");
pUrlData->name = mprintf("%b", &cfile);
pUrlData->canonical = mprintf("file://%T", pUrlData->name);
blob_reset(&cfile);
| | | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
free(zFile);
zFile = 0;
pUrlData->protocol = "file";
pUrlData->path = mprintf("");
pUrlData->name = mprintf("%b", &cfile);
pUrlData->canonical = mprintf("file://%T", pUrlData->name);
blob_reset(&cfile);
}else if( pUrlData->user!=0 && pUrlData->passwd==0
&& (urlFlags & URL_PROMPT_PW)!=0 ){
url_prompt_for_password_local(pUrlData);
}else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){
if( isatty(fileno(stdin)) && ( urlFlags & URL_REMEMBER_PW )==0 ){
if( save_password_prompt(pUrlData->passwd) ){
pUrlData->flags = urlFlags |= URL_REMEMBER_PW;
}else{
|
| ︙ | ︙ | |||
789 790 791 792 793 794 795 | ** Given a URL for a remote repository clone point, try to come up with a ** reasonable basename of a local clone of that repository. ** ** * If the URL has a path, use the tail of the path, with any suffix ** elided. ** ** * If the URL is just a domain name, without a path, then use the | | | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
** Given a URL for a remote repository clone point, try to come up with a
** reasonable basename of a local clone of that repository.
**
** * If the URL has a path, use the tail of the path, with any suffix
** elided.
**
** * If the URL is just a domain name, without a path, then use the
** first element of the domain name, except skip over "www." if
** present and if there is a ".com" or ".org" or similar suffix.
**
** The string returned is obtained from fossil_malloc(). NULL might be
** returned if there is an error.
*/
char *url_to_repo_basename(const char *zUrl){
const char *zTail = 0;
|
| ︙ | ︙ |
Changes to src/user.c.
| ︙ | ︙ | |||
397 398 399 400 401 402 403 |
}
if( g.localOpen ){
db_lset("default-user", g.argv[3]);
}else{
db_set("default-user", g.argv[3], 0);
}
}
| | > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
}
if( g.localOpen ){
db_lset("default-user", g.argv[3]);
}else{
db_set("default-user", g.argv[3], 0);
}
}
}else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) ||
( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
db_prepare(&q, "SELECT login, info FROM user ORDER BY login");
while( db_step(&q)==SQLITE_ROW ){
fossil_print("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1));
}
db_finalize(&q);
}else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){
|
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
408 409 410 411 412 413 414 |
"original",
"output",
};
int i, j, n;
if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1;
for(; zName[0]!=0; zName++){
| > | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 |
"original",
"output",
};
int i, j, n;
if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1;
for(; zName[0]!=0; zName++){
if( zName[0]=='/'
&& sqlite3_strglob("/ci-comment-????????????.txt", zName)==0 ){
return 1;
}
if( zName[0]!='-' ) continue;
for(i=0; i<count(azTemp); i++){
n = (int)strlen(azTemp[i]);
if( memcmp(azTemp[i], zName+1, n) ) continue;
if( zName[n+1]==0 ) return 1;
|
| ︙ | ︙ | |||
752 753 754 755 756 757 758 |
md5sum_step_text(" 0\n", -1);
continue;
}
fseek(in, 0L, SEEK_END);
sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
fseek(in, 0L, SEEK_SET);
md5sum_step_text(zBuf, -1);
| | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 |
md5sum_step_text(" 0\n", -1);
continue;
}
fseek(in, 0L, SEEK_END);
sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
fseek(in, 0L, SEEK_SET);
md5sum_step_text(zBuf, -1);
/*printf("%s %s %s",md5sum_current_state(),zName,zBuf);fflush(stdout);*/
for(;;){
int n;
n = fread(zBuf, 1, sizeof(zBuf), in);
if( n<=0 ) break;
md5sum_step_text(zBuf, n);
}
fclose(in);
|
| ︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 |
/* Add RID values for merged-in files */
db_multi_exec(
"INSERT OR IGNORE INTO idMap(oldrid, newrid)"
" SELECT vfile.mrid, blob.rid FROM vfile, blob"
" WHERE blob.uuid=vfile.mhash;"
);
| | | | 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 |
/* Add RID values for merged-in files */
db_multi_exec(
"INSERT OR IGNORE INTO idMap(oldrid, newrid)"
" SELECT vfile.mrid, blob.rid FROM vfile, blob"
" WHERE blob.uuid=vfile.mhash;"
);
if( dryRun ){
Stmt q;
db_prepare(&q, "SELECT oldrid, newrid, blob.uuid"
" FROM idMap, blob WHERE blob.rid=idMap.newrid");
while( db_step(&q)==SQLITE_ROW ){
fossil_print("%8d -> %8d %.25s\n",
db_column_int(&q,0),
db_column_int(&q,1),
db_column_text(&q,2));
}
db_finalize(&q);
}
|
| ︙ | ︙ | |||
1067 1068 1069 1070 1071 1072 1073 |
" UNION SELECT %d"
")"
"SELECT group_concat(x,' ') FROM allrid"
" WHERE x<>0 AND x NOT IN (SELECT oldrid FROM idMap);",
oldVid
);
if( zUnresolved[0] ){
| | > > > > > > > | 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 |
" UNION SELECT %d"
")"
"SELECT group_concat(x,' ') FROM allrid"
" WHERE x<>0 AND x NOT IN (SELECT oldrid FROM idMap);",
oldVid
);
if( zUnresolved[0] ){
fossil_fatal("Unresolved RID values: %s\n"
"\n"
"Local check-out database is out of sync with repository file:\n"
"\n"
" %s\n"
"\n"
"Has the repository file been replaced?\n",
zUnresolved, db_repository_filename());
}
/* Make the changes to the VFILE and VMERGE tables */
if( !dryRun ){
db_multi_exec(
"UPDATE vfile"
" SET rid=(SELECT newrid FROM idMap WHERE oldrid=vfile.rid)"
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
83 84 85 86 87 88 89 |
}
int wiki_tagid2(const char *zPrefix, const char *zPageName){
return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'",
zPrefix, zPageName);
}
/*
| | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
}
int wiki_tagid2(const char *zPrefix, const char *zPageName){
return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'",
zPrefix, zPageName);
}
/*
** Return the RID of the next or previous version of a wiki page.
** Return 0 if rid is the last/first version.
*/
int wiki_next(int tagid, double mtime){
return db_int(0,
"SELECT srcid FROM tagxref"
" WHERE tagid=%d AND mtime>%.16g"
" ORDER BY mtime ASC LIMIT 1",
|
| ︙ | ︙ | |||
411 412 413 414 415 416 417 |
/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
if( db_get_boolean("wiki-about",1)==0 ){
return WIKITYPE_NORMAL;
}else
| | | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
if( db_get_boolean("wiki-about",1)==0 ){
return WIKITYPE_NORMAL;
}else
if( sqlite3_strglob("checkin/*", zPageName)==0
&& db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
){
return WIKITYPE_CHECKIN;
}else
if( sqlite3_strglob("branch/*", zPageName)==0 ){
return WIKITYPE_BRANCH;
}else
|
| ︙ | ︙ | |||
445 446 447 448 449 450 451 | /* ** Add an appropriate style_header() for either the /wiki or /wikiedit page ** for zPageName. zExtra is an empty string for /wiki but has the text ** "Edit: " for /wikiedit. ** ** If the page is /wiki and the page is one of the special times (check-in, | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | /* ** Add an appropriate style_header() for either the /wiki or /wikiedit page ** for zPageName. zExtra is an empty string for /wiki but has the text ** "Edit: " for /wikiedit. ** ** If the page is /wiki and the page is one of the special times (check-in, ** branch, or tag) and the "p" query parameter is omitted, then do a ** redirect to the display of the check-in, branch, or tag rather than ** continuing to the plain wiki display. */ static int wiki_page_header( int eType, /* Page type. Might be WIKITYPE_UNKNOWN */ const char *zPageName, /* Name of the page */ const char *zExtra /* Extra prefix text on the page header */ |
| ︙ | ︙ | |||
467 468 469 470 471 472 473 |
}
case WIKITYPE_CHECKIN: {
zPageName += 8;
if( zExtra[0]==0 && !P("p") ){
cgi_redirectf("%R/info/%s",zPageName);
}else{
style_header("Notes About Check-in %S", zPageName);
| | > | 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
}
case WIKITYPE_CHECKIN: {
zPageName += 8;
if( zExtra[0]==0 && !P("p") ){
cgi_redirectf("%R/info/%s",zPageName);
}else{
style_header("Notes About Check-in %S", zPageName);
style_submenu_element("Check-in Timeline","%R/timeline?f=%s",
zPageName);
style_submenu_element("Check-in Info","%R/info/%s", zPageName);
}
break;
}
case WIKITYPE_BRANCH: {
zPageName += 7;
if( zExtra[0]==0 && !P("p") ){
|
| ︙ | ︙ | |||
741 742 743 744 745 746 747 |
** Note that the sandbox is a special case: it is a pseudo-page with
** no rid and the /wikiajax API does not allow anyone to actually save
** a sandbox page, but it is reported as writable here (with rid 0).
*/
static int wiki_ajax_can_write(const char *zPageName, int * pRid){
int rid = 0;
const char * zErr = 0;
| | | 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 |
** Note that the sandbox is a special case: it is a pseudo-page with
** no rid and the /wikiajax API does not allow anyone to actually save
** a sandbox page, but it is reported as writable here (with rid 0).
*/
static int wiki_ajax_can_write(const char *zPageName, int * pRid){
int rid = 0;
const char * zErr = 0;
if(pRid) *pRid = 0;
if(!zPageName || !*zPageName
|| !wiki_name_is_wellformed((unsigned const char *)zPageName)){
zErr = "Invalid page name.";
}else if(is_sandbox(zPageName)){
return 1;
}else{
|
| ︙ | ︙ | |||
764 765 766 767 768 769 770 |
}else if(!rid && !g.perm.NewWiki){
zErr = "Requires new-wiki permissions.";
}else{
zErr = "Cannot happen! Please report this as a bug.";
}
}
ajax_route_error(403, "%s", zErr);
| | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
}else if(!rid && !g.perm.NewWiki){
zErr = "Requires new-wiki permissions.";
}else{
zErr = "Cannot happen! Please report this as a bug.";
}
}
ajax_route_error(403, "%s", zErr);
return 0;
}
/*
** Emits an array of attachment info records for the given wiki page
** artifact.
**
|
| ︙ | ︙ | |||
1010 1011 1012 1013 1014 1015 1016 |
**
** Responds with JSON. On error, an object in the form documented by
** ajax_route_error(). On success, an object in the form documented
** for wiki_ajax_emit_page_object().
*/
static void wiki_ajax_route_fetch(void){
const char * zPageName = P("page");
| | | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 |
**
** Responds with JSON. On error, an object in the form documented by
** ajax_route_error(). On success, an object in the form documented
** for wiki_ajax_emit_page_object().
*/
static void wiki_ajax_route_fetch(void){
const char * zPageName = P("page");
if( zPageName==0 || zPageName[0]==0 ){
ajax_route_error(400,"Missing page name.");
return;
}
cgi_set_content_type("application/json");
wiki_ajax_emit_page_object(zPageName, 1);
}
|
| ︙ | ︙ | |||
1201 1202 1203 1204 1205 1206 1207 | } /* ** WEBPAGE: wikiajax hidden ** ** An internal dispatcher for wiki AJAX operations. Not for direct ** client use. All routes defined by this interface are app-internal, | | | 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 |
}
/*
** WEBPAGE: wikiajax hidden
**
** An internal dispatcher for wiki AJAX operations. Not for direct
** client use. All routes defined by this interface are app-internal,
** subject to change
*/
void wiki_ajax_page(void){
const char * zName = P("name");
AjaxRoute routeName = {0,0,0,0};
const AjaxRoute * pRoute = 0;
const AjaxRoute routes[] = {
/* Keep these sorted by zName (for bsearch()) */
|
| ︙ | ︙ | |||
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 |
"CSRF violation (make sure sending of HTTP "
"Referer headers is enabled for XHR "
"connections).");
return;
}
pRoute->xCallback();
}
/*
** WEBPAGE: wikiedit
** URL: /wikedit?name=PAGENAME
**
** The main front-end for the Ajax-based wiki editor app. Passing
** in the name of an unknown page will trigger the creation
| > > > > > > > > > > > > > > > > | 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 |
"CSRF violation (make sure sending of HTTP "
"Referer headers is enabled for XHR "
"connections).");
return;
}
pRoute->xCallback();
}
/*
** Emits a preview-toggle option widget for /wikiedit and /fileedit.
*/
void wikiedit_emit_toggle_preview(void){
CX("<div class='input-with-label'>"
"<input type='checkbox' id='edit-shift-enter-preview' "
"></input><label for='edit-shift-enter-preview'>"
"Shift-enter previews</label>"
"<div class='help-buttonlet'>"
"When enabled, shift-enter switches between preview and edit modes. "
"Some software-based keyboards misinteract with this, so it can be "
"disabled when needed."
"</div>"
"</div>");
}
/*
** WEBPAGE: wikiedit
** URL: /wikedit?name=PAGENAME
**
** The main front-end for the Ajax-based wiki editor app. Passing
** in the name of an unknown page will trigger the creation
|
| ︙ | ︙ | |||
1309 1310 1311 1312 1313 1314 1315 |
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
CX("<div id='wikiedit-edit-status''>"
"<span class='name'></span>"
"<span class='links'></span>"
"</div>");
| | | | 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 |
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
CX("<div id='wikiedit-edit-status''>"
"<span class='name'></span>"
"<span class='links'></span>"
"</div>");
/* Main tab container... */
CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>");
/* The .hidden class on the following tab elements is to help lessen
the FOUC effect of the tabs before JS re-assembles them. */
/******* Page list *******/
{
CX("<div id='wikiedit-tab-pages' "
"data-tab-parent='wikiedit-tabs' "
"data-tab-label='Wiki Page List' "
"class='hidden'"
">");
CX("<div>Loading wiki pages list...</div>");
CX("</div>"/*#wikiedit-tab-pages*/);
}
/******* Content tab *******/
{
CX("<div id='wikiedit-tab-content' "
"data-tab-parent='wikiedit-tabs' "
"data-tab-label='Editor' "
"class='hidden'"
">");
|
| ︙ | ︙ | |||
1369 1370 1371 1372 1373 1374 1375 |
"<div class='help-buttonlet'>"
"Reload the file from the server, discarding "
"any local edits. To help avoid accidental loss of "
"edits, it requires confirmation (a second click) within "
"a few seconds or it will not reload."
"</div>"
"</div>");
| | | 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 |
"<div class='help-buttonlet'>"
"Reload the file from the server, discarding "
"any local edits. To help avoid accidental loss of "
"edits, it requires confirmation (a second click) within "
"a few seconds or it will not reload."
"</div>"
"</div>");
wikiedit_emit_toggle_preview();
CX("</div>");
CX("<div class='flex-container flex-column stretch'>");
CX("<textarea name='content' id='wikiedit-content-editor' "
"class='wikiedit' rows='25'>");
CX("</textarea>");
CX("</div>"/*textarea wrapper*/);
CX("</div>"/*#tab-file-content*/);
|
| ︙ | ︙ | |||
1887 1888 1889 1890 1891 1892 1893 | ** wsort Sort names by this label ** wrid rid of the most recent version of the page ** wmtime time most recent version was created ** wcnt Number of versions of this wiki page ** ** The wrid value is zero for deleted wiki pages. */ | | | 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 | ** wsort Sort names by this label ** wrid rid of the most recent version of the page ** wmtime time most recent version was created ** wcnt Number of versions of this wiki page ** ** The wrid value is zero for deleted wiki pages. */ static const char listAllWikiPages[] = @ SELECT @ substr(tag.tagname, 6) AS wname, @ lower(substr(tag.tagname, 6)) AS sortname, @ tagxref.value+0 AS wrid, @ max(tagxref.mtime) AS wmtime, @ count(*) AS wcnt @ FROM |
| ︙ | ︙ | |||
2115 2116 2117 2118 2119 2120 2121 |
if( !rid ) {
/*
** At present, technote tags are prefixed with 'sym-', which shouldn't
** be the case, so we check for both with and without the prefix until
** such time as tags have the errant prefix dropped.
*/
rid = db_int(0, "SELECT e.objid"
| | | | | | | | | 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 |
if( !rid ) {
/*
** At present, technote tags are prefixed with 'sym-', which shouldn't
** be the case, so we check for both with and without the prefix until
** such time as tags have the errant prefix dropped.
*/
rid = db_int(0, "SELECT e.objid"
" FROM event e, tag t, tagxref tx"
" WHERE e.type='e'"
" AND e.tagid IS NOT NULL"
" AND e.objid IN"
" (SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag"
" WHERE tagname GLOB '%q'))"
" OR e.objid IN"
" (SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag"
" WHERE tagname GLOB 'sym-%q'))"
" ORDER BY e.mtime DESC LIMIT 1",
zETime, zETime);
}
return rid;
}
/*
** COMMAND: wiki*
**
|
| ︙ | ︙ | |||
2464 2465 2466 2467 2468 2469 2470 |
}
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const int wrid = db_column_int(&q, 2);
if(!showAll && !wrid){
continue;
}
| | | 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 |
}
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const int wrid = db_column_int(&q, 2);
if(!showAll && !wrid){
continue;
}
if( !showCkBr &&
(sqlite3_strglob("checkin/*", zName)==0 ||
sqlite3_strglob("branch/*", zName)==0) ){
continue;
}
if( showIds ){
const char *zUuid = db_column_text(&q, 1);
fossil_print("%s ",zUuid);
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
2217 2218 2219 2220 2221 2222 2223 |
iMatchCnt = 1;
}else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){
iMatchCnt = 2;
}else if( iMatchCnt==2 ){
if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){
zStart++;
n -= 2;
| | | 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 |
iMatchCnt = 1;
}else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){
iMatchCnt = 2;
}else if( iMatchCnt==2 ){
if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){
zStart++;
n -= 2;
}
*pLen = n;
return zStart;
}else{
iMatchCnt = 0;
}
}
return 0;
|
| ︙ | ︙ |
Changes to src/winhttp.c.
| ︙ | ︙ | |||
666 667 668 669 670 671 672 |
fossil_panic("unable to get path to the temporary directory.");
}
/* Use a subdirectory for temp files (can then be excluded from virus scan) */
zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
file_isdir(zTempSubDirPath, ExtFILE)==1 ){
wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
| | | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 |
fossil_panic("unable to get path to the temporary directory.");
}
/* Use a subdirectory for temp files (can then be excluded from virus scan) */
zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
file_isdir(zTempSubDirPath, ExtFILE)==1 ){
wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
}
if( g.fHttpTrace ){
zTempPrefix = mprintf("httptrace");
}else{
zTempPrefix = mprintf("%sfossil_server_P%d",
fossil_unicode_to_utf8(zTmpPath), iPort);
}
fossil_print("Temporary files: %s*\n", zTempPrefix);
|
| ︙ | ︙ | |||
1370 1371 1372 1373 1374 1375 1376 |
if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
SERVICE_ALL_ACCESS);
if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
QueryServiceStatus(hSvc, &sstat);
if( sstat.dwCurrentState!=SERVICE_RUNNING ){
fossil_print("Starting service '%s'", zSvcName);
| | | | | | | | 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 |
if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
SERVICE_ALL_ACCESS);
if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
QueryServiceStatus(hSvc, &sstat);
if( sstat.dwCurrentState!=SERVICE_RUNNING ){
fossil_print("Starting service '%s'", zSvcName);
if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
if( !StartServiceW(hSvc, 0, NULL) ){
winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
}
QueryServiceStatus(hSvc, &sstat);
}
while( sstat.dwCurrentState==SERVICE_START_PENDING ||
sstat.dwCurrentState==SERVICE_STOPPED ){
Sleep(100);
fossil_print(".");
QueryServiceStatus(hSvc, &sstat);
}
if( sstat.dwCurrentState==SERVICE_RUNNING ){
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
356 357 358 359 360 361 362 |
}
}else{
nullContent = 1;
}
/* The isWriter flag must be true in order to land the new file */
if( !isWriter ){
| | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
}
}else{
nullContent = 1;
}
/* The isWriter flag must be true in order to land the new file */
if( !isWriter ){
blob_appendf(&pXfer->err,"Write permissions for unversioned files missing");
goto end_accept_unversioned_file;
}
/* Make sure we have a valid g.rcvid marker */
content_rcvid_init(0);
/* Check to see if current content really should be overwritten. Ideally,
|
| ︙ | ︙ | |||
1589 1590 1591 1592 1593 1594 1595 |
xfer.nextIsPrivate = 1;
}
}else
/* pragma NAME VALUE...
**
| | | 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 |
xfer.nextIsPrivate = 1;
}
}else
/* pragma NAME VALUE...
**
** The client issues pragmas to try to influence the behavior of the
** server. These are requests only. Unknown pragmas are silently
** ignored.
*/
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
/* pragma send-private
**
|
| ︙ | ︙ | |||
1853 1854 1855 1856 1857 1858 1859 |
const char *zArg = db_column_text(&q, 1);
i64 iMtime = db_column_int64(&q, 2);
memset(&x, 0, sizeof(x));
url_parse_local(zUrl, URL_OMIT_USER, &x);
if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){
@ pragma link %F(x.canonical) %F(zArg) %lld(iMtime)
}
| | | 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 |
const char *zArg = db_column_text(&q, 1);
i64 iMtime = db_column_int64(&q, 2);
memset(&x, 0, sizeof(x));
url_parse_local(zUrl, URL_OMIT_USER, &x);
if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){
@ pragma link %F(x.canonical) %F(zArg) %lld(iMtime)
}
url_unparse(&x);
}
db_finalize(&q);
}
/* Send the server timestamp last, in case prior processing happened
** to use up a significant fraction of our time window.
*/
|
| ︙ | ︙ | |||
1878 1879 1880 1881 1882 1883 1884 | ** ** Usage: %fossil test-xfer ?OPTIONS? XFERFILE ** ** Pass the sync-protocol input file XFERFILE into the server-side sync ** protocol handler. Generate a reply on standard output. ** ** This command was original created to help debug the server side of | | | 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 | ** ** Usage: %fossil test-xfer ?OPTIONS? XFERFILE ** ** Pass the sync-protocol input file XFERFILE into the server-side sync ** protocol handler. Generate a reply on standard output. ** ** This command was original created to help debug the server side of ** sync messages. The XFERFILE is the uncompressed content of an ** "xfer" HTTP request from client to server. This command interprets ** that message and generates the content of an HTTP reply (without any ** encoding and without the HTTP reply headers) and writes that reply ** on standard output. ** ** One possible usages scenario is to capture some XFERFILE examples ** using a command like: |
| ︙ | ︙ | |||
2575 2576 2577 2578 2579 2580 2581 |
if( iStatus>=4 && uvPullOnly==1 ){
fossil_warning(
"Warning: uv-pull-only \n"
" Unable to push unversioned content because you lack\n"
" sufficient permission on the server\n"
);
uvPullOnly = 2;
| | | 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 |
if( iStatus>=4 && uvPullOnly==1 ){
fossil_warning(
"Warning: uv-pull-only \n"
" Unable to push unversioned content because you lack\n"
" sufficient permission on the server\n"
);
uvPullOnly = 2;
}
if( iStatus<=3 || uvPullOnly ){
db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
}else if( iStatus==4 ){
db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName);
}else if( iStatus==5 ){
db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)",
zName);
|
| ︙ | ︙ | |||
2698 2699 2700 2701 2702 2703 2704 |
** The server can send pragmas to try to convey meta-information to
** the client. These are informational only. Unknown pragmas are
** silently ignored.
*/
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
/* pragma server-version VERSION ?DATE? ?TIME?
**
| | | | 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 |
** The server can send pragmas to try to convey meta-information to
** the client. These are informational only. Unknown pragmas are
** silently ignored.
*/
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
/* pragma server-version VERSION ?DATE? ?TIME?
**
** The server announces to the server what version of Fossil it
** is running. The DATE and TIME are a pure numeric ISO8601 time
** for the specific check-in of the client.
*/
if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
if( xfer.nToken>=5 ){
xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
}
}
/* pragma uv-pull-only
** pragma uv-push-ok
**
** If the server is unwilling to accept new unversioned content (because
** this client lacks the necessary permissions) then it sends a
** "uv-pull-only" pragma so that the client will know not to waste
** bandwidth trying to upload unversioned content. If the server
** does accept new unversioned content, it sends "uv-push-ok".
*/
else if( syncFlags & SYNC_UNVERSIONED ){
if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){
|
| ︙ | ︙ |
Changes to src/zip.c.
| ︙ | ︙ | |||
136 137 138 139 140 141 142 |
return 512;
}
static int archiveDeviceCharacteristics(sqlite3_file *pFile){
return 0;
}
static int archiveOpen(
| | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
return 512;
}
static int archiveDeviceCharacteristics(sqlite3_file *pFile){
return 0;
}
static int archiveOpen(
sqlite3_vfs *pVfs, const char *zName,
sqlite3_file *pFile, int flags, int *pOutFlags
){
static struct sqlite3_io_methods methods = {
1, /* iVersion */
archiveClose,
archiveRead,
archiveWrite,
|
| ︙ | ︙ | |||
245 246 247 248 249 250 251 | ** Append a single file to a growing ZIP archive. ** ** pFile is the file to be appended. zName is the name ** that the file should be saved as. */ static void zip_add_file_to_zip( Archive *p, | | | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
** Append a single file to a growing ZIP archive.
**
** pFile is the file to be appended. zName is the name
** that the file should be saved as.
*/
static void zip_add_file_to_zip(
Archive *p,
const char *zName,
const Blob *pFile,
int mPerm
){
z_stream stream;
int nameLen;
int toOut = 0;
int iStart;
unsigned long iCRC = 0;
|
| ︙ | ︙ | |||
372 373 374 375 376 377 378 | put16(&zExTime[2], 5); blob_append(&toc, zExTime, 9); nEntry++; } static void zip_add_file_to_sqlar( Archive *p, | | | | | | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
put16(&zExTime[2], 5);
blob_append(&toc, zExTime, 9);
nEntry++;
}
static void zip_add_file_to_sqlar(
Archive *p,
const char *zName,
const Blob *pFile,
int mPerm
){
int nName = (int)strlen(zName);
if( p->db==0 ){
assert( p->vfs.zName==0 );
p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p);
p->vfs.iVersion = 1;
p->vfs.szOsFile = sizeof(ArchiveFile);
p->vfs.mxPathname = 512;
p->vfs.pAppData = (void*)p->pBlob;
p->vfs.xOpen = archiveOpen;
p->vfs.xDelete = archiveDelete;
p->vfs.xAccess = archiveAccess;
p->vfs.xFullPathname = archiveFullPathname;
p->vfs.xRandomness = archiveRandomness;
p->vfs.xSleep = archiveSleep;
p->vfs.xCurrentTime = archiveCurrentTime;
p->vfs.xGetLastError = archiveGetLastError;
sqlite3_vfs_register(&p->vfs, 0);
sqlite3_open_v2("file:xyz.db", &p->db,
SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName
);
assert( p->db );
blob_zero(&p->tmp);
sqlite3_exec(p->db,
"PRAGMA page_size=512;"
"PRAGMA journal_mode = off;"
"PRAGMA cache_spill = off;"
"BEGIN;"
"CREATE TABLE sqlar("
"name TEXT PRIMARY KEY, -- name of the file\n"
"mode INT, -- access permissions\n"
"mtime INT, -- last modification time\n"
"sz INT, -- original file size\n"
"data BLOB -- compressed content\n"
");", 0, 0, 0
);
sqlite3_prepare(p->db,
"INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
&p->pInsert, 0
);
assert( p->pInsert );
sqlite3_bind_int64(p->pInsert, 3, unixTime);
blob_zero(p->pBlob);
}
|
| ︙ | ︙ | |||
435 436 437 438 439 440 441 |
sqlite3_bind_int(p->pInsert, 4, 0);
sqlite3_bind_null(p->pInsert, 5);
}else{
sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
if( mPerm==PERM_LNK ){
sqlite3_bind_int(p->pInsert, 2, 0120755);
sqlite3_bind_int(p->pInsert, 4, -1);
| | | | | | | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
sqlite3_bind_int(p->pInsert, 4, 0);
sqlite3_bind_null(p->pInsert, 5);
}else{
sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
if( mPerm==PERM_LNK ){
sqlite3_bind_int(p->pInsert, 2, 0120755);
sqlite3_bind_int(p->pInsert, 4, -1);
sqlite3_bind_text(p->pInsert, 5,
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
);
}else{
unsigned int nIn = blob_size(pFile);
unsigned long int nOut = nIn;
sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
sqlite3_bind_int(p->pInsert, 4, nIn);
zip_blob_minsize(&p->tmp, nIn);
compress( (unsigned char*)
blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
);
if( nOut>=(unsigned long)nIn ){
sqlite3_bind_blob(p->pInsert, 5,
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
);
}else{
sqlite3_bind_blob(p->pInsert, 5,
blob_buffer(&p->tmp), nOut, SQLITE_STATIC
);
}
}
}
sqlite3_step(p->pInsert);
sqlite3_reset(p->pInsert);
}
static void zip_add_file(
Archive *p,
const char *zName,
const Blob *pFile,
int mPerm
){
if( p->eType==ARCHIVE_ZIP ){
zip_add_file_to_zip(p, zName, pFile, mPerm);
}else{
zip_add_file_to_sqlar(p, zName, pFile, mPerm);
}
|
| ︙ | ︙ | |||
784 785 786 787 788 789 790 |
" || substr(blob.uuid, 1, 10)"
" FROM event, blob"
" WHERE event.objid=%d"
" AND blob.rid=%d",
db_get("project-name", "unnamed"), rid, rid
);
}
| | | 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 |
" || substr(blob.uuid, 1, 10)"
" FROM event, blob"
" WHERE event.objid=%d"
" AND blob.rid=%d",
db_get("project-name", "unnamed"), rid, rid
);
}
zip_of_checkin(eType, rid, zOut ? &zip : 0,
zName, pInclude, pExclude, listFlag);
glob_free(pInclude);
glob_free(pExclude);
if( zOut ){
blob_write_to_file(&zip, zOut);
blob_reset(&zip);
}
|
| ︙ | ︙ | |||
945 946 947 948 949 950 951 |
zInclude = P("in");
if( zInclude ) pInclude = glob_create(zInclude);
zExclude = P("ex");
if( zExclude ) pExclude = glob_create(zExclude);
if( zInclude==0 && zExclude==0 ){
etag_check_for_invariant_name(z);
}
| | | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 |
zInclude = P("in");
if( zInclude ) pInclude = glob_create(zInclude);
zExclude = P("ex");
if( zExclude ) pExclude = glob_create(zExclude);
if( zInclude==0 && zExclude==0 ){
etag_check_for_invariant_name(z);
}
if( eType==ARCHIVE_ZIP
&& nName>4
&& fossil_strcmp(&zName[nName-4], ".zip")==0
){
/* Special case: Remove the ".zip" suffix. */
nName -= 4;
zName[nName] = 0;
}else if( eType==ARCHIVE_SQLAR
&& nName>6
&& fossil_strcmp(&zName[nName-6], ".sqlar")==0
){
/* Special case: Remove the ".sqlar" suffix. */
nName -= 6;
zName[nName] = 0;
}else{
|
| ︙ | ︙ |
Changes to test/amend.test.
| ︙ | ︙ | |||
304 305 306 307 308 309 310 |
set t5exp "*"
foreach tag $tagt {
lappend tags -tag $tag
lappend cancels -cancel $tag
}
foreach res $result {
append t1exp ", $res"
| < > > > | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
set t5exp "*"
foreach tag $tagt {
lappend tags -tag $tag
lappend cancels -cancel $tag
}
foreach res $result {
append t1exp ", $res"
append t3exp "Add*tag*\"$res\".*"
append t5exp "Cancel*tag*\"$res\".*"
}
foreach res [lsort -nocase $result] {
append t2exp "sym-$res*"
}
eval fossil amend $HASH $tags
test amend-tag-$tc.1 {[string match "*hash:*$HASH*tags:*$t1exp*" $RESULT]}
fossil tag ls --raw $HASH
test amend-tag-$tc.2 {[string match $t2exp $RESULT]}
fossil timeline -n 1
test amend-tag-$tc.3 {[string match $t3exp $RESULT]}
|
| ︙ | ︙ |
Changes to test/commit-warning.test.
| ︙ | ︙ | |||
170 171 172 173 174 175 176 |
# of source files that MUST NEVER BE TEXT.
#
test_block_in_checkout pre-commit-warnings-fossil-1 {
fossil test-commit-warning --no-settings
} {
test pre-commit-warnings-fossil-1 {[normalize_result] eq \
[subst -nocommands -novariables [string trim {
| < < < < < < < < < < < < | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# of source files that MUST NEVER BE TEXT.
#
test_block_in_checkout pre-commit-warnings-fossil-1 {
fossil test-commit-warning --no-settings
} {
test pre-commit-warnings-fossil-1 {[normalize_result] eq \
[subst -nocommands -novariables [string trim {
1\tcompat/zlib/contrib/blast/test.pk\tbinary data
1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data
1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings
1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data
1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings
1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings
|
| ︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | 1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings 1\tcompat/zlib/win32/zlib.def\tCR/LF line endings 1\tcompat/zlib/zlib.3.pdf\tbinary data 1\tcompat/zlib/zlib.map\tCR/LF line endings 1\tskins/blitz/arrow_project.png\tbinary data 1\tskins/blitz/dir.png\tbinary data 1\tskins/blitz/file.png\tbinary data 1\tskins/blitz/fossil_100.png\tbinary data 1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data 1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data 1\tskins/blitz/rss_20.png\tbinary data | > < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | 1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings 1\tcompat/zlib/win32/zlib.def\tCR/LF line endings 1\tcompat/zlib/zlib.3.pdf\tbinary data 1\tcompat/zlib/zlib.map\tCR/LF line endings 1\textsrc/pikchr.wasm\tbinary data 1\tskins/blitz/arrow_project.png\tbinary data 1\tskins/blitz/dir.png\tbinary data 1\tskins/blitz/file.png\tbinary data 1\tskins/blitz/fossil_100.png\tbinary data 1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data 1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data 1\tskins/blitz/rss_20.png\tbinary data 1\tsrc/alerts/bflat2.wav\tbinary data 1\tsrc/alerts/bflat3.wav\tbinary data 1\tsrc/alerts/bloop.wav\tbinary data 1\tsrc/alerts/plunk.wav\tbinary data 1\tsrc/sounds/0.wav\tbinary data 1\tsrc/sounds/1.wav\tbinary data 1\tsrc/sounds/2.wav\tbinary data |
| ︙ | ︙ |
Changes to test/delta1.test.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 | # Use test script files as the basis for this test. # # For each test, copy the file intact to "./t1". Make # some random changes in "./t2". Then call test-delta on the # two files to make sure that deltas between these two files # work properly. # | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# Use test script files as the basis for this test.
#
# For each test, copy the file intact to "./t1". Make
# some random changes in "./t2". Then call test-delta on the
# two files to make sure that deltas between these two files
# work properly.
#
set filelist [lsort [glob $testdir/*]]
foreach f $filelist {
if {[file isdir $f]} continue
set base [file root [file tail $f]]
set f1 [read_file $f]
write_file t1 $f1
for {set i 0} {$i<100} {incr i} {
write_file t2 [random_changes $f1 1 1 0 0.1]
|
| ︙ | ︙ |
Changes to test/diff.test.
| ︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 |
fossil diff file5.dat
test diff-file5-1 {[normalize_result] eq {Index: file5.dat
==================================================================
--- file5.dat
+++ file5.dat
cannot compute difference between binary files}}
###############################################################################
test_cleanup
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
fossil diff file5.dat
test diff-file5-1 {[normalize_result] eq {Index: file5.dat
==================================================================
--- file5.dat
+++ file5.dat
cannot compute difference between binary files}}
###############################################################################
write_file file6a.dat "{\n \"abc\": {\n \"def\": false,\n \"ghi\": false\n }\n}\n"
write_file file6b.dat "{\n \"abc\": {\n \"def\": false,\n \"ghi\": false\n },\n \"jkl\": {\n \"mno\": {\n \"pqr\": false\n }\n }\n}\n"
fossil xdiff -y -W 16 file6a.dat file6b.dat
test diff-file-6-1 {[normalize_result] eq {========== file6a.dat ===== versus ===== file6b.dat =====
1 { 1 {
2 "abc": { 2 "abc": {
3 "def": false, 3 "def": false,
4 "ghi": false 4 "ghi": false
> 5 },
> 6 "jkl": {
> 7 "mno": {
> 8 "pqr": false
> 9 }
5 } 10 }
6 } 11 }}}
###############################################################################
fossil rm file1.dat
fossil diff -v file1.dat
test diff-deleted-file-1 {[normalize_result] eq {DELETED file1.dat
Index: file1.dat
==================================================================
--- file1.dat
+++ /dev/null
@@ -1,1 +0,0 @@
-test file 1 (one line no term).}}
###############################################################################
write_file file6.dat "test file 6 (one line no term)."
fossil add file6.dat
fossil diff -v file6.dat
test diff-added-file-1 {[normalize_result] eq {ADDED file6.dat
Index: file6.dat
==================================================================
--- /dev/null
+++ file6.dat
@@ -0,0 +1,1 @@
+test file 6 (one line no term).}}
###############################################################################
test_cleanup
|
Changes to test/fake-editor.tcl.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
close $channel
return ""
}
###############################################################################
set fileName [lindex $argv 0]
if {[file exists $fileName]} {
set data [readFile $fileName]
} else {
set data ""
}
| > > > > > | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
close $channel
return ""
}
###############################################################################
set fileName [lindex $argv 0]
if {[regexp {^CYGWIN} $::tcl_platform(os)]} {
# Under Cygwin, we get a Windows path but must access using the unix path.
set fileName [exec cygpath --unix $fileName]
}
if {[file exists $fileName]} {
set data [readFile $fileName]
} else {
set data ""
}
|
| ︙ | ︙ |
Changes to test/json.test.
| ︙ | ︙ | |||
175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
proc test_json_payload {testname okfields badfields} {
test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
}
#### VERSION AKA HAI
# The JSON API generally assumes we have a respository, so let it have one.
test_setup
# Stop backoffice from running during this test as it can cause hangs.
fossil settings backoffice-disable 1
# Check for basic envelope fields in the result with an error
fossil_json -expectError
| > > > > > > > > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
proc test_json_payload {testname okfields badfields} {
test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
}
#### VERSION AKA HAI
# The JSON API generally assumes we have a respository, so let it have one.
# Set FOSSIL_USER to ensure consistent results in "json user list"
set _fossil_user ""
if [info exists env(FOSSIL_USER)] {
set _fossil_user $env(FOSSIL_USER)
}
set ::env(FOSSIL_USER) "JSON-TEST-USER"
test_setup
# Stop backoffice from running during this test as it can cause hangs.
fossil settings backoffice-disable 1
# Check for basic envelope fields in the result with an error
fossil_json -expectError
|
| ︙ | ︙ | |||
274 275 276 277 278 279 280 |
test_json_payload json-login-a {authToken name capabilities loginCookieName} {}
set AuthAnon [dict get $JR payload]
proc test_hascaps {testname need caps} {
foreach n [split $need {}] {
test $testname-$n {[string first $n $caps] >= 0}
}
}
| | | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
test_json_payload json-login-a {authToken name capabilities loginCookieName} {}
set AuthAnon [dict get $JR payload]
proc test_hascaps {testname need caps} {
foreach n [split $need {}] {
test $testname-$n {[string first $n $caps] >= 0}
}
}
test_hascaps json-login-c "hz" [dict get $AuthAnon capabilities]
fossil user new U1 User-1 Uone
fossil user capabilities U1 s
write_file u1 {
{
"command":"login",
"payload":{
|
| ︙ | ︙ | |||
885 886 887 888 889 890 891 |
# Fossil repository db file could not be found.
fossil close
fossil_json HAI -expectError
test json-RC-4102-CLI-exit {$CODE != 0}
test_json_envelope json-RC-4102-CLI-exit {fossil timestamp command procTimeUs \
procTimeMs resultCode resultText} {payload}
test json-RC-4102 {[dict get $JR resultCode] eq "FOSSIL-4102"}
| < > > > > > > | 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 |
# Fossil repository db file could not be found.
fossil close
fossil_json HAI -expectError
test json-RC-4102-CLI-exit {$CODE != 0}
test_json_envelope json-RC-4102-CLI-exit {fossil timestamp command procTimeUs \
procTimeMs resultCode resultText} {payload}
test json-RC-4102 {[dict get $JR resultCode] eq "FOSSIL-4102"}
# FOSSIL-4103 FSL_JSON_E_DB_NOT_VALID
# Fossil repository db file is not valid.
write_file nope.fossil {
This is not a fossil repo. It ought to be a SQLite db with a well-known schema,
but it is actually just a block of text.
}
fossil_json HAI -R nope.fossil -expectError
test json-RC-4103-CLI-exit {$CODE != 0}
if { $JR ne "" } {
test_json_envelope json-RC-4103-CLI {fossil timestamp command procTimeUs \
procTimeMs resultCode resultText} {payload}
test json-RC-4103 {[dict get $JR resultCode] eq "FOSSIL-4103"}
} else {
test json-RC-4103 0 knownBug
}
###############################################################################
test_cleanup
if { $_fossil_user eq "" } {
unset ::env(FOSSIL_USER)
} else {
set ::env(FOSSIL_USER) $_fossil_user
}
|
Changes to test/merge1.test.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
111 - This is line one OF the demo program - 1111
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t23 {
| | | | | | | | | | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
111 - This is line one OF the demo program - 1111
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t23 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line ONE of the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line one OF the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line one OF the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line ONE of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
fossil 3-way-merge t1 t3 t2 a32 -expectError
test merge1-2.1 {[same_file t32 a32]}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-2.2 {[same_file t23 a23]}
write_file_indented t1 {
111 - This is line one of the demo program - 1111
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
|
| ︙ | ︙ | |||
156 157 158 159 160 161 162 |
write_file_indented t3 {
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
| | | | | | | | | | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
write_file_indented t3 {
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t23 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
fossil 3-way-merge t1 t3 t2 a32 -expectError
test merge1-4.1 {[same_file t32 a32]}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-4.2 {[same_file t23 a23]}
write_file_indented t1 {
111 - This is line one of the demo program - 1111
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
|
| ︙ | ︙ | |||
295 296 297 298 299 300 301 |
KLMN
OPQR
STUV
XYZ.
}
write_file_indented t23 {
abcd
| | | | | | | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
KLMN
OPQR
STUV
XYZ.
}
write_file_indented t23 {
abcd
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2)
efgh 2
ijkl 2
mnop 2
qrst
uvwx
yzAB 2
CDEF 2
GHIJ 2
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
efgh
ijkl
mnop
qrst
uvwx
yzAB
CDEF
GHIJ
======= MERGED IN content follows =============================== (line 2)
efgh
ijkl
mnop 3
qrst 3
uvwx 3
yzAB 3
CDEF
GHIJ
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KLMN
OPQR
STUV
XYZ.
}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-7.1 {[same_file t23 a23]}
write_file_indented t2 {
abcd
efgh 2
ijkl 2
mnop
|
| ︙ | ︙ | |||
363 364 365 366 367 368 369 |
KLMN
OPQR
STUV
XYZ.
}
write_file_indented t23 {
abcd
| | | | | | | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
KLMN
OPQR
STUV
XYZ.
}
write_file_indented t23 {
abcd
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2)
efgh 2
ijkl 2
mnop
qrst
uvwx
yzAB 2
CDEF 2
GHIJ 2
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
efgh
ijkl
mnop
qrst
uvwx
yzAB
CDEF
GHIJ
======= MERGED IN content follows =============================== (line 2)
efgh
ijkl
mnop 3
qrst 3
uvwx 3
yzAB 3
CDEF
GHIJ
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KLMN
OPQR
STUV
XYZ.
}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-7.2 {[same_file t23 a23]}
###############################################################################
test_cleanup
|
Changes to test/merge2.test.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 | ############################################################################ # # Tests of the delta mechanism. # test_setup "" | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
############################################################################
#
# Tests of the delta mechanism.
#
test_setup ""
set filelist [lsort [glob $testdir/*]]
foreach f $filelist {
if {[file isdir $f]} continue
set base [file root [file tail $f]]
if {[string match "utf16*" $base]} continue
set f1 [read_file $f]
write_file t1 $f1
for {set i 0} {$i<100} {incr i} {
|
| ︙ | ︙ |
Changes to test/merge3.test.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 | ############################################################################ # # Tests of the 3-way merge # test_setup "" | | | > | | > | > > | > > | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
############################################################################
#
# Tests of the 3-way merge
#
test_setup ""
proc merge-test {testid basis v1 v2 result {fossil_args ""}} {
write_file t1 [join [string trim $basis] \n]\n
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
set x [read_file t4]
regsub -all \
{<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
$x {MINE:} x
regsub -all \
{\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
$x {COM:} x
regsub -all \
{======= MERGED IN content follows =+ \(line \d+\)} \
$x {YOURS:} x
regsub -all \
{>>>>>>> END MERGE CONFLICT >+} \
$x {END} x
set x [split [string trim $x] \n]
set result [string trim $result]
if {$x!=$result} {
protOut " Expected \[$result\]"
protOut " Got \[$x\]"
test merge3-$testid 0
} else {
|
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
| | | | | | | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
} -expectError
merge-test 4 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
1 2 MINE: 3b 4b 5b 6b COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9
} -expectError
merge-test 5 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8 9
} {
1 2 3 4 5c 6c 7c 8 9
} {
1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9
} -expectError
merge-test 6 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9
} {
1 2 3 4 5c 6c 7c 8 9
} {
1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9
} -expectError
merge-test 7 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9
} {
1 2 3 4 5c 6c 7c 8c 9
} {
1 2 MINE: 3b 4b 5b 6b 7 8b COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9
} -expectError
merge-test 8 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9b
} {
1 2 3 4 5c 6c 7c 8c 9
} {
1 2 MINE: 3b 4b 5b 6b 7 8b 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END
} -expectError
merge-test 9 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3 4 5c 6c 7c 8 9
} {
|
| ︙ | ︙ | |||
138 139 140 141 142 143 144 |
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3b 4c 5 6c 7c 8 9
} {
1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
| | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3b 4c 5 6c 7c 8 9
} {
1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
} -expectError
merge-test 12 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b4b 5 6 7 8b 9b
} {
1 2 3b4b 5 6c 7c 8 9
} {
|
| ︙ | ︙ | |||
193 194 195 196 197 198 199 |
1 2 3 4 5 6 7 8 9
} {
1 6 7 8 9
} {
1 2 3 4 9
} {
1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
| | | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
1 2 3 4 5 6 7 8 9
} {
1 6 7 8 9
} {
1 2 3 4 9
} {
1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
} -expectError
merge-test 25 {
1 2 3 4 5 6 7 8 9
} {
1 7 8 9
} {
1 2 3 9
} {
1 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
} -expectError
merge-test 30 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 6 7 9
} {
1 3 4 5 6 7 8 9
|
| ︙ | ︙ | |||
248 249 250 251 252 253 254 |
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 6 7 8 9
} {
1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
| | | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 6 7 8 9
} {
1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
} -expectError
merge-test 35 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 9
} {
1 7 8 9
} {
1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
} -expectError
merge-test 40 {
2 3 4 5 6 7 8
} {
3 4 5 6 7 8
} {
2 3 4 5 6 7
|
| ︙ | ︙ | |||
303 304 305 306 307 308 309 |
2 3 4 5 6 7 8
} {
6 7 8
} {
2 3 4
} {
MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
| | | | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
2 3 4 5 6 7 8
} {
6 7 8
} {
2 3 4
} {
MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
} -expectError
merge-test 45 {
2 3 4 5 6 7 8
} {
7 8
} {
2 3
} {
MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
} -expectError
merge-test 50 {
2 3 4 5 6 7 8
} {
2 3 4 5 6 7
} {
3 4 5 6 7 8
|
| ︙ | ︙ | |||
357 358 359 360 361 362 363 |
2 3 4 5 6 7 8
} {
2 3 4
} {
6 7 8
} {
MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
| | | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
2 3 4 5 6 7 8
} {
2 3 4
} {
6 7 8
} {
MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
} -expectError
merge-test 55 {
2 3 4 5 6 7 8
} {
2 3
} {
7 8
} {
MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
} -expectError
merge-test 60 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3 4 5 6 7 8 9
} {
1 2 3 4 5 6 7 9
|
| ︙ | ︙ | |||
412 413 414 415 416 417 418 |
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 2 3 4 9
} {
1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
| | | | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 2 3 4 9
} {
1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
} -expectError
merge-test 65 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6b 7 8 9
} {
1 2 3 9
} {
1 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
} -expectError
merge-test 70 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 6 7 9
} {
1 2b 3 4 5 6 7 8 9
|
| ︙ | ︙ | |||
467 468 469 470 471 472 473 |
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
| | | | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
} -expectError
merge-test 75 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 9
} {
1 2b 3b 4b 5b 6b 7 8 9
} {
1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9
} -expectError
merge-test 80 {
2 3 4 5 6 7 8
} {
2b 3 4 5 6 7 8
} {
2 3 4 5 6 7
|
| ︙ | ︙ | |||
522 523 524 525 526 527 528 |
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6 7 8
} {
2 3 4
} {
MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
| | | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6 7 8
} {
2 3 4
} {
MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
} -expectError
merge-test 85 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6b 7 8
} {
2 3
} {
MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
} -expectError
merge-test 90 {
2 3 4 5 6 7 8
} {
2 3 4 5 6 7
} {
2b 3 4 5 6 7 8
|
| ︙ | ︙ | |||
577 578 579 580 581 582 583 |
2 3 4 5 6 7 8
} {
2 3 4
} {
2b 3b 4b 5b 6 7 8
} {
MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
| | | | 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 |
2 3 4 5 6 7 8
} {
2 3 4
} {
2b 3b 4b 5b 6 7 8
} {
MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
} -expectError
merge-test 95 {
2 3 4 5 6 7 8
} {
2 3
} {
2b 3b 4b 5b 6b 7 8
} {
MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END
} -expectError
merge-test 100 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3 4 5 7 8 9 a b c d e
} {
1 2b 3 4 5 7 8 9 a b c d e
|
| ︙ | ︙ | |||
623 624 625 626 627 628 629 |
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 9b a b c d e
} {
1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END
| | | | 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 9b a b c d e
} {
1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END
} -expectError
merge-test 104 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b a b c d e
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 MINE: 9b a b c d e COM: 9 YOURS: 9b END
} -expectError
###############################################################################
test_cleanup
|
Changes to test/merge4.test.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 | ############################################################################ # # Tests of the 3-way merge # test_setup "" | | | | | | | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
############################################################################
#
# Tests of the 3-way merge
#
test_setup ""
proc merge-test {testid basis v1 v2 result1 result2 {fossil_args ""}} {
write_file t1 [join [string trim $basis] \n]\n
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
set x [read_file t4]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
set x [split [string trim $x] \n]
set y [read_file t5]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
set y [split [string trim $y] \n]
set result1 [string trim $result1]
if {$x!=$result1} {
protOut " Expected \[$result1\]"
protOut " Got \[$x\]"
test merge4-$testid 0
|
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
1 2b 3b 4b 5 6b 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
1 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < 9
} {
1 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < 9
| | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
1 2b 3b 4b 5 6b 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
1 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < 9
} {
1 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < 9
} -expectError
merge-test 1001 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4 5 6 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
2b 3b 4b 5 6b 7b 8b
} {
2 3 4c 5c 6c 7 8
} {
> 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 <
} {
> 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b <
| | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
2b 3b 4b 5 6b 7b 8b
} {
2 3 4c 5c 6c 7 8
} {
> 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 <
} {
> 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b <
} -expectError
merge-test 1003 {
2 3 4 5 6 7 8
} {
2b 3b 4 5 6 7b 8b
} {
2 3 4c 5c 6c 7 8
} {
|
| ︙ | ︙ |
Changes to test/merge5.test.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 | # http://www.hwaci.com/drh/ # ############################################################################ # # Tests of the "merge" command # | > | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# http://www.hwaci.com/drh/
#
############################################################################
#
# Tests of the "merge" command
#
if {! $::QUIET} {
puts "Skipping Merge5 tests"
}
protOut {
fossil sqlite3 --no-repository reacts badly to SQL dumped from
repositories created from fossil older than version 2.0.
}
test merge5-sqlite3-issue false knownBug
test_cleanup_then_return
|
| ︙ | ︙ |
Changes to test/merge_renames.test.
| ︙ | ︙ | |||
260 261 262 263 264 265 266 | fossil update trunk write_file f1 "f1.2" fossil add f1 fossil commit -b b2 -m "add f1" fossil update trunk fossil merge b1 | | > | | > | | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
fossil update trunk
write_file f1 "f1.2"
fossil add f1
fossil commit -b b2 -m "add f1"
fossil update trunk
fossil merge b1
fossil merge b2 -expectError
test_status_list merge_renames-8-1 $RESULT {
MERGE f1
WARNING: 1 merge conflicts
}
fossil revert
fossil merge --integrate b1
fossil merge b2 -expectError
test_status_list merge_renames-8-2 $RESULT {
MERGE f1
WARNING: 1 merge conflicts
}
#############################################
# Test 9 #
# Merging a delete/rename/add combination #
#############################################
|
| ︙ | ︙ | |||
306 307 308 309 310 311 312 | ADDED f1 } test_status_list merge_renames-9-1 $RESULT $expectedMerge fossil changes test_status_list merge_renames-9-2 $RESULT " MERGED_WITH [commit_id b] ADDED_BY_MERGE f1 | | | | | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
ADDED f1
}
test_status_list merge_renames-9-1 $RESULT $expectedMerge
fossil changes
test_status_list merge_renames-9-2 $RESULT "
MERGED_WITH [commit_id b]
ADDED_BY_MERGE f1
RENAMED f1 -> f2
DELETED f2 -> f2 (overwritten by rename)
"
test_file_contents merge_renames-9-3 f1 "f1.1"
test_file_contents merge_renames-9-4 f2 "f1"
# Undo and ensure a dry run merge results in no changes
fossil undo
test_status_list merge_renames-9-5 $RESULT {
UNDO f1
UNDO f2
}
fossil merge -n b -expectError
test_status_list merge_renames-9-6 $RESULT "
$expectedMerge
REMINDER: this was a dry run - no files were actually changed.
"
test merge_renames-9-7 {[fossil changes] eq ""}
###################################################################
|
| ︙ | ︙ | |||
366 367 368 369 370 371 372 |
test_status_list merge_renames-10-4 $RESULT {
RENAME f1 -> f2
RENAME f2 -> f1
}
test_file_contents merge_renames-10-5 f1 "f1"
test_file_contents merge_renames-10-6 f2 "f2"
test_status_list merge_renames-10-7 [fossil changes] "
| | | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
test_status_list merge_renames-10-4 $RESULT {
RENAME f1 -> f2
RENAME f2 -> f1
}
test_file_contents merge_renames-10-5 f1 "f1"
test_file_contents merge_renames-10-6 f2 "f2"
test_status_list merge_renames-10-7 [fossil changes] "
RENAMED f1 -> f2
RENAMED f2 -> f1
BACKOUT [commit_id trunk]
"
fossil commit -m "swap back" ;# V
fossil merge b
test_status_list merge_renames-10-8 $RESULT {
UPDATE f1
|
| ︙ | ︙ | |||
493 494 495 496 497 498 499 | ADD f2 } fossil merge trunk fossil commit -m "merge trunk" --tag c4 fossil mv --hard f2 f2n test_status_list merge_renames-13-3 $RESULT " RENAME f2 f2n | | | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
ADD f2
}
fossil merge trunk
fossil commit -m "merge trunk" --tag c4
fossil mv --hard f2 f2n
test_status_list merge_renames-13-3 $RESULT "
RENAME f2 f2n
MOVED_FILE [file normalize $repoDir]/f2
"
fossil commit -m "renamed f2->f2n" --tag c5
fossil update trunk
fossil merge b
test_status_list merge_renames-13-4 $RESULT {ADDED f2n}
fossil commit -m "merge f2n" --tag m1 --tag c6
|
| ︙ | ︙ |
Changes to test/merge_warn.test.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 | write_file f4 "f4" fossil add f4 fossil commit -m "add f4" fossil update trunk write_file f1 "f1.1" write_file f3 "f3.1" | | > | | | | < | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
write_file f4 "f4"
fossil add f4
fossil commit -m "add f4"
fossil update trunk
write_file f1 "f1.1"
write_file f3 "f3.1"
fossil merge --integrate mrg -expectError
test_status_list merge_warn-1 $RESULT {
WARNING: 1 unmanaged files were overwritten
WARNING: 2 merge conflicts
DELETE f1
MERGE f2
ADDED f3 (overwrites an unmanaged file), original copy backed up locally
WARNING: local edits lost for f1
}
test merge_warn-2 {
[string first "ignoring --integrate: mrg is not a leaf" $RESULT]>=0
}
###############################################################################
|
| ︙ | ︙ |
Changes to test/revert.test.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
# Test with a single filename argument
#
revert-test 1-2 f0 {
UNMANAGE f0
} -changes {
DELETED f1
EDITED f2
| | | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# Test with a single filename argument
#
revert-test 1-2 f0 {
UNMANAGE f0
} -changes {
DELETED f1
EDITED f2
RENAMED f3 -> f3n
} -addremove {
ADDED f0
} -exists {f0 f2 f3n} -notexists f3
revert-test 1-3 f1 {
REVERT f1
} -changes {
ADDED f0
EDITED f2
RENAMED f3 -> f3n
} -exists {f0 f1 f2 f3n} -notexists f3
revert-test 1-4 f2 {
REVERT f2
} -changes {
ADDED f0
DELETED f1
RENAMED f3 -> f3n
} -exists {f0 f2 f3n} -notexists {f1 f3}
# Both files involved in a rename are reverted regardless of which filename
# is used as an argument to 'fossil revert'
#
revert-test 1-5 f3 {
REVERT f3
|
| ︙ | ︙ |
Added test/rewrite-test-output.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
#!/usr/bin/env tclsh
# Script to anonymise test results for comparison.
# - Replaces hashes, pids and similar with fixed strings
# - Rewrites temporary paths to standardise them in output
# Pick up options
set EXTRA 0
set i [lsearch $argv -extra]
while { $i >= 0 } {
incr EXTRA
set argv [lreplace $argv $i $i]
set i [lsearch $argv -extra]
}
# With no arguments or "-", use stdin.
set fname "-"
if { [llength $argv] > 0 } {
set fname [lindex $argv 0]
}
# Any -options, or an empty first argument, is an error.
if { [llength $argv] > 1 || [regexp {^-.+} $fname] } {
puts stderr "Error: argument error"
puts stderr "usage: \[-extra\] [file tail $argv0] ?FILE"
puts stderr " Rewrite test output to ease comparison of outputs."
puts stderr " With -extra, more output is rewritten as is summaries"
puts stderr " to make diff(1) mor euseful across runs and platforms."
exit 1
} elseif { $fname ne "-" && ! [file exists $fname] } {
puts stderr "File does not exist: '$fname'"
exit 1
}
proc common_rewrites { line testname } {
# Normalise the fossil commands with path as just fossil
regsub {^(?:[A-Z]:)?/.*?/fossil(?:\.exe)? } $line {fossil } line
if {[string match "Usage: *" $line]} {
regsub {^(Usage: )/.*?/fossil(?:\.exe)? } $line {\1fossil } line
regsub {^(Usage: )[A-Z]:\\.*?\\fossil(?:\.exe)? } $line {\1fossil } line
}
# Accept 40 and 64 byte hashes as such
regsub -all {[[:<:]][0-9a-f]{40}[[:>:]]} $line HASH line
regsub -all {[[:<:]][0-9a-f]{64}[[:>:]]} $line HASH line
# Date and time
regsub -all {[[:<:]]\d{4}-\d\d-\d\d \d\d:\d\d:\d\d[[:>:]]} $line {YYYY-mm-dd HH:MM:SS} line
if { [lsearch -exact {"amend" "wiki"} $testname] >= 0 } {
# With embedded T and milliseconds
regsub { \d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}$} $line { YYYY-mm-ddTHH:MM:SS.NNN} line
}
if { [lsearch -exact {"amend" "th1-hooks" "wiki"} $testname] >= 0 } {
regsub {[[:<:]]\d{4}-\d\d-\d\d[[:>:]]} $line {YYYY-mm-dd} line
}
# Timelines have HH:MM:SS [HASH], but don't mess with the zero'ed version.
regsub {^(?!00:00:00 \[0000000000\])\d\d:\d\d:\d\d \[[0-9a-f]{10}\] } $line {HH:MM:SS [HASH] } line
# Temporary directories
regsub -all {(?:[A-Z]:)?/.*?/repo_\d+/\d+_\d+} $line {/TMP/repo_PID/SEC_SEQ} line
# Home directories only seem present with .fossil or _fossil. Simplify to .fossil.
regsub -all {(?:[A-Z]:)?/.*?/home_\d+/[._]fossil[[:>:]]} $line {/TMP/home_PID/.fossil} line
# Users in output
regsub { (\(user: )[^\)]*\)$} $line { \1USER)} line
return $line
}
#
# tests/tests_unix/tests_windows contain tuples of
#
# 1. A regular expression to match current line
# 2. A substitution for the current line
#
# Some common patterns applicable to multiples tests are appended below.
#
# The common_rewrites procedure is run first, so use e.g. HASH as needed.
#
dict set tests "amend" {
{^(fossil artifact) [0-9a-f]{10}}
{\1 HASH}
{^U [^ ]+$}
{U USER}
{^Z [0-9a-f]{32}$}
{Z CHECKSUM}
{^(ed -s \./ci-comment-).*?(\.txt)$}
{\1UNIQ\2}
{^(fossil amend HASH -date \{?)\d\d/\d\d/\d{4}}
{\1dd/mm/YYYY}
{^(fossil amend HASH -date \{.* )\d{4}(\})$}
{\1YYYY\2}
{^(fossil amend HASH -date \{.* )\d\d:}
{\1HH:}
{^(fossil amend HASH -date \{)[A-Z][a-z]{2} [A-Z][a-z]{2} [ 0-9]\d }
{\1Day Mon dd }
{(\] Edit \[)[0-9a-f]{16}.[0-9a-f]{10}(\]: )}
{\1HASH1|HASH2\2}
{(\] Edit \[.*?&dp=)[0-9a-f]{16}}
{\1dp=HASH}
}
dict set tests "cmdline" {
{^(fossil test-echo --args) .*/}
{\1 /TMP/}
{^(g\.nameOfExe =) \[[^\]]+[/\\]fossil(?:\.exe)?\]$}
{\1 [/PATH/FOSSILCMD]}
{^(argv\[0\] =) \[[^\]]+[/\\]fossil(?:\.exe)?\]$}
{\1 [/PATH/FOSSILCMD]}
}
dict set tests "contains-selector" {
{^(fossil test-contains-selector) .*?/(compare-selector.css )}
{\1 /TMP/\2}
}
dict set tests "json" {
{^(Content-Length) \d+$}
{\1 LENGTH}
{^(Cookie: fossil-)[0-9a-f]{16}(\=HASH%2F)\d+\.\d+(%2Fanonymous)$}
{\1CODE\2NOW\3}
{^(GET /json/cap\?authToken\=HASH)/\d+\.\d+/(anonymous )}
{\1/NOW/\2}
{^(Cookie: fossil-)[0-9a-f]{16}\=[0-9A-F]{50}%2F[0-9a-f]{16}%2F(.*)$}
{\1CODE=SHA1%2FCODE%2F\2}
{("authToken":").+?(")}
{\1AUTHTOKEN\2}
{("averageArtifactSize":)\d+()}
{\1SIZE\2}
{("compiler":").+?(")}
{\1COMPILER\2}
{("loginCookieName":").+?(")}
{\1COOKIE\2}
{("manifestVersion":"\[)[0-9a-f]{10}(\]")}
{\1HASH\2}
{("manifestYear":")\d{4}(")}
{\1YYYY\2}
{("name":").+?(")}
{\1NAME\2}
{("password":")[0-9a-f]+(")}
{\1PASSWORD\2}
{("projectCode":")[0-9a-f]{40}(")}
{\1HASH\2}
{("procTimeMs":)\d+}
{\1MSEC}
{("procTimeUs":)\d+}
{\1USEC}
{("releaseVersion":")\d+\.\d+(")}
{\1VERSION\2}
{("releaseVersionNumber":")\d+(")}
{\1VERSION_NUMBER\2}
{("timestamp":)\d+}
{\1SEC}
{("seed":)\d+()}
{\1SEED\2}
{("uid":)\d+()}
{\1UID\2}
{("uncompressedArtifactSize":)\d+()}
{\1SIZE\2}
{("user":").+?(")}
{\1USER\2}
{("version":"YYYY-mm-dd HH:MM:SS )\[[0-9a-f]{10}\] \(\d+\.\d+\.\d+\)"}
{\1[HASH] (major.minor.patch)}
{^(Date:) [A-Z][a-z]{2}, \d\d? [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d [-+]\d{4}$}
{\1 Day, dd Mon YYYY HH:MM:SS TZ}
}
dict set tests "merge_renames" {
{^(size: {7})\d+( bytes)$}
{\1N\2}
{^(type: {7}Check-in by ).+?( on YYYY-mm-dd HH:MM:SS)$}
{\1USER\2}
}
dict set tests "set-manifest" {
{^(project-code: )[0-9a-f]{40}$}
{\1HASH} line
}
dict set tests "stash" {
{^(---|\+\+\+) NUL$}
{\1 /dev/null}
{(^ 1: \[)[0-9a-f]{14}(\] on YYYY-mm-dd HH:MM:SS)$}
{\1HASH\2}
{(^ 1: \[)[0-9a-f]{14}(\] from YYYY-mm-dd HH:MM:SS)$}
{\1HASH\2}
}
dict set tests "th1" {
{^(fossil test-th-source) (?:[A-Z]:)?.*?/(th1-)\d+([.]th1)$}
{\1 /TMP/\2PID\3}
{^(?:[A-Z]:)?[/\\].*?[/\\]fossil(?:\.exe)?$}
{/PATH/FOSSILCMD}
{[[:<:]](Content-Security-Policy[[:>:]].*'nonce-)[0-9a-f]{48}(';)}
{\1NONCE\2}
{^(<link rel="stylesheet" href="/style.css\?id=)[0-9a-f]+(" type="text/css">)$}
{\1ID\2}
{^\d+\.\d{3}(s by)$}
{N.MMM\1}
{^(Fossil) \d+\.\d+ \[[0-9a-f]{10}\] (YYYY-mm-dd HH:MM:SS)$}
{\1 N.M [HASH] \2}
{^(<script nonce=")[0-9a-f]{48}(">/\* style\.c:)\d+}
{\1NONCE\2LINENO}
}
dict set tests "th1-docs" {
{^(check-ins: ).*}
{\1COUNT}
{^(local-root: ).*}
{\1/PATH/}
{^(repository: ).*}
{\1/PATH/REPO}
{^(comment: ).*}
{\1/COMMENT/}
{^(tags: ).*}
{\1/TAGS/}
{(--ipaddr 127\.0\.0\.1) .*? (--localauth)}
{\1 REPO \2}
}
dict set tests "th1-hooks" {
{^(?:/[^:]*/fossil|[A-Z]:\\[^:]*\\fossil\.exe): (unknown command:|use \"help\")}
{fossil: \1}
{^(project-code: )[0-9a-f]{40}$}
{\1HASH}
}
dict set tests "th1-tcl" {
{^(fossil test-th-render --open-config) \{?.*?[/\\]test[/\\]([^/\\]*?)\}?$}
{\1 /CHECKOUT/test/\2}
{^(fossil)(?:\.exe)?( 3 \{test-th-render --open-config )(?:\{[A-Z]:)?[/\\].*?[/\\]test[/\\](th1-tcl9.txt\})\}?$}
{\1\2/CHECKOUT/test/\3}
{^\d{10}$}
{SEC}
}
dict set tests "unversioned" {
{^(fossil user new uvtester.*) \d+$}
{\1 PASSWORD}
{^(fossil .*http://uvtester:)\d+(@localhost:)\d+}
{\1PASSWORD\2PORT}
{^(Pull from http://uvtester@localhost:)\d+}
{\1PORT}
{^(ERROR \(1\): Usage:) .*?[/\\]fossil(?:\.exe)? (unversioned)}
{\1 /PATH/fossil \2}
{^(Started Fossil server, pid \")\d+(\", port \")\d+}
{\1PID\2PORT}
{^(Now in client directory \")(?:[A-Z]:)?/.*?/uvtest_\d+_\d+\"}
{\1/TMP/uvtest_SEC_SEQ}
{^(Stopped Fossil server, pid \")\d+(\", using argument \")(?:\d+|[^\"]*\.stopper)(\")}
{\1PID\2PID_OR_SCRIPT\3}
{^(This is unversioned file #4\.) \d+ \d+}
{\1 PID SEC}
{^(This is unversioned file #4\. PID SEC) \d+ \d+}
{\1 PID SEC}
{^[0-9a-f]{12}( YYYY-mm-dd HH:MM:SS *)(\d+)( *)\2( unversioned4.txt)$}
{HASH \1SZ\3SZ\4}
{^[0-9a-f]{40}$}
{\1HASH}
{^((?:Clone|Pull)? done, wire bytes sent: )\d+( received: )\d+( remote: )(?:127\.0.0\.1|::1)$}
{\1SENT\2RECV\3LOCALIP}
{^(project-id: )[0-9a-f]{40}$}
{\1HASH}
{^(server-id: )[0-9a-f]{40}$}
{\1HASH}
{^(admin-user: uvtester \(password is ").*("\))$}
{\1PASSWORD\2}
{^(repository: ).*?/uvtest_\d+_\d+/(uvrepo.fossil)$}
{\1/TMP/uvtest_SEC_SEQ/\2}
{^(local-root: ).*?/uvtest_\d+_\d+/$}
{\1/TMP/uvtest_SEC_SEQ/}
{^(project-code: )[0-9a-f]{40}$}
{\1HASH}
}
dict set tests "utf" {
{^(fossil test-looks-like-utf) (?:[A-Z]:)?/.*?/([^/\\]*?)\}?$}
{\1 /TMP/test/\2}
{^(File ")(?:[A-Z]:)?/.*?/(utf-check-\d+-\d+-\d+-\d+.jnk" has \d+ bytes\.)$}
{\1/TMP/\2}
}
dict set tests "wiki" {
{^(fossil (?:attachment|wiki) .*--technote )[0-9a-f]{21}$}
{\1HASH}
{^(fossil (?:attachment|wiki) .* (?:a13|f15|fa) --technote )[0-9a-f]+$}
{\1ID}
{^[0-9a-f]{40}( YYYY-mm-dd HH:MM:SS)}
{HASH\1}
{(\] Add attachment \[/artifact/)[0-9a-f]{16}(|)}
{\1HASH\2}
{ (to tech note \[/technote/)[0-9a-f]{16}\|[0-9a-f]{10}(\] \(user:)}
{\1HASH1|HASH2\2}
{^(ambiguous tech note id: )[0-9a-f]+$}
{\1ID}
{^(Attached fa to tech note )[0-9a-f]{21}(?:[0-9a-f]{19})?\.$}
{\1HASH.}
{^(Date:) [A-Z][a-z]{2}, \d\d? [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d [-+]\d{4}$}
{\1 Day, dd Mon YYYY HH:MM:SS TZ}
{(Content-Security-Policy.*'nonce-)[0-9a-f]{48}(';)}
{\1NONCE\2}
{^(<link rel="stylesheet" href="/style.css\?id=)[0-9a-f]+(" type="text/css">)$}
{\1ID\2}
{^(added by )[^ ]*( on)$}
{\1USER\2}
{^(<script nonce=['\"])[0-9a-f]{48}(['\"]>/\* [a-z]+\.c:)\d+}
{\1NONCE\2LINENO}
{^(<script nonce=['\"])[0-9a-f]{48}(['\"]>)$}
{\1NONCE\2}
{^(projectCode: ")[0-9a-f]{40}(",)$}
{\1HASH\2}
{^\d+\.\d+(s by)$}
{N.SUB\1}
{^(window\.fossil.version = ")\d+\.\d+ \[[0-9a-f]{10}\] (YYYY-mm-dd HH:MM:SS(?: UTC";)?)$}
{\1N.M [HASH] \2}
{^(Fossil) \d+\.\d+ \[[0-9a-f]{10}\]( YYYY-mm-dd HH:MM:SS)$}
{\1 N.M [HASH]\2}
{^(type: Wiki-edit by ).+?( on YYYY-mm-dd HH:MM:SS)$$}
{\1USER\2}
{^(size: )\d+( bytes)$}
{\1N\2}
{^U [^ ]+$}
{U USER}
{^Z [0-9a-f]{32}$}
{Z CHECKSUM}
}
#
# Some pattersn are used in multiple groups
#
set testnames {"th1" "th1-docs" "th1-hooks"}
set pat {^((?:ERROR \(1\): )?/[*]{5} Subprocess) \d+ (exit)}
set sub {\1 PID \2}
foreach testname $testnames {
dict lappend tests $testname $pat $sub
}
set testnames {"th1-docs" "th1-hooks"}
set pat {(?:[A-Z]:)?/.*?/(test-http-(?:in|out))-\d+-\d+-\d+(\.txt)}
set sub {/TMP/\1-PID-SEQ-SEC\2}
foreach testname $testnames {
dict lappend tests $testname $pat $sub
}
set testnames {"json" "th1" "wiki"}
set pat {^(Content-Length:) \d+$}
set sub {\1 LENGTH}
foreach testname $testnames {
dict lappend tests $testname $pat $sub
}
set testnames {"th1" "wiki"}
set pat {^\d+\.\d+(s by)$}
set sub {N.SUB\1}
foreach testname $testnames {
dict lappend tests $testname $pat $sub
}
#
# Main
#
if { $fname eq "-" } {
set fd stdin
} else {
set fd [open $fname r]
}
# Platforms we detect
set UNKOWN_PLATFORM 0
set UNIX 1
set WINDOWS 2
set CYGWIN 3
# One specific wiki test creates repetitive output of varying length
set wiki_f13_cmd1 "fossil wiki create {timestamp of 2399999} f13 --technote 2399999"
set wiki_f13_cmd2 "fossil wiki list --technote --show-technote-ids"
set wiki_f13_cmd3 "fossil wiki export a13 --technote ID"
set collecting_f3 0
set collecting_f3_verbose 0
# Collected lines for summaries in --extra mode
set amend_ed_lines [list]
set amend_ed_failed 0
set symlinks_lines [list]
set symlinks_failed 0
set test_simplify_name_lines [list]
set test_simplify_name_failed 0
# State information s we progress
set check_json_empty_line 0
set lineno 0
set platform $UNKOWN_PLATFORM
set prev_line ""
set testname ""
while { [gets $fd line] >= 0 } {
incr lineno
if { $lineno == 1 } {
if { [string index $line 0] in {"\UFFEF" "\UFEFF"} } {
set line [string range $line 1 end]
}
}
# Remove RESULT status while matching (inserted again in output).
# If collecting lines of output, include $result_prefix as needed.
regexp {^(RESULT \([01]\): )?(.*)} $line match result_prefix line
if { [regsub {^\*{5} ([^ ]+) \*{6}$} $line {\1} new_testname] } {
# Pick up test name for special handling below
set testname "$new_testname"
} elseif { [regexp {^\*{5} End of } $line] } {
# Test done. Handle --extra before resetting.
if { $EXTRA } {
if { $testname eq "symlinks" } {
if { $symlinks_failed } {
foreach l $symlinks_lines {
puts "$l"
}
} else {
puts "All symlinks tests OK (not run on Windows)"
}
}
regsub {(: )\d+( errors so far)} $line {\1N\2} line
}
set testname ""
} elseif { $testname ne "" } {
if { $platform == $UNKOWN_PLATFORM } {
if { [regexp {^[A-Z]:/.*?/fossil\.exe } $line] } {
set platform $WINDOWS
} elseif { [regexp {^/.*?/fossil\.exe } $line] } {
# No drive, but still .exe - must be CYGWIN
set platform $CYGWIN
} elseif { [regexp {^/.*?/fossil } $line] } {
set platform $UNIX
}
}
# Do common and per testname rewrites
set line [common_rewrites $line $testname]
if { [dict exists $tests $testname] } {
foreach {pat sub} [dict get $tests $testname] {
regsub $pat $line $sub line
}
}
# On Windows, HTTP headers may get printed with an extra newline
if { $testname eq "json" } {
if { $check_json_empty_line == 1 } {
if { "$result_prefix$line" eq "" } {
set check_json_empty_line 2
continue
}
set check_json_empty_line 0
} elseif { [regexp {^(?:$|GET |POST |[A-Z][A-Za-z]*(?:-[A-Z][A-Za-z]*)*: )} $line] } {
set check_json_empty_line 1
} else {
if { $check_json_empty_line == 2 } {
# The empty line we skipped was meant to be followed by a new
# HTTP header or empty line, but it was not.
puts ""
}
set check_json_empty_line 0
}
}
# Summarise repetitive output of varying length for f13 in wiki test
if { $testname eq "wiki" } {
if { $collecting_f3 == 2 } {
if { $collecting_f3_verbose == 1 && [regexp {^HASH } $line] } {
incr collecting_f3_verbose
} elseif { $line eq $wiki_f13_cmd3 } {
incr collecting_f3
puts "\[...\]"
} else {
continue
}
} elseif { $collecting_f3 == 1 } {
if { $line eq $wiki_f13_cmd2 } {
incr collecting_f3
} elseif { $collecting_f3_verbose == 0 } {
incr collecting_f3_verbose
}
} elseif { $line eq $wiki_f13_cmd1 } {
incr collecting_f3
}
}
if { $EXTRA } {
if { $line eq "ERROR (0): " && $platform == $WINDOWS } {
if { [string match "fossil http --in *" $prev_line] } {
continue
}
}
if { $testname eq "amend" } {
# The amend-comment-5.N tests are not run on Windows
if { $line eq "fossil amend {} -close" } {
if { $amend_ed_failed } {
foreach l $amend_ed_lines {
puts "$l"
}
} else {
puts "All amend tests based on ed -s OK (not run on Windows)"
}
set amend_ed_lines [list]
} elseif { [llength $amend_ed_lines] } {
if { [regexp {^test amend-comment-5\.\d+ (.*)} $line match status] } {
lappend amend_ed_lines "$result_prefix$line"
if { $status ne "OK" } {
incr amend_ed_failed
}
continue
} elseif { [string range $line 0 4] eq "test " } {
# Handle change in tests by simply emitting what we got
foreach l $amend_ed_lines {
puts "$l"
}
set amend_ed_lines [list]
} else {
lappend amend_ed_lines "$result_prefix$line"
continue
}
} elseif { $line eq "fossil settings editor {ed -s}" } {
lappend amend_ed_lines "$result_prefix$line"
continue
}
} elseif { $testname eq "cmdline" } {
if { [regexp {^(fossil test-echo) (.*)} $line match test args] } {
if { ($platform == $UNIX && $args in {"*" "*.*"})
|| ($platform == $WINDOWS && $args eq "--args /TMP/fossil-cmd-line-101.txt")
|| ($platform == $CYGWIN && $args in {"*" "*.*"}) } {
set line "$test ARG_FOR_PLATFORM"
}
}
} elseif { $testname eq "commit-warning" } {
if { [regexp {^(micro-smile|pale facepalm) .*} $line match desc] } {
set line "$desc PLATFORM_SPECIFIC_BYTES"
}
} elseif { $testname eq "file1" } {
# test-simplify-name with question marks is specific to Windows
# They all immediately preceed "fossil test-relative-name --chdir . ."
if { $line eq "fossil test-relative-name --chdir . ." } {
if { $test_simplify_name_failed } {
foreach l $test_simplify_name_lines {
puts "$l"
}
} else {
puts "ALL Windows specific test-relative-name tests OK (if on Windows)"
}
set test_simplify_name_lines [list]
} elseif { [regexp {^fossil test-simplify-name .*([/\\])\?\1} $line] } {
lappend test_simplify_name_lines $line
continue
} elseif { [llength $test_simplify_name_lines] } {
if { [regexp {^test simplify-name-\d+ (.*)} $line match status] } {
if { $status ne "OK" } {
incr test_simplify_name_failed
}
}
lappend test_simplify_name_lines "$result_prefix$line"
continue
}
} elseif { $testname eq "settings-repo" } {
if { [regexp {^fossil test-th-eval (?:--open-config )?\{setting case-sensitive\}$} $prev_line] } {
if { ($platform == $UNIX && $line eq "on")
|| ($platform == $WINDOWS && $line eq "off")
|| ($platform == $CYGWIN && $line eq "off")
} {
set line "EXPECTED_FOR_PLATFORM"
}
}
} elseif { $testname eq "symlinks" } {
# Collect all lines and post-process at the end
lappend symlinks_lines "$result_prefix$line"
if { [regexp {^test symlinks-[^ ]* (.*)} $line match status] } {
if { $status ne "OK" } {
#TODO: incr symlinks_failed
}
}
continue
} elseif { $testname in {"th1" "th1-docs" "th1-hooks"} } {
# Special case that spans a couple of tests
# "Subprocess PID exit(0)" is sent on stderr on Unix. On Windows, there is no output
if { [regexp {^(ERROR \(1\): )?/\*{5} Subprocess PID exit\(0\) \*{5}/$} $line match prefix] } {
if { $prefix eq "" } {
continue
} elseif { $prefix eq "ERROR (1): " } {
set line "RESULT (0): "
}
} elseif { $testname eq "th1" } {
if { [regexp {^fossil test-th-eval --vfs ([^ ]+) \{globalState vfs\}$} $line match vfs] } {
if { ($platform == $UNIX && $vfs == "unix-dotfile")
|| ($platform == $WINDOWS && $vfs == "win32-longpath")
|| ($platform == $CYGWIN && $vfs == "win32-longpath") } {
regsub $vfs $line {EXEPECTED_VFS} line
}
} elseif { $prev_line eq "fossil test-th-eval --vfs EXEPECTED_VFS {globalState vfs}" } {
# Replace $vfs from previous line
regsub "^$vfs\$" $line {EXEPECTED_VFS} line
} elseif { $prev_line eq "fossil test-th-eval {set tcl_platform(platform)}" } {
if { $platform == $UNIX } {
regsub {^unix$} $line {EXPECTED_PLATFORM} line
} elseif { $platform == $WINDOWS } {
regsub {^windows$} $line {EXPECTED_PLATFORM} line
} elseif { $platform == $CYGWIN } {
regsub {^unix$} $line {EXPECTED_PLATFORM} line
}
} elseif { [string match "fossil test-th-eval --th-trace *" $prev_line] } {
if { ($result_prefix eq "RESULT (1): " && $line eq "")
|| ($result_prefix eq "" && $line eq "ERROR (0): ") } {
set result_prefix ""
set line "RESULT (0): / ERROR (1): "
}
}
} elseif { $testname eq "th1-docs" } {
# In th1-docs, the fossil check-out is exposed in various states.
regsub {(^project-code:) CE59BB9F186226D80E49D1FA2DB29F935CCA0333} $line {\1 HASH} line
if { [regexp {^merged-from: HASH YYYY-mm-dd HH:MM:SS UTC$} $line] } {
continue
}
}
}
}
} elseif { $EXTRA } {
# Fix up summaries to be generic and easy to diff(1)
if { [regsub {(^\*{5} (Final|Ignored) results: )\d+} $line {\1N} line] } {
regsub {\d+} $line {N} line
} elseif { [regexp {^(\*{5} (?:Considered failure|Ignored failure|Skipped test))s: (.*)} $line match desc vals] } {
if { $vals ne ""} {
foreach val [split $vals " "] {
puts "$desc: $val"
}
continue
}
}
}
# Not exactly correct if we continue'd, but OK for the purpose
set prev_line "$result_prefix$line"
puts "$prev_line"
}
|
Changes to test/set-manifest.test.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 |
test_setup
#### Verify classic behavior of the manifest setting
# Setting is off by default, and there are no extra files.
fossil settings manifest
test "set-manifest-1" {[regexp {^manifest *$} $RESULT]}
| | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
test_setup
#### Verify classic behavior of the manifest setting
# Setting is off by default, and there are no extra files.
fossil settings manifest
test "set-manifest-1" {[regexp {^manifest *$} $RESULT]}
set filelist [lsort [glob -nocomplain manifest*]]
test "set-manifest-1-n" {[llength $filelist] == 0}
# Classic behavior: TRUE value creates manifest and manifest.uuid
set truths [list true on 1]
foreach v $truths {
fossil settings manifest $v
test "set-manifest-2-$v" {$RESULT eq ""}
fossil settings manifest
test "set-manifest-2-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
set filelist [lsort [glob manifest*]]
test "set-manifest-2-$v-n" {[llength $filelist] == 2}
foreach f $filelist {
test "set-manifest-2-$v-f-$f" {[file isfile $f]}
}
}
# ... and manifest.uuid is the checkout's hash
|
| ︙ | ︙ | |||
86 87 88 89 90 91 92 |
# Classic behavior: FALSE value removes manifest and manifest.uuid
set falses [list false off 0]
foreach v $falses {
fossil settings manifest $v
test "set-manifest-3-$v" {$RESULT eq ""}
fossil settings manifest
test "set-manifest-3-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
| | | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# Classic behavior: FALSE value removes manifest and manifest.uuid
set falses [list false off 0]
foreach v $falses {
fossil settings manifest $v
test "set-manifest-3-$v" {$RESULT eq ""}
fossil settings manifest
test "set-manifest-3-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
set filelist [lsort [glob -nocomplain manifest*]]
test "set-manifest-3-$v-n" {[llength $filelist] == 0}
}
# Classic behavior: unset removes manifest and manifest.uuid
fossil unset manifest
test "set-manifest-4" {$RESULT eq ""}
fossil settings manifest
test "set-manifest-4-a" {[regexp {^manifest *$} $RESULT]}
set filelist [lsort [glob -nocomplain manifest*]]
test "set-manifest-4-n" {[llength $filelist] == 0}
##### Tags Manifest feature extends the manifest setting
# Manifest Tags: use letters r, u, and t to select each of manifest,
# manifest.uuid, and manifest.tags files.
set truths [list r u t ru ut rt rut]
foreach v $truths {
fossil settings manifest $v
test "set-manifest-5-$v" {$RESULT eq ""}
fossil settings manifest
test "set-manifest-5-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
set filelist [lsort [glob manifest*]]
test "set-manifest-5-$v-n" {[llength $filelist] == [string length $v]}
foreach f $filelist {
test "set-manifest-5-$v-f-$f" {[file isfile $f]}
}
}
# Quick check for tags applied in trunk
|
| ︙ | ︙ |
Changes to test/settings-repo.test.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
set all_settings [get_all_settings]
foreach name $all_settings {
#
# HACK: Make 100% sure that there are no non-default setting values
# present anywhere.
#
| > > > | > | | | | | | | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
set all_settings [get_all_settings]
foreach name $all_settings {
#
# HACK: Make 100% sure that there are no non-default setting values
# present anywhere.
#
if {$name eq "manifest"} {
fossil unset $name --exact --global -expectError
} else {
fossil unset $name --exact --global
}
fossil unset $name --exact
#
# NOTE: Query for the hard-coded default value of this setting and
# save it.
#
fossil test-th-eval "setting $name"
set defaults($name) [normalize_result]
}
###############################################################################
fossil settings bad-setting some_value -expectError
test settings-set-bad-local {
[normalize_result] eq "no such setting: bad-setting"
}
fossil settings bad-setting some_value --global -expectError
test settings-set-bad-global {
[normalize_result] eq "no such setting: bad-setting"
}
###############################################################################
fossil unset bad-setting -expectError
test settings-unset-bad-local {
[normalize_result] eq "no such setting: bad-setting"
}
fossil unset bad-setting --global -expectError
test settings-unset-bad-global {
[normalize_result] eq "no such setting: bad-setting"
}
###############################################################################
fossil settings ssl some_value -expectError
test settings-set-ambiguous-local {
[normalize_result] eq
"ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}
fossil settings ssl some_value --global -expectError
test settings-set-ambiguous-global {
[normalize_result] eq
"ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}
###############################################################################
fossil unset ssl -expectError
test settings-unset-ambiguous-local {
[normalize_result] eq
"ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}
fossil unset ssl --global -expectError
test settings-unset-ambiguous-global {
[normalize_result] eq
"ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}
###############################################################################
|
| ︙ | ︙ | |||
240 241 242 243 244 245 246 |
[regexp -- [string map [list %name% $name] $pattern(5)] $data]
}
fossil test-th-eval --open-config "setting $name"
set data [normalize_result]
test settings-set-check2-versionable-$name {
| | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
[regexp -- [string map [list %name% $name] $pattern(5)] $data]
}
fossil test-th-eval --open-config "setting $name"
set data [normalize_result]
test settings-set-check2-versionable-$name {
$data eq ""
}
file delete $fileName
fossil settings $name --exact
set data [normalize_result]
|
| ︙ | ︙ |
Changes to test/settings.test.
| ︙ | ︙ | |||
90 91 92 93 94 95 96 |
set data [normalize_result]
test settings-query-local-$name {
[regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
[regexp -- [string map [list %name% $name] $pattern(2)] $data]
}
| > > > | > | | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
set data [normalize_result]
test settings-query-local-$name {
[regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
[regexp -- [string map [list %name% $name] $pattern(2)] $data]
}
if {$name eq "manifest"} {
fossil settings $name --exact --global -expectError
} else {
fossil settings $name --exact --global
}
set data [normalize_result]
if {$name eq "manifest"} {
test settings-query-global-$name {
$data eq "cannot set 'manifest' globally"
}
} else {
test settings-query-global-$name {
[regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
[regexp -- [string map [list %name% $name] $pattern(2)] $data]
}
}
}
###############################################################################
fossil settings bad-setting -expectError
test settings-query-bad-local {
[normalize_result] eq "no such setting: bad-setting"
}
fossil settings bad-setting --global -expectError
test settings-query-bad-global {
[normalize_result] eq "no such setting: bad-setting"
}
###############################################################################
test_cleanup
|
Changes to test/stash.test.
| ︙ | ︙ | |||
139 140 141 142 143 144 145 |
test stash-1-list-1 {[regexp {^1: \[[0-9a-z]+\] on } [first_data_line]]}
test stash-1-list-2 {[regexp {^\s+stash 1\s*$} [second_data_line]]}
set diff_stash_1 {DELETE f1
Index: f1
==================================================================
--- f1
| | | | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
test stash-1-list-1 {[regexp {^1: \[[0-9a-z]+\] on } [first_data_line]]}
test stash-1-list-2 {[regexp {^\s+stash 1\s*$} [second_data_line]]}
set diff_stash_1 {DELETE f1
Index: f1
==================================================================
--- f1
+++ /dev/null
@@ -1,1 +0,0 @@
-f1
CHANGED f2
--- f2
+++ f2
@@ -1,1 +1,1 @@
-f2
+f2.1
CHANGED f3n
--- f3n
+++ f3n
ADDED f0
Index: f0
==================================================================
--- /dev/null
+++ f0
@@ -0,0 +1,1 @@
+f0}
########
# fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
# fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?
|
| ︙ | ︙ | |||
183 184 185 186 187 188 189 |
UPDATE f2
UPDATE f3n
ADDED f0
} -changes {
ADDED f0
MISSING f1
EDITED f2
| | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
UPDATE f2
UPDATE f3n
ADDED f0
} -changes {
ADDED f0
MISSING f1
EDITED f2
RENAMED f3 -> f3n
} -addremove {
DELETED f1
} -exists {f0 f2 f3n} -notexists {f1 f3}
# Confirm there is no longer a stash saved
fossil stash list
test stash-2-list {[first_data_line] eq "empty stash"}
# Test stashed mv without touching the file system
# Issue reported by email to fossil-users
# from Warren Young, dated Tue, 9 Feb 2016 01:22:54 -0700
# with checkin [b8c7af5bd9] plus a local patch on CentOS 5
# 64 bit intel, 8-byte pointer, 4-byte integer
# Stashed renamed file said:
# fossil: ./src/delta.c:231: checksum: Assertion '...' failed.
# Should be triggered by this stash-WY-1 test.
fossil checkout --force c1
fossil clean
fossil mv --soft f1 f1new
stash-test WY-1 {-expectError save -m "Reported 2016-02-09"} {
REVERT f1
DELETE f1new
} -changes {
} -addremove {
} -exists {f1 f2 f3} -notexists {f1new} -knownbugs {-code -result}
# TODO: add tests that verify the saved stash is sensible. Possibly
# by applying it and checking results. But until the SQLITE_CONSTRAINT
|
| ︙ | ︙ | |||
263 264 265 266 267 268 269 |
ADDED f3
} -exists {f1 f2 f3} -notexists {}
#fossil status
fossil stash show
test stash-3-1-show {[normalize_result] eq {ADDED f3
Index: f3
==================================================================
| | | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
ADDED f3
} -exists {f1 f2 f3} -notexists {}
#fossil status
fossil stash show
test stash-3-1-show {[normalize_result] eq {ADDED f3
Index: f3
==================================================================
--- /dev/null
+++ f3
@@ -0,0 +1,1 @@
+f3}}
stash-test 3-1-pop {pop} {
ADDED f3
} -changes {
ADDED f3
|
| ︙ | ︙ | |||
290 291 292 293 294 295 296 |
fossil commit -m "baseline"
fossil mv --hard f2 f2n
test_result_state stash-3-2-mv "mv --hard f2 f2n" [concat {
RENAME f2 f2n
MOVED_FILE} [file normalize f2] {
}] -changes {
| | | | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
fossil commit -m "baseline"
fossil mv --hard f2 f2n
test_result_state stash-3-2-mv "mv --hard f2 f2n" [concat {
RENAME f2 f2n
MOVED_FILE} [file normalize f2] {
}] -changes {
RENAMED f2 -> f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}
stash-test 3-2 {save -m f2n} {
REVERT f2
DELETE f2n
} -exists {f1 f2} -notexists {f2n} -knownbugs {-result}
fossil stash show
test stash-3-2-show-1 {![regexp {\sf1} $RESULT]} knownBug
test stash-3-2-show-2 {[regexp {\sf2n} $RESULT]}
stash-test 3-2-pop {pop} {
UPDATE f1
UPDATE f2n
} -changes {
RENAMED f2 -> f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}
########
# fossil stash snapshot ?-m|--comment COMMENT? ?FILES...?
|
| ︙ | ︙ | |||
366 367 368 369 370 371 372 |
file rename -force f3 f3n
fossil mv f3 f3n
stash-test 4-3 {snapshot -m "snap 3"} {
} -changes {
ADDED f0
DELETED f1
EDITED f2
| | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
file rename -force f3 f3n
fossil mv f3 f3n
stash-test 4-3 {snapshot -m "snap 3"} {
} -changes {
ADDED f0
DELETED f1
EDITED f2
RENAMED f3 -> f3n
} -addremove {
} -exists {f0 f2 f3n} -notexists {f1 f3}
fossil stash diff
test stash-4-3-diff-CODE {!$::CODE} knownBug
fossil stash show
test stash-4-3-show-1 {[regexp {DELETE f1} $RESULT]}
test stash-4-3-show-2 {[regexp {CHANGED f2} $RESULT]}
|
| ︙ | ︙ |
Changes to test/symlinks.test.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
set path [file dirname [info script]]
if {$is_windows} {
puts "Symlinks are not supported on Windows."
test_cleanup_then_return
}
| < < < < < < < > > > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
set path [file dirname [info script]]
if {$is_windows} {
puts "Symlinks are not supported on Windows."
test_cleanup_then_return
}
require_no_open_checkout
###############################################################################
test_setup; set rootDir [file normalize [pwd]]
# Using tempHomePath, allow-symlinks will always be off at this point.
fossil set allow-symlinks on
fossil test-th-eval --open-config {repository}
set repository [normalize_result]
if {[string length $repository] == 0} {
puts "Detection of the open repository file failed."
test_cleanup_then_return
|
| ︙ | ︙ | |||
60 61 62 63 64 65 66 |
test symlinks-dir-1 {[file exists [file join $rootDir subdirA f1.txt]] eq 1}
test symlinks-dir-2 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
test symlinks-dir-3 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-4 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}
fossil add [file join $rootDir symdirA f1.txt]
| > > > | > > > | | | | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
test symlinks-dir-1 {[file exists [file join $rootDir subdirA f1.txt]] eq 1}
test symlinks-dir-2 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
test symlinks-dir-3 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-4 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}
fossil add [file join $rootDir symdirA f1.txt]
test symlinks-skip-dir-traversal {[normalize_result] eq \
"SKIP symdirA/f1.txt"}
fossil commit -m "c1" -expectError
test symlinks-empty-commit {[normalize_result] eq \
"nothing has changed; use --allow-empty to override"}
###############################################################################
fossil ls
test symlinks-dir-5 {[normalize_result] eq ""}
###############################################################################
fossil extras
test symlinks-dir-6 {[normalize_result] eq \
"subdirA/f1.txt\nsubdirA/f2.txt\nsymdirA"}
###############################################################################
fossil close
file delete [file join $rootDir subdirA f1.txt]
test symlinks-dir-7 {[file exists [file join $rootDir subdirA f1.txt]] eq 0}
test symlinks-dir-8 {[file exists [file join $rootDir symdirA f1.txt]] eq 0}
test symlinks-dir-9 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-10 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}
###############################################################################
fossil open --force $repository
set code [catch {file readlink [file join $rootDir symdirA]} result]
test symlinks-dir-11 {$code == 0}
test symlinks-dir-12 {$result eq [file join $rootDir subdirA]}
test symlinks-dir-13 {[file exists [file join $rootDir subdirA f1.txt]] eq 0}
test symlinks-dir-14 {[file exists [file join $rootDir symdirA f1.txt]] eq 0}
test symlinks-dir-15 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-16 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}
###############################################################################
#
# TODO: Add tests for symbolic links as files here, including tests with the
# "allow-symlinks" setting on and off.
#
###############################################################################
test_cleanup
|
Changes to test/tester.tcl.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# This is the main test script. To run a regression test, do this:
#
# tclsh ../test/tester.tcl ../bld/fossil
#
# Where ../test/tester.tcl is the name of this file and ../bld/fossil
# is the name of the executable to be tested.
#
# We use some things introduced in 8.6 such as lmap. auto.def should
# have found us a suitable Tcl installation.
package require Tcl 8.6
set testfiledir [file normalize [file dirname [info script]]]
set testrundir [pwd]
set testdir [file normalize [file dirname $argv0]]
set fossilexe [file normalize [lindex $argv 0]]
set is_windows [expr {$::tcl_platform(platform) eq "windows"}]
if {$::is_windows} {
if {[string length [file extension $fossilexe]] == 0} {
append fossilexe .exe
}
set outside_fossil_repo [expr ![file exists "$::testfiledir\\..\\_FOSSIL_"]]
} else {
| > > > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# This is the main test script. To run a regression test, do this:
#
# tclsh ../test/tester.tcl ../bld/fossil
#
# Where ../test/tester.tcl is the name of this file and ../bld/fossil
# is the name of the executable to be tested.
#
# To run a subset of tests (i.e. only one or more of the test/*.test
# scripts), append the script base names as arguments:
#
# tclsh ../test/tester.tcl ../bld/fossil <script-basename>...
#
# We use some things introduced in 8.6 such as lmap. auto.def should
# have found us a suitable Tcl installation.
package require Tcl 8.6
set testfiledir [file normalize [file dirname [info script]]]
set testrundir [pwd]
set testdir [file normalize [file dirname $argv0]]
set fossilexe [file normalize [lindex $argv 0]]
set is_windows [expr {$::tcl_platform(platform) eq "windows"}]
set is_cygwin [regexp {^CYGWIN} $::tcl_platform(os)]
if {$::is_windows} {
if {[string length [file extension $fossilexe]] == 0} {
append fossilexe .exe
}
set outside_fossil_repo [expr ![file exists "$::testfiledir\\..\\_FOSSIL_"]]
} else {
|
| ︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
#
set result [list \
access-log \
admin-log \
allow-symlinks \
auto-captcha \
auto-hyperlink \
auto-shun \
autosync \
autosync-tries \
backoffice-disable \
backoffice-logfile \
backoffice-nodelay \
binary-glob \
case-sensitive \
chat-alert-sound \
chat-initial-history \
chat-inline-images \
chat-keep-count \
chat-keep-days \
chat-poll-timeout \
clean-glob \
clearsign \
comment-format \
crlf-glob \
crnl-glob \
default-csp \
default-perms \
diff-binary \
diff-command \
dont-push \
dotfiles \
editor \
email-admin \
email-renew-interval \
email-self \
email-send-command \
email-send-db \
email-send-dir \
email-send-method \
email-send-relayhost \
email-subname \
email-url \
empty-dirs \
encoding-glob \
exec-rel-paths \
fileedit-glob \
forbid-delta-manifests \
gdiff-command \
gmerge-command \
hash-digits \
hooks \
http-port \
https-login \
ignore-glob \
keep-glob \
localauth \
lock-timeout \
main-branch \
mainmenu \
manifest \
max-cache-entry \
max-loadavg \
max-upload \
mimetypes \
mtime-changes \
pgp-command \
preferred-diff-type \
proxy \
redirect-to-https \
relative-paths \
repo-cksum \
repolist-skin \
safe-html \
self-register \
sitemap-extra \
ssh-command \
ssl-ca-location \
ssl-identity \
tclsh \
th1-setup \
| > > > > > > > > > | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
#
set result [list \
access-log \
admin-log \
allow-symlinks \
auto-captcha \
auto-hyperlink \
auto-hyperlink-delay \
auto-hyperlink-mouseover \
auto-shun \
autosync \
autosync-tries \
backoffice-disable \
backoffice-logfile \
backoffice-nodelay \
binary-glob \
case-sensitive \
chat-alert-sound \
chat-initial-history \
chat-inline-images \
chat-keep-count \
chat-keep-days \
chat-poll-timeout \
chat-timeline-user \
clean-glob \
clearsign \
comment-format \
crlf-glob \
crnl-glob \
default-csp \
default-perms \
diff-binary \
diff-command \
dont-commit \
dont-push \
dotfiles \
editor \
email-admin \
email-listid \
email-renew-interval \
email-self \
email-send-command \
email-send-db \
email-send-dir \
email-send-method \
email-send-relayhost \
email-subname \
email-url \
empty-dirs \
encoding-glob \
exec-rel-paths \
fileedit-glob \
forbid-delta-manifests \
forum-close-policy \
gdiff-command \
gmerge-command \
hash-digits \
hooks \
http-port \
https-login \
ignore-glob \
keep-glob \
large-file-size \
localauth \
lock-timeout \
main-branch \
mainmenu \
manifest \
max-cache-entry \
max-loadavg \
max-upload \
mimetypes \
mtime-changes \
mv-rm-files \
pgp-command \
preferred-diff-type \
proxy \
redirect-to-https \
relative-paths \
repo-cksum \
repolist-skin \
safe-html \
self-pw-reset \
self-register \
sitemap-extra \
ssh-command \
ssl-ca-location \
ssl-identity \
tclsh \
th1-setup \
|
| ︙ | ︙ | |||
431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
proc require_no_open_checkout {} {
if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \
$::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} {
return
}
catch {exec $::fossilexe info} res
if {[regexp {local-root:} $res]} {
set projectName <unknown>
set localRoot <unknown>
regexp -line -- {^project-name: (.*)$} $res dummy projectName
set projectName [string trim $projectName]
regexp -line -- {^local-root: (.*)$} $res dummy localRoot
set localRoot [string trim $localRoot]
error "Detected an open checkout of project \"$projectName\",\
| > > | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
proc require_no_open_checkout {} {
if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \
$::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} {
return
}
catch {exec $::fossilexe info} res
if {[regexp {local-root:} $res]} {
global skipped_tests testfile
lappend skipped_tests $testfile
set projectName <unknown>
set localRoot <unknown>
regexp -line -- {^project-name: (.*)$} $res dummy projectName
set projectName [string trim $projectName]
regexp -line -- {^local-root: (.*)$} $res dummy localRoot
set localRoot [string trim $localRoot]
error "Detected an open checkout of project \"$projectName\",\
|
| ︙ | ︙ | |||
468 469 470 471 472 473 474 475 476 477 478 479 |
}
after [expr {$try * 100}]
}
error "Could not delete \"$path\", error: $error"
}
proc test_cleanup_then_return {} {
uplevel 1 [list test_cleanup]
return -code return
}
proc test_cleanup {} {
| > > | > > > > | 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 |
}
after [expr {$try * 100}]
}
error "Could not delete \"$path\", error: $error"
}
proc test_cleanup_then_return {} {
global skipped_tests testfile
lappend skipped_tests $testfile
uplevel 1 [list test_cleanup]
return -code return
}
proc test_cleanup {} {
if {$::KEEP} {
# To avoid errors with require_no_open_checkout, cd out of here.
if {[info exists ::tempSavedPwd]} {cd $::tempSavedPwd; unset ::tempSavedPwd}
return
}
if {![info exists ::tempRepoPath]} {return}
if {![file exists $::tempRepoPath]} {return}
if {![file isdirectory $::tempRepoPath]} {return}
set tempPathEnd [expr {[string length $::tempPath] - 1}]
if {[string length $::tempPath] == 0 || \
[string range $::tempRepoPath 0 $tempPathEnd] ne $::tempPath} {
error "Temporary repository path has wrong parent during cleanup."
|
| ︙ | ︙ | |||
502 503 504 505 506 507 508 |
# Finally, attempt to gracefully delete the temporary home directory,
# unless forbidden by external forces.
if {![info exists ::tempKeepHome]} {delete_temporary_home}
}
proc delete_temporary_home {} {
if {$::KEEP} {return}; # All cleanup disabled?
| | | 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
# Finally, attempt to gracefully delete the temporary home directory,
# unless forbidden by external forces.
if {![info exists ::tempKeepHome]} {delete_temporary_home}
}
proc delete_temporary_home {} {
if {$::KEEP} {return}; # All cleanup disabled?
if {$::is_windows || $::is_cygwin} {
robust_delete [file join $::tempHomePath _fossil]
} else {
robust_delete [file join $::tempHomePath .fossil]
}
robust_delete $::tempHomePath
}
|
| ︙ | ︙ | |||
829 830 831 832 833 834 835 836 837 838 839 840 841 842 |
lappend bad_test $name
if {$::HALT} {exit 1}
}
}
}
set bad_test {}
set ignored_test {}
# Return a random string N characters long.
#
set vocabulary 01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
append vocabulary " ()*^!.eeeeeeeeaaaaattiioo "
set nvocabulary [string length $vocabulary]
proc rand_str {N} {
| > | 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 |
lappend bad_test $name
if {$::HALT} {exit 1}
}
}
}
set bad_test {}
set ignored_test {}
set skipped_tests {}
# Return a random string N characters long.
#
set vocabulary 01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
append vocabulary " ()*^!.eeeeeeeeaaaaattiioo "
set nvocabulary [string length $vocabulary]
proc rand_str {N} {
|
| ︙ | ︙ | |||
993 994 995 996 997 998 999 | set inFileName [file join $::tempPath [appendArgs test-http-in- $suffix]] set outFileName [file join $::tempPath [appendArgs test-http-out- $suffix]] set data [subst [read_file $dataFileName]] write_file $inFileName $data fossil http --in $inFileName --out $outFileName --ipaddr 127.0.0.1 \ | | | 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 |
set inFileName [file join $::tempPath [appendArgs test-http-in- $suffix]]
set outFileName [file join $::tempPath [appendArgs test-http-out- $suffix]]
set data [subst [read_file $dataFileName]]
write_file $inFileName $data
fossil http --in $inFileName --out $outFileName --ipaddr 127.0.0.1 \
$repository --localauth --th-trace -expectError
set result [expr {[file exists $outFileName] ? [read_file $outFileName] : ""}]
if {1} {
catch {file delete $inFileName}
catch {file delete $outFileName}
}
|
| ︙ | ︙ | |||
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 |
} error] != 0} {
error "Could not write file \"$tempFile\" in directory \"$tempPath\",\
please set TEMP variable in environment, error: $error"
}
set tempHomePath [file join $tempPath home_[pid]]
if {[catch {
file mkdir $tempHomePath
} error] != 0} {
error "Could not make directory \"$tempHomePath\",\
please set TEMP variable in environment, error: $error"
}
protInit $fossilexe
set ::tempKeepHome 1
foreach testfile $argv {
protOut "***** $testfile ******"
if { [catch {source $testdir/$testfile.test} testerror testopts] } {
test test-framework-$testfile 0
protOut "!!!!! $testfile: $testerror"
protOutDict $testopts"
} else {
test test-framework-$testfile 1
}
protOut "***** End of $testfile: [llength $bad_test] errors so far ******"
}
unset ::tempKeepHome; delete_temporary_home
set nErr [llength $bad_test]
if {$nErr>0 || !$::QUIET} {
protOut "***** Final results: $nErr errors out of $test_count tests" 1
}
if {$nErr>0} {
protOut "***** Considered failures: $bad_test" 1
}
set nErr [llength $ignored_test]
if {$nErr>0 || !$::QUIET} {
protOut "***** Ignored results: $nErr ignored errors out of $test_count tests" 1
}
if {$nErr>0} {
protOut "***** Ignored failures: $ignored_test" 1
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > | 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 |
} error] != 0} {
error "Could not write file \"$tempFile\" in directory \"$tempPath\",\
please set TEMP variable in environment, error: $error"
}
set tempHomePath [file join $tempPath home_[pid]]
# Close stdin to avoid errors on wrapped text for narrow terminals.
# Closing stdin means that terminal detection returns 0 width, in turn
# causing the relvant strings to be printed on a single line.
# However, closing stdin makes file descriptor 0 avaailable on some systems
# and/or TCL implementations, which triggers fossil to complain about opening
# databases using fd 0. Avoid this by opening the script, consuming fd 0.
close stdin
set possibly_fd0 [open [info script] r]
if {[catch {
file mkdir $tempHomePath
} error] != 0} {
error "Could not make directory \"$tempHomePath\",\
please set TEMP variable in environment, error: $error"
}
protInit $fossilexe
set ::tempKeepHome 1
# Start in tempHomePath to help avoid errors with require_no_open_checkout
set startPwd [pwd]
cd $tempHomePath
foreach testfile $argv {
protOut "***** $testfile ******"
if { [catch {source $testdir/$testfile.test} testerror testopts] } {
test test-framework-$testfile 0
protOut "!!!!! $testfile: $testerror"
protOutDict $testopts"
} else {
test test-framework-$testfile 1
}
protOut "***** End of $testfile: [llength $bad_test] errors so far ******"
}
cd $startPwd
unset ::tempKeepHome; delete_temporary_home
# Clean up the file descriptor
close $possibly_fd0
set nErr [llength $bad_test]
if {$nErr>0 || !$::QUIET} {
protOut "***** Final results: $nErr errors out of $test_count tests" 1
}
if {$nErr>0} {
protOut "***** Considered failures: $bad_test" 1
}
set nErr [llength $ignored_test]
if {$nErr>0 || !$::QUIET} {
protOut "***** Ignored results: $nErr ignored errors out of $test_count tests" 1
}
if {$nErr>0} {
protOut "***** Ignored failures: $ignored_test" 1
}
set nSkipped [llength $skipped_tests]
if {$nSkipped>0} {
protOut "***** Skipped tests: $skipped_tests" 1
}
if {$bad_test>0} {
exit 1
}
|
Changes to test/th1-docs.test.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
fossil test-th-eval "hasfeature tcl"
if {[normalize_result] ne "1"} {
puts "Fossil was not compiled with Tcl support."
test_cleanup_then_return
}
| < < < < < < < < | < | < > > > > > > > > > < | | | < | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
fossil test-th-eval "hasfeature tcl"
if {[normalize_result] ne "1"} {
puts "Fossil was not compiled with Tcl support."
test_cleanup_then_return
}
###############################################################################
test_setup
###############################################################################
set env(TH1_ENABLE_DOCS) 1; # TH1 docs must be enabled for this test.
set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test.
###############################################################################
set data [fossil info]
regexp -line -- {^repository: (.*)$} $data dummy repository
if {[string length $repository] == 0 || ![file exists $repository]} {
error "unable to locate repository"
}
set dataFileName [file join $::testdir th1-docs-input.txt]
set origFileStat [file join $::testdir fileStat.th1]
if {![file exists $origFileStat]} {
error "unable to locate [$origFileStat]"
}
file copy $origFileStat fileStat.th1
fossil add fileStat.th1
fossil commit -m "Add fileStat.th1"
###############################################################################
set RESULT [test_fossil_http \
$repository $dataFileName /doc/trunk/fileStat.th1]
test th1-docs-1a {[regexp {<title>Unnamed Fossil Project: fileStat.th1</title>} $RESULT]}
test th1-docs-1b {[regexp {>\[[0-9a-f]{40,64}\]<} $RESULT]}
test th1-docs-1c {[regexp { contains \d+ files\.} $RESULT]}
###############################################################################
test_cleanup
|
Changes to test/th1-hooks.test.
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
test th1-cmd-hooks-1b {[normalize_result] eq \
{<h1><b>command_hook timeline</b></h1>
+++ some stuff here +++
<h1><b>command_hook timeline command_notify timeline</b></h1>}}
###############################################################################
| | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
test th1-cmd-hooks-1b {[normalize_result] eq \
{<h1><b>command_hook timeline</b></h1>
+++ some stuff here +++
<h1><b>command_hook timeline command_notify timeline</b></h1>}}
###############################################################################
fossil timeline custom3 -expectError; # NOTE: Bad "WHEN" argument.
test th1-cmd-hooks-1c {[normalize_result] eq \
{<h1><b>command_hook timeline</b></h1>
unknown check-in or invalid date: custom3}}
###############################################################################
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 |
fossil test3
test th1-custom-cmd-3a {[string trim $RESULT] eq \
{<h1><b>command_hook test3</b></h1>}}
###############################################################################
| | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
fossil test3
test th1-custom-cmd-3a {[string trim $RESULT] eq \
{<h1><b>command_hook test3</b></h1>}}
###############################################################################
fossil test4 -expectError
test th1-custom-cmd-4a {[first_data_line] eq \
{<h1><b>command_hook test4</b></h1>}}
test th1-custom-cmd-4b {[regexp -- \
{: unknown command: test4$} [second_data_line]]}
|
| ︙ | ︙ |
Changes to test/th1-tcl.test.
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl3.txt]]
| | | | | | | | | | > > > > > > > | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl3.txt]]
test th1-tcl-3 {$RESULT eq {<hr><p class="thmainError">ERROR:\
invalid command name "bad_command"</p>}}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl4.txt]]
test th1-tcl-4 {$RESULT eq {<hr><p class="thmainError">ERROR:\
divide by zero</p>}}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl5.txt]]
test th1-tcl-5 {$RESULT eq {<hr><p class="thmainError">ERROR:\
Tcl command not found: bad_command</p>} || $RESULT eq {<hr><p\
class="thmainError">ERROR: invalid command name "bad_command"</p>}}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl6.txt]]
test th1-tcl-6 {$RESULT eq {<hr><p class="thmainError">ERROR:\
no such command: bad_command</p>}}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl7.txt]]
test th1-tcl-7 {$RESULT eq {<hr><p class="thmainError">ERROR:\
syntax error in expression: "2**0"</p>}}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl8.txt]]
test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\
cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\
class="thmainError">ERROR: tailcall can only be called from a proc or\
lambda</p>} || $RESULT eq {<hr><p class="thmainError">ERROR: This test\
requires Tcl 8.6 or higher.</p>}}
###############################################################################
fossil test-th-render --open-config \
[file nativename [file join $path th1-tcl9.txt]]
# Under cygwin, the printed name with Usage: strips the extension
if { $::is_cygwin && [file extension $fossilexe] eq ".exe" } {
set fossilexeref [string range $fossilexe 0 end-4]
} else {
set fossilexeref $fossilexe
}
test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexeref] 3 \
[list test-th-render --open-config [file nativename [file join $path \
th1-tcl9.txt]]]]}
###############################################################################
fossil test-th-eval "tclMakeSafe a"
test th1-tcl-10 {[normalize_result] eq \
|
| ︙ | ︙ |
Changes to test/th1.test.
| ︙ | ︙ | |||
728 729 730 731 732 733 734 |
###############################################################################
fossil test-th-eval "trace {}"
test th1-trace-1 {$RESULT eq {}}
###############################################################################
| | | | | | | | | | > > | 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
###############################################################################
fossil test-th-eval "trace {}"
test th1-trace-1 {$RESULT eq {}}
###############################################################################
fossil test-th-eval --th-trace "trace {}" -expectError
set normalized_result [normalize_result]
regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \
$normalized_result {} normalized_result
if {$th1Hooks} {
test th1-trace-2 {$normalized_result eq \
{------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>
------------------- END TRACE LOG -------------------}}
} else {
test th1-trace-2 {$normalized_result eq \
{------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>
th1-setup {} => TH_OK<br>
------------------- END TRACE LOG -------------------}}
}
###############################################################################
fossil test-th-eval "trace {this is a trace message.}"
test th1-trace-3 {$RESULT eq {}}
###############################################################################
fossil test-th-eval --th-trace "trace {this is a trace message.}" -expectError
set normalized_result [normalize_result]
regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \
$normalized_result {} normalized_result
if {$th1Hooks} {
test th1-trace-4 {$normalized_result eq \
{------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>
this is a trace message.
------------------- END TRACE LOG -------------------}}
} else {
test th1-trace-4 {$normalized_result eq \
{------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>
th1-setup {} => TH_OK<br>
this is a trace message.
------------------- END TRACE LOG -------------------}}
}
###############################################################################
fossil test-th-eval "defHeader {Page Title Here}"
test th1-defHeader-1 {$RESULT eq \
{TH_ERROR: wrong # args: should be "defHeader"}}
###############################################################################
fossil test-th-eval "defHeader"
test th1-defHeader-2 {[string match *<body> [normalize_result]] || \
[string match "*<body class=\"\$current_feature\
rpage-\$requested_page\
cpage-\$canonical_page\">" [normalize_result]]}
###############################################################################
fossil test-th-eval "styleHeader {Page Title Here}"
test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
###############################################################################
|
| ︙ | ︙ | |||
1019 1020 1021 1022 1023 1024 1025 |
###############################################################################
fossil test-th-eval "globalState vfs"
test th1-globalState-14 {[string length $RESULT] == 0}
###############################################################################
| | | 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 |
###############################################################################
fossil test-th-eval "globalState vfs"
test th1-globalState-14 {[string length $RESULT] == 0}
###############################################################################
if {$is_windows || $is_cygwin} {
set altVfs win32-longpath
} else {
set altVfs unix-dotfile
}
###############################################################################
|
| ︙ | ︙ | |||
1060 1061 1062 1063 1064 1065 1066 |
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint \
builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
combobox continue copybtn date decorate defHeader dir enable_htmlify \
enable_output encode64 error expr for foreach getParameter glob_match \
globalState hascap hasfeature html htmlize http httpize if info \
| | | | | | 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint \
builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
combobox continue copybtn date decorate defHeader dir enable_htmlify \
enable_output encode64 error expr for foreach getParameter glob_match \
globalState hascap hasfeature html htmlize http httpize if info \
insertCsrf lappend lindex linecount list llength lsearch markdown nonce \
proc puts query randhex redirect regexp reinitialize rename render \
repository return searchable set setParameter setting stime string \
styleFooter styleHeader styleScript submenu tclReady trace unset \
unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
if {$th1Tcl} {
test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
} else {
test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
}
|
| ︙ | ︙ |
Changes to test/unversioned.test.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
puts "The \"sha1\" package is not available."
test_cleanup_then_return
}
require_no_open_checkout
test_setup; set rootDir [file normalize [pwd]]
fossil test-th-eval --open-config {repository}
set repository [normalize_result]
if {[string length $repository] == 0} {
puts "Detection of the open repository file failed."
test_cleanup_then_return
}
write_file unversioned1.txt "This is unversioned file #1."
write_file unversioned2.txt " This is unversioned file #2. "
write_file "unversioned space.txt" "\nThis is unversioned file #3.\n"
write_file unversioned4.txt "This is unversioned file #4."
write_file unversioned5.txt "This is unversioned file #5."
set env(VISUAL) [appendArgs \
[info nameofexecutable] " " [file join $path fake-editor.tcl]]
###############################################################################
| > > > > > > > > > > | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
puts "The \"sha1\" package is not available."
test_cleanup_then_return
}
require_no_open_checkout
test_setup; set rootDir [file normalize [pwd]]
# Avoid delays from the backoffice.
fossil set backoffice-disable 1
fossil test-th-eval --open-config {repository}
set repository [normalize_result]
if {[string length $repository] == 0} {
puts "Detection of the open repository file failed."
test_cleanup_then_return
}
write_file unversioned1.txt "This is unversioned file #1."
write_file unversioned2.txt " This is unversioned file #2. "
write_file "unversioned space.txt" "\nThis is unversioned file #3.\n"
write_file unversioned4.txt "This is unversioned file #4."
write_file unversioned5.txt "This is unversioned file #5."
set env(VISUAL) [appendArgs \
[info nameofexecutable] " " [file join $path fake-editor.tcl]]
###############################################################################
# Under cygwin, the printed name with Usage: strips the extension
if { $::is_cygwin && [file extension $fossilexe] eq ".exe" } {
set fossilexeref [string range $fossilexe 0 end-4]
} else {
set fossilexeref $fossilexe
}
fossil unversioned -expectError
test unversioned-1 {[normalize_result] eq \
[string map [list %fossil% [file nativename $fossilexeref]] {Usage: %fossil%\
unversioned add|cat|edit|export|list|revert|remove|sync|touch}]}
###############################################################################
fossil unversioned list
test unversioned-2 {[normalize_result] eq {}}
|
| ︙ | ︙ | |||
310 311 312 313 314 315 316 |
fossil user new uvtester "Unversioned Test User" $password
fossil user capabilities uvtester oy
###############################################################################
foreach {pid port outTmpFile} [test_start_server $repository stopArg] {}
| > | > > | > | | | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
fossil user new uvtester "Unversioned Test User" $password
fossil user capabilities uvtester oy
###############################################################################
foreach {pid port outTmpFile} [test_start_server $repository stopArg] {}
if {! $::QUIET} {
puts [appendArgs "Started Fossil server, pid \"" $pid \" ", port \"" $port \".]
}
set remote [appendArgs http://uvtester: $password @localhost: $port /]
###############################################################################
set clientDir [file join $tempPath [appendArgs \
uvtest_ [string trim [clock seconds] -] _ [getSeqNo]]]
set savedPwd [pwd]
file mkdir $clientDir; cd $clientDir
if {! $::QUIET} {
puts [appendArgs "Now in client directory \"" [pwd] \".]
}
write_file unversioned-client1.txt "This is unversioned client file #1."
###############################################################################
fossil clone --save-http-password $remote uvrepo.fossil
fossil open -f uvrepo.fossil
###############################################################################
fossil unversioned list
test unversioned-45 {[normalize_result] eq {}}
###############################################################################
fossil_maybe_answer y unversioned sync $remote
test unversioned-46 {[regexp \
{Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 0 received: 2
\n? done, wire bytes sent: \d+ received: \d+ remote: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}
###############################################################################
fossil unversioned ls
test unversioned-47 {[normalize_result] eq {unversioned2.txt
unversioned5.txt}}
|
| ︙ | ︙ | |||
387 388 389 390 391 392 393 |
fossil_maybe_answer y unversioned revert $remote
test unversioned-52 {[regexp \
{Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 0 received: 2
| | | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
fossil_maybe_answer y unversioned revert $remote
test unversioned-52 {[regexp \
{Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 0 received: 2
\n? done, wire bytes sent: \d+ received: \d+ remote: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}
###############################################################################
fossil unversioned list
test unversioned-53 {[regexp \
{^[0-9a-f]{12} 2016-10-01 00:00:00 30 30\
|
| ︙ | ︙ | |||
412 413 414 415 416 417 418 |
fossil_maybe_answer y unversioned sync $remote
test unversioned-55 {[regexp \
{Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 1 received: 0
Round-trips: 2 Artifacts sent: 1 received: 0
| | > | > > | > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
fossil_maybe_answer y unversioned sync $remote
test unversioned-55 {[regexp \
{Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 1 Artifacts sent: 0 received: 0
Round-trips: 2 Artifacts sent: 1 received: 0
Round-trips: 2 Artifacts sent: 1 received: 0
\n? done, wire bytes sent: \d+ received: \d+ remote: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}
###############################################################################
fossil close
test unversioned-56 {[normalize_result] eq {}}
###############################################################################
cd $savedPwd; unset savedPwd
file delete -force $clientDir
if {! $::QUIET} {
puts [appendArgs "Now in server directory \"" [pwd] \".]
}
###############################################################################
set stopped [test_stop_server $stopArg $pid $outTmpFile]
if {! $::QUIET} {
puts [appendArgs \
[expr {$stopped ? "Stopped" : "Could not stop"}] \
" Fossil server, pid \"" $pid "\", using argument \"" \
$stopArg \".]
}
###############################################################################
fossil unversioned list
test unversioned-57 {[regexp \
{^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 35 35\
unversioned-client1\.txt
|
| ︙ | ︙ |
Added test/update.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
#
# Copyright (c) 2024 Preben Guldnerg <preben@guldberg.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the Simplified BSD License (also
# known as the "2-Clause License" or "FreeBSD License".)
#
# This program is distributed in the hope that it will be useful,
# but without any warranty; without even the implied warranty of
# merchantability or fitness for a particular purpose.
#
# Author contact information:
# drh@hwaci.com
# http://www.hwaci.com/drh/
#
############################################################################
#
# Tests for the "update" command.
#
# Track number of tests we have set up in test_update_setup. This helps ensure
# that generated files are ordered in `fossil update --verbose` mode.
set UPDATE_TEST 0
proc test_update_setup {desc} {
global UPDATE_TEST
incr UPDATE_TEST
fossil revert
fossil update
return [format "test-%02u-%s.txt" $UPDATE_TEST $desc]
}
# The output is in file name order, so massage $RESULT to remove initial UNCHANGED
# files. Only do this if we have the expected branch information.
proc test_update {testname message changes {fossil_args ""}} {
fossil update --verbose {*}$fossil_args
if { [regsub {\n-{79}\nupdated-from: [0-9a-z]{40} .*} $::RESULT {} test_result ] } {
regsub {^(?:UNCHANGED [-a-z0-9.]+\n)*} $test_result {} test_result
} else {
set test_result $::RESULT
}
test "update-message-$testname" {$message == $test_result}
fossil changes
test "update-changes-$testname" {$changes == $::RESULT}
}
# Use a sequence number for file content that is not important for the test.
set UPDATE_SEQ_NO 0
proc write_seq_to_file {fname} {
global UPDATE_SEQ_NO
incr UPDATE_SEQ_NO
write_file $fname "$UPDATE_SEQ_NO\n"
}
# Make sure we are not in an open repository and initialize new repository
test_setup
###############################################################################
fossil update --verbose
test update-already-up-to-date {
[regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date$} $RESULT]
}
# Remaining tests are carried out in the order update_cmd() performs checks.
#
# Common approach for tests below:
# 1. Set the testname
# 2. Set the file name, done by calling update_setup
# 3. Set message and changes, the expected message message and subsequent changes
# 3. Optionally set up and commit a common base for the next steps
# 4. Commit a change to the repository (new tip)
# 5. Update to the previous version
# 6. Make changes
# 7. Call test_update to attempt and update to tip
set testname "conflict-standard"
set fname [test_update_setup $testname]
set message "CONFLICT $fname"
set changes "EDITED $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil up previous
write_seq_to_file $fname
fossil add $fname
test_update $testname $message $changes -expectError
set testname "add-overwrites"
set fname [test_update_setup $testname]
set message "ADD $fname - overwrites an unmanaged file, original copy backed up locally"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil up previous
write_seq_to_file $fname
test_update $testname $message $changes -expectError
set testname "add-standard"
set fname [test_update_setup $testname]
set message "ADD $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil up previous
test_update $testname $message $changes
set testname "update-change"
set fname [test_update_setup $testname]
set message "UPDATE $fname - change to unmanaged file"
set changes "DELETED $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $fname"
fossil up previous
fossil rm --hard $fname
test_update $testname $message $changes
set testname "update-standard"
set fname [test_update_setup $testname]
set message "UPDATE $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $testname"
fossil up previous
test_update $testname $message $changes
set testname "update-missing"
set fname [test_update_setup $testname]
set message "UPDATE $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $fname"
fossil up previous
file delete $fname
test_update $testname $message $changes
set testname "conflict-deleted"
set fname [test_update_setup $testname]
set message "CONFLICT $fname - edited locally but deleted by update"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil rm --hard $fname
fossil commit -m "Remove $fname"
fossil up previous
file delete $fname
test_update $testname $message $changes -expectError
set testname "remove"
set fname [test_update_setup $testname]
set message "REMOVE $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil rm --hard $fname
fossil commit -m "Remove $fname"
fossil up previous
test_update $testname $message $changes
set testname "merge-renamed"
set fname [test_update_setup $testname]
set message "MERGE $fname -> $fname.renamed"
set changes "EDITED $fname.renamed"
write_file $fname "center\n"
fossil add $fname
fossil commit -m "Add $fname"
write_file $fname "top\ncenter\n"
fossil mv --hard $fname "$fname.renamed"
fossil commit -m "Update and rename $fname"
fossil up previous
write_file $fname "center\nbelow\n"
test_update $testname $message $changes
set testname "merge-standard"
set fname [test_update_setup $testname]
set message "MERGE $fname"
set changes "EDITED $fname"
write_file $fname "center\n"
fossil add $fname
fossil commit -m "Add $fname"
write_file $fname "top\ncenter\n"
fossil commit -m "Update $fname"
fossil up previous
write_file $fname "center\nbelow\n"
test_update $testname $message $changes
# TODO: test for "Cannot merge symlink" would be platform dependent
set testname "merge-conflict"
set fname [test_update_setup $testname]
set message "MERGE $fname\n***** 1 merge conflicts in $fname"
set changes "CONFLICT $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $fname"
fossil up previous
write_seq_to_file $fname
test_update $testname $message $changes -expectError
# TODO: test for "Cannot merge binary file"?
set testname "edited"
set fname [test_update_setup $testname]
set message "EDITED $fname\nADD $fname.other"
set changes "EDITED $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file "$fname.other"
fossil add $fname.other
fossil commit -m "Add $fname.other"
fossil up previous
write_seq_to_file $fname
test_update $testname $message $changes
set testname "unchanged"
set fname [test_update_setup $testname]
set message "ADD $fname\nUNCHANGED $fname.unchanged"
set changes ""
write_seq_to_file "$fname.unchanged"
fossil add "$fname.unchanged"
fossil commit -m "Add $fname.unchanged"
write_seq_to_file "$fname"
fossil add "$fname"
fossil commit -m "Add $fname"
fossil up previous
test_update $testname $message $changes
###############################################################################
test_cleanup
|
Changes to tools/codecheck1.c.
| ︙ | ︙ | |||
602 603 604 605 606 607 608 |
if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
const char *zExpr = azArg[fmtArg+i];
if( never_safe(zExpr) ){
printf("%s:%d: Argument %d to %.*s() is not safe for"
" a query parameter\n",
zFilename, lnFCall, i+fmtArg, szFName, zFCall);
nErr++;
| | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
const char *zExpr = azArg[fmtArg+i];
if( never_safe(zExpr) ){
printf("%s:%d: Argument %d to %.*s() is not safe for"
" a query parameter\n",
zFilename, lnFCall, i+fmtArg, szFName, zFCall);
nErr++;
}else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){
printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
zFilename, lnFCall, i+fmtArg, szFName, zFCall);
nErr++;
}
}
}
|
| ︙ | ︙ |
Changes to tools/makeheaders.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> #include <string.h> | | > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <stdlib.h>
#include <ctype.h>
#include <memory.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#if defined( __MINGW32__) || defined(__DMC__) || \
defined(_MSC_VER) || defined(__POCC__)
# ifndef WIN32
# define WIN32
# endif
#else
# include <unistd.h>
#endif
|
| ︙ | ︙ | |||
2224 2225 2226 2227 2228 2229 2230 |
if (pToken->zText[pToken->nText-1] == '\r') { nArg--; }
if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
}else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Export);
}else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Local);
| > | | 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 |
if (pToken->zText[pToken->nText-1] == '\r') { nArg--; }
if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
}else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Export);
}else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Local);
}else if( nArg==15 &&
strncmp(zArg,"MAKEHEADERS_STOPLOCAL_INTERFACE",15)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Local);
}else{
PushIfMacro(0,zArg,nArg,pToken->nLine,0);
}
}else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
/*
** Push an #ifdef.
|
| ︙ | ︙ |
Changes to tools/mkindex.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | ** legacy commands. Test commands are unsupported commands used for testing ** and analysis only. ** ** Commands are 1st-tier by default. If the command name begins with ** "test-" or if the command name has a "test" argument, then it becomes ** a test command. If the command name has a "2nd-tier" argument or ends ** with a "*" character, it is second tier. If the command name has an "alias" | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | ** legacy commands. Test commands are unsupported commands used for testing ** and analysis only. ** ** Commands are 1st-tier by default. If the command name begins with ** "test-" or if the command name has a "test" argument, then it becomes ** a test command. If the command name has a "2nd-tier" argument or ends ** with a "*" character, it is second tier. If the command name has an "alias" ** argument or ends with a "#" character, it is an alias: another name ** (a one-to-one replacement) for a command. Examples: ** ** COMMAND: abcde* ** COMMAND: fghij 2nd-tier ** COMMAND: mnopq# ** COMMAND: rstuv alias ** COMMAND: test-xyzzy |
| ︙ | ︙ |
Changes to tools/skintxt2config.c.
|
| | | 1 2 3 4 5 6 7 8 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* ** Copyright (c) 2021 Stephan Beal (https://wanderinghorse.net/home/stephan/) ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) |
| ︙ | ︙ | |||
100 101 102 103 104 105 106 |
end:
fclose(f);
if(rc){
free(zMem);
}else{
*zContent = zMem;
*nContent = fpos;
| | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
end:
fclose(f);
if(rc){
free(zMem);
}else{
*zContent = zMem;
*nContent = fpos;
}
return rc;
}
/*
** Expects zFilename to be one of the conventional skin filename
** parts. This routine converts it to config format and emits it to
** App.ostr.
|
| ︙ | ︙ |
Changes to tools/sqlcompattest.c.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | #error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def" #endif #define QUOTE(VAL) #VAL #define STR(MACRO_VAL) QUOTE(MACRO_VAL) char zMinimumVersionNumber[8]="nn.nn.nn"; | | > | > | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
#error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def"
#endif
#define QUOTE(VAL) #VAL
#define STR(MACRO_VAL) QUOTE(MACRO_VAL)
char zMinimumVersionNumber[8]="nn.nn.nn";
strncpy((char *)&zMinimumVersionNumber,STR(MINIMUM_SQLITE_VERSION),
sizeof(zMinimumVersionNumber));
long major, minor, release, version;
sscanf(zMinimumVersionNumber, "%li.%li.%li", &major, &minor, &release);
version=(major*1000000)+(minor*1000)+release;
int i;
static const char *zRequiredOpts[] = {
"ENABLE_FTS4", /* Required for repository search */
"ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */
};
/* Check minimum SQLite version number */
if( sqlite3_libversion_number()<version ){
printf("found system SQLite version %s but need %s or later, "
"consider removing --disable-internal-sqlite\n",
sqlite3_libversion(),STR(MINIMUM_SQLITE_VERSION));
return 1;
}
for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){
if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){
printf("system SQLite library omits required build option -DSQLITE_%s\n",
|
| ︙ | ︙ |
Changes to www/changes.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 |
<title>Change Log</title>
<h2 id='v2_24'>Changes for version 2.24 (pending)</h2>
* Add the x= query paramater to the [/help?cmd=/timeline|/timeline page].
<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
* Add ability to "close" forum threads, such that unprivileged users
may no longer respond to them. Only administrators can close
threads or respond to them by default, and the
[/help?cmd=forum-close-policy|forum-close-policy setting] can be
| > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<title>Change Log</title>
<h2 id='v2_24'>Changes for version 2.24 (pending)</h2>
* Add the x= query paramater to the [/help?cmd=/timeline|/timeline page].
* Moved the /museum/repo.fossil file referenced from the Dockerfile from
the ENTRYPOINT to the CMD part to allow use of --repolist mode.
* The /uvlist page now shows the hash algorithm used so that
outsiders don't have to guess it from the hash length when
double-checking hashes of downloaded files on their end.
* The hash itself is now shown in a fixed-width font on the /uvlist
page, suiting this tabular display.
<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
* Add ability to "close" forum threads, such that unprivileged users
may no longer respond to them. Only administrators can close
threads or respond to them by default, and the
[/help?cmd=forum-close-policy|forum-close-policy setting] can be
|
| ︙ | ︙ |
Changes to www/customskin.md.
| ︙ | ︙ | |||
416 417 418 419 420 421 422 | ## <a id="vars"></a>TH1 Variables Before expanding the TH1 within the header and footer, Fossil first initializes a number of TH1 variables to values that depend on repository settings and the specific page being generated. | | | | | | | | | | | | | | | | | | | | | 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
## <a id="vars"></a>TH1 Variables
Before expanding the TH1 within the header and footer, Fossil first
initializes a number of TH1 variables to values that depend on
repository settings and the specific page being generated.
* **`project_name`** - The project_name variable is filled with the
name of the project as configured under the Admin/Configuration
menu.
* **`project_description`** - The project_description variable is
filled with the description of the project as configured under
the Admin/Configuration menu.
* **`title`** - The title variable holds the title of the page being
generated.
The title variable is special in that it is deleted after
the header script runs and before the footer script. This is
necessary to avoid a conflict with a variable by the same name used
in my ticket-screen scripts.
* **`baseurl`** - The root of the URL namespace for this server.
* **`secureurl`** - The same as $baseurl except that if the scheme is
"http:" it is changed to "https:"
* **`home`** - The $baseurl without the scheme and hostname. For example,
if the $baseurl is "http://projectX.com/cgi-bin/fossil" then the
$home will be just "/cgi-bin/fossil".
* **`index_page`** - The landing page URI as
specified by the Admin/Configuration setup page.
* **`current_page`** - The name of the page currently being processed,
without the leading "/" and without query parameters.
Examples: "timeline", "doc/trunk/README.txt", "wiki".
* **`csrf_token`** - A token used to prevent cross-site request forgery.
* **`default_csp`** - [Fossil’s default CSP](./defcsp.md) unless
[overridden by custom TH1 code](./defcsp.md#th1). Useful within
the skin for inserting the CSP into a `<meta>` tag within [a
custom `<head>` element](#headfoot).
* **`nonce`** - The value of the cryptographic nonce for the request
being processed.
* **`release_version`** - The release version of Fossil. Ex: "1.31"
* **`manifest_version`** - A prefix on the check-in hash of the
specific version of fossil that is running. Ex: "\[47bb6432a1\]"
* **`manifest_date`** - The date of the source-code check-in for the
version of fossil that is running.
* **`compiler_name`** - The name and version of the compiler used to
build the fossil executable.
* **`login`** - This variable only exists if the user has logged in.
The value is the username of the user.
* **`stylesheet_url`** - A URL for the internal style-sheet maintained
by Fossil.
* **`logo_image_url`** - A URL for the logo image for this project, as
configured on the Admin/Logo page.
* **`background_image_url`** - A URL for a background image for this
project, as configured on the Admin/Logo page.
All of the above are variables in the sense that either the header or the
footer is free to change or erase them. But they should probably be treated
as constants. New predefined values are likely to be added in future
releases of Fossil.
|
| ︙ | ︙ |
Changes to www/tech_overview.wiki.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | file that people are normally referring to when they say "a Fossil repository". The checkout database is found in the working checkout for a project and contains state information that is unique to that working checkout. Fossil does not always use all three database files. The web interface, for example, typically only uses the repository database. And the | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | file that people are normally referring to when they say "a Fossil repository". The checkout database is found in the working checkout for a project and contains state information that is unique to that working checkout. Fossil does not always use all three database files. The web interface, for example, typically only uses the repository database. And the [/help/settings | fossil settings] command only opens the configuration database when the --global option is used. But other commands use all three databases at once. For example, the [/help/status | fossil status] command will first locate the checkout database, then use the checkout database to find the repository database, then open the configuration database. Whenever multiple databases are used at the same time, they are all opened on the same SQLite database connection using SQLite's [http://www.sqlite.org/lang_attach.html | ATTACH] command. |
| ︙ | ︙ | |||
161 162 163 164 165 166 167 | * %FOSSIL_HOME%/_fossil * %LOCALAPPDATA%/_fossil * %APPDATA%/_fossil * %USERPROFILES%/_fossil * %HOMEDRIVE%%HOMEPATH%/_fossil | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | * %FOSSIL_HOME%/_fossil * %LOCALAPPDATA%/_fossil * %APPDATA%/_fossil * %USERPROFILES%/_fossil * %HOMEDRIVE%%HOMEPATH%/_fossil The second case is the one that usually determines the name. Note that the FOSSIL_HOME environment variable can always be set to determine the location of the configuration database. Note also that the configuration database file itself is called ".fossil" or "fossil.db" on unix but "_fossil" on windows. The [/help?cmd=info|fossil info] command will show the location of the configuration database on a line that starts with "config-db:". |
| ︙ | ︙ |
Changes to www/unvers.wiki.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 | <blockquote><pre> CREATE TABLE unversioned( uvid INTEGER PRIMARY KEY AUTOINCREMENT, -- unique ID for this file name TEXT UNIQUE, -- Name of the file rcvid INTEGER, -- From whence this file was received mtime DATETIME, -- Last change (seconds since 1970) | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | <blockquote><pre> CREATE TABLE unversioned( uvid INTEGER PRIMARY KEY AUTOINCREMENT, -- unique ID for this file name TEXT UNIQUE, -- Name of the file rcvid INTEGER, -- From whence this file was received mtime DATETIME, -- Last change (seconds since 1970) hash TEXT, -- SHA1 or SHA3-256 hash of uncompressed content sz INTEGER, -- Size of uncompressed content encoding INT, -- 0: plaintext 1: zlib compressed content BLOB -- File content ); </pre></blockquote> Fossil does not create the table ahead of need. |
| ︙ | ︙ | |||
104 105 106 107 108 109 110 | to "diff" your local version against the remote and send only the changes. We point this out because one use-case for unversioned content is to send large, frequently-changing files. Appreciate the consequences before making each change. There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>". The first is the regular HTTP payload compression step, done on all | | | 104 105 106 107 108 109 110 111 112 113 114 | to "diff" your local version against the remote and send only the changes. We point this out because one use-case for unversioned content is to send large, frequently-changing files. Appreciate the consequences before making each change. There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>". The first is the regular HTTP payload compression step, done on all syncs. The second is that Fossil sends hash exchanges to determine when it can avoid sending duplicate content over the wire unnecessarily. See the [./sync.wiki|synchronization protocol documentation] for further information. |