Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From release To trunk
2024-05-21
| ||
11:18 | Update the built-in SQLite to the first 3.46.0 release candidate. ... (Leaf check-in: 2ba99c27 user: drh tags: trunk) | |
2024-05-18
| ||
14:12 | Relax constraints on PATHINFO names such that the "fossil ui /" command can be used if some repositories have non-ASCII filenames. Response to forum post ec3ab5b1f5. ... (check-in: 362a7b7c user: drh tags: trunk) | |
2024-04-24
| ||
21:25 | Fix or disable brittle test cases that were broken by changes in 2.23. ... (check-in: 5ad70808 user: drh tags: branch-2.24) | |
2024-04-23
| ||
13:43 | Update the homepage for the 2.24 release. ... (check-in: dee02ab6 user: drh tags: trunk) | |
13:25 | Version 2.24 ... (check-in: 8be0372c user: drh tags: trunk, release, version-2.24) | |
2024-04-22
| ||
16:29 | cgi.md: be less specific about the Apache version in which the Content-Length change happened because a new forum post reports that it happens at least as far back as 2.4.41. ... (check-in: 9af5f386 user: stephan tags: trunk) | |
Changes to VERSION.
|
| | | 1 | 2.25 |
Changes to extsrc/shell.c.
︙ | ︙ | |||
6000 6001 6002 6003 6004 6005 6006 | ** once prior to any call to seriesColumn() or seriesRowid() or ** seriesEof(). ** ** The query plan selected by seriesBestIndex is passed in the idxNum ** parameter. (idxStr is not used in this implementation.) idxNum ** is a bitmask showing which constraints are available: ** | | | | < < | | > > | | | | > > > > > > > > > > > > > > > > > > | | | | | > > > > > | | > > > > > > > > > > > > > > > > > > | > > | > > > > > | | > | | | > > > | 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 | ** once prior to any call to seriesColumn() or seriesRowid() or ** seriesEof(). ** ** The query plan selected by seriesBestIndex is passed in the idxNum ** parameter. (idxStr is not used in this implementation.) idxNum ** is a bitmask showing which constraints are available: ** ** 0x01: start=VALUE ** 0x02: stop=VALUE ** 0x04: step=VALUE ** 0x08: descending order ** 0x10: ascending order ** 0x20: LIMIT VALUE ** 0x40: OFFSET VALUE ** ** This routine should initialize the cursor and position it so that it ** is pointing at the first row, or pointing off the end of the table ** (so that seriesEof() will return true) if the table is empty. */ static int seriesFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStrUnused, int argc, sqlite3_value **argv ){ series_cursor *pCur = (series_cursor *)pVtabCursor; int i = 0; (void)idxStrUnused; if( idxNum & 0x01 ){ pCur->ss.iBase = sqlite3_value_int64(argv[i++]); }else{ pCur->ss.iBase = 0; } if( idxNum & 0x02 ){ pCur->ss.iTerm = sqlite3_value_int64(argv[i++]); }else{ pCur->ss.iTerm = 0xffffffff; } if( idxNum & 0x04 ){ pCur->ss.iStep = sqlite3_value_int64(argv[i++]); if( pCur->ss.iStep==0 ){ pCur->ss.iStep = 1; }else if( pCur->ss.iStep<0 ){ if( (idxNum & 0x10)==0 ) idxNum |= 0x08; } }else{ pCur->ss.iStep = 1; } if( idxNum & 0x20 ){ sqlite3_int64 iLimit = sqlite3_value_int64(argv[i++]); sqlite3_int64 iTerm; if( idxNum & 0x40 ){ sqlite3_int64 iOffset = sqlite3_value_int64(argv[i++]); if( iOffset>0 ){ pCur->ss.iBase += pCur->ss.iStep*iOffset; } } if( iLimit>=0 ){ iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep; if( pCur->ss.iStep<0 ){ if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm; }else{ if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm; } } } for(i=0; i<argc; i++){ if( sqlite3_value_type(argv[i])==SQLITE_NULL ){ /* If any of the constraints have a NULL value, then return no rows. ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */ pCur->ss.iBase = 1; pCur->ss.iTerm = 0; pCur->ss.iStep = 1; break; } } if( idxNum & 0x08 ){ pCur->ss.isReversing = pCur->ss.iStep > 0; }else{ pCur->ss.isReversing = pCur->ss.iStep < 0; } setupSequence( &pCur->ss ); return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the generate_series virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. ** ** In this implementation idxNum is used to represent the ** query plan. idxStr is unused. ** ** The query plan is represented by bits in idxNum: ** ** 0x01 start = $value -- constraint exists ** 0x02 stop = $value -- constraint exists ** 0x04 step = $value -- constraint exists ** 0x08 output is in descending order ** 0x10 output is in ascending order ** 0x20 LIMIT $value -- constraint exists ** 0x40 OFFSET $value -- constraint exists */ static int seriesBestIndex( sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo ){ int i, j; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ #ifndef ZERO_ARGUMENT_GENERATE_SERIES int bStartSeen = 0; /* EQ constraint seen on the START column */ #endif int unusableMask = 0; /* Mask of unusable constraints */ int nArg = 0; /* Number of arguments that seriesFilter() expects */ int aIdx[5]; /* Constraints on start, stop, step, LIMIT, OFFSET */ const struct sqlite3_index_constraint *pConstraint; /* This implementation assumes that the start, stop, and step columns ** are the last three columns in the virtual table. */ assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ int iCol; /* 0 for start, 1 for stop, 2 for step */ int iMask; /* bitmask for those column */ int op = pConstraint->op; if( op>=SQLITE_INDEX_CONSTRAINT_LIMIT && op<=SQLITE_INDEX_CONSTRAINT_OFFSET ){ if( pConstraint->usable==0 ){ /* do nothing */ }else if( op==SQLITE_INDEX_CONSTRAINT_LIMIT ){ aIdx[3] = i; idxNum |= 0x20; }else{ assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET ); aIdx[4] = i; idxNum |= 0x40; } continue; } if( pConstraint->iColumn<SERIES_COLUMN_START ) continue; iCol = pConstraint->iColumn - SERIES_COLUMN_START; assert( iCol>=0 && iCol<=2 ); iMask = 1 << iCol; #ifndef ZERO_ARGUMENT_GENERATE_SERIES if( iCol==0 && op==SQLITE_INDEX_CONSTRAINT_EQ ){ bStartSeen = 1; } #endif if( pConstraint->usable==0 ){ unusableMask |= iMask; continue; }else if( op==SQLITE_INDEX_CONSTRAINT_EQ ){ idxNum |= iMask; aIdx[iCol] = i; } } if( aIdx[3]==0 ){ /* Ignore OFFSET if LIMIT is omitted */ idxNum &= ~0x60; aIdx[4] = 0; } for(i=0; i<5; i++){ if( (j = aIdx[i])>=0 ){ pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3; } } /* The current generate_column() implementation requires at least one ** argument (the START value). Legacy versions assumed START=0 if the ** first argument was omitted. Compile with -DZERO_ARGUMENT_GENERATE_SERIES ** to obtain the legacy behavior */ #ifndef ZERO_ARGUMENT_GENERATE_SERIES if( !bStartSeen ){ sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = sqlite3_mprintf( "first argument to \"generate_series()\" missing or unusable"); return SQLITE_ERROR; } #endif if( (unusableMask & ~idxNum)!=0 ){ /* The start, stop, and step columns are inputs. Therefore if there ** are unusable constraints on any of start, stop, or step then ** this plan is unusable */ return SQLITE_CONSTRAINT; } if( (idxNum & 0x03)==0x03 ){ /* Both start= and stop= boundaries are available. This is the ** the preferred case */ pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0)); pIdxInfo->estimatedRows = 1000; if( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0].iColumn==0 ){ if( pIdxInfo->aOrderBy[0].desc ){ idxNum |= 0x08; }else{ idxNum |= 0x10; } pIdxInfo->orderByConsumed = 1; } }else if( (idxNum & 0x21)==0x21 ){ /* We have start= and LIMIT */ pIdxInfo->estimatedRows = 2500; }else{ /* If either boundary is missing, we have to generate a huge span ** of numbers. Make this case very expensive so that the query ** planner will work hard to avoid it. */ pIdxInfo->estimatedRows = 2147483647; } pIdxInfo->idxNum = idxNum; |
︙ | ︙ | |||
7473 7474 7475 7476 7477 7478 7479 | mode_t mode, /* MODE parameter passed to writefile() */ sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */ ){ if( zFile==0 ) return 1; #if !defined(_WIN32) && !defined(WIN32) if( S_ISLNK(mode) ){ const char *zTo = (const char*)sqlite3_value_text(pData); | > > | | 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 | mode_t mode, /* MODE parameter passed to writefile() */ sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */ ){ if( zFile==0 ) return 1; #if !defined(_WIN32) && !defined(WIN32) if( S_ISLNK(mode) ){ const char *zTo = (const char*)sqlite3_value_text(pData); if( zTo==0 ) return 1; unlink(zFile); if( symlink(zTo, zFile)<0 ) return 1; }else #endif { if( S_ISDIR(mode) ){ if( mkdir(zFile, mode) ){ /* The mkdir() call to create the directory failed. This might not ** be an error though - if there is already a directory at the same |
︙ | ︙ | |||
7559 7560 7561 7562 7563 7564 7565 | times[0].tv_nsec = times[1].tv_nsec = 0; times[0].tv_sec = time(0); times[1].tv_sec = mtime; if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){ return 1; } #else | | > > > > > | | | | | | > | 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 | times[0].tv_nsec = times[1].tv_nsec = 0; times[0].tv_sec = time(0); times[1].tv_sec = mtime; if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){ return 1; } #else /* Legacy unix. ** ** Do not use utimes() on a symbolic link - it sees through the link and ** modifies the timestamps on the target. Or fails if the target does ** not exist. */ if( 0==S_ISLNK(mode) ){ struct timeval times[2]; times[0].tv_usec = times[1].tv_usec = 0; times[0].tv_sec = time(0); times[1].tv_sec = mtime; if( utimes(zFile, times) ){ return 1; } } #endif } return 0; } |
︙ | ︙ | |||
11637 11638 11639 11640 11641 11642 11643 | */ static void sqlarUncompressFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ uLong nData; | | > | | | 11697 11698 11699 11700 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 | */ static void sqlarUncompressFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ uLong nData; sqlite3_int64 sz; assert( argc==2 ); sz = sqlite3_value_int(argv[1]); if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){ sqlite3_result_value(context, argv[0]); }else{ uLongf szf = sz; const Bytef *pData= sqlite3_value_blob(argv[0]); Bytef *pOut = sqlite3_malloc(sz); if( pOut==0 ){ sqlite3_result_error_nomem(context); }else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){ sqlite3_result_error(context, "error in uncompress()", -1); }else{ sqlite3_result_blob(context, pOut, szf, SQLITE_TRANSIENT); } sqlite3_free(pOut); } } #ifdef _WIN32 |
︙ | ︙ | |||
15474 15475 15476 15477 15478 15479 15480 15481 15482 15483 15484 15485 15486 15487 15488 15489 15490 15491 15492 15493 15494 15495 15496 | #ifndef SQLITE_OMIT_VIRTUALTABLE #define DBDATA_PADDING_BYTES 100 typedef struct DbdataTable DbdataTable; typedef struct DbdataCursor DbdataCursor; /* Cursor object */ struct DbdataCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ sqlite3_stmt *pStmt; /* For fetching database pages */ int iPgno; /* Current page number */ u8 *aPage; /* Buffer containing page */ int nPage; /* Size of aPage[] in bytes */ int nCell; /* Number of cells on aPage[] */ int iCell; /* Current cell number */ int bOnePage; /* True to stop after one page */ int szDb; sqlite3_int64 iRowid; /* Only for the sqlite_dbdata table */ | > > > > > > > > > | | 15535 15536 15537 15538 15539 15540 15541 15542 15543 15544 15545 15546 15547 15548 15549 15550 15551 15552 15553 15554 15555 15556 15557 15558 15559 15560 15561 15562 15563 15564 15565 15566 15567 15568 15569 15570 15571 15572 15573 15574 | #ifndef SQLITE_OMIT_VIRTUALTABLE #define DBDATA_PADDING_BYTES 100 typedef struct DbdataTable DbdataTable; typedef struct DbdataCursor DbdataCursor; typedef struct DbdataBuffer DbdataBuffer; /* ** Buffer type. */ struct DbdataBuffer { u8 *aBuf; sqlite3_int64 nBuf; }; /* Cursor object */ struct DbdataCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ sqlite3_stmt *pStmt; /* For fetching database pages */ int iPgno; /* Current page number */ u8 *aPage; /* Buffer containing page */ int nPage; /* Size of aPage[] in bytes */ int nCell; /* Number of cells on aPage[] */ int iCell; /* Current cell number */ int bOnePage; /* True to stop after one page */ int szDb; sqlite3_int64 iRowid; /* Only for the sqlite_dbdata table */ DbdataBuffer rec; sqlite3_int64 nRec; /* Size of pRec[] in bytes */ sqlite3_int64 nHdr; /* Size of header in bytes */ int iField; /* Current field number */ u8 *pHdrPtr; u8 *pPtr; u32 enc; /* Text encoding */ |
︙ | ︙ | |||
15534 15535 15536 15537 15538 15539 15540 15541 15542 15543 15544 15545 15546 15547 | #define DBPTR_COLUMN_SCHEMA 2 #define DBPTR_SCHEMA \ "CREATE TABLE x(" \ " pgno INTEGER," \ " child INTEGER," \ " schema TEXT HIDDEN" \ ")" /* ** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual ** table. */ static int dbdataConnect( sqlite3 *db, | > > > > > > > > > > > > > > > > > > > > > > > > > | 15604 15605 15606 15607 15608 15609 15610 15611 15612 15613 15614 15615 15616 15617 15618 15619 15620 15621 15622 15623 15624 15625 15626 15627 15628 15629 15630 15631 15632 15633 15634 15635 15636 15637 15638 15639 15640 15641 15642 | #define DBPTR_COLUMN_SCHEMA 2 #define DBPTR_SCHEMA \ "CREATE TABLE x(" \ " pgno INTEGER," \ " child INTEGER," \ " schema TEXT HIDDEN" \ ")" /* ** Ensure the buffer passed as the first argument is at least nMin bytes ** in size. If an error occurs while attempting to resize the buffer, ** SQLITE_NOMEM is returned. Otherwise, SQLITE_OK. */ static int dbdataBufferSize(DbdataBuffer *pBuf, sqlite3_int64 nMin){ if( nMin>pBuf->nBuf ){ sqlite3_int64 nNew = nMin+16384; u8 *aNew = (u8*)sqlite3_realloc64(pBuf->aBuf, nNew); if( aNew==0 ) return SQLITE_NOMEM; pBuf->aBuf = aNew; pBuf->nBuf = nNew; } return SQLITE_OK; } /* ** Release the allocation managed by buffer pBuf. */ static void dbdataBufferFree(DbdataBuffer *pBuf){ sqlite3_free(pBuf->aBuf); memset(pBuf, 0, sizeof(*pBuf)); } /* ** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual ** table. */ static int dbdataConnect( sqlite3 *db, |
︙ | ︙ | |||
15675 15676 15677 15678 15679 15680 15681 | } pCsr->pStmt = 0; pCsr->iPgno = 1; pCsr->iCell = 0; pCsr->iField = 0; pCsr->bOnePage = 0; sqlite3_free(pCsr->aPage); | < | | 15770 15771 15772 15773 15774 15775 15776 15777 15778 15779 15780 15781 15782 15783 15784 | } pCsr->pStmt = 0; pCsr->iPgno = 1; pCsr->iCell = 0; pCsr->iField = 0; pCsr->bOnePage = 0; sqlite3_free(pCsr->aPage); dbdataBufferFree(&pCsr->rec); pCsr->aPage = 0; } /* ** Close an sqlite_dbdata or sqlite_dbptr cursor. */ static int dbdataClose(sqlite3_vtab_cursor *pCursor){ |
︙ | ︙ | |||
15819 15820 15821 15822 15823 15824 15825 | static void dbdataValue( sqlite3_context *pCtx, u32 enc, int eType, u8 *pData, sqlite3_int64 nData ){ | > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < | < < | | < | 15913 15914 15915 15916 15917 15918 15919 15920 15921 15922 15923 15924 15925 15926 15927 15928 15929 15930 15931 15932 15933 15934 15935 15936 15937 15938 15939 15940 15941 15942 15943 15944 15945 15946 15947 15948 15949 15950 15951 15952 15953 15954 15955 15956 15957 15958 15959 15960 15961 15962 15963 15964 15965 15966 15967 15968 15969 15970 15971 15972 15973 15974 15975 15976 15977 15978 15979 15980 15981 15982 15983 15984 15985 15986 15987 15988 15989 15990 15991 15992 15993 15994 | static void dbdataValue( sqlite3_context *pCtx, u32 enc, int eType, u8 *pData, sqlite3_int64 nData ){ if( eType>=0 ){ if( dbdataValueBytes(eType)<=nData ){ switch( eType ){ case 0: case 10: case 11: sqlite3_result_null(pCtx); break; case 8: sqlite3_result_int(pCtx, 0); break; case 9: sqlite3_result_int(pCtx, 1); break; case 1: case 2: case 3: case 4: case 5: case 6: case 7: { sqlite3_uint64 v = (signed char)pData[0]; pData++; switch( eType ){ case 7: case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; case 4: v = (v<<8) + pData[0]; pData++; case 3: v = (v<<8) + pData[0]; pData++; case 2: v = (v<<8) + pData[0]; pData++; } if( eType==7 ){ double r; memcpy(&r, &v, sizeof(r)); sqlite3_result_double(pCtx, r); }else{ sqlite3_result_int64(pCtx, (sqlite3_int64)v); } break; } default: { int n = ((eType-12) / 2); if( eType % 2 ){ switch( enc ){ #ifndef SQLITE_OMIT_UTF16 case SQLITE_UTF16BE: sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT); break; case SQLITE_UTF16LE: sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT); break; #endif default: sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT); break; } }else{ sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT); } } } }else{ if( eType==7 ){ sqlite3_result_double(pCtx, 0.0); }else if( eType<7 ){ sqlite3_result_int(pCtx, 0); }else if( eType%2 ){ sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); }else{ sqlite3_result_blob(pCtx, "", 0, SQLITE_STATIC); } } } } /* This macro is a copy of the MX_CELL() macro in the SQLite core. Given ** a page-size, it returns the maximum number of cells that may be present |
︙ | ︙ | |||
15937 15938 15939 15940 15941 15942 15943 | if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; }else{ return SQLITE_OK; } }else{ /* If there is no record loaded, load it now. */ | | | 16043 16044 16045 16046 16047 16048 16049 16050 16051 16052 16053 16054 16055 16056 16057 | if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; }else{ return SQLITE_OK; } }else{ /* If there is no record loaded, load it now. */ if( pCsr->nRec==0 ){ int bHasRowid = 0; int nPointer = 0; sqlite3_int64 nPayload = 0; sqlite3_int64 nHdr = 0; int iHdr; int U, X; int nLocal; |
︙ | ︙ | |||
15981 15982 15983 15984 15985 15986 15987 15988 15989 15990 15991 15992 15993 15994 | /* Load the "byte of payload including overflow" field */ if( bNextPage || iOff>pCsr->nPage || iOff<=iCellPtr ){ 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); } | > | 16087 16088 16089 16090 16091 16092 16093 16094 16095 16096 16097 16098 16099 16100 16101 | /* Load the "byte of payload including overflow" field */ if( bNextPage || iOff>pCsr->nPage || iOff<=iCellPtr ){ bNextPage = 1; }else{ iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload); if( nPayload>0x7fffff00 ) nPayload &= 0x3fff; if( nPayload==0 ) nPayload = 1; } /* If this is a leaf intkey cell, load the rowid */ if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){ iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey); } |
︙ | ︙ | |||
16015 16016 16017 16018 16019 16020 16021 | if( bNextPage || nLocal+iOff>pCsr->nPage ){ bNextPage = 1; }else{ /* Allocate space for payload. And a bit more to catch small buffer ** overruns caused by attempting to read a varint or similar from ** near the end of a corrupt record. */ | | | < | | | > > > | | | | | | < | | < | | 16122 16123 16124 16125 16126 16127 16128 16129 16130 16131 16132 16133 16134 16135 16136 16137 16138 16139 16140 16141 16142 16143 16144 16145 16146 16147 16148 16149 16150 16151 16152 16153 16154 16155 16156 16157 16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180 16181 16182 16183 16184 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 16202 16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 | if( bNextPage || nLocal+iOff>pCsr->nPage ){ bNextPage = 1; }else{ /* Allocate space for payload. And a bit more to catch small buffer ** overruns caused by attempting to read a varint or similar from ** near the end of a corrupt record. */ rc = dbdataBufferSize(&pCsr->rec, nPayload+DBDATA_PADDING_BYTES); if( rc!=SQLITE_OK ) return rc; assert( nPayload!=0 ); /* Load the nLocal bytes of payload */ memcpy(pCsr->rec.aBuf, &pCsr->aPage[iOff], nLocal); iOff += nLocal; /* Load content from overflow pages */ if( nPayload>nLocal ){ sqlite3_int64 nRem = nPayload - nLocal; u32 pgnoOvfl = get_uint32(&pCsr->aPage[iOff]); while( nRem>0 ){ u8 *aOvfl = 0; int nOvfl = 0; int nCopy; rc = dbdataLoadPage(pCsr, pgnoOvfl, &aOvfl, &nOvfl); assert( rc!=SQLITE_OK || aOvfl==0 || nOvfl==pCsr->nPage ); if( rc!=SQLITE_OK ) return rc; if( aOvfl==0 ) break; nCopy = U-4; if( nCopy>nRem ) nCopy = nRem; memcpy(&pCsr->rec.aBuf[nPayload-nRem], &aOvfl[4], nCopy); nRem -= nCopy; pgnoOvfl = get_uint32(aOvfl); sqlite3_free(aOvfl); } nPayload -= nRem; } memset(&pCsr->rec.aBuf[nPayload], 0, DBDATA_PADDING_BYTES); pCsr->nRec = nPayload; iHdr = dbdataGetVarintU32(pCsr->rec.aBuf, &nHdr); if( nHdr>nPayload ) nHdr = 0; pCsr->nHdr = nHdr; pCsr->pHdrPtr = &pCsr->rec.aBuf[iHdr]; pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nHdr]; pCsr->iField = (bHasRowid ? -1 : 0); } } }else{ pCsr->iField++; if( pCsr->iField>0 ){ sqlite3_int64 iType; if( pCsr->pHdrPtr>=&pCsr->rec.aBuf[pCsr->nRec] || pCsr->iField>=DBDATA_MX_FIELD ){ bNextPage = 1; }else{ int szField = 0; pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType); szField = dbdataValueBytes(iType); if( (pCsr->nRec - (pCsr->pPtr - pCsr->rec.aBuf))<szField ){ pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nRec]; }else{ pCsr->pPtr += szField; } } } } if( bNextPage ){ sqlite3_free(pCsr->aPage); pCsr->aPage = 0; pCsr->nRec = 0; if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; }else{ if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->rec.aBuf[pCsr->nHdr] ){ return SQLITE_OK; } /* Advance to the next cell. The next iteration of the loop will load ** the record and so on. */ pCsr->nRec = 0; pCsr->iCell++; } } } assert( !"can't get here" ); return SQLITE_OK; |
︙ | ︙ | |||
16281 16282 16283 16284 16285 16286 16287 | break; case DBDATA_COLUMN_FIELD: sqlite3_result_int(ctx, pCsr->iField); break; case DBDATA_COLUMN_VALUE: { if( pCsr->iField<0 ){ sqlite3_result_int64(ctx, pCsr->iIntkey); | | | | 16388 16389 16390 16391 16392 16393 16394 16395 16396 16397 16398 16399 16400 16401 16402 16403 16404 16405 16406 16407 | break; case DBDATA_COLUMN_FIELD: sqlite3_result_int(ctx, pCsr->iField); break; case DBDATA_COLUMN_VALUE: { if( pCsr->iField<0 ){ sqlite3_result_int64(ctx, pCsr->iIntkey); }else if( &pCsr->rec.aBuf[pCsr->nRec] >= pCsr->pPtr ){ sqlite3_int64 iType; dbdataGetVarintU32(pCsr->pHdrPtr, &iType); dbdataValue( ctx, pCsr->enc, iType, pCsr->pPtr, &pCsr->rec.aBuf[pCsr->nRec] - pCsr->pPtr ); } break; } } } return SQLITE_OK; |
︙ | ︙ | |||
25935 25936 25937 25938 25939 25940 25941 | */ static struct { int iId; /* ID that triggers a simulated fault. -1 means "any" */ int iErr; /* The error code to return on a fault */ int iCnt; /* Trigger the fault only if iCnt is already zero */ int iInterval; /* Reset iCnt to this value after each fault */ int eVerbose; /* When to print output */ | > > > | | | > > > > | 26042 26043 26044 26045 26046 26047 26048 26049 26050 26051 26052 26053 26054 26055 26056 26057 26058 26059 26060 26061 26062 26063 26064 26065 26066 26067 26068 26069 26070 26071 26072 26073 26074 26075 26076 26077 26078 26079 26080 26081 26082 | */ static struct { int iId; /* ID that triggers a simulated fault. -1 means "any" */ int iErr; /* The error code to return on a fault */ int iCnt; /* Trigger the fault only if iCnt is already zero */ int iInterval; /* Reset iCnt to this value after each fault */ int eVerbose; /* When to print output */ int nHit; /* Number of hits seen so far */ int nRepeat; /* Turn off after this many hits. 0 for never */ int nSkip; /* Skip this many before first fault */ } faultsim_state = {-1, 0, 0, 0, 0, 0, 0}; /* ** This is the fault-sim callback */ static int faultsim_callback(int iArg){ if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){ return SQLITE_OK; } if( faultsim_state.iCnt ){ if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--; if( faultsim_state.eVerbose>=2 ){ oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); } return SQLITE_OK; } if( faultsim_state.eVerbose>=1 ){ oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); } faultsim_state.iCnt = faultsim_state.iInterval; faultsim_state.nHit++; if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){ faultsim_state.iCnt = -1; } return faultsim_state.iErr; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** |
︙ | ︙ | |||
29116 29117 29118 29119 29120 29121 29122 | isOk = 3; for(kk=2; kk<nArg; kk++){ const char *z = azArg[kk]; if( z[0]=='-' && z[1]=='-' ) z++; if( cli_strcmp(z,"off")==0 ){ sqlite3_test_control(testctrl, 0); }else if( cli_strcmp(z,"on")==0 ){ | | > | > > > > > > > > > | 29230 29231 29232 29233 29234 29235 29236 29237 29238 29239 29240 29241 29242 29243 29244 29245 29246 29247 29248 29249 29250 29251 29252 29253 29254 29255 29256 29257 29258 29259 29260 29261 29262 29263 29264 29265 29266 29267 29268 29269 29270 29271 29272 29273 29274 | isOk = 3; for(kk=2; kk<nArg; kk++){ const char *z = azArg[kk]; if( z[0]=='-' && z[1]=='-' ) z++; if( cli_strcmp(z,"off")==0 ){ sqlite3_test_control(testctrl, 0); }else if( cli_strcmp(z,"on")==0 ){ faultsim_state.iCnt = faultsim_state.nSkip; if( faultsim_state.iErr==0 ) faultsim_state.iErr = 1; faultsim_state.nHit = 0; sqlite3_test_control(testctrl, faultsim_callback); }else if( cli_strcmp(z,"reset")==0 ){ faultsim_state.iCnt = faultsim_state.nSkip; faultsim_state.nHit = 0; sqlite3_test_control(testctrl, faultsim_callback); }else if( cli_strcmp(z,"status")==0 ){ oputf("faultsim.iId: %d\n", faultsim_state.iId); oputf("faultsim.iErr: %d\n", faultsim_state.iErr); oputf("faultsim.iCnt: %d\n", faultsim_state.iCnt); oputf("faultsim.nHit: %d\n", faultsim_state.nHit); oputf("faultsim.iInterval: %d\n", faultsim_state.iInterval); oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose); oputf("faultsim.nRepeat: %d\n", faultsim_state.nRepeat); oputf("faultsim.nSkip: %d\n", faultsim_state.nSkip); }else if( cli_strcmp(z,"-v")==0 ){ if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++; }else if( cli_strcmp(z,"-q")==0 ){ if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--; }else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){ faultsim_state.iId = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-errcode")==0 && kk+1<nArg ){ faultsim_state.iErr = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-interval")==0 && kk+1<nArg ){ faultsim_state.iInterval = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-repeat")==0 && kk+1<nArg ){ faultsim_state.nRepeat = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){ faultsim_state.nSkip = atoi(azArg[++kk]); }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){ bShowHelp = 1; }else{ eputf("Unrecognized fault_install argument: \"%s\"\n", azArg[kk]); rc = 1; bShowHelp = 1; |
︙ | ︙ | |||
29160 29161 29162 29163 29164 29165 29166 29167 29168 29169 29170 29171 29172 29173 | " reset Reset the trigger counter\n" " status Show current status\n" " -v Increase verbosity\n" " -q Decrease verbosity\n" " --errcode N When triggered, return N as error code\n" " --id ID Trigger only for the ID specified\n" " --interval N Trigger only after every N-th call\n" ); } break; } } } if( isOk==0 && iCtrl>=0 ){ | > > | 29284 29285 29286 29287 29288 29289 29290 29291 29292 29293 29294 29295 29296 29297 29298 29299 | " reset Reset the trigger counter\n" " status Show current status\n" " -v Increase verbosity\n" " -q Decrease verbosity\n" " --errcode N When triggered, return N as error code\n" " --id ID Trigger only for the ID specified\n" " --interval N Trigger only after every N-th call\n" " --repeat N Turn off after N hits. 0 means never\n" " --skip N Skip the first N encounters\n" ); } break; } } } if( isOk==0 && iCtrl>=0 ){ |
︙ | ︙ |
Changes to extsrc/sqlite3.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** 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 | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** 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 ** de8182cf1773ac0d04268d896a613841cf6b. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif /************** Begin file sqliteInt.h ***************************************/ |
︙ | ︙ | |||
457 458 459 460 461 462 463 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.46.0" #define SQLITE_VERSION_NUMBER 3046000 | | | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.46.0" #define SQLITE_VERSION_NUMBER 3046000 #define SQLITE_SOURCE_ID "2024-05-21 11:11:29 de8182cf1773ac0d04268d896a613841cf6bf61f9f030342170657d5e06f2acb" /* ** 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 |
︙ | ︙ | |||
10251 10252 10253 10254 10255 10256 10257 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular | | | | < | | < > | | < | > > > > | > > | > > > > > > > > > > > > > > > > > | 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular ** order, as long as rows with the same values in all columns identified ** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows ** contain the same values for all columns identified by "colUsed", all but ** one such row may optionally be omitted from the result.)^ ** The virtual table is not required to omit rows that are duplicates ** over the "colUsed" columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. ** <li value="3"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the ** virtual table must return rows in the order defined by "aOrderBy" as ** if the sqlite3_vtab_distinct() interface had returned 0. However if ** two or more rows in the result have the same values for all columns ** identified by "colUsed", then all but one such row may optionally be ** omitted.)^ Like when the return value is 2, the virtual table ** is not required to omit rows that are duplicates over the "colUsed" ** columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** </ol> ** ** <p>The following table summarizes the conditions under which the ** virtual table is allowed to set the "orderByConsumed" flag based on ** the value returned by sqlite3_vtab_distinct(). This table is a ** restatement of the previous four paragraphs: ** ** <table border=1 cellspacing=0 cellpadding=10 width="90%"> ** <tr> ** <td valign="top">sqlite3_vtab_distinct() return value ** <td valign="top">Rows are returned in aOrderBy order ** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent ** <td valign="top">Duplicates over all colUsed columns may be omitted ** <tr><td>0<td>yes<td>yes<td>no ** <tr><td>1<td>no<td>yes<td>no ** <tr><td>2<td>no<td>yes<td>yes ** <tr><td>3<td>yes<td>yes<td>yes ** </table> ** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" ** (or "IS NOT DISTINCT FROM") and not "==". ** ** If a virtual table implementation is unable to meet the requirements |
︙ | ︙ | |||
12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 12322 12323 12324 12325 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** In all cases, if an error occurs the state of the final contents of the ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup | > > > > > > > > > > > > > > > > > > > > > > > > | 12333 12334 12335 12336 12337 12338 12339 12340 12341 12342 12343 12344 12345 12346 12347 12348 12349 12350 12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 12365 12366 12367 12368 12369 12370 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** In all cases, if an error occurs the state of the final contents of the ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); /* ** CAPI3REF: Add A Single Change To A Changegroup ** METHOD: sqlite3_changegroup ** ** This function adds the single change currently indicated by the iterator ** passed as the second argument to the changegroup object. The rules for ** adding the change are just as described for [sqlite3changegroup_add()]. ** ** If the change is successfully added to the changegroup, SQLITE_OK is ** returned. Otherwise, an SQLite error code is returned. ** ** The iterator must point to a valid entry when this function is called. ** If it does not, SQLITE_ERROR is returned and no change is added to the ** changegroup. Additionally, the iterator must not have been opened with ** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also ** returned. */ SQLITE_API int sqlite3changegroup_add_change( sqlite3_changegroup*, sqlite3_changeset_iter* ); /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup |
︙ | ︙ | |||
14610 14611 14612 14613 14614 14615 14616 | #define TK_FILTER 166 #define TK_COLUMN 167 #define TK_AGG_FUNCTION 168 #define TK_AGG_COLUMN 169 #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 | | | | 14655 14656 14657 14658 14659 14660 14661 14662 14663 14664 14665 14666 14667 14668 14669 14670 | #define TK_FILTER 166 #define TK_COLUMN 167 #define TK_AGG_FUNCTION 168 #define TK_AGG_COLUMN 169 #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 #define TK_UPLUS 173 #define TK_UMINUS 174 #define TK_TRUTH 175 #define TK_REGISTER 176 #define TK_VECTOR 177 #define TK_SELECT_COLUMN 178 #define TK_IF_NULL_ROW 179 #define TK_ASTERISK 180 #define TK_SPAN 181 |
︙ | ︙ | |||
19236 19237 19238 19239 19240 19241 19242 19243 19244 19245 19246 19247 19248 19249 | unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ union { Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ } u3; Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ | > | 19281 19282 19283 19284 19285 19286 19287 19288 19289 19290 19291 19292 19293 19294 19295 | unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ unsigned rowidUsed :1; /* The ROWID of this table is referenced */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ union { Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ } u3; Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ |
︙ | ︙ | |||
20780 20781 20782 20783 20784 20785 20786 | 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*); | | | 20826 20827 20828 20829 20830 20831 20832 20833 20834 20835 20836 20837 20838 20839 20840 | 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 int 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*); |
︙ | ︙ | |||
21191 21192 21193 21194 21195 21196 21197 21198 21199 21200 21201 21202 21203 21204 21205 | SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr); SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*); SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); SQLITE_PRIVATE void sqlite3Error(sqlite3*,int); SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*); SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int); SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); #if defined(SQLITE_NEED_ERR_NAME) SQLITE_PRIVATE const char *sqlite3ErrName(int); #endif | > > | 21237 21238 21239 21240 21241 21242 21243 21244 21245 21246 21247 21248 21249 21250 21251 21252 21253 | SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr); SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*); SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); SQLITE_PRIVATE void sqlite3Error(sqlite3*,int); SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*); SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int); #if !defined(SQLITE_OMIT_BLOB_LITERAL) SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); #endif SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); #if defined(SQLITE_NEED_ERR_NAME) SQLITE_PRIVATE const char *sqlite3ErrName(int); #endif |
︙ | ︙ | |||
31943 31944 31945 31946 31947 31948 31949 | sqlite3_str_append(pAccum, ".", 1); } sqlite3_str_appendall(pAccum, pItem->zName); }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; | | | 31991 31992 31993 31994 31995 31996 31997 31998 31999 32000 32001 32002 32003 32004 32005 | sqlite3_str_append(pAccum, ".", 1); } sqlite3_str_appendall(pAccum, pItem->zName); }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; assert( pSel!=0 ); /* Because of tag-20240424-1 */ if( pSel->selFlags & SF_NestedFrom ){ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); }else if( pSel->selFlags & SF_MultiValue ){ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", pItem->u1.nRow); }else{ |
︙ | ︙ | |||
32726 32727 32728 32729 32730 32731 32732 | StrAccum x; int n = 0; char zLine[1000]; sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pTab ){ | | | > > | 32774 32775 32776 32777 32778 32779 32780 32781 32782 32783 32784 32785 32786 32787 32788 32789 32790 32791 | StrAccum x; int n = 0; char zLine[1000]; sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pTab ){ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed, pItem->fg.rowidUsed ? "+rowid" : ""); } if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); }else if( pItem->fg.jointype & JT_LEFT ){ sqlite3_str_appendf(&x, " LEFT-JOIN"); }else if( pItem->fg.jointype & JT_RIGHT ){ sqlite3_str_appendf(&x, " RIGHT-JOIN"); |
︙ | ︙ | |||
32767 32768 32769 32770 32771 32772 32773 32774 32775 32776 32777 32778 32779 32780 32781 32782 32783 32784 32785 32786 | if( pItem->pSelect ) n++; if( pItem->fg.isTabFunc ) n++; if( pItem->fg.isUsing ) n++; if( pItem->fg.isUsing ){ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } if( pItem->pSelect ){ if( pItem->pTab ){ Table *pTab = pItem->pTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } sqlite3TreeViewPop(&pView); } } | > > | 32817 32818 32819 32820 32821 32822 32823 32824 32825 32826 32827 32828 32829 32830 32831 32832 32833 32834 32835 32836 32837 32838 | if( pItem->pSelect ) n++; if( pItem->fg.isTabFunc ) n++; if( pItem->fg.isUsing ) n++; if( pItem->fg.isUsing ){ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } if( pItem->pSelect ){ sqlite3TreeViewPush(&pView, i+1<pSrc->nSrc); if( pItem->pTab ){ Table *pTab = pItem->pTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); sqlite3TreeViewPop(&pView); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } sqlite3TreeViewPop(&pView); } } |
︙ | ︙ | |||
32876 32877 32878 32879 32880 32881 32882 | if( p->pOrderBy ){ sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY"); } if( p->pLimit ){ sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); if( p->pLimit->pRight ){ | | | 32928 32929 32930 32931 32932 32933 32934 32935 32936 32937 32938 32939 32940 32941 32942 | if( p->pOrderBy ){ sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY"); } if( p->pLimit ){ sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); if( p->pLimit->pRight ){ sqlite3TreeViewItem(pView, "OFFSET", 0); sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); sqlite3TreeViewPop(&pView); } sqlite3TreeViewPop(&pView); } if( p->pPrior ){ const char *zOp = "UNION"; |
︙ | ︙ | |||
84675 84676 84677 84678 84679 84680 84681 | sqlite3 *db, /* Database handle */ const void *pRec, /* Pointer to buffer containing record */ int nRec, /* Size of buffer pRec in bytes */ int iCol, /* Column to extract */ sqlite3_value **ppVal /* OUT: Extracted value */ ){ u32 t = 0; /* a column type code */ | | | | | | 84727 84728 84729 84730 84731 84732 84733 84734 84735 84736 84737 84738 84739 84740 84741 84742 84743 84744 | sqlite3 *db, /* Database handle */ const void *pRec, /* Pointer to buffer containing record */ int nRec, /* Size of buffer pRec in bytes */ int iCol, /* Column to extract */ sqlite3_value **ppVal /* OUT: Extracted value */ ){ u32 t = 0; /* a column type code */ u32 nHdr; /* Size of the header in the record */ u32 iHdr; /* Next unread header byte */ i64 iField; /* Next unread data byte */ u32 szField = 0; /* Size of the current data field */ int i; /* Column index */ u8 *a = (u8*)pRec; /* Typecast byte array */ Mem *pMem = *ppVal; /* Write result into this Mem object */ assert( iCol>0 ); iHdr = getVarint32(a, nHdr); if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; |
︙ | ︙ | |||
88136 88137 88138 88139 88140 88141 88142 | p->nChange = 0; } } } /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ | | | 88188 88189 88190 88191 88192 88193 88194 88195 88196 88197 88198 88199 88200 88201 88202 | p->nChange = 0; } } } /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ (void)sqlite3VdbeCheckFk(p, 0); } /* If the auto-commit flag is set and this is the only active writer ** VM, then we do either a commit or rollback of the current transaction. ** ** Note: This block also runs if one of the special errors handled ** above has occurred. |
︙ | ︙ | |||
97639 97640 97641 97642 97643 97644 97645 | ** ** A pseudo-table created by this opcode is used to hold a single ** row output from the sorter so that the row can be decomposed into ** individual columns using the OP_Column opcode. The OP_Column opcode ** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by | | > | 97691 97692 97693 97694 97695 97696 97697 97698 97699 97700 97701 97702 97703 97704 97705 97706 | ** ** A pseudo-table created by this opcode is used to hold a single ** row output from the sorter so that the row can be decomposed into ** individual columns using the OP_Column opcode. The OP_Column opcode ** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by ** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor ** will return NULL for every column. */ case OP_OpenPseudo: { VdbeCursor *pCx; assert( pOp->p1>=0 ); assert( pOp->p3>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p3, CURTYPE_PSEUDO); |
︙ | ︙ | |||
105797 105798 105799 105800 105801 105802 105803 | sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); } break; } #ifdef SQLITE_ENABLE_STMT_SCANSTATUS case 9: /* nexec */ | | | | 105850 105851 105852 105853 105854 105855 105856 105857 105858 105859 105860 105861 105862 105863 105864 105865 105866 105867 | sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); } break; } #ifdef SQLITE_ENABLE_STMT_SCANSTATUS case 9: /* nexec */ sqlite3_result_int64(ctx, pOp->nExec); break; case 10: /* ncycle */ sqlite3_result_int64(ctx, pOp->nCycle); break; #else case 9: /* nexec */ case 10: /* ncycle */ sqlite3_result_int(ctx, 0); break; #endif |
︙ | ︙ | |||
107193 107194 107195 107196 107197 107198 107199 | #ifndef SQLITE_OMIT_TRIGGER if( pParse->pTriggerTab!=0 ){ int op = pParse->eTriggerOp; assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); if( pParse->bReturning ){ if( (pNC->ncFlags & NC_UBaseReg)!=0 && ALWAYS(zTab==0 | | > | 107246 107247 107248 107249 107250 107251 107252 107253 107254 107255 107256 107257 107258 107259 107260 107261 | #ifndef SQLITE_OMIT_TRIGGER if( pParse->pTriggerTab!=0 ){ int op = pParse->eTriggerOp; assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); if( pParse->bReturning ){ if( (pNC->ncFlags & NC_UBaseReg)!=0 && ALWAYS(zTab==0 || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0 || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0)) ){ pExpr->iTable = op!=TK_DELETE; pTab = pParse->pTriggerTab; } }else if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){ pExpr->iTable = 1; pTab = pParse->pTriggerTab; |
︙ | ︙ | |||
107492 107493 107494 107495 107496 107497 107498 | ** 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. */ | > | | > > > | 107546 107547 107548 107549 107550 107551 107552 107553 107554 107555 107556 107557 107558 107559 107560 107561 107562 107563 107564 107565 | ** 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( pMatch ){ if( pExpr->iColumn>=0 ){ pMatch->colUsed |= sqlite3ExprColUsed(pExpr); }else{ pMatch->fg.rowidUsed = 1; } } pExpr->op = eNewExprOp; lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); #ifndef SQLITE_OMIT_AUTHORIZATION |
︙ | ︙ | |||
107945 107946 107947 107948 107949 107950 107951 | #ifndef SQLITE_OMIT_WINDOWFUNC pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0)); #else pNC->ncFlags &= ~NC_AllowAgg; #endif } } | < | < | 108003 108004 108005 108006 108007 108008 108009 108010 108011 108012 108013 108014 108015 108016 108017 108018 108019 | #ifndef SQLITE_OMIT_WINDOWFUNC pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0)); #else pNC->ncFlags &= ~NC_AllowAgg; #endif } } else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){ is_agg = 1; } sqlite3WalkExprList(pWalker, pList); if( is_agg ){ if( pExpr->pLeft ){ assert( pExpr->pLeft->op==TK_ORDER ); assert( ExprUseXList(pExpr->pLeft) ); sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); } |
︙ | ︙ | |||
108554 108555 108556 108557 108558 108559 108560 108561 108562 108563 108564 108565 108566 108567 | } /* 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; sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; | > | 108610 108611 108612 108613 108614 108615 108616 108617 108618 108619 108620 108621 108622 108623 108624 | } /* 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]; assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/ 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; sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; |
︙ | ︙ | |||
110313 110314 110315 110316 110317 110318 110319 110320 110321 110322 110323 110324 110325 110326 110327 110328 110329 110330 110331 110332 110333 110334 | /* ** Recursively delete an expression tree. */ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ assert( p!=0 ); assert( db!=0 ); assert( !ExprUseUValue(p) || p->u.iValue>=0 ); assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); #ifdef SQLITE_DEBUG if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ assert( p->pLeft==0 ); assert( p->pRight==0 ); assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); assert( !ExprUseXList(p) || p->x.pList==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); | > < > > > > > > > > > > > > > | 110370 110371 110372 110373 110374 110375 110376 110377 110378 110379 110380 110381 110382 110383 110384 110385 110386 110387 110388 110389 110390 110391 110392 110393 110394 110395 110396 110397 110398 110399 110400 110401 110402 110403 110404 110405 110406 110407 110408 110409 110410 110411 110412 110413 110414 110415 110416 110417 110418 110419 110420 110421 110422 110423 110424 110425 | /* ** Recursively delete an expression tree. */ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ assert( p!=0 ); assert( db!=0 ); exprDeleteRestart: assert( !ExprUseUValue(p) || p->u.iValue>=0 ); assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); #ifdef SQLITE_DEBUG if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ assert( p->pLeft==0 ); assert( p->pRight==0 ); assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); assert( !ExprUseXList(p) || p->x.pList==0 ); } #endif if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); if( p->pRight ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlite3ExprDeleteNN(db, p->pRight); }else if( ExprUseXSelect(p) ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlite3SelectDelete(db, p->x.pSelect); }else{ sqlite3ExprListDelete(db, p->x.pList); #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(p, EP_WinFunc) ){ sqlite3WindowDelete(db, p->y.pWin); } #endif } if( p->pLeft && p->op!=TK_SELECT_COLUMN ){ Expr *pLeft = p->pLeft; if( !ExprHasProperty(p, EP_Static) && !ExprHasProperty(pLeft, EP_Static) ){ /* Avoid unnecessary recursion on unary operators */ sqlite3DbNNFreeNN(db, p); p = pLeft; goto exprDeleteRestart; }else{ sqlite3ExprDeleteNN(db, pLeft); } } } if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbNNFreeNN(db, p); } } SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ |
︙ | ︙ | |||
110375 110376 110377 110378 110379 110380 110381 | /* ** Arrange to cause pExpr to be deleted when the pParse is deleted. ** This is similar to sqlite3ExprDelete() except that the delete is ** deferred until the pParse is deleted. ** ** The pExpr might be deleted immediately on an OOM error. ** | | | | | | 110445 110446 110447 110448 110449 110450 110451 110452 110453 110454 110455 110456 110457 110458 110459 110460 110461 110462 110463 | /* ** Arrange to cause pExpr to be deleted when the pParse is deleted. ** This is similar to sqlite3ExprDelete() except that the delete is ** deferred until the pParse is deleted. ** ** The pExpr might be deleted immediately on an OOM error. ** ** Return 0 if the delete was successfully deferred. Return non-zero ** if the delete happened immediately because of an OOM. */ SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); } /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the ** expression. */ SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ if( p ){ |
︙ | ︙ | |||
111338 111339 111340 111341 111342 111343 111344 | || pDef->xFinalize!=0 || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 || ExprHasProperty(pExpr, EP_WinFunc) ){ pWalker->eCode = 0; return WRC_Abort; } | | | 111408 111409 111410 111411 111412 111413 111414 111415 111416 111417 111418 111419 111420 111421 111422 | || pDef->xFinalize!=0 || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 || ExprHasProperty(pExpr, EP_WinFunc) ){ pWalker->eCode = 0; return WRC_Abort; } return WRC_Prune; } /* ** These routines are Walker callbacks used to check expressions to ** see if they are "constant" for some definition of constant. The ** Walker.eCode value determines the type of "constant" we are looking |
︙ | ︙ | |||
115613 115614 115615 115616 115617 115618 115619 | sqlite3 *db = pParse->db; assert( iAgg>=0 ); if( pExpr->op!=TK_AGG_FUNCTION ){ if( iAgg<pAggInfo->nColumn && pAggInfo->aCol[iAgg].pCExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); | | < | < | 115683 115684 115685 115686 115687 115688 115689 115690 115691 115692 115693 115694 115695 115696 115697 115698 115699 115700 115701 115702 115703 115704 115705 115706 115707 115708 | sqlite3 *db = pParse->db; assert( iAgg>=0 ); if( pExpr->op!=TK_AGG_FUNCTION ){ if( iAgg<pAggInfo->nColumn && pAggInfo->aCol[iAgg].pCExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; } } }else{ assert( pExpr->op==TK_AGG_FUNCTION ); if( ALWAYS(iAgg<pAggInfo->nFunc) && pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aFunc[iAgg].pFExpr = pExpr; } } } } return WRC_Continue; } |
︙ | ︙ | |||
124217 124218 124219 124220 124221 124222 124223 | /* Test for cycles in generated columns and illegal expressions ** in CHECK constraints and in DEFAULT clauses. */ if( p->tabFlags & TF_HasGenerated ){ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } | < < < | 124285 124286 124287 124288 124289 124290 124291 124292 124293 124294 124295 124296 124297 124298 | /* Test for cycles in generated columns and illegal expressions ** in CHECK constraints and in DEFAULT clauses. */ if( p->tabFlags & TF_HasGenerated ){ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } } /* Add the table to the in-memory representation of the database. */ if( db->init.busy ){ Table *pOld; Schema *pSchema = p->pSchema; |
︙ | ︙ | |||
140349 140350 140351 140352 140353 140354 140355 | /* Initialize the VDBE program */ pParse->nMem = 6; /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ | | | 140414 140415 140416 140417 140418 140419 140420 140421 140422 140423 140424 140425 140426 140427 140428 | /* Initialize the VDBE program */ pParse->nMem = 6; /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ if( sqlite3GetInt32(pValue->z, &mxErr) ){ if( mxErr<=0 ){ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; } }else{ pObjTab = sqlite3LocateTable(pParse, 0, zRight, iDb>=0 ? db->aDb[iDb].zDbSName : 0); } |
︙ | ︙ | |||
141557 141558 141559 141560 141561 141562 141563 141564 141565 141566 141567 141568 141569 141570 | } /* Clear all content from pragma virtual table cursor. */ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ int i; sqlite3_finalize(pCsr->pPragma); pCsr->pPragma = 0; for(i=0; i<ArraySize(pCsr->azArg); i++){ sqlite3_free(pCsr->azArg[i]); pCsr->azArg[i] = 0; } } /* Close a pragma virtual table cursor */ | > | 141622 141623 141624 141625 141626 141627 141628 141629 141630 141631 141632 141633 141634 141635 141636 | } /* Clear all content from pragma virtual table cursor. */ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ int i; sqlite3_finalize(pCsr->pPragma); pCsr->pPragma = 0; pCsr->iRowid = 0; for(i=0; i<ArraySize(pCsr->azArg); i++){ sqlite3_free(pCsr->azArg[i]); pCsr->azArg[i] = 0; } } /* Close a pragma virtual table cursor */ |
︙ | ︙ | |||
142357 142358 142359 142360 142361 142362 142363 | ** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj); */ SQLITE_PRIVATE void *sqlite3ParserAddCleanup( Parse *pParse, /* Destroy when this Parser finishes */ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ void *pPtr /* Pointer to object to be cleaned up */ ){ | > > > > > | > | 142423 142424 142425 142426 142427 142428 142429 142430 142431 142432 142433 142434 142435 142436 142437 142438 142439 142440 142441 142442 142443 | ** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj); */ SQLITE_PRIVATE void *sqlite3ParserAddCleanup( Parse *pParse, /* Destroy when this Parser finishes */ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ void *pPtr /* Pointer to object to be cleaned up */ ){ ParseCleanup *pCleanup; if( sqlite3FaultSim(300) ){ pCleanup = 0; sqlite3OomFault(pParse->db); }else{ pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); } if( pCleanup ){ pCleanup->pNext = pParse->pCleanup; pParse->pCleanup = pCleanup; pCleanup->pPtr = pPtr; pCleanup->xCleanup = xCleanup; }else{ xCleanup(pParse->db, pPtr); |
︙ | ︙ | |||
145130 145131 145132 145133 145134 145135 145136 | CollSeq *pColl; int i,j; Expr *p; struct ExprList_item *a; NameContext sNC; assert( pSelect!=0 ); | < | > > > > > > > | < < | | 145202 145203 145204 145205 145206 145207 145208 145209 145210 145211 145212 145213 145214 145215 145216 145217 145218 145219 145220 145221 145222 145223 145224 145225 145226 145227 145228 145229 145230 145231 145232 145233 145234 145235 145236 145237 145238 145239 145240 145241 145242 | CollSeq *pColl; int i,j; Expr *p; struct ExprList_item *a; NameContext sNC; assert( pSelect!=0 ); assert( (pSelect->selFlags & SF_Resolved)!=0 ); assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); if( db->mallocFailed || IN_RENAME_OBJECT ) return; while( pSelect->pPrior ) pSelect = pSelect->pPrior; a = pSelect->pEList->a; memset(&sNC, 0, sizeof(sNC)); sNC.pSrcList = pSelect->pSrc; for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ const char *zType; i64 n; int m = 0; Select *pS2 = pSelect; pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); p = a[i].pExpr; /* pCol->szEst = ... // Column size est for SELECT tables never used */ pCol->affinity = sqlite3ExprAffinity(p); while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); pS2 = pS2->pNext; pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr); } if( pCol->affinity<=SQLITE_AFF_NONE ){ pCol->affinity = aff; } if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){ for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); } if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ pCol->affinity = SQLITE_AFF_BLOB; }else if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){ pCol->affinity = SQLITE_AFF_BLOB; |
︙ | ︙ | |||
145182 145183 145184 145185 145186 145187 145188 | zType = sqlite3StdType[j]; break; } } } } if( zType ){ | | | | | 145258 145259 145260 145261 145262 145263 145264 145265 145266 145267 145268 145269 145270 145271 145272 145273 145274 145275 145276 145277 | zType = sqlite3StdType[j]; break; } } } } if( zType ){ const i64 k = sqlite3Strlen30(zType); n = sqlite3Strlen30(pCol->zCnName); pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2); pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); if( pCol->zCnName ){ memcpy(&pCol->zCnName[n+1], zType, k+1); pCol->colFlags |= COLFLAG_HASTYPE; } } pColl = sqlite3ExprCollSeq(pParse, p); if( pColl ){ assert( pTab->pIndex==0 ); sqlite3ColumnSetColl(db, pCol, pColl->zName); |
︙ | ︙ | |||
149213 149214 149215 149216 149217 149218 149219 | int i; SrcList *pTabList; SrcItem *pFrom; if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; | < | | 149289 149290 149291 149292 149293 149294 149295 149296 149297 149298 149299 149300 149301 149302 149303 | int i; SrcList *pTabList; SrcItem *pFrom; if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; assert( (p->selFlags & SF_Resolved) ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; assert( pTab!=0 ); if( (pTab->tabFlags & TF_Ephemeral)!=0 ){ /* A sub-query in the FROM clause of a SELECT */ Select *pSel = pFrom->pSelect; |
︙ | ︙ | |||
152574 152575 152576 152577 152578 152579 152580 152581 152582 152583 152584 152585 152586 152587 | pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName); pItem->fg.eEName = pList->a[i].fg.eEName; } } } return pNew; } /* ** Generate code for the RETURNING trigger. Unlike other triggers ** that invoke a subprogram in the bytecode, the code for RETURNING ** is generated in-line. */ static void codeReturningTrigger( | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 152649 152650 152651 152652 152653 152654 152655 152656 152657 152658 152659 152660 152661 152662 152663 152664 152665 152666 152667 152668 152669 152670 152671 152672 152673 152674 152675 152676 152677 152678 152679 152680 152681 152682 152683 152684 152685 152686 152687 152688 152689 152690 152691 152692 152693 152694 152695 152696 152697 152698 152699 152700 152701 152702 152703 152704 152705 152706 152707 152708 152709 152710 152711 152712 152713 152714 152715 152716 152717 152718 152719 152720 152721 152722 152723 152724 152725 152726 152727 152728 | pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName); pItem->fg.eEName = pList->a[i].fg.eEName; } } } return pNew; } /* If the Expr node is a subquery or an EXISTS operator or an IN operator that ** uses a subquery, and if the subquery is SF_Correlated, then mark the ** expression as EP_VarSelect. */ static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ UNUSED_PARAMETER(NotUsed); if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 ){ testcase( ExprHasProperty(pExpr, EP_VarSelect) ); ExprSetProperty(pExpr, EP_VarSelect); } return WRC_Continue; } /* ** If the SELECT references the table pWalker->u.pTab, then do two things: ** ** (1) Mark the SELECT as as SF_Correlated. ** (2) Set pWalker->eCode to non-zero so that the caller will know ** that (1) has happened. */ static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ int i; SrcList *pSrc; assert( pSelect!=0 ); pSrc = pSelect->pSrc; assert( pSrc!=0 ); for(i=0; i<pSrc->nSrc; i++){ if( pSrc->a[i].pTab==pWalker->u.pTab ){ testcase( pSelect->selFlags & SF_Correlated ); pSelect->selFlags |= SF_Correlated; pWalker->eCode = 1; break; } } return WRC_Continue; } /* ** Scan the expression list that is the argument to RETURNING looking ** for subqueries that depend on the table which is being modified in the ** statement that is hosting the RETURNING clause (pTab). Mark all such ** subqueries as SF_Correlated. If the subqueries are part of an ** expression, mark the expression as EP_VarSelect. ** ** https://sqlite.org/forum/forumpost/2c83569ce8945d39 */ static void sqlite3ProcessReturningSubqueries( ExprList *pEList, Table *pTab ){ Walker w; memset(&w, 0, sizeof(w)); w.xExprCallback = sqlite3ExprWalkNoop; w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; w.u.pTab = pTab; sqlite3WalkExprList(&w, pEList); if( w.eCode ){ w.xExprCallback = sqlite3ReturningSubqueryVarSelect; w.xSelectCallback = sqlite3SelectWalkNoop; sqlite3WalkExprList(&w, pEList); } } /* ** Generate code for the RETURNING trigger. Unlike other triggers ** that invoke a subprogram in the bytecode, the code for RETURNING ** is generated in-line. */ static void codeReturningTrigger( |
︙ | ︙ | |||
152611 152612 152613 152614 152615 152616 152617 152618 152619 152620 152621 152622 152623 152624 | } memset(&sSelect, 0, sizeof(sSelect)); memset(&sFrom, 0, sizeof(sFrom)); sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); sSelect.pSrc = &sFrom; sFrom.nSrc = 1; sFrom.a[0].pTab = pTab; sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); if( pParse->nErr==0 ){ assert( db->mallocFailed==0 ); sqlite3GenerateColumnNames(pParse, &sSelect); } sqlite3ExprListDelete(db, sSelect.pEList); | > | 152752 152753 152754 152755 152756 152757 152758 152759 152760 152761 152762 152763 152764 152765 152766 | } memset(&sSelect, 0, sizeof(sSelect)); memset(&sFrom, 0, sizeof(sFrom)); sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); sSelect.pSrc = &sFrom; sFrom.nSrc = 1; sFrom.a[0].pTab = pTab; sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); if( pParse->nErr==0 ){ assert( db->mallocFailed==0 ); sqlite3GenerateColumnNames(pParse, &sSelect); } sqlite3ExprListDelete(db, sSelect.pEList); |
︙ | ︙ | |||
152637 152638 152639 152640 152641 152642 152643 152644 152645 152646 152647 152648 152649 152650 | pParse->pTriggerTab = pTab; if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK && ALWAYS(!db->mallocFailed) ){ int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; i<nCol; i++){ Expr *pCol = pNew->a[i].pExpr; assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ sqlite3ExprCodeFactorable(pParse, pCol, reg+i); if( sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ | > | 152779 152780 152781 152782 152783 152784 152785 152786 152787 152788 152789 152790 152791 152792 152793 | pParse->pTriggerTab = pTab; if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK && ALWAYS(!db->mallocFailed) ){ int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; sqlite3ProcessReturningSubqueries(pNew, pTab); pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; i<nCol; i++){ Expr *pCol = pNew->a[i].pExpr; assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ sqlite3ExprCodeFactorable(pParse, pCol, reg+i); if( sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ |
︙ | ︙ | |||
156067 156068 156069 156070 156071 156072 156073 | ** really are "CREATE" and "TABLE". If this is not the case, then ** sqlite3_declare_vtab() is being misused. */ z = (const unsigned char*)zCreateTable; for(i=0; aKeyword[i]; i++){ int tokenType = 0; do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE ); | | > > > | 156210 156211 156212 156213 156214 156215 156216 156217 156218 156219 156220 156221 156222 156223 156224 156225 156226 156227 | ** really are "CREATE" and "TABLE". If this is not the case, then ** sqlite3_declare_vtab() is being misused. */ z = (const unsigned char*)zCreateTable; for(i=0; aKeyword[i]; i++){ int tokenType = 0; do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE ); if( tokenType!=aKeyword[i] ){ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); return SQLITE_ERROR; } } sqlite3_mutex_enter(db->mutex); pCtx = db->pVtabCtx; if( !pCtx || pCtx->bDeclared ){ sqlite3Error(db, SQLITE_MISUSE_BKPT); sqlite3_mutex_leave(db->mutex); |
︙ | ︙ | |||
158588 158589 158590 158591 158592 158593 158594 158595 158596 158597 158598 158599 158600 158601 | addrNxt, r1, nEq); VdbeCoverage(pParse->pVdbe); } pLevel->regFilter = 0; pLevel->addrBrk = 0; } } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( Parse *pParse, /* Parsing context */ | > > > > > > > > > > > > > > > > > > > > > | 158734 158735 158736 158737 158738 158739 158740 158741 158742 158743 158744 158745 158746 158747 158748 158749 158750 158751 158752 158753 158754 158755 158756 158757 158758 158759 158760 158761 158762 158763 158764 158765 158766 158767 158768 | addrNxt, r1, nEq); VdbeCoverage(pParse->pVdbe); } pLevel->regFilter = 0; pLevel->addrBrk = 0; } } /* ** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...) ** operator. Return true if level pLoop is guaranteed to visit only one ** row for each key generated for the index. */ static int whereLoopIsOneRow(WhereLoop *pLoop){ if( pLoop->u.btree.pIndex->onError && pLoop->nSkip==0 && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol ){ int ii; for(ii=0; ii<pLoop->u.btree.nEq; ii++){ if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){ return 0; } } return 1; } return 0; } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( Parse *pParse, /* Parsing context */ |
︙ | ︙ | |||
158667 158668 158669 158670 158671 158672 158673 | */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); | | | 158834 158835 158836 158837 158838 158839 158840 158841 158842 158843 158844 158845 158846 158847 158848 | */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN match flag")); } /* Compute a safe address to jump to if we discover that the table for ** this loop is empty and can never contribute content. */ for(j=iLevel; j>0; j--){ if( pWInfo->a[j].iLeftJoin ) break; if( pWInfo->a[j].pRJ ) break; |
︙ | ︙ | |||
159336 159337 159338 159339 159340 159341 159342 | /* The following assert() is not a requirement, merely an observation: ** The OR-optimization doesn't work for the right hand table of ** a LEFT JOIN: */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); } /* Record the instruction used to terminate the loop. */ | | > > | 159503 159504 159505 159506 159507 159508 159509 159510 159511 159512 159513 159514 159515 159516 159517 159518 159519 | /* The following assert() is not a requirement, merely an observation: ** The OR-optimization doesn't work for the right hand table of ** a LEFT JOIN: */ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); } /* Record the instruction used to terminate the loop. */ if( (pLoop->wsFlags & WHERE_ONEROW) || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop)) ){ pLevel->op = OP_Noop; }else if( bRev ){ pLevel->op = OP_Prev; }else{ pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; |
︙ | ︙ | |||
159991 159992 159993 159994 159995 159996 159997 | pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; mAll |= pWInfo->a[k].pWLoop->maskSelf; if( pRight->fg.viaCoroutine ){ sqlite3VdbeAddOp3( v, OP_Null, 0, pRight->regResult, pRight->regResult + pRight->pSelect->pEList->nExpr-1 ); | < > | | | | < | 160160 160161 160162 160163 160164 160165 160166 160167 160168 160169 160170 160171 160172 160173 160174 160175 160176 160177 160178 | pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; mAll |= pWInfo->a[k].pWLoop->maskSelf; if( pRight->fg.viaCoroutine ){ sqlite3VdbeAddOp3( v, OP_Null, 0, pRight->regResult, pRight->regResult + pRight->pSelect->pEList->nExpr-1 ); } sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); iIdxCur = pWInfo->a[k].iIdxCur; if( iIdxCur ){ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); } } if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ mAll |= pLoop->maskSelf; for(k=0; k<pWC->nTerm; k++){ WhereTerm *pTerm = &pWC->a[k]; if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0 |
︙ | ︙ | |||
161698 161699 161700 161701 161702 161703 161704 161705 161706 161707 161708 161709 161710 161711 161712 161713 161714 161715 161716 161717 161718 | /* If this term has child terms, then they are also part of the ** pWC->a[] array. So this term can be ignored, as a LIMIT clause ** will only be added if each of the child terms passes the ** (leftCursor==iCsr) test below. */ continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; } /* Check condition (5). Return early if it is not met. */ if( pOrderBy ){ for(ii=0; ii<pOrderBy->nExpr; ii++){ Expr *pExpr = pOrderBy->a[ii].pExpr; if( pExpr->op!=TK_COLUMN ) return; if( pExpr->iTable!=iCsr ) return; if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; } } /* All conditions are met. Add the terms to the where-clause object. */ assert( p->pLimit->op==TK_LIMIT ); | > < < | > > > > | 161866 161867 161868 161869 161870 161871 161872 161873 161874 161875 161876 161877 161878 161879 161880 161881 161882 161883 161884 161885 161886 161887 161888 161889 161890 161891 161892 161893 161894 161895 161896 161897 161898 161899 161900 161901 161902 | /* If this term has child terms, then they are also part of the ** pWC->a[] array. So this term can be ignored, as a LIMIT clause ** will only be added if each of the child terms passes the ** (leftCursor==iCsr) test below. */ continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; if( pWC->a[ii].prereqRight!=0 ) return; } /* Check condition (5). Return early if it is not met. */ if( pOrderBy ){ for(ii=0; ii<pOrderBy->nExpr; ii++){ Expr *pExpr = pOrderBy->a[ii].pExpr; if( pExpr->op!=TK_COLUMN ) return; if( pExpr->iTable!=iCsr ) return; if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; } } /* All conditions are met. Add the terms to the where-clause object. */ assert( p->pLimit->op==TK_LIMIT ); if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){ whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); } if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){ whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); } } } /* ** Initialize a preallocated WhereClause structure. */ SQLITE_PRIVATE void sqlite3WhereClauseInit( |
︙ | ︙ | |||
162234 162235 162236 162237 162238 162239 162240 162241 162242 162243 162244 162245 162246 162247 | static Expr *whereRightSubexprIsColumn(Expr *p){ p = sqlite3ExprSkipCollateAndLikely(p->pRight); if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ return p; } return 0; } /* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). ** Return NULL if there are no more matching WhereTerms. */ static WhereTerm *whereScanNext(WhereScan *pScan){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 162405 162406 162407 162408 162409 162410 162411 162412 162413 162414 162415 162416 162417 162418 162419 162420 162421 162422 162423 162424 162425 162426 162427 162428 162429 162430 162431 162432 162433 162434 162435 162436 162437 162438 162439 162440 162441 162442 162443 162444 162445 162446 162447 162448 162449 162450 162451 162452 162453 162454 | static Expr *whereRightSubexprIsColumn(Expr *p){ p = sqlite3ExprSkipCollateAndLikely(p->pRight); if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ return p; } return 0; } /* ** Term pTerm is guaranteed to be a WO_IN term. It may be a component term ** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)". ** This function checks to see if the term is compatible with an index ** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so, ** it returns a pointer to the name of the collation sequence (e.g. "BINARY" ** or "NOCASE") used by the comparison in pTerm. If it is not compatible ** with affinity idxaff, NULL is returned. */ static SQLITE_NOINLINE const char *indexInAffinityOk( Parse *pParse, WhereTerm *pTerm, u8 idxaff ){ Expr *pX = pTerm->pExpr; Expr inexpr; assert( pTerm->eOperator & WO_IN ); if( sqlite3ExprIsVector(pX->pLeft) ){ int iField = pTerm->u.x.iField - 1; inexpr.flags = 0; inexpr.op = TK_EQ; inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr; assert( ExprUseXSelect(pX) ); inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr; pX = &inexpr; } if( sqlite3IndexAffinityOk(pX, idxaff) ){ CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX); return pRet ? pRet->zName : sqlite3StrBINARY; } return 0; } /* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). ** Return NULL if there are no more matching WhereTerms. */ static WhereTerm *whereScanNext(WhereScan *pScan){ |
︙ | ︙ | |||
162285 162286 162287 162288 162289 162290 162291 | pScan->aiColumn[j] = pX->iColumn; pScan->nEquiv++; } } if( (pTerm->eOperator & pScan->opMask)!=0 ){ /* Verify the affinity and collating sequence match */ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ | | > > > > > > | | | | | > > | | | 162492 162493 162494 162495 162496 162497 162498 162499 162500 162501 162502 162503 162504 162505 162506 162507 162508 162509 162510 162511 162512 162513 162514 162515 162516 162517 162518 162519 162520 162521 162522 162523 | pScan->aiColumn[j] = pX->iColumn; pScan->nEquiv++; } } if( (pTerm->eOperator & pScan->opMask)!=0 ){ /* Verify the affinity and collating sequence match */ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ const char *zCollName; Parse *pParse = pWC->pWInfo->pParse; pX = pTerm->pExpr; if( (pTerm->eOperator & WO_IN) ){ zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff); if( !zCollName ) continue; }else{ CollSeq *pColl; if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ continue; } assert(pX->pLeft); pColl = sqlite3ExprCompareCollSeq(pParse, pX); zCollName = pColl ? pColl->zName : sqlite3StrBINARY; } if( sqlite3StrICmp(zCollName, pScan->zCollName) ){ continue; } } if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0)) && pX->op==TK_COLUMN && pX->iTable==pScan->aiCur[0] |
︙ | ︙ | |||
163305 163306 163307 163308 163309 163310 163311 | } /* No matches cause a break out of the loop */ break; } if( i==n ){ nOrderBy = n; | | | 163520 163521 163522 163523 163524 163525 163526 163527 163528 163529 163530 163531 163532 163533 163534 | } /* No matches cause a break out of the loop */ break; } if( i==n ){ nOrderBy = n; if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ eDistinct = 1; } } } |
︙ | ︙ | |||
165945 165946 165947 165948 165949 165950 165951 165952 165953 165954 165955 165956 165957 165958 | ** Return true if pTerm is a virtual table LIMIT or OFFSET term. */ static int isLimitTerm(WhereTerm *pTerm){ assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; } /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This ** function marks a subset of those constraints usable, invokes the ** xBestIndex method and adds the returned plan to pBuilder. ** | > > > > > > > > > > > > > > > | 166160 166161 166162 166163 166164 166165 166166 166167 166168 166169 166170 166171 166172 166173 166174 166175 166176 166177 166178 166179 166180 166181 166182 166183 166184 166185 166186 166187 166188 | ** Return true if pTerm is a virtual table LIMIT or OFFSET term. */ static int isLimitTerm(WhereTerm *pTerm){ assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; } /* ** Return true if the first nCons constraints in the pUsage array are ** marked as in-use (have argvIndex>0). False otherwise. */ static int allConstraintsUsed( struct sqlite3_index_constraint_usage *aUsage, int nCons ){ int ii; for(ii=0; ii<nCons; ii++){ if( aUsage[ii].argvIndex<=0 ) return 0; } return 1; } /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This ** function marks a subset of those constraints usable, invokes the ** xBestIndex method and adds the returned plan to pBuilder. ** |
︙ | ︙ | |||
166086 166087 166088 166089 166090 166091 166092 166093 | ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } assert( pbRetryLimit || !isLimitTerm(pTerm) ); | > > > > > | | > > | | | 166316 166317 166318 166319 166320 166321 166322 166323 166324 166325 166326 166327 166328 166329 166330 166331 166332 166333 166334 166335 166336 166337 166338 166339 166340 166341 166342 166343 | ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET ** terms. And if there are any, they should follow all other terms. */ assert( pbRetryLimit || !isLimitTerm(pTerm) ); assert( !isLimitTerm(pTerm) || i>=nConstraint-2 ); assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) ); if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){ /* If there is an IN(...) term handled as an == (separate call to ** xFilter for each value on the RHS of the IN) and a LIMIT or ** OFFSET term handled as well, the plan is unusable. Similarly, ** if there is a LIMIT/OFFSET and there are other unused terms, ** the plan cannot be used. In these cases set variable *pbRetryLimit ** to true to tell the caller to retry with LIMIT and OFFSET ** disabled. */ if( pIdxInfo->needToFreeIdxStr ){ sqlite3_free(pIdxInfo->idxStr); pIdxInfo->idxStr = 0; pIdxInfo->needToFreeIdxStr = 0; } *pbRetryLimit = 1; return SQLITE_OK; |
︙ | ︙ | |||
167855 167856 167857 167858 167859 167860 167861 167862 167863 167864 167865 167866 167867 167868 | pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName, (double)sqlite3LogEstToInt(pTab->nRowLogEst))); } } nSearch += pLoop->nOut; } } /* ** The index pIdx is used by a query and contains one or more expressions. ** In other words pIdx is an index on an expression. iIdxCur is the cursor ** number for the index and iDataCur is the cursor number for the corresponding ** table. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 168092 168093 168094 168095 168096 168097 168098 168099 168100 168101 168102 168103 168104 168105 168106 168107 168108 168109 168110 168111 168112 168113 168114 168115 168116 168117 168118 168119 168120 168121 168122 168123 168124 168125 168126 168127 168128 168129 168130 168131 168132 168133 168134 168135 168136 168137 168138 168139 168140 168141 168142 168143 168144 168145 168146 168147 168148 168149 168150 168151 168152 168153 168154 168155 168156 168157 | pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName, (double)sqlite3LogEstToInt(pTab->nRowLogEst))); } } nSearch += pLoop->nOut; } } /* ** Expression Node callback for sqlite3ExprCanReturnSubtype(). ** ** Only a function call is able to return a subtype. So if the node ** is not a function call, return WRC_Prune immediately. ** ** A function call is able to return a subtype if it has the ** SQLITE_RESULT_SUBTYPE property. ** ** Assume that every function is able to pass-through a subtype from ** one of its argument (using sqlite3_result_value()). Most functions ** are not this way, but we don't have a mechanism to distinguish those ** that are from those that are not, so assume they all work this way. ** That means that if one of its arguments is another function and that ** other function is able to return a subtype, then this function is ** able to return a subtype. */ static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ int n; FuncDef *pDef; sqlite3 *db; if( pExpr->op!=TK_FUNCTION ){ return WRC_Prune; } assert( ExprUseXList(pExpr) ); db = pWalker->pParse->db; n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ pWalker->eCode = 1; return WRC_Prune; } return WRC_Continue; } /* ** Return TRUE if expression pExpr is able to return a subtype. ** ** A TRUE return does not guarantee that a subtype will be returned. ** It only indicates that a subtype return is possible. False positives ** are acceptable as they only disable an optimization. False negatives, ** on the other hand, can lead to incorrect answers. */ static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ Walker w; memset(&w, 0, sizeof(w)); w.pParse = pParse; w.xExprCallback = exprNodeCanReturnSubtype; sqlite3WalkExpr(&w, pExpr); return w.eCode; } /* ** The index pIdx is used by a query and contains one or more expressions. ** In other words pIdx is an index on an expression. iIdxCur is the cursor ** number for the index and iDataCur is the cursor number for the corresponding ** table. ** |
︙ | ︙ | |||
167889 167890 167891 167892 167893 167894 167895 | pExpr = pIdx->aColExpr->a[i].pExpr; }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); }else{ continue; } if( sqlite3ExprIsConstant(0,pExpr) ) continue; | | < < < < < < < | < | 168178 168179 168180 168181 168182 168183 168184 168185 168186 168187 168188 168189 168190 168191 168192 168193 168194 168195 168196 | pExpr = pIdx->aColExpr->a[i].pExpr; }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); }else{ continue; } if( sqlite3ExprIsConstant(0,pExpr) ) continue; if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ /* Functions that might set a subtype should not be replaced by the ** value taken from an expression index since the index omits the ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ continue; } p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); if( p==0 ) break; p->pIENext = pParse->pIdxEpr; #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace & 0x200 ){ sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i); |
︙ | ︙ | |||
168881 168882 168883 168884 168885 168886 168887 | assert( pLevel->iTabCur==pSrc->iCursor ); if( pSrc->fg.viaCoroutine ){ int m, n; n = pSrc->regResult; assert( pSrc->pTab!=0 ); m = pSrc->pTab->nCol; sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); | < < > | 169162 169163 169164 169165 169166 169167 169168 169169 169170 169171 169172 169173 169174 169175 169176 169177 | assert( pLevel->iTabCur==pSrc->iCursor ); if( pSrc->fg.viaCoroutine ){ int m, n; n = pSrc->regResult; assert( pSrc->pTab!=0 ); m = pSrc->pTab->nCol; sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); } sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if( (ws & WHERE_INDEXED) || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) ){ if( ws & WHERE_MULTI_OR ){ Index *pIx = pLevel->u.pCoveringIdx; int iDb = sqlite3SchemaToIndex(db, pIx->pSchema); |
︙ | ︙ | |||
172586 172587 172588 172589 172590 172591 172592 | #define TK_FILTER 166 #define TK_COLUMN 167 #define TK_AGG_FUNCTION 168 #define TK_AGG_COLUMN 169 #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 | | | | 172866 172867 172868 172869 172870 172871 172872 172873 172874 172875 172876 172877 172878 172879 172880 172881 | #define TK_FILTER 166 #define TK_COLUMN 167 #define TK_AGG_FUNCTION 168 #define TK_AGG_COLUMN 169 #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 #define TK_UPLUS 173 #define TK_UMINUS 174 #define TK_TRUTH 175 #define TK_REGISTER 176 #define TK_VECTOR 177 #define TK_SELECT_COLUMN 178 #define TK_IF_NULL_ROW 179 #define TK_ASTERISK 180 #define TK_SPAN 181 |
︙ | ︙ | |||
173618 173619 173620 173621 173622 173623 173624 | 0, /* FILTER => nothing */ 0, /* COLUMN => nothing */ 0, /* AGG_FUNCTION => nothing */ 0, /* AGG_COLUMN => nothing */ 0, /* TRUEFALSE => nothing */ 0, /* ISNOT => nothing */ 0, /* FUNCTION => nothing */ | < > | 173898 173899 173900 173901 173902 173903 173904 173905 173906 173907 173908 173909 173910 173911 173912 173913 | 0, /* FILTER => nothing */ 0, /* COLUMN => nothing */ 0, /* AGG_FUNCTION => nothing */ 0, /* AGG_COLUMN => nothing */ 0, /* TRUEFALSE => nothing */ 0, /* ISNOT => nothing */ 0, /* FUNCTION => nothing */ 0, /* UPLUS => nothing */ 0, /* UMINUS => nothing */ 0, /* TRUTH => nothing */ 0, /* REGISTER => nothing */ 0, /* VECTOR => nothing */ 0, /* SELECT_COLUMN => nothing */ 0, /* IF_NULL_ROW => nothing */ 0, /* ASTERISK => nothing */ 0, /* SPAN => nothing */ |
︙ | ︙ | |||
173887 173888 173889 173890 173891 173892 173893 | /* 166 */ "FILTER", /* 167 */ "COLUMN", /* 168 */ "AGG_FUNCTION", /* 169 */ "AGG_COLUMN", /* 170 */ "TRUEFALSE", /* 171 */ "ISNOT", /* 172 */ "FUNCTION", | | | | 174167 174168 174169 174170 174171 174172 174173 174174 174175 174176 174177 174178 174179 174180 174181 174182 | /* 166 */ "FILTER", /* 167 */ "COLUMN", /* 168 */ "AGG_FUNCTION", /* 169 */ "AGG_COLUMN", /* 170 */ "TRUEFALSE", /* 171 */ "ISNOT", /* 172 */ "FUNCTION", /* 173 */ "UPLUS", /* 174 */ "UMINUS", /* 175 */ "TRUTH", /* 176 */ "REGISTER", /* 177 */ "VECTOR", /* 178 */ "SELECT_COLUMN", /* 179 */ "IF_NULL_ROW", /* 180 */ "ASTERISK", /* 181 */ "SPAN", |
︙ | ︙ | |||
176749 176750 176751 176752 176753 176754 176755 | break; case 214: /* expr ::= NOT expr */ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); {yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/} break; case 216: /* expr ::= PLUS|MINUS expr */ { | > > > > > > > > | | > | 177029 177030 177031 177032 177033 177034 177035 177036 177037 177038 177039 177040 177041 177042 177043 177044 177045 177046 177047 177048 177049 177050 177051 177052 177053 | break; case 214: /* expr ::= NOT expr */ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); {yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/} break; case 216: /* expr ::= PLUS|MINUS expr */ { Expr *p = yymsp[0].minor.yy454; u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); assert( TK_UPLUS>TK_PLUS ); assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); if( p && p->op==TK_UPLUS ){ p->op = op; yymsp[-1].minor.yy454 = p; }else{ yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0); /*A-overwrites-B*/ } } break; case 217: /* expr ::= expr PTR expr */ { ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454); pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454); yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); |
︙ | ︙ | |||
208364 208365 208366 208367 208368 208369 208370 | if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ cnt = jsonbArrayCount(p, i); } if( !eErr ) sqlite3_result_int64(ctx, cnt); jsonParseFree(p); } | < < < < < < < | 208653 208654 208655 208656 208657 208658 208659 208660 208661 208662 208663 208664 208665 208666 | 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 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; } |
︙ | ︙ | |||
208435 208436 208437 208438 208439 208440 208441 | ** convenience. ** ** NUMBER ==> $[NUMBER] // PG compatible ** LABEL ==> $.LABEL // PG compatible ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); | | | 208717 208718 208719 208720 208721 208722 208723 208724 208725 208726 208727 208728 208729 208730 208731 | ** convenience. ** ** NUMBER ==> $[NUMBER] // PG compatible ** LABEL ==> $.LABEL // PG compatible ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ 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]==']' ){ |
︙ | ︙ | |||
228294 228295 228296 228297 228298 228299 228300 228301 228302 228303 228304 228305 | /* Make sure the buffer contains at least 10 bytes of input data, or all ** remaining data if there are less than 10 bytes available. This is ** sufficient either for the 'T' or 'P' byte and the varint that follows ** it, or for the two single byte values otherwise. */ p->rc = sessionInputBuffer(&p->in, 2); if( p->rc!=SQLITE_OK ) return p->rc; /* If the iterator is already at the end of the changeset, return DONE. */ if( p->in.iNext>=p->in.nData ){ return SQLITE_DONE; } | > > > < < < | 228576 228577 228578 228579 228580 228581 228582 228583 228584 228585 228586 228587 228588 228589 228590 228591 228592 228593 228594 228595 228596 228597 | /* Make sure the buffer contains at least 10 bytes of input data, or all ** remaining data if there are less than 10 bytes available. This is ** sufficient either for the 'T' or 'P' byte and the varint that follows ** it, or for the two single byte values otherwise. */ p->rc = sessionInputBuffer(&p->in, 2); if( p->rc!=SQLITE_OK ) return p->rc; sessionDiscardData(&p->in); p->in.iCurrent = p->in.iNext; /* If the iterator is already at the end of the changeset, return DONE. */ if( p->in.iNext>=p->in.nData ){ return SQLITE_DONE; } op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ if( pbNew ) *pbNew = 1; p->bPatchset = (op=='P'); if( sessionChangesetReadTblhdr(p) ) return p->rc; if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; p->in.iCurrent = p->in.iNext; |
︙ | ︙ | |||
230036 230037 230038 230039 230040 230041 230042 230043 230044 230045 230046 230047 230048 230049 | /* ** sqlite3_changegroup handle. */ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ sqlite3 *db; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */ }; /* ** This function is called to merge two changes to the same row together as | > | 230318 230319 230320 230321 230322 230323 230324 230325 230326 230327 230328 230329 230330 230331 230332 | /* ** sqlite3_changegroup handle. */ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ SessionBuffer rec; sqlite3 *db; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */ }; /* ** This function is called to merge two changes to the same row together as |
︙ | ︙ | |||
230334 230335 230336 230337 230338 230339 230340 | sessionAppendBlob(pOut, aRec, nRec, &rc); } return rc; } /* | > | | > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < < > > > > > > > < | < < | < < < < < < | | | | | | | < | < | < < < | | < < < < | | < < < < | < | < < < < | < < | | > | < < | | | < < < < < < < < < | < | | < < < < | < < < | < < < < > < < < < | > > < > | | | | | | > > | | > > > > > > > > > > > > > > > > > > | 230617 230618 230619 230620 230621 230622 230623 230624 230625 230626 230627 230628 230629 230630 230631 230632 230633 230634 230635 230636 230637 230638 230639 230640 230641 230642 230643 230644 230645 230646 230647 230648 230649 230650 230651 230652 230653 230654 230655 230656 230657 230658 230659 230660 230661 230662 230663 230664 230665 230666 230667 230668 230669 230670 230671 230672 230673 230674 230675 230676 230677 230678 230679 230680 230681 230682 230683 230684 230685 230686 230687 230688 230689 230690 230691 230692 230693 230694 230695 230696 230697 230698 230699 230700 230701 230702 230703 230704 230705 230706 230707 230708 230709 230710 230711 230712 230713 230714 230715 230716 230717 230718 230719 230720 230721 230722 230723 230724 230725 230726 230727 230728 230729 230730 230731 230732 230733 230734 230735 230736 230737 230738 230739 230740 230741 230742 230743 230744 230745 230746 230747 230748 230749 230750 230751 230752 230753 230754 230755 230756 230757 230758 230759 230760 230761 230762 230763 230764 230765 230766 230767 230768 230769 230770 230771 230772 230773 230774 230775 230776 230777 230778 230779 230780 230781 230782 230783 230784 230785 230786 230787 230788 230789 230790 230791 230792 230793 230794 230795 230796 230797 230798 230799 230800 230801 | sessionAppendBlob(pOut, aRec, nRec, &rc); } return rc; } /* ** Locate or create a SessionTable object that may be used to add the ** change currently pointed to by iterator pIter to changegroup pGrp. ** If successful, set output variable (*ppTab) to point to the table ** object and return SQLITE_OK. Otherwise, if some error occurs, return ** an SQLite error code and leave (*ppTab) set to NULL. */ static int sessionChangesetFindTable( sqlite3_changegroup *pGrp, const char *zTab, sqlite3_changeset_iter *pIter, SessionTable **ppTab ){ int rc = SQLITE_OK; SessionTable *pTab = 0; int nTab = (int)strlen(zTab); u8 *abPK = 0; int nCol = 0; *ppTab = 0; sqlite3changeset_pk(pIter, &abPK, &nCol); /* Search the list for an existing table */ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; } /* If one was not found above, create a new table now */ if( !pTab ){ SessionTable **ppNew; pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1); if( !pTab ){ return SQLITE_NOMEM; } memset(pTab, 0, sizeof(SessionTable)); pTab->nCol = nCol; pTab->abPK = (u8*)&pTab[1]; memcpy(pTab->abPK, abPK, nCol); pTab->zName = (char*)&pTab->abPK[nCol]; memcpy(pTab->zName, zTab, nTab+1); if( pGrp->db ){ pTab->nCol = 0; rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); if( rc ){ assert( pTab->azCol==0 ); sqlite3_free(pTab); return rc; } } /* The new object must be linked on to the end of the list, not ** simply added to the start of it. This is to ensure that the ** tables within the output of sqlite3changegroup_output() are in ** the right order. */ for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext); *ppNew = pTab; } /* Check that the table is compatible. */ if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ rc = SQLITE_SCHEMA; } *ppTab = pTab; return rc; } /* ** Add the change currently indicated by iterator pIter to the hash table ** belonging to changegroup pGrp. */ static int sessionOneChangeToHash( sqlite3_changegroup *pGrp, sqlite3_changeset_iter *pIter, int bRebase ){ int rc = SQLITE_OK; int nCol = 0; int op = 0; int iHash = 0; int bIndirect = 0; SessionChange *pChange = 0; SessionChange *pExist = 0; SessionChange **pp = 0; SessionTable *pTab = 0; u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; /* Ensure that only changesets, or only patchsets, but not a mixture ** of both, are being combined. It is an error to try to combine a ** changeset and a patchset. */ if( pGrp->pList==0 ){ pGrp->bPatch = pIter->bPatchset; }else if( pIter->bPatchset!=pGrp->bPatch ){ rc = SQLITE_ERROR; } if( rc==SQLITE_OK ){ const char *zTab = 0; sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); } if( rc==SQLITE_OK && nCol<pTab->nCol ){ SessionBuffer *pBuf = &pGrp->rec; rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); aRec = pBuf->aBuf; nRec = pBuf->nBuf; assert( pGrp->db ); } if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ /* Search for existing entry. If found, remove it from the hash table. ** Code below may link it back in. */ iHash = sessionChangeHash( pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange ); for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ int bPkOnly1 = 0; int bPkOnly2 = 0; if( pIter->bPatchset ){ bPkOnly1 = (*pp)->op==SQLITE_DELETE; bPkOnly2 = op==SQLITE_DELETE; } if( sessionChangeEqual(pTab, bPkOnly1, (*pp)->aRecord, bPkOnly2, aRec) ){ pExist = *pp; *pp = (*pp)->pNext; pTab->nEntry--; break; } } } if( rc==SQLITE_OK ){ rc = sessionChangeMerge(pTab, bRebase, pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange ); } if( rc==SQLITE_OK && pChange ){ pChange->pNext = pTab->apChange[iHash]; pTab->apChange[iHash] = pChange; pTab->nEntry++; } if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } /* ** Add all changes in the changeset traversed by the iterator passed as ** the first argument to the changegroup hash tables. */ static int sessionChangesetToHash( sqlite3_changeset_iter *pIter, /* Iterator to read from */ sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ int bRebase /* True if hash table is for rebasing */ ){ u8 *aRec; int nRec; int rc = SQLITE_OK; while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ rc = sessionOneChangeToHash(pGrp, pIter, bRebase); if( rc!=SQLITE_OK ) break; } if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } /* ** Serialize a changeset (or patchset) based on all changesets (or patchsets) ** added to the changegroup object passed as the first argument. |
︙ | ︙ | |||
230589 230590 230591 230592 230593 230594 230595 230596 230597 230598 230599 230600 230601 230602 | rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; } /* ** Obtain a buffer containing a changeset representing the concatenation ** of all changesets added to the group so far. */ SQLITE_API int sqlite3changegroup_output( sqlite3_changegroup *pGrp, | > > > > > > > > > > > > > > > > > | 230914 230915 230916 230917 230918 230919 230920 230921 230922 230923 230924 230925 230926 230927 230928 230929 230930 230931 230932 230933 230934 230935 230936 230937 230938 230939 230940 230941 230942 230943 230944 | rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; } /* ** Add a single change to a changeset-group. */ SQLITE_API int sqlite3changegroup_add_change( sqlite3_changegroup *pGrp, sqlite3_changeset_iter *pIter ){ if( pIter->in.iCurrent==pIter->in.iNext || pIter->rc!=SQLITE_OK || pIter->bInvert ){ /* Iterator does not point to any valid entry or is an INVERT iterator. */ return SQLITE_ERROR; } return sessionOneChangeToHash(pGrp, pIter, 0); } /* ** Obtain a buffer containing a changeset representing the concatenation ** of all changesets added to the group so far. */ SQLITE_API int sqlite3changegroup_output( sqlite3_changegroup *pGrp, |
︙ | ︙ | |||
230639 230640 230641 230642 230643 230644 230645 230646 230647 230648 230649 230650 230651 230652 | /* ** Delete a changegroup object. */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); sqlite3_free(pGrp); } } /* ** Combine two changesets together. */ | > | 230981 230982 230983 230984 230985 230986 230987 230988 230989 230990 230991 230992 230993 230994 230995 | /* ** Delete a changegroup object. */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); sqlite3_free(pGrp->rec.aBuf); sqlite3_free(pGrp); } } /* ** Combine two changesets together. */ |
︙ | ︙ | |||
231040 231041 231042 231043 231044 231045 231046 231047 231048 231049 231050 231051 231052 231053 | /* ** Destroy a rebaser object */ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ sessionDeleteTable(0, p->grp.pList); sqlite3_free(p); } } /* ** Global configuration */ | > | 231383 231384 231385 231386 231387 231388 231389 231390 231391 231392 231393 231394 231395 231396 231397 | /* ** Destroy a rebaser object */ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ sessionDeleteTable(0, p->grp.pList); sqlite3_free(p->grp.rec.aBuf); sqlite3_free(p); } } /* ** Global configuration */ |
︙ | ︙ | |||
252191 252192 252193 252194 252195 252196 252197 | 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); | | | 252535 252536 252537 252538 252539 252540 252541 252542 252543 252544 252545 252546 252547 252548 252549 | 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-05-20 17:52:35 08058d66d1bde4fcf8324482ee4c6c030c681383470d5076b6f75b74aac2ae29", -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){ |
︙ | ︙ |
Changes to extsrc/sqlite3.h.
︙ | ︙ | |||
144 145 146 147 148 149 150 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.46.0" #define SQLITE_VERSION_NUMBER 3046000 | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.46.0" #define SQLITE_VERSION_NUMBER 3046000 #define SQLITE_SOURCE_ID "2024-05-21 11:11:29 de8182cf1773ac0d04268d896a613841cf6bf61f9f030342170657d5e06f2acb" /* ** 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 |
︙ | ︙ | |||
9938 9939 9940 9941 9942 9943 9944 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular | | | | < | | < > | | < | > > > > | > > | > > > > > > > > > > > > > > > > > | 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular ** order, as long as rows with the same values in all columns identified ** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows ** contain the same values for all columns identified by "colUsed", all but ** one such row may optionally be omitted from the result.)^ ** The virtual table is not required to omit rows that are duplicates ** over the "colUsed" columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. ** <li value="3"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the ** virtual table must return rows in the order defined by "aOrderBy" as ** if the sqlite3_vtab_distinct() interface had returned 0. However if ** two or more rows in the result have the same values for all columns ** identified by "colUsed", then all but one such row may optionally be ** omitted.)^ Like when the return value is 2, the virtual table ** is not required to omit rows that are duplicates over the "colUsed" ** columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** </ol> ** ** <p>The following table summarizes the conditions under which the ** virtual table is allowed to set the "orderByConsumed" flag based on ** the value returned by sqlite3_vtab_distinct(). This table is a ** restatement of the previous four paragraphs: ** ** <table border=1 cellspacing=0 cellpadding=10 width="90%"> ** <tr> ** <td valign="top">sqlite3_vtab_distinct() return value ** <td valign="top">Rows are returned in aOrderBy order ** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent ** <td valign="top">Duplicates over all colUsed columns may be omitted ** <tr><td>0<td>yes<td>yes<td>no ** <tr><td>1<td>no<td>yes<td>no ** <tr><td>2<td>no<td>yes<td>yes ** <tr><td>3<td>yes<td>yes<td>yes ** </table> ** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" ** (or "IS NOT DISTINCT FROM") and not "==". ** ** If a virtual table implementation is unable to meet the requirements |
︙ | ︙ | |||
11999 12000 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** In all cases, if an error occurs the state of the final contents of the ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup | > > > > > > > > > > > > > > > > > > > > > > > > | 12020 12021 12022 12023 12024 12025 12026 12027 12028 12029 12030 12031 12032 12033 12034 12035 12036 12037 12038 12039 12040 12041 12042 12043 12044 12045 12046 12047 12048 12049 12050 12051 12052 12053 12054 12055 12056 12057 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** In all cases, if an error occurs the state of the final contents of the ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); /* ** CAPI3REF: Add A Single Change To A Changegroup ** METHOD: sqlite3_changegroup ** ** This function adds the single change currently indicated by the iterator ** passed as the second argument to the changegroup object. The rules for ** adding the change are just as described for [sqlite3changegroup_add()]. ** ** If the change is successfully added to the changegroup, SQLITE_OK is ** returned. Otherwise, an SQLite error code is returned. ** ** The iterator must point to a valid entry when this function is called. ** If it does not, SQLITE_ERROR is returned and no change is added to the ** changegroup. Additionally, the iterator must not have been opened with ** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also ** returned. */ SQLITE_API int sqlite3changegroup_add_change( sqlite3_changegroup*, sqlite3_changeset_iter* ); /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup |
︙ | ︙ |
Changes to src/add.c.
︙ | ︙ | |||
1055 1056 1057 1058 1059 1060 1061 | vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("no check-out in which to rename files"); } if( g.argc<4 ){ usage("OLDNAME NEWNAME"); } | | | 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 | vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("no check-out in which to rename files"); } if( g.argc<4 ){ usage("OLDNAME NEWNAME"); } zDest = file_case_preferred_name(".",g.argv[g.argc-1]); db_begin_transaction(); if( g.argv[1][0]=='r' ){ /* i.e. "rename" */ moveFiles = 0; }else if( softFlag ){ moveFiles = 0; }else if( hardFlag ){ moveFiles = 1; |
︙ | ︙ | |||
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 | mv_one_file(vid, zFrom, zTo, dryRunFlag); if( moveFiles ) add_file_to_move(zFrom, zTo); } db_finalize(&q); undo_reset(); db_end_transaction(0); if( moveFiles ) process_files_to_move(dryRunFlag); } /* ** Function for stash_apply to be able to restore a file and indicate ** newly ADDED state. */ int stash_add_files_in_sfile(int vid){ return add_files_in_sfile(vid); } | > | 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 | mv_one_file(vid, zFrom, zTo, dryRunFlag); if( moveFiles ) add_file_to_move(zFrom, zTo); } db_finalize(&q); undo_reset(); db_end_transaction(0); if( moveFiles ) process_files_to_move(dryRunFlag); fossil_free(zDest); } /* ** Function for stash_apply to be able to restore a file and indicate ** newly ADDED state. */ int stash_add_files_in_sfile(int vid){ return add_files_in_sfile(vid); } |
Changes to src/checkin.c.
︙ | ︙ | |||
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | blob_reset(&report); /* The status command ends with warnings about ambiguous leaves (forks). */ if( command==STATUS ){ leaf_ambiguity_warning(vid, vid); } } /* ** Take care of -r version of ls command */ static void ls_cmd_rev( const char *zRev, /* Revision string given */ int verboseFlag, /* Verbose flag given */ int showAge, /* Age flag given */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 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 | blob_reset(&report); /* The status command ends with warnings about ambiguous leaves (forks). */ if( command==STATUS ){ leaf_ambiguity_warning(vid, vid); } } /* zIn is a string that is guaranteed to be followed by \n. Return ** a pointer to the next line after the \n. The returned value might ** point to the \000 string terminator. */ static const char *next_line(const char *zIn){ const char *z = strchr(zIn, '\n'); assert( z!=0 ); return z+1; } /* zIn is a non-empty list of filenames in sorted order and separated ** by \n. There might be a cluster of lines that have the same n-character ** prefix. Return a pointer to the start of the last line of that ** cluster. The return value might be zIn if the first line of zIn is ** unique in its first n character. */ static const char *last_line(const char *zIn, int n){ const char *zLast = zIn; const char *z; while( 1 ){ z = next_line(zLast); if( z[0]==0 || (n>0 && strncmp(zIn, z, n)!=0) ) break; zLast = z; } return zLast; } /* ** Print a section of a filelist hierarchy graph. This is a helper ** routine for print_filelist_as_tree() below. */ static const char *print_filelist_section( const char *zIn, /* List of filenames, separated by \n */ const char *zLast, /* Last filename in the list to print */ const char *zPrefix, /* Prefix so put before each output line */ int nDir /* Ignore this many characters of directory name */ ){ /* Unicode box-drawing characters: U+251C, U+2514, U+2502 */ const char *zENTRY = "\342\224\234\342\224\200\342\224\200 "; const char *zLASTE = "\342\224\224\342\224\200\342\224\200 "; const char *zCONTU = "\342\224\202 "; const char *zBLANK = " "; while( zIn<=zLast ){ int i; for(i=nDir; zIn[i]!='\n' && zIn[i]!='/'; i++){} if( zIn[i]=='/' ){ char *zSubPrefix; const char *zSubLast = last_line(zIn, i+1); zSubPrefix = mprintf("%s%s", zPrefix, zSubLast==zLast ? zBLANK : zCONTU); fossil_print("%s%s%.*s\n", zPrefix, zSubLast==zLast ? zLASTE : zENTRY, i-nDir, &zIn[nDir]); zIn = print_filelist_section(zIn, zSubLast, zSubPrefix, i+1); fossil_free(zSubPrefix); }else{ fossil_print("%s%s%.*s\n", zPrefix, zIn==zLast ? zLASTE : zENTRY, i-nDir, &zIn[nDir]); zIn = next_line(zIn); } } return zIn; } /* ** Input blob pList is a list of filenames, one filename per line, ** in sorted order and with / directory separators. Output this list ** as a tree in a manner similar to the "tree" command on Linux. */ static void print_filelist_as_tree(Blob *pList){ char *zAll; const char *zLast; fossil_print("%s\n", g.zLocalRoot); zAll = blob_str(pList); if( zAll[0] ){ zLast = last_line(zAll, 0); print_filelist_section(zAll, zLast, "", 0); } } /* ** Take care of -r version of ls command */ static void ls_cmd_rev( const char *zRev, /* Revision string given */ int verboseFlag, /* Verbose flag given */ int showAge, /* Age flag given */ int timeOrder, /* Order by time flag given */ int treeFmt /* Show output in the tree format */ ){ Stmt q; char *zOrderBy = "pathname COLLATE nocase"; char *zName; Blob where; int rid; int i; Blob out; /* Handle given file names */ blob_zero(&where); for(i=2; i<g.argc; i++){ Blob fname; file_tree_name(g.argv[i], &fname, 0, 1); zName = blob_str(&fname); |
︙ | ︙ | |||
647 648 649 650 651 652 653 654 655 656 657 658 | "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" " blob.size\n" " FROM fileage, blob\n" " WHERE blob.rid=fileage.fid %s\n" " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/ ); blob_reset(&where); while( db_step(&q)==SQLITE_ROW ){ const char *zTime = db_column_text(&q,0); const char *zFile = db_column_text(&q,1); int size = db_column_int(&q,2); | > > > | > > > > | 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 | "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" " blob.size\n" " FROM fileage, blob\n" " WHERE blob.rid=fileage.fid %s\n" " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/ ); blob_reset(&where); if( treeFmt ) blob_init(&out, 0, 0); while( db_step(&q)==SQLITE_ROW ){ const char *zTime = db_column_text(&q,0); const char *zFile = db_column_text(&q,1); int size = db_column_int(&q,2); if( treeFmt ){ blob_appendf(&out, "%s\n", zFile); }else if( verboseFlag ){ fossil_print("%s %7d %s\n", zTime, size, zFile); }else if( showAge ){ fossil_print("%s %s\n", zTime, zFile); }else{ fossil_print("%s\n", zFile); } } db_finalize(&q); if( treeFmt ){ print_filelist_as_tree(&out); blob_reset(&out); } } /* ** COMMAND: ls ** ** Usage: %fossil ls ?OPTIONS? ?PATHS ...? ** |
︙ | ︙ | |||
690 691 692 693 694 695 696 | ** ** The -t option changes the sort order. Without -t, files are sorted by ** path and name (case insensitive sort if -r). If neither --age nor -r ** are used, -t sorts by modification time, otherwise by commit time. ** ** Options: ** --age Show when each file was committed | | | | | > | > > > > > | | 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 | ** ** The -t option changes the sort order. Without -t, files are sorted by ** path and name (case insensitive sort if -r). If neither --age nor -r ** are used, -t sorts by modification time, otherwise by commit time. ** ** Options: ** --age Show when each file was committed ** --hash With -v, verify file status using hashing ** rather than relying on file sizes and mtimes ** -r VERSION The specific check-in to list ** -R|--repository REPO Extract info from repository REPO ** -t Sort output in time order ** --tree Tree format ** -v|--verbose Provide extra information about each file ** ** See also: [[changes]], [[extras]], [[status]], [[tree]] */ void ls_cmd(void){ int vid; Stmt q; int verboseFlag; int showAge; int treeFmt; int timeOrder; char *zOrderBy = "pathname"; Blob where; int i; int useHash = 0; const char *zName; const char *zRev; verboseFlag = find_option("verbose","v", 0)!=0; if( !verboseFlag ){ verboseFlag = find_option("l","l", 0)!=0; /* deprecated */ } showAge = find_option("age",0,0)!=0; zRev = find_option("r","r",1); timeOrder = find_option("t","t",0)!=0; if( verboseFlag ){ useHash = find_option("hash",0,0)!=0; } treeFmt = find_option("tree",0,0)!=0; if( treeFmt ){ if( zRev==0 ) zRev = "current"; } if( zRev!=0 ){ db_find_and_open_repository(0, 0); verify_all_options(); ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder,treeFmt); return; }else if( find_option("R",0,1)!=0 ){ fossil_fatal("the -r is required in addition to -R"); } db_must_be_within_tree(); vid = db_lget_int("checkout", 0); |
︙ | ︙ | |||
826 827 828 829 830 831 832 833 834 835 836 837 838 839 | }else{ fossil_print("%s%s\n", type, zPathname); } free(zFullName); } db_finalize(&q); } /* ** COMMAND: extras ** ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? ** ** Print a list of all files in the source tree that are not part of the | > > > > > > > > > > > > > > > > > > > > > > > > > | 920 921 922 923 924 925 926 927 928 929 930 931 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 | }else{ fossil_print("%s%s\n", type, zPathname); } free(zFullName); } db_finalize(&q); } /* ** COMMAND: tree ** ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? ** ** List all files in the current check-out in after the fashion of the ** "tree" command. If PATHS is included, only the named files ** (or their children if directories) are shown. ** ** Options: ** -r VERSION The specific check-in to list ** -R|--repository REPO Extract info from repository REPO ** ** See also: [[ls]] */ void tree_cmd(void){ const char *zRev; zRev = find_option("r","r",1); if( zRev==0 ) zRev = "current"; db_find_and_open_repository(0, 0); verify_all_options(); ls_cmd_rev(zRev,0,0,0,1); } /* ** COMMAND: extras ** ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? ** ** Print a list of all files in the source tree that are not part of the |
︙ | ︙ | |||
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 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 | ** --abs-paths Display absolute pathnames ** --case-sensitive BOOL Override case-sensitive setting ** --dotfiles Include files beginning with a dot (".") ** --header Identify the repository if there are extras ** --ignore CSG Ignore files matching patterns from the argument ** --rel-paths Display pathnames relative to the current working ** directory ** ** See also: [[changes]], [[clean]], [[status]] */ void extras_cmd(void){ Blob report = BLOB_INITIALIZER; const char *zIgnoreFlag = find_option("ignore",0,1); unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; unsigned flags = C_EXTRA; int showHdr = find_option("header",0,0)!=0; Glob *pIgnore; if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; db_must_be_within_tree(); if( determine_cwd_relative_option() ){ flags |= C_RELPATH; } if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; /* We should be done with options.. */ verify_all_options(); if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } pIgnore = glob_create(zIgnoreFlag); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); glob_free(pIgnore); blob_zero(&report); status_report(&report, flags); if( blob_size(&report) ){ if( showHdr ){ fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"), g.zLocalRoot); } | > > > > > > > > > | > | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 | ** --abs-paths Display absolute pathnames ** --case-sensitive BOOL Override case-sensitive setting ** --dotfiles Include files beginning with a dot (".") ** --header Identify the repository if there are extras ** --ignore CSG Ignore files matching patterns from the argument ** --rel-paths Display pathnames relative to the current working ** directory ** --tree Show output in the tree format ** ** See also: [[changes]], [[clean]], [[status]] */ void extras_cmd(void){ Blob report = BLOB_INITIALIZER; const char *zIgnoreFlag = find_option("ignore",0,1); unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; unsigned flags = C_EXTRA; int showHdr = find_option("header",0,0)!=0; int treeFmt = find_option("tree",0,0)!=0; Glob *pIgnore; if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; db_must_be_within_tree(); if( determine_cwd_relative_option() ){ flags |= C_RELPATH; } if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; if( treeFmt ){ flags &= ~C_RELPATH; } /* We should be done with options.. */ verify_all_options(); if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } pIgnore = glob_create(zIgnoreFlag); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); glob_free(pIgnore); blob_zero(&report); status_report(&report, flags); if( blob_size(&report) ){ if( showHdr ){ fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"), g.zLocalRoot); } if( treeFmt ){ print_filelist_as_tree(&report); }else{ blob_write_to_file(&report, "-"); } } blob_reset(&report); } /* ** COMMAND: clean ** |
︙ | ︙ |
Changes to src/file.c.
︙ | ︙ | |||
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 | return z; } /* ** Count the number of objects (files and subdirectories) in a given ** directory. Return the count. Return -1 if the object is not a ** directory. */ int file_directory_size(const char *zDir, const char *zGlob, int omitDotFiles){ void *zNative; DIR *d; int n = -1; zNative = fossil_utf8_to_path(zDir,1); d = opendir(zNative); if( d ){ struct dirent *pEntry; n = 0; while( (pEntry=readdir(d))!=0 ){ if( pEntry->d_name[0]==0 ) continue; | > > > | > > > > > > | 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 | return z; } /* ** Count the number of objects (files and subdirectories) in a given ** directory. Return the count. Return -1 if the object is not a ** directory. ** ** This routine never counts the two "." and ".." special directory ** entries, even if the provided glob would match them. */ int file_directory_size(const char *zDir, const char *zGlob, int omitDotFiles){ void *zNative; DIR *d; int n = -1; zNative = fossil_utf8_to_path(zDir,1); d = opendir(zNative); if( d ){ struct dirent *pEntry; n = 0; while( (pEntry=readdir(d))!=0 ){ if( pEntry->d_name[0]==0 ) continue; if( pEntry->d_name[0]=='.' && (omitDotFiles /* Skip the special "." and ".." entries. */ || pEntry->d_name[1]==0 || (pEntry->d_name[1]=='.' && pEntry->d_name[2]==0))){ continue; } if( zGlob ){ char *zUtf8 = fossil_path_to_utf8(pEntry->d_name); int rc = sqlite3_strglob(zGlob, zUtf8); fossil_path_free(zUtf8); if( rc ) continue; } n++; |
︙ | ︙ |
Changes to src/fossil.page.chat.js.
︙ | ︙ | |||
125 126 127 128 129 130 131 132 133 134 135 136 137 138 | return resized; })(); fossil.FRK = ForceResizeKludge/*for debugging*/; const Chat = ForceResizeKludge.chat = (function(){ const cs = { // the "Chat" object (result of this function) verboseErrors: false /* if true then certain, mostly extraneous, error messages may be sent to the console. */, e:{/*map of certain DOM elements.*/ messageInjectPoint: E1('#message-inject-point'), pageTitle: E1('head title'), loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */, inputWrapper: E1("#chat-input-area"), inputElementWrapper: E1('#chat-input-line-wrapper'), fileSelectWrapper: E1('#chat-input-file-area'), | > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | return resized; })(); fossil.FRK = ForceResizeKludge/*for debugging*/; const Chat = ForceResizeKludge.chat = (function(){ const cs = { // the "Chat" object (result of this function) verboseErrors: false /* if true then certain, mostly extraneous, error messages may be sent to the console. */, playedBeep: false /* used for the beep-once setting */, e:{/*map of certain DOM elements.*/ messageInjectPoint: E1('#message-inject-point'), pageTitle: E1('head title'), loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */, inputWrapper: E1("#chat-input-area"), inputElementWrapper: E1('#chat-input-line-wrapper'), fileSelectWrapper: E1('#chat-input-file-area'), |
︙ | ︙ | |||
408 409 410 411 412 413 414 415 416 417 418 419 420 421 | hidden */ "chat-only-mode": false, /* When set to a URI, it is assumed to be an audio file, which gets played when new messages arrive. When true, the first entry in the audio file selection list will be used. */ "audible-alert": true, /* When on, show the list of "active" users - those from whom we have messages in the currently-loaded history (noting that deletions are also messages). */ "active-user-list": false, /* When on, the [active-user-list] setting includes the timestamp of each user's most recent message. */ "active-user-list-timestamps": false, | > > > | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | hidden */ "chat-only-mode": false, /* When set to a URI, it is assumed to be an audio file, which gets played when new messages arrive. When true, the first entry in the audio file selection list will be used. */ "audible-alert": true, /* */ "beep-once": false, /* When on, show the list of "active" users - those from whom we have messages in the currently-loaded history (noting that deletions are also messages). */ "active-user-list": false, /* When on, the [active-user-list] setting includes the timestamp of each user's most recent message. */ "active-user-list-timestamps": false, |
︙ | ︙ | |||
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 | } }, /** Plays a new-message notification sound IF the audible-alert setting is true, else this is a no-op. Returns this. */ playNewMessageSound: function f(){ if(f.uri){ try{ if(!f.audio) f.audio = new Audio(f.uri); f.audio.currentTime = 0; f.audio.play(); }catch(e){ console.error("Audio playblack failed.", f.uri, e); } } return this; }, /** Sets the current new-message audio alert URI (must be a repository-relative path which responds with an audio file). Pass a falsy value to disable audio alerts. Returns this. */ setNewMessageSound: function f(uri){ delete this.playNewMessageSound.audio; this.playNewMessageSound.uri = uri; this.settings.set('audible-alert', uri); return this; }, /** Expects e to be one of the elements in this.e.views. | > > > > > > > | 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 | } }, /** Plays a new-message notification sound IF the audible-alert setting is true, else this is a no-op. Returns this. */ playNewMessageSound: function f(){ if(f.uri){ if(!cs.pageIsActive /* ^^^ this could also arguably apply when chat is visible */ && this.playedBeep && this.settings.getBool('beep-once',false)){ return; } try{ this.playedBeep = true; if(!f.audio) f.audio = new Audio(f.uri); f.audio.currentTime = 0; f.audio.play(); }catch(e){ console.error("Audio playblack failed.", f.uri, e); } } return this; }, /** Sets the current new-message audio alert URI (must be a repository-relative path which responds with an audio file). Pass a falsy value to disable audio alerts. Returns this. */ setNewMessageSound: function f(uri){ this.playedBeep = false; delete this.playNewMessageSound.audio; this.playNewMessageSound.uri = uri; this.settings.set('audible-alert', uri); return this; }, /** Expects e to be one of the elements in this.e.views. |
︙ | ︙ | |||
778 779 780 781 782 783 784 | /** Returns a new Error() object encapsulating state from the given fetch() response promise. */ cs._newResponseError = function(response){ return new Error([ "HTTP status ", response.status,": ",response.url,": ", response.statusText].join('')); }; | | | | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 | /** Returns a new Error() object encapsulating state from the given fetch() response promise. */ cs._newResponseError = function(response){ return new Error([ "HTTP status ", response.status,": ",response.url,": ", response.statusText].join('')); }; /** Helper for reporting HTTP-level response errors via fetch(). If response.ok then response.json() is returned, else an Error is thrown. */ cs._fetchJsonOrError = function(response){ if(response.ok) return response.json(); else throw cs._newResponseError(response); }; /** Removes the given message ID from the local chat record and, if the message was posted by this user OR this user in an admin/setup, also submits it for removal on the remote. id may optionally be a DOM element, in which case it must be a .message-row element. |
︙ | ︙ | |||
817 818 819 820 821 822 823 824 825 826 827 828 829 830 | }); }else{ this.deleteMessageElem(id); } }; document.addEventListener('visibilitychange', function(ev){ cs.pageIsActive = ('visible' === document.visibilityState); if(cs.pageIsActive){ cs.e.pageTitle.innerText = cs.pageTitleOrig; if(document.activeElement!==cs.inputElement()){ /* An attempt to resolve usability problem reported by Joe M. where the Pale Moon browser is giving input focus to the Preview button. The down-side of this is that it will deselect any text which was previously selected on this | > | 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 | }); }else{ this.deleteMessageElem(id); } }; document.addEventListener('visibilitychange', function(ev){ cs.pageIsActive = ('visible' === document.visibilityState); cs.playedBeep = false; if(cs.pageIsActive){ cs.e.pageTitle.innerText = cs.pageTitleOrig; if(document.activeElement!==cs.inputElement()){ /* An attempt to resolve usability problem reported by Joe M. where the Pale Moon browser is giving input focus to the Preview button. The down-side of this is that it will deselect any text which was previously selected on this |
︙ | ︙ | |||
1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 | select: selectSound, callback: function(ev){ const v = ev.target.value; Chat.setNewMessageSound(v); F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+"."); if(v) setTimeout(()=>Chat.playNewMessageSound(), 0); } },{ label: "Play notification for your own messages", hint: "When enabled, the audio notification will be played for all messages, "+ "including your own. When disabled only messages from other users "+ "will trigger a notification.", boolValue: 'alert-own-messages' }] | > > > > | 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 | select: selectSound, callback: function(ev){ const v = ev.target.value; Chat.setNewMessageSound(v); F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+"."); if(v) setTimeout(()=>Chat.playNewMessageSound(), 0); } },{ label: "Notify only once when away", hint: "Notify only for the first message received after chat is hidden from view.", boolValue: 'beep-once' },{ label: "Play notification for your own messages", hint: "When enabled, the audio notification will be played for all messages, "+ "including your own. When disabled only messages from other users "+ "will trigger a notification.", boolValue: 'alert-own-messages' }] |
︙ | ︙ | |||
1920 1921 1922 1923 1924 1925 1926 | if(Chat===v) v = Chat.settings.defaults[k]; if(valueKludges.hasOwnProperty(v)) v = valueKludges[v]; Chat.settings.set(k,v) /* fires event listeners so that the Config area checkboxes get in sync */; }); })(); | | | 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 | if(Chat===v) v = Chat.settings.defaults[k]; if(valueKludges.hasOwnProperty(v)) v = valueKludges[v]; Chat.settings.set(k,v) /* fires event listeners so that the Config area checkboxes get in sync */; }); })(); (function(){/*set up message preview*/ const btnPreview = Chat.e.btnPreview; Chat.setPreviewText = function(t){ this.setCurrentView(this.e.viewPreview); this.e.previewContent.innerHTML = t; this.e.viewPreview.querySelectorAll('a').forEach(addAnchorTargetBlank); this.inputFocus(); |
︙ | ︙ |
Changes to src/graph.c.
︙ | ︙ | |||
977 978 979 980 981 982 983 | aMap = p->aiRailMap; for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */ if( nTimewarp==0 ){ /* Priority bits: ** ** 0x04 The preferred branch ** | | > > > > > > > > < < < < < | 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 | aMap = p->aiRailMap; for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */ if( nTimewarp==0 ){ /* Priority bits: ** ** 0x04 The preferred branch ** ** 0x02 A merge rail - a rail that contains merge lines into ** the preferred branch. Only applies if a preferred branch ** is defined. This improves the display of r=BRANCH ** options to /timeline. ** ** 0x01 A rail that merges with the preferred branch */ u8 aPriority[GR_MAX_RAIL]; memset(aPriority, 0, p->mxRail+1); if( zLeftBranch ){ char *zLeft = persistBranchName(p, zLeftBranch); for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->zBranch==zLeft ){ aPriority[pRow->iRail] |= 4; for(i=0; i<=p->mxRail; i++){ if( pRow->mergeIn[i] ) aPriority[i] |= 1; } if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; } } for(i=0; i<=p->mxRail; i++){ if( p->mergeRail & BIT(i) ){ aPriority[i] |= 2; } } }else{ j = 1; aPriority[0] = 4; for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->iRail==0 ){ for(i=0; i<=p->mxRail; i++){ if( pRow->mergeIn[i] ) aPriority[i] |= 1; } if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; } } } #if 0 fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail); fprintf(stderr,"Priority:"); for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]); fprintf(stderr,"\n"); #endif |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 | const char *zBase = g.zRepositoryName; int isReadonly = 0; g.zPhase = "process_one_web_page"; #if !defined(_WIN32) signal(SIGSEGV, sigsegv_handler); #endif /* Handle universal query parameters */ if( PB("utc") ){ g.fTimeFormat = 1; }else if( PB("localtime") ){ g.fTimeFormat = 2; } | > > > > > > > | 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 | const char *zBase = g.zRepositoryName; int isReadonly = 0; g.zPhase = "process_one_web_page"; #if !defined(_WIN32) signal(SIGSEGV, sigsegv_handler); #endif /* Decode %HH escapes in PATHINFO */ if( strchr(zPathInfo,'%') ){ char *z = fossil_strdup(zPathInfo); dehttpize(z); zPathInfo = z; } /* Handle universal query parameters */ if( PB("utc") ){ g.fTimeFormat = 1; }else if( PB("localtime") ){ g.fTimeFormat = 2; } |
︙ | ︙ | |||
1755 1756 1757 1758 1759 1760 1761 | zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt); if( g.fHttpTrace ){ @ <!-- Looking for repository named "%h(zRepo)" --> fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); } | | | | > > | > > | < > > | > | | > > > | > > > | 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 | zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt); if( g.fHttpTrace ){ @ <!-- Looking for repository named "%h(zRepo)" --> fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); } /* Restrictions on the URI for security: ** ** 1. Reject characters that are not ASCII alphanumerics, ** "-", "_", ".", "/", or unicode (above ASCII). ** In other words: No ASCII punctuation or control characters ** other than "-", "_", "." and "/". ** 2. Exception to rule 1: Allow /X:/ where X is any ASCII ** alphabetic character at the beginning of the name on windows. ** 3. "-" may not occur immediately after "/" ** 4. "." may not be adjacent to another "." or to "/" ** ** Any character does not satisfy these constraints a Not Found ** error is returned. */ szFile = 0; for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){ char c = zRepo[j]; if( c>='a' && c<='z' ) continue; if( c>='A' && c<='Z' ) continue; if( c>='0' && c<='9' ) continue; if( (c&0x80)==0x80 ) continue; #if defined(_WIN32) || defined(__CYGWIN__) /* Allow names to begin with "/X:/" on windows */ if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){ continue; } #endif if( c=='/' ) continue; if( c=='_' ) continue; if( c=='-' && zRepo[j-1]!='/' ) continue; if( c=='.' && zRepo[j-1]!='.' && zRepo[j-1]!='/' && zRepo[j+1]!='.' && zRepo[j+1]!='/' ){ continue; } if( c=='.' && g.fAllowACME && j==(int)nBase+1 && strncmp(&zRepo[j-1],"/.well-known/",12)==0 ){ /* We allow .well-known as the top-level directory for ACME */ continue; |
︙ | ︙ |
Changes to src/merge.c.
︙ | ︙ | |||
272 273 274 275 276 277 278 | db_must_be_within_tree(); debug_show_vfile(); } /* ** COMMAND: merge | | > | > | | > > > > | 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 | db_must_be_within_tree(); debug_show_vfile(); } /* ** COMMAND: merge ** COMMAND: cherry-pick alias ** COMMAND: cherrypick ** ** Usage: %fossil merge ?OPTIONS? ?VERSION ...? ** Or: %fossil cherrypick ?OPTIONS? ?VERSION ...? ** ** The argument VERSION is a version that should be merged into the ** current check-out. All changes from VERSION back to the nearest ** common ancestor are merged. Except, if either of the --cherrypick ** or --backout options are used only the changes associated with the ** single check-in VERSION are merged. The --backout option causes ** the changes associated with VERSION to be removed from the current ** check-out rather than added. When invoked with the name ** "cherrypick" instead of "merge", this command works exactly like ** "merge --cherrypick". ** ** Files which are renamed in the merged-in branch will be renamed in ** the current check-out. ** ** If the VERSION argument is omitted, then Fossil attempts to find ** a recent fork on the current branch to merge. ** ** If there are multiple VERSION arguments, then each VERSION is merged ** (or cherrypicked) in the order that they appear on the command-line. ** ** Options: ** --backout Do a reverse cherrypick merge against VERSION. ** In other words, back out the changes that were ** added by VERSION. ** --baseline BASELINE Use BASELINE as the "pivot" of the merge instead ** of the nearest common ancestor. This allows |
︙ | ︙ | |||
335 336 337 338 339 340 341 | const char *zPivot; /* The value of --baseline */ int debugFlag; /* True if --debug is present */ int showVfileFlag; /* True if the --show-vfile flag is present */ int keepMergeFlag; /* True if --keep-merge-files is present */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ | > > > | | | 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 | const char *zPivot; /* The value of --baseline */ int debugFlag; /* True if --debug is present */ int showVfileFlag; /* True if the --show-vfile flag is present */ int keepMergeFlag; /* True if --keep-merge-files is present */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ const char *zVersion; /* The VERSION argument */ int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ int nMerge = 0; /* Number of prior merges processed */ Stmt q; /* SQL statment used for merge processing */ /* Notation: ** ** V The current check-out ** M The version being merged in ** P The "pivot" - the most recent common ancestor of V and M. ** N The "name pivot" - for detecting renames */ undo_capture_command_line(); verboseFlag = find_option("verbose","v",0)!=0; forceMissingFlag = find_option("force-missing",0,0)!=0; if( !verboseFlag ){ verboseFlag = find_option("detail",0,0)!=0; /* deprecated */ } pickFlag = find_option("cherrypick",0,0)!=0; if('c'==*g.zCmdName /*called as cherrypick, possibly a short form*/){ pickFlag = 1; } integrateFlag = find_option("integrate",0,0)!=0; backoutFlag = find_option("backout",0,0)!=0; zBinGlob = find_option("binary",0,1); dryRunFlag = find_option("dry-run","n",0)!=0; if( !dryRunFlag ){ |
︙ | ︙ | |||
399 400 401 402 403 404 405 | } if( !dryRunFlag ){ if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "merge") ){ fossil_fatal("merge abandoned due to sync failure"); } } | > > > > > > > > > > > > > > > | | > | > > | | > > > > > | 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 | } if( !dryRunFlag ){ if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "merge") ){ fossil_fatal("merge abandoned due to sync failure"); } } /* ** A "multi-merge" means two or more other check-ins are being merged ** into the current check-in. In other words, there are two or more ** VERSION arguments on the command-line. Multi-merge works by doing ** the merges one by one, as long as there are no conflicts. At the ** bottom of this routine, a jump is made back up to this point if there ** are more merges yet to be done and no errors have yet been seen. ** ** Related variables: ** bMultiMerge True if there are one or more merges yet to do ** zVersion The name of the current checking being merged in ** nMerge Number of prior merges */ merge_next_child: /* Find mid, the artifactID of the version to be merged into ** the current check-out. */ if( g.argc>=3 ){ int i; /* Mid is specified as an argument on the command-line */ zVersion = g.argv[2]; mid = name_to_typed_rid(zVersion, "ci"); if( mid==0 || !is_a_version(mid) ){ fossil_fatal("not a version: %s", zVersion); } bMultiMerge = g.argc>3; if( bMultiMerge ){ for(i=3; i<g.argc; i++) g.argv[i-1] = g.argv[i]; g.argc--; } }else if( g.argc==2 ){ /* No version specified on the command-line so pick the most recent ** leaf that is (1) not the version currently checked out and (2) ** has not already been merged into the current check-out and (3) ** the leaf is not closed and (4) the leaf is in the same branch ** as the current check-out. |
︙ | ︙ | |||
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | " datetime(event.mtime,toLocal())," " coalesce(ecomment, comment)," " coalesce(euser, user)" " FROM event, blob" " WHERE event.objid=%d AND blob.rid=%d", mid, mid ); if( db_step(&q)==SQLITE_ROW ){ char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 3), db_column_text(&q, 2)); comment_print(zCom, db_column_text(&q,2), 0, -1, get_comment_format()); fossil_free(zCom); } db_finalize(&q); }else{ usage("?OPTIONS? ?VERSION?"); return; } | > > | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | " datetime(event.mtime,toLocal())," " coalesce(ecomment, comment)," " coalesce(euser, user)" " FROM event, blob" " WHERE event.objid=%d AND blob.rid=%d", mid, mid ); zVersion = 0; if( db_step(&q)==SQLITE_ROW ){ char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 3), db_column_text(&q, 2)); comment_print(zCom, db_column_text(&q,2), 0, -1, get_comment_format()); fossil_free(zCom); zVersion = mprintf("%S",db_column_text(&q,0)); } db_finalize(&q); }else{ usage("?OPTIONS? ?VERSION?"); return; } |
︙ | ︙ | |||
465 466 467 468 469 470 471 | if( pickFlag || backoutFlag ){ if( integrateFlag ){ fossil_fatal("incompatible options: --integrate and --cherrypick " "with --backout"); } pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid); if( pid<=0 ){ | | | | 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 | if( pickFlag || backoutFlag ){ if( integrateFlag ){ fossil_fatal("incompatible options: --integrate and --cherrypick " "with --backout"); } pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid); if( pid<=0 ){ fossil_fatal("cannot find an ancestor for %s", zVersion); } }else{ if( !zPivot ){ pivot_set_primary(mid); pivot_set_secondary(vid); db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0"); while( db_step(&q)==SQLITE_ROW ){ pivot_set_secondary(db_column_int(&q,0)); } db_finalize(&q); pid = pivot_find(0); if( pid<=0 ){ fossil_fatal("cannot find a common ancestor between the current " "check-out and %s", zVersion); } } pivot_set_primary(mid); pivot_set_secondary(vid); nid = pivot_find(1); if( nid!=pid ){ pivot_set_primary(nid); |
︙ | ︙ | |||
506 507 508 509 510 511 512 | } if( !forceFlag && mid==pid ){ fossil_print("Merge skipped because it is a no-op. " " Use --force to override.\n"); return; } if( integrateFlag && !is_a_leaf(mid)){ | | | | | 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 | } if( !forceFlag && mid==pid ){ fossil_print("Merge skipped because it is a no-op. " " Use --force to override.\n"); return; } if( integrateFlag && !is_a_leaf(mid)){ fossil_warning("ignoring --integrate: %s is not a leaf", zVersion); integrateFlag = 0; } if( integrateFlag && content_is_private(mid) ){ fossil_warning( "ignoring --integrate: %s is on a private branch" "\n Use \"fossil amend --close\" (after commit) to close the leaf.", zVersion); integrateFlag = 0; } if( verboseFlag ){ print_checkin_description(mid, 12, integrateFlag ? "integrate:" : "merge-from:"); print_checkin_description(pid, 12, "baseline:"); } vfile_check_signature(vid, CKSIG_ENOTFILE); if( nMerge==0 ) db_begin_transaction(); if( !dryRunFlag ) undo_begin(); if( load_vfile_from_rid(mid) && !forceMissingFlag ){ fossil_fatal("missing content, unable to merge"); } if( load_vfile_from_rid(pid) && !forceMissingFlag ){ fossil_fatal("missing content, unable to merge"); } |
︙ | ︙ | |||
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 | } db_finalize(&q); /* Report on conflicts */ if( nConflict ){ fossil_warning("WARNING: %d merge conflicts", nConflict); } if( nOverwrite ){ fossil_warning("WARNING: %d unmanaged files were overwritten", nOverwrite); } | > > > > > > > > > > > > > > | | > > > > > | 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 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 | } db_finalize(&q); /* Report on conflicts */ if( nConflict ){ fossil_warning("WARNING: %d merge conflicts", nConflict); if( bMultiMerge ){ int i; Blob msg; blob_init(&msg, 0, 0); blob_appendf(&msg, "The following %ss were not attempted due to prior conflicts:", pickFlag ? "cherrypick" : backoutFlag ? "backout" : "merge" ); for(i=2; i<g.argc; i++){ blob_appendf(&msg, " %s", g.argv[i]); } fossil_warning("%s", blob_str(&msg)); blob_zero(&msg); } } if( nOverwrite ){ fossil_warning("WARNING: %d unmanaged files were overwritten", nOverwrite); } if( dryRunFlag && !bMultiMerge ){ fossil_warning("REMINDER: this was a dry run -" " no files were actually changed."); } /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); if( pickFlag ){ vmerge_insert(-1, mid); /* For a cherrypick merge, make the default check-in comment the same ** as the check-in comment on the check-in that is being merged in. */ db_multi_exec( "REPLACE INTO vvar(name,value)" " SELECT 'ci-comment', coalesce(ecomment,comment) FROM event" " WHERE type='ci' AND objid=%d", mid ); }else if( backoutFlag ){ vmerge_insert(-2, pid); }else if( integrateFlag ){ vmerge_insert(-4, mid); }else{ vmerge_insert(0, mid); } if( bMultiMerge && nConflict==0 ){ nMerge++; goto merge_next_child; } if( !dryRunFlag ) undo_finish(); db_end_transaction(dryRunFlag); } |
Changes to src/path.c.
︙ | ︙ | |||
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | for(p=path.pStart; p; p=p->u.pTo){ int fnid, pfnid; if( !p->fromIsParent && (p->u.pTo==0 || p->u.pTo->fromIsParent) ){ /* Skip nodes where the parent is not on the path */ continue; } db_bind_int(&q1, ":mid", p->rid); while( db_step(&q1)==SQLITE_ROW ){ fnid = db_column_int(&q1, 1); pfnid = db_column_int(&q1, 0); if( pfnid==0 ){ pfnid = fnid; fnid = 0; } if( !p->fromIsParent ){ int t = fnid; fnid = pfnid; pfnid = t; } if( zDebug ){ | > > > > > > > > | | < | 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 | for(p=path.pStart; p; p=p->u.pTo){ int fnid, pfnid; if( !p->fromIsParent && (p->u.pTo==0 || p->u.pTo->fromIsParent) ){ /* Skip nodes where the parent is not on the path */ continue; } db_bind_int(&q1, ":mid", p->rid); if( zDebug ){ fossil_print("%s check-in %.16z %z rid %d\n", zDebug, db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid), db_text(0, "SELECT date(mtime) FROM event WHERE objid=%d", p->rid), p->rid ); } while( db_step(&q1)==SQLITE_ROW ){ fnid = db_column_int(&q1, 1); pfnid = db_column_int(&q1, 0); if( pfnid==0 ){ pfnid = fnid; fnid = 0; } if( !p->fromIsParent ){ int t = fnid; fnid = pfnid; pfnid = t; } if( zDebug ){ fossil_print("%s %d[%z] -> %d[%z]\n", zDebug, pfnid, db_text(0, "SELECT name FROM filename WHERE fnid=%d", pfnid), fnid, db_text(0, "SELECT name FROM filename WHERE fnid=%d", fnid)); } for(pChng=pAll; pChng; pChng=pChng->pNext){ if( pChng->curName==pfnid ){ |
︙ | ︙ |
Changes to src/stash.c.
︙ | ︙ | |||
579 580 581 582 583 584 585 | 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", stashid); | | > > > > < | 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 | 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", stashid); char **newArgv; int i = 2; Stmt q; if( nFile==0 ){ fossil_fatal("No modified files match the provided pattern."); } newArgv = fossil_malloc( sizeof(char*)*(nFile+2) ); db_prepare(&q,"SELECT origname FROM stashfile WHERE stashid=%d", stashid); while( db_step(&q)==SQLITE_ROW ){ newArgv[i++] = mprintf("%s%s", g.zLocalRoot, db_column_text(&q, 0)); } db_finalize(&q); newArgv[0] = g.argv[0]; newArgv[1] = 0; g.argv = newArgv; g.argc = nFile+2; } /* Make sure the stash has committed before running the revert, so that ** 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); |
︙ | ︙ |
Changes to src/tag.c.
︙ | ︙ | |||
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 | ** Fossil-internal prefixes include "sym-" ** (branch name), "wiki-", "event-" ** (technote), and "tkt-" (ticket). The ** prefix is stripped from the resulting ** list unless --raw is provided. Ignored if ** ARTIFACT-ID is provided. ** --raw List raw names of tags ** --tagtype TYPE List only tags of type TYPE, which must ** be one of: cancel, singleton, propagated ** ** The option --raw allows the manipulation of all types of tags ** used for various internal purposes in fossil. It also shows ** "cancel" tags for the "find" and "list" subcommands. You should ** not use this option to make changes unless you are sure what ** you are doing. ** | > > > > > | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | ** Fossil-internal prefixes include "sym-" ** (branch name), "wiki-", "event-" ** (technote), and "tkt-" (ticket). The ** prefix is stripped from the resulting ** list unless --raw is provided. Ignored if ** ARTIFACT-ID is provided. ** --raw List raw names of tags ** --sep SEP Separator when concatenating values ** --tagtype TYPE List only tags of type TYPE, which must ** be one of: cancel, singleton, propagated ** --values List tag values ** If --sep is supplied, list all values of a tag on ** the same line, separated by SEP; otherwise list ** each value on its own line. ** ** The option --raw allows the manipulation of all types of tags ** used for various internal purposes in fossil. It also shows ** "cancel" tags for the "find" and "list" subcommands. You should ** not use this option to make changes unless you are sure what ** you are doing. ** |
︙ | ︙ | |||
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 | if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){ Stmt q; const int fRaw = find_option("raw","",0)!=0; const char *zTagType = find_option("tagtype","t",1); const int fInverse = find_option("inverse","v",0)!=0; 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; | > > > > | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | 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 | if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){ Stmt q; const int fRaw = find_option("raw","",0)!=0; const char *zTagType = find_option("tagtype","t",1); const int fInverse = find_option("inverse","v",0)!=0; const char *zTagPrefix = find_option("prefix","",1); int nTagType = fRaw ? -1 : 0; int fValues = find_option("values","",0)!=0; const char *zSep = find_option("sep","",1); 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; if( !fValues ){ db_prepare(&q, "SELECT tagname FROM tag" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=tag.tagid" " AND tagtype%s%d)" " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' " " END ORDER BY tagname COLLATE uintnocase", zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, nTagType, zTagPrefix, zTagPrefix ); }else{ if( zSep ){ db_prepare(&q, /* work around group_concat() with DISTINCT and custom separator */ "SELECT tagname," " rtrim(replace(group_concat(DISTINCT value||'@!' " " ORDER BY value ASC), '@!,', %Q),'@!')" " FROM tagxref, tag" " WHERE tagxref.tagid=tag.tagid AND tagtype%s%d" " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END" " GROUP BY tagname" " ORDER BY tagname COLLATE uintnocase", ( zSep && strlen(zSep)>0 ) ? zSep : ",", zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, nTagType, zTagPrefix, zTagPrefix ); }else{ db_prepare(&q, "SELECT DISTINCT tagname, value" " FROM tagxref, tag" " WHERE tagxref.tagid=tag.tagid AND tagtype%s%d" " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END" " ORDER BY tagname, value COLLATE uintnocase", zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, nTagType, zTagPrefix, zTagPrefix ); } } while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zValue = db_column_text(&q, 1); int nWidth = fValues ? 20 : 0; const char *zzValue = (fValues && zValue) ? mprintf(" %s", zValue) : ""; if( fRaw ){ fossil_print("%-*s%s\n", nWidth, zName, zzValue); }else if( nTagPrefix>0 ){ assert(db_column_bytes(&q,0)>=nTagPrefix); fossil_print("%-*s%s\n", nWidth, &zName[nTagPrefix], zzValue); }else if( strncmp(zName, "sym-", 4)==0 ){ fossil_print("%-*s%s\n", nWidth, &zName[4], zzValue); } } db_finalize(&q); }else if( g.argc==4 ){ char const *zObjId = g.argv[3]; const int rid = name_to_rid(zObjId); const int objType = whatis_rid_type(rid); |
︙ | ︙ |
Changes to src/timeline.c.
︙ | ︙ | |||
1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 | ** match a check-in that is already in the timeline. ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change ** p=CHECKIN Parents and ancestors of CHECKIN ** bt=PRIOR ... going back to PRIOR ** d=CHECKIN Children and descendants of CHECKIN ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... shortest path from CX back to CHECKIN ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN ** d=CX ... from CX up to the time of CHECKIN ** from=CX ... shortest path from CX up to CHECKIN | > > > | 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 | ** match a check-in that is already in the timeline. ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change ** p=CHECKIN Parents and ancestors of CHECKIN ** bt=PRIOR ... going back to PRIOR ** p2=CKIN2 ... use CKIN2 if CHECKIN is not found ** d=CHECKIN Children and descendants of CHECKIN ** d2=CKIN2 ... Use CKIN2 if CHECKIN is not found ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... shortest path from CX back to CHECKIN ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN ** d=CX ... from CX up to the time of CHECKIN ** from=CX ... shortest path from CX up to CHECKIN |
︙ | ︙ | |||
1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ const char *zTo2 = 0; int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ int noMerge = P("shortest")==0; /* Follow merge links if shorter */ int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ int pd_rid; double rBefore, rAfter, rCirca; /* Boundary times */ const char *z; char *zOlderButton = 0; /* URL for Older button at the bottom */ char *zOlderButtonLabel = 0; /* Label for the Older Button */ char *zNewerButton = 0; /* URL for Newer button at the top */ char *zNewerButtonLabel = 0; /* Label for the Newer button */ int selectedRid = 0; /* Show a highlight on this RID */ | > | 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ const char *zTo2 = 0; int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ int noMerge = P("shortest")==0; /* Follow merge links if shorter */ int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ int pd_rid; const char *zDPName; /* Value of p=, d=, or dp= params */ double rBefore, rAfter, rCirca; /* Boundary times */ const char *z; char *zOlderButton = 0; /* URL for Older button at the bottom */ char *zOlderButtonLabel = 0; /* Label for the Older Button */ char *zNewerButton = 0; /* URL for Newer button at the top */ char *zNewerButtonLabel = 0; /* Label for the Newer button */ int selectedRid = 0; /* Show a highlight on this RID */ |
︙ | ︙ | |||
1900 1901 1902 1903 1904 1905 1906 | } } }else{ nEntry = 50; } /* Query parameters d=, p=, and f= and variants */ | < | < | > | 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 | } } }else{ nEntry = 50; } /* Query parameters d=, p=, and f= and variants */ p_rid = name_choice("p","p2", &zDPName); d_rid = name_choice("d","d2", &zDPName); z = P("f"); f_rid = z ? name_to_typed_rid(z,"ci") : 0; z = P("df"); if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ nEntry = 0; useDividers = 0; cgi_replace_query_parameter("d",fossil_strdup(z)); zDPName = z; } /* Undocumented query parameter to set JS mode */ builtin_set_js_delivery_mode(P("jsmode"),1); secondaryRid = name_to_typed_rid(P("sel2"),"ci"); selectedRid = name_to_typed_rid(P("sel1"),"ci"); |
︙ | ︙ | |||
1934 1935 1936 1937 1938 1939 1940 | ** present or if this repository lacks a "cherrypick" table. */ if( PB("ncp") || !db_table_exists("repository","cherrypick") ){ showCherrypicks = 0; } /* To view the timeline, must have permission to read project data. */ | | | 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 | ** present or if this repository lacks a "cherrypick" table. */ if( PB("ncp") || !db_table_exists("repository","cherrypick") ){ showCherrypicks = 0; } /* To view the timeline, must have permission to read project data. */ pd_rid = name_choice("dp","dp2",&zDPName); if( pd_rid ){ p_rid = d_rid = pd_rid; } login_check_credentials(); if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) || (bisectLocal && !g.perm.Setup) ){ |
︙ | ︙ | |||
2327 2328 2329 2330 2331 2332 2333 | if( !haveParameterN ) nEntry = 10; } db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" ); zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", p_rid ? p_rid : d_rid); | | | 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 | if( !haveParameterN ) nEntry = 10; } db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" ); zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", p_rid ? p_rid : d_rid); zCiName = zDPName; if( zCiName==0 ) zCiName = zUuid; blob_append_sql(&sql, " AND event.objid IN ok"); nd = 0; if( d_rid ){ Stmt s; double rStopTime = 9e99; zFwdTo = P("ft"); |
︙ | ︙ |
Changes to test/amend.test.
︙ | ︙ | |||
294 295 296 297 298 299 300 | {000000 lower Upper alpha 0alpha} {000000 0alpha Upper alpha lower} } set tc 0 foreach {tagt result} $tagtests { incr tc set tags {} set cancels {} | | < | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | {000000 lower Upper alpha 0alpha} {000000 0alpha Upper alpha lower} } set tc 0 foreach {tagt result} $tagtests { incr tc set tags {} set cancels {} set t1exp [join $result ", "] set t2exp "*" set t3exp "*" set t5exp "*" foreach tag $tagt { lappend tags -tag $tag lappend cancels -cancel $tag } foreach res $result { append t3exp "Add*tag*\"$res\".*" append t5exp "Cancel*tag*\"$res\".*" } foreach res [lsort -nocase $result] { append t2exp "sym-$res*" } eval fossil amend $HASH $tags |
︙ | ︙ |
Changes to test/merge5.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 | 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. } | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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 # Verify the results of a check-out # proc checkout-test {testid expected_content} { set flist {} foreach {status filename} [exec $::fossilexe ls -l] { |
︙ | ︙ |
Changes to test/stash.test.
︙ | ︙ | |||
166 167 168 169 170 171 172 | @@ -0,0 +1,1 @@ +f0} ######## # fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? # fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS? | | | | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | @@ -0,0 +1,1 @@ +f0} ######## # fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? # fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS? #fossil stash show #test stash-1-show {[normalize_result] eq $diff_stash_1} #fossil stash diff #test stash-1-diff {[normalize_result] eq $diff_stash_1} knownBug ######## # fossil stash pop stash-test 2 pop { DELETE f1 UPDATE f2 |
︙ | ︙ | |||
204 205 206 207 208 209 210 | # 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 | | | | | | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | # 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 # error is fixed, there is nothing stashed to test. # Test stashing the combination of a renamed file and an added file that |
︙ | ︙ | |||
294 295 296 297 298 299 300 | RENAME f2 f2n MOVED_FILE} [file normalize f2] { }] -changes { RENAMED f2 -> f2n } -addremove { } -exists {f1 f2n} -notexists {f2} | > | | | | | | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | RENAME f2 f2n MOVED_FILE} [file normalize f2] { }] -changes { RENAMED f2 -> f2n } -addremove { } -exists {f1 f2n} -notexists {f2} fossil stash save -m f2n #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 { |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
306 307 308 309 310 311 312 313 314 315 316 317 318 319 | 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 \ | > | 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | clean-glob \ clearsign \ comment-format \ crlf-glob \ crnl-glob \ default-csp \ default-perms \ default-skin \ diff-binary \ diff-command \ dont-commit \ dont-push \ dotfiles \ editor \ email-admin \ |
︙ | ︙ |
Changes to test/utf.test.
︙ | ︙ | |||
33 34 35 36 37 38 39 | proc utf-check {testname args} { global tempPath set i 1 foreach {fileName result} $args { set fileName [file join $tempPath $fileName] fossil test-looks-like-utf $fileName set result [string map [list %TEMP% $tempPath \r\n \n] $result] | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | proc utf-check {testname args} { global tempPath set i 1 foreach {fileName result} $args { set fileName [file join $tempPath $fileName] fossil test-looks-like-utf $fileName set result [string map [list %TEMP% $tempPath \r\n \n] $result] # if {$::RESULT ne $result} {puts stdout $::RESULT; exit} test utf-check-$testname.$i {$::RESULT eq $result} incr i } } unset -nocomplain enc array set enc [list \ |
︙ | ︙ | |||
17607 17608 17609 17610 17611 17612 17613 | Has flag LOOK_LONE_LF: no Has flag LOOK_CRLF: no Has flag LOOK_LONG: no Has flag LOOK_INVALID: no Has flag LOOK_ODD: no Has flag LOOK_SHORT: no} | | | | | | | | | | | | | | | | | 17607 17608 17609 17610 17611 17612 17613 17614 17615 17616 17617 17618 17619 17620 17621 17622 17623 17624 17625 17626 17627 17628 17629 17630 17631 17632 17633 17634 17635 | Has flag LOOK_LONE_LF: no Has flag LOOK_CRLF: no Has flag LOOK_LONG: no Has flag LOOK_INVALID: no Has flag LOOK_ODD: no Has flag LOOK_SHORT: no} #utf-check 1179 utf-check-1179-2-129-1.jnk \ #{File "%TEMP%/utf-check-1179-2-129-1.jnk" has 7 bytes. #Starts with UTF-8 BOM: no #Starts with UTF-16 BOM: yes #Looks like UTF-8: yes #Has flag LOOK_NUL: no #Has flag LOOK_CR: no #Has flag LOOK_LONE_CR: no #Has flag LOOK_LF: no #Has flag LOOK_LONE_LF: no #Has flag LOOK_CRLF: no #Has flag LOOK_LONG: no #Has flag LOOK_INVALID: yes #Has flag LOOK_ODD: no #Has flag LOOK_SHORT: no} utf-check 1180 utf-check-1180-2-130-0.jnk \ {File "%TEMP%/utf-check-1180-2-130-0.jnk" has 4 bytes. Starts with UTF-8 BOM: no Starts with UTF-16 BOM: yes Looks like UTF-16: yes Has flag LOOK_NUL: no |
︙ | ︙ | |||
24119 24120 24121 24122 24123 24124 24125 | Has flag LOOK_LONE_LF: no Has flag LOOK_CRLF: no Has flag LOOK_LONG: no Has flag LOOK_INVALID: yes Has flag LOOK_ODD: no Has flag LOOK_SHORT: no} | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 24119 24120 24121 24122 24123 24124 24125 24126 24127 24128 24129 24130 24131 24132 24133 24134 24135 24136 24137 24138 24139 24140 24141 24142 24143 24144 24145 24146 24147 24148 24149 24150 24151 24152 24153 24154 24155 24156 24157 24158 24159 24160 24161 24162 24163 | Has flag LOOK_LONE_LF: no Has flag LOOK_CRLF: no Has flag LOOK_LONG: no Has flag LOOK_INVALID: yes Has flag LOOK_ODD: no Has flag LOOK_SHORT: no} #utf-check 1586 utf-check-1586-3-128-0.jnk \ #{File "%TEMP%/utf-check-1586-3-128-0.jnk" has 6 bytes. #Starts with UTF-8 BOM: no #Starts with UTF-16 BOM: reversed #Looks like UTF-16: no #Has flag LOOK_NUL: yes #Has flag LOOK_CR: no #Has flag LOOK_LONE_CR: no #Has flag LOOK_LF: no #Has flag LOOK_LONE_LF: no #Has flag LOOK_CRLF: no #Has flag LOOK_LONG: no #Has flag LOOK_INVALID: no #Has flag LOOK_ODD: no #Has flag LOOK_SHORT: no} #utf-check 1587 utf-check-1587-3-128-1.jnk \ #{File "%TEMP%/utf-check-1587-3-128-1.jnk" has 7 bytes. #Starts with UTF-8 BOM: no #Starts with UTF-16 BOM: reversed #Looks like UTF-8: no #Has flag LOOK_NUL: yes #Has flag LOOK_CR: no #Has flag LOOK_LONE_CR: no #Has flag LOOK_LF: no #Has flag LOOK_LONE_LF: no #Has flag LOOK_CRLF: no #Has flag LOOK_LONG: no #Has flag LOOK_INVALID: yes #Has flag LOOK_ODD: no #Has flag LOOK_SHORT: no} utf-check 1588 utf-check-1588-3-129-0.jnk \ {File "%TEMP%/utf-check-1588-3-129-0.jnk" has 6 bytes. Starts with UTF-8 BOM: no Starts with UTF-16 BOM: no Looks like UTF-8: no Has flag LOOK_NUL: yes |
︙ | ︙ |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <title>Change Log</title> <h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2> * Apache change work-around → As part of a security fix, the Apache webserver mod_cgi module has stopped relaying the Content-Length field of the HTTP reply header from the CGI programs back to the client in cases where the connection is to be closed and the client is able to read until end-of-file. The HTTP and CGI specs allow for this, though it does seem rude. Older versions of Fossil were depending on the Content-Length header field being set. To work around the change to Apache, Fossil has been enhanced to cope with a missing Content-Length in the reply header. See [forum:/forumpost/12ac403fd29cfc89|forum thread 12ac403fd29cfc89]. * [./customskin.md|Skin] enhancements: <ul> <li> Reworked the default skin to make everything more readable: larger fonts, more whitespace, deeper indents to show hierarchy and to |
︙ | ︙ | |||
39 40 41 42 43 44 45 | <li> Automatically highlight the endpoints for from=,to= queries. <li> Add the to2=Z query parameter to augment from=X,to=Y so that the path from X to Z is shown if Y cannot be found. </ul> * 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 | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <li> Automatically highlight the endpoints for from=,to= queries. <li> Add the to2=Z query parameter to augment from=X,to=Y so that the path from X to Z is shown if Y cannot be found. </ul> * 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 viewers don't have to guess. The hash is shown in a fixed-width font for a more visually pleasing display. * If the [/help?cmd=autosync|autosync setting] contains keyword "all", the automatic sync occurs against all defined remote repositories, not just the default. * Markdown formatter: improved handling of indented fenced code blocks that contain blank lines. * When doing a "[/help?cmd=add|fossil add]" on a system with case-insensitive |
︙ | ︙ |
Added www/colordiff.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Colorized Diffs The oldest and most widely compatible method to get colorized diffs in Fossil is to use its web UI: fossil ui --page '/vdiff?from=2024-04-01&to=trunk' That syntax is admittedly awkward, and it doesn’t work where “from” is the current checkout. Fortunately, there are many other methods to get colorized `diff` output from Fossil. <a id="ui"></a> ## `fossil diff -b` This produces a graphical diff in HTML format and sends it to the user’s default web browser for viewing. <a id="ui"></a> ## `fossil diff -tk` You may be surprised to learn that the prior feature doesn’t use any of the skinning or chrome from Fossil UI. This is because it is meant as a functional replacement for an older method of getting colorized diffs, “`fossil diff -tk`”. The feature was added after Apple stopped shipping Tcl/Tk in macOS, and the third-party replacements often failed to work correctly. It’s useful on other platforms as well. <a id="git"></a> ## Delegate to Git It may be considered sacrilege by some, but the most direct method for those who want Git-like diff behavior may be to delegate diff behavior to Git: fossil set --global diff-command 'git diff --no-index' The flag permits it to diff files that aren’t inside a Git repository. <a id="diffutils"></a> ## GNU Diffutils If your system is from 2016 or later, it may include [GNU Diffutils][gd] 3.4 or newer, which lets you say: fossil set --global diff-command 'diff -dwu --color=always' You might think you could give `--color=auto`, but that fails with commands like “`fossil diff | less`” since the pipe turns the output non-interactive from the perspective of the underlying `diff` instance. This use of unconditional colorization means you will then have to remember to add the `-i` option to `fossil diff` commands when producing `patch(1)` files or piping diff output to another command that doesn’t understand ANSI escape sequences, such as [`diffstat`][ds]. [ds]: https://invisible-island.net/diffstat/ [gd]: https://www.gnu.org/software/diffutils/ <a id="bat"></a> ## Bat, the Cat with Wings We can work around the `--color=auto` problem by switching from GNU less as our pager to [`bat`][bat], as it can detect GNU diff output and colorize it for you: fossil set --global diff-command 'diff -dwu --color=auto' fossil diff | bat In this author’s experience, that works a lot more reliably than GNU less’s ANSI color escape code handling, even when you set `LESS=-R` in your environment. The reason we don’t leave the `diff-command` unset in this case is that Fossil produces additional lines at the start which confuse the diff format detection in `bat`. Forcing output through an external diff command solves that. It also means that if you forget to pipe the output through `bat`, you still get colorized output from GNU diff. [bat]: https://github.com/sharkdp/bat <a id="colordiff"></a> ## Colordiff A method that works on systems predating GNU diffutils 3.4 or the widespread availability of `bat` is to install [`colordiff`][cdurl], as it is included in [many package systems][cdpkg], including ones for outdated OSes. That then lets you say: fossil set --global diff-command 'colordiff -dwu' The main reason we list this alternative last is that it has the same limitation of unconditional color as [above](#diffutils). [cdurl]: https://www.colordiff.org/ [cdpkg]: https://repology.org/project/colordiff/versions <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Changes to www/embeddeddoc.wiki.
︙ | ︙ | |||
239 240 241 242 243 244 245 | </ul> When the symbolic name is a date and time, fossil shows the version of the document that was most recently checked in as of the date and time specified. So, for example, to see what the fossil website looked like at the beginning of 2010, enter: | | | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | </ul> When the symbolic name is a date and time, fossil shows the version of the document that was most recently checked in as of the date and time specified. So, for example, to see what the fossil website looked like at the beginning of 2010, enter: <pre><a href="https://fossil-scm.org/home/doc/2010-01-01/www/index.wiki">https://fossil-scm.org/home/doc/<b>2010-01-01</b>/www/index.wiki </a></pre> The file that encodes this document is stored in the fossil source tree under the name "<b>www/embeddeddoc.wiki</b>" and so that name forms the last part of the URL for this document. As I sit writing this documentation file, I am testing my work by |
︙ | ︙ |
Changes to www/gitusers.md.
︙ | ︙ | |||
137 138 139 140 141 142 143 | [gpull]: https://git-scm.com/docs/git-pull [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well #### <a id="close" name="dotfile"></a> Closing a Check-Out The [`fossil close`][close] command dissociates a check-out directory from the | | | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | [gpull]: https://git-scm.com/docs/git-pull [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well #### <a id="close" name="dotfile"></a> Closing a Check-Out The [`fossil close`][close] command dissociates a check-out directory from the Fossil repository database, _nondestructively_ inverting [`fossil open`][open]. (Contrast Git’s [closest alternative](#worktree), `git worktree remove`, which *is* destructive!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the `.fslckout` file at the root of a check-out directory. This is a SQLite |
︙ | ︙ | |||
298 299 300 301 302 303 304 | constants are equal. Yet the constants are *not* equal because Fossil reads from a single disk file rather than visit potentially many files in sequence as Git must, so the OS’s buffer cache can result in [still better performance][35pct]. | | | | | | | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | constants are equal. Yet the constants are *not* equal because Fossil reads from a single disk file rather than visit potentially many files in sequence as Git must, so the OS’s buffer cache can result in [still better performance][35pct]. Unlike Git’s log, Fossil’s timeline shows info across all branches by default, a feature for maintaining better situational awareness. It is possible to restrict the timeline to a single branch using `fossil timeline -b`. Similarly, to restrict the timeline using the web UI equivalent, click the name of a branch on the `/timeline` or `/brlist` page. (Or manually, by adding the `r=` query parameter.) Note that even in this case, the Fossil timeline still shows other branches where they interact with the one you’ve referenced in this way; again, better situational awareness. #### <a id="emu-log"></a> Emulating `git log` |
︙ | ︙ | |||
455 456 457 458 459 460 461 462 463 464 465 466 467 | character and such, because it doesn’t recognize Fossil commit messages and apply similar rules as to Git commit messages. [cskin]: ./customskin.md [lsl]: https://chris.beams.io/posts/git-commit/#limit-50 <a id="staging"></a> ## There Is No Staging Area Fossil omits the "Git index" or "staging area" concept. When you type "`fossil commit`" _all_ changes in your check-out are committed, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > > > > > > < < < < < < | < < < | < | | < < | | > | 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 | character and such, because it doesn’t recognize Fossil commit messages and apply similar rules as to Git commit messages. [cskin]: ./customskin.md [lsl]: https://chris.beams.io/posts/git-commit/#limit-50 <a id="autocommit"></a> ## Fossil Never Auto-Commits There are several features in Git besides its `commit` command that produce a new commit to the repository, and by default, they do it without prompting or even asking for a commit message. These include Git’s [`rebase`](#rebase), `merge`, and [`cherrypick`](#cpickrev) commands, plus the [commit splitting](#comsplit) sub-feature “`git commit -p`”. Fossil never does this, on firm philosophical grounds: we wish to be able to test that each potentially repository-changing command does not break anything _before_ freezing it immutably into the [Merkle tree](./blockchain.md). Where Fossil has equivalent commands, they modify the checkout tree alone, requiring a separate `commit` command afterward, withheld until the user has satisfied themselves that the command’s result is correct. We believe this is the main reason Git lacks an [autosync](#autosync) feature: making push a manual step gives the user a chance to rewrite history after triggering one of these autocommits locally, should the automatic commit fail to work out as expected. Fossil chooses the inverse path under the philosophy that commits are *commitments,* not something you’re allowed to go back and rewrite later. This is also why there is no automatic commit message writing feature in Fossil, as in these autocommit-triggering Git commands. The user is meant to write the commit message by hand after they are sure it’s correct, in clear-headed retrospective fashion. Having the tool do it prospectively before one can test the result is simply backwards. <a id="staging"></a> ## There Is No Staging Area Fossil omits the "Git index" or "staging area" concept. When you type "`fossil commit`" _all_ changes in your check-out are committed, by default. There is no need for the "-a" option as with Git. If you only want to commit _some_ of the changes, list the names of the files or directories you want to commit as arguments, like this: fossil commit src/feature.c doc/feature.md examples/feature Note that the last element is a directory name, meaning “any changed file under the `examples/feature` directory.” <a id="comsplit"></a> ## Commit Splitting [Git’s commit splitting features][gcspl] rely on other features of Git that Fossil purposefully lacks, as covered in the prior two sections: [autocommit](#autocommit) and [the staging area](#staging). While there is no direct Fossil equivalent for `git add -p`, `git commit -p`, or `git rebase -i`, you can get the same effect by converting an uncommitted change set to a patch and then running it through [Patchouli]. Rather than use `fossil diff -i` to produce such a patch, a safer and more idiomatic method would be: fossil stash save -m 'my big ball-o-hackage' fossil stash diff > my-changes.patch That stores your changes in the stash, then lets you operate on a copy of that patch. Each time you re-run the second command, it will take the current state of the working directory into account to produce a potentially different patch, likely smaller because it leaves out patch hunks already applied. In this way, the combination of working tree and stash replaces the need for Git’s index feature. We believe we know how to do commit splitting in a way compatible with the Fossil philosophy, without following Git’s ill-considered design leads. It amounts to automating the above process through an interactive variant of [`fossil stash apply`][stash], as currently prototyped in the third-party tool [`fnc`][fnc] and [its interactive `stash` command][fncsta]. We merely await someone’s [contribution][ctrb] of this feature into Fossil proper. [ctrb]: https://fossil-scm.org/fossil/doc/trunk/www/contribute.wiki [fnc]: https://fnc.bsdbox.org/ [fncsta]: https://fnc.bsdbox.org/uv/doc/fnc.1.html#stash [gcspl]: https://git-scm.com/docs/git-rebase#_splitting_commits [Patchouli]: https://pypi.org/project/patchouli/ |
︙ | ︙ | |||
815 816 817 818 819 820 821 | draft was written, it’s been revised multiple times to address less common objections as well. Chances are not good that you are going to come up with a new objection that we haven’t already considered and addressed there. There is only one sub-feature of `git rebase` that is philosophically compatible with Fossil yet which currently has no functional equivalent. | | | > < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | draft was written, it’s been revised multiple times to address less common objections as well. Chances are not good that you are going to come up with a new objection that we haven’t already considered and addressed there. There is only one sub-feature of `git rebase` that is philosophically compatible with Fossil yet which currently has no functional equivalent. We [covered this and the workaround for its lack](#comsplit) above. [3]: ./rebaseharm.md <a id="cdiff"></a> ## Colorized Diffs When you run `git diff` on an ANSI X3.64 capable terminal, it uses color to distinguish insertions, deletions, and replacements, but as of this writing, `fossil diff` produces traditional uncolored [unified diff format][udiff] output, suitable for producing a [patch file][pfile]. There are [many methods](./colordiff.md) for solving this. Viewed this way, Fossil doesn’t lack colorized diffs, it simply has *one* method where they *aren’t* colorized. [pfile]: https://en.wikipedia.org/wiki/Patch_(Unix) [udiff]: https://en.wikipedia.org/wiki/Diff#Unified_format ## <a id="show"></a> Showing Information About Commits While there is no direct equivalent to Git’s “`show`” command, similar |
︙ | ︙ | |||
934 935 936 937 938 939 940 | The `--numstat` output is a bit cryptic, so we recommend delegating this task to [the widely-available `diffstat` tool][dst], which gives a histogram in its default output mode rather than bare integers: fossil diff -i -v --from 2020-04-01 | diffstat We gave the `-i` flag in both cases to force Fossil to use its internal | | | | < < | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | The `--numstat` output is a bit cryptic, so we recommend delegating this task to [the widely-available `diffstat` tool][dst], which gives a histogram in its default output mode rather than bare integers: fossil diff -i -v --from 2020-04-01 | diffstat We gave the `-i` flag in both cases to force Fossil to use its internal diff implementation, bypassing [your local `diff-command` setting][dcset] since the `--numstat` option has no effect when you have an external diff command set. If you leave off the `-v` flag in the second example, the `diffstat` output won’t include info about any newly-added files. [dcset]: https://fossil-scm.org/home/help?cmd=diff-command [dst]: https://invisible-island.net/diffstat/diffstat.html <a id="btnames"></a> ## Branch and Tag Names |
︙ | ︙ |
Changes to www/hints.wiki.
1 2 | <title>Fossil Tips And Usage Hints</title> | > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <title>Fossil Tips And Usage Hints</title> A collection of useful hints and tricks in no particular order: 1. Click on two nodes of any timeline graph in succession to see a diff between the two versions. 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands to get a pop-up window containing a complete side-by-side diff. (NB: The pop-up window is run as a separate Tcl/Tk process, so you will need to have Tcl/Tk installed on your machine for this to work. Visit [http://www.activestate.com/activetcl] to for a quick download of |
︙ | ︙ | |||
56 57 58 59 60 61 62 | on line numbers. This feature only works right with files with a mimetype of text/plain, of course. 10. When editing documentation to be checked in as managed files, you can preview what the documentation will look like by using the special "ckout" branch name in the "doc" URL while running "fossil ui". See the [./embeddeddoc.wiki | embedded documentation] for details. | > > > > > > > > > > | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | on line numbers. This feature only works right with files with a mimetype of text/plain, of course. 10. When editing documentation to be checked in as managed files, you can preview what the documentation will look like by using the special "ckout" branch name in the "doc" URL while running "fossil ui". See the [./embeddeddoc.wiki | embedded documentation] for details. 11. Use the "[/help?cmd=ui|fossil ui /]" command to bring up a menu of all of your local Fossil repositories in your web browser. 12. If you have a bunch of Fossil repositories living on a remote machine that you are able to access using ssh using a command like "ssh login@remote", then you can bring up a user interface for all those remote repositories using the command: "[/help?cmd=ui|fossil ui login@remote:/]". This works by tunneling all HTTP traffic through SSH to the remote machine. |
Changes to www/index.wiki.
︙ | ︙ | |||
82 83 84 85 86 87 88 | atomic even if interrupted by a power loss or system crash. Automatic [./selfcheck.wiki | self-checks] verify that all aspects of the repository are consistent prior to each commit. 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license]. <hr> | | | | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | atomic even if interrupted by a power loss or system crash. Automatic [./selfcheck.wiki | self-checks] verify that all aspects of the repository are consistent prior to each commit. 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license]. <hr> <h3>Latest Release: 2.24 ([/timeline?c=version-2.24|2024-04-23])</h3> * [/uv/download.html|Download] * [./changes.wiki#v2_24|Change Summary] * [/timeline?p=version-2.24&bt=version-2.23&y=ci|Check-ins in version 2.24] * [/timeline?df=version-2.24&y=ci|Check-ins derived from the 2.24 release] * [/timeline?t=release|Timeline of all past releases] <hr> <h3>Quick Start</h3> 1. [/uv/download.html|Download] or install using a package manager or [./build.wiki|compile from sources]. |
︙ | ︙ |
Changes to www/mkindex.tcl.
︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 | chat.md {Fossil Chat} checkin_names.wiki {Check-in And Version Names} checkin.wiki {Check-in Checklist} childprojects.wiki {Child Projects} chroot.md {Server Chroot Jail} ckout-workflows.md {Check-Out Workflows} co-vs-up.md {Checkout vs Update} copyright-release.html {Contributor License Agreement} concepts.wiki {Fossil Core Concepts} contact.md {Developer Contact Information} containers.md {OCI Containers} contribute.wiki {Contributing Code or Documentation To The Fossil Project} css-tricks.md {Fossil CSS Tips and Tricks} customgraph.md {Theming: Customizing the Timeline Graph} | > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | chat.md {Fossil Chat} checkin_names.wiki {Check-in And Version Names} checkin.wiki {Check-in Checklist} childprojects.wiki {Child Projects} chroot.md {Server Chroot Jail} ckout-workflows.md {Check-Out Workflows} co-vs-up.md {Checkout vs Update} colordiff.md {Colorized Diffs} copyright-release.html {Contributor License Agreement} concepts.wiki {Fossil Core Concepts} contact.md {Developer Contact Information} containers.md {OCI Containers} contribute.wiki {Contributing Code or Documentation To The Fossil Project} css-tricks.md {Fossil CSS Tips and Tricks} customgraph.md {Theming: Customizing the Timeline Graph} |
︙ | ︙ |
Changes to www/permutedindex.html.
︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <li><a href="serverext.wiki">CGI Server Extensions</a></li> <li><a href="checkin_names.wiki">Check-in And Version Names</a></li> <li><a href="checkin.wiki">Check-in Checklist</a></li> <li><a href="ckout-workflows.md">Check-Out Workflows</a></li> <li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li> <li><a href="co-vs-up.md">Checkout vs Update</a></li> <li><a href="childprojects.wiki">Child Projects</a></li> <li><a href="build.wiki">Compiling and Installing Fossil</a></li> <li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li> <li><a href="copyright-release.html">Contributor License Agreement</a></li> <li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li> <li><a href="customskin.md">Custom Skins</a></li> <li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li> <li><a href="antibot.wiki">Defense against Spiders and Robots</a></li> | > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <li><a href="serverext.wiki">CGI Server Extensions</a></li> <li><a href="checkin_names.wiki">Check-in And Version Names</a></li> <li><a href="checkin.wiki">Check-in Checklist</a></li> <li><a href="ckout-workflows.md">Check-Out Workflows</a></li> <li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li> <li><a href="co-vs-up.md">Checkout vs Update</a></li> <li><a href="childprojects.wiki">Child Projects</a></li> <li><a href="colordiff.md">Colorized Diffs</a></li> <li><a href="build.wiki">Compiling and Installing Fossil</a></li> <li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li> <li><a href="copyright-release.html">Contributor License Agreement</a></li> <li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li> <li><a href="customskin.md">Custom Skins</a></li> <li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li> <li><a href="antibot.wiki">Defense against Spiders and Robots</a></li> |
︙ | ︙ |