Fossil SCM
Latest merge from trunk.
Commit
d03791648f6bc0237d8a7464c213899b7632c5e89d812d04665f096075372b99
Parent
6759efcc813e66c…
108 files changed
+1027
-523
+1112
-736
+133
-35
+23
+2
-2
+1
-1
+7
-7
+18
-5
+1
-1
+1
-1
+18
-7
+7
-7
+1
-1
+2
-2
+3
-3
+7
-7
+1
-1
+32
-9
+19
-21
+1
-1
+1
-1
+1
-1
+1
-1
+8
-8
+9
-9
+19
-1
+3
-3
+1
-1
+22
-2
+5
-5
+115
-73
+1
-1
+4
-3
+2
-2
+3
-3
+1
-1
+1
-1
+4
-4
+1
-1
+1
-1
+3
-3
+1
-1
+1
-1
+1
-1
+2
-2
+1
-1
+2
-2
+1
-1
+1
-1
+1
-1
+33
-14
+7
+21
-100
+4
-4
+1
-1
+1
-1
+15
-9
+16
-8
+3
-4
+4
-4
+42
-1
+1
-1
+3
-3
+2
-2
+17
-5
+1
-1
+7
-7
+3
-3
+1
-1
+1
-1
+3
-1
+3
-3
+2
-2
+2
-2
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+16
-12
+1
-1
+10
-5
+17
-5
+1
-1
+3
-3
+1
-1
+1
-1
+5
-5
+19
-18
+7
-7
+1
-1
+2
-2
+1
-1
+3
-3
+8
-5
+1
-1
+1
-1
+5
-5
+1
-1
+18
-6
+81
-3
+3
-3
+1
-1
+61
-23
+1
-1
+4
+18
-3
+1
-1
~
extsrc/shell.c
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
src/accordion.js
~
src/add.c
~
src/ajax.c
~
src/alerts.c
~
src/allrepo.c
~
src/backlink.c
~
src/bisect.c
~
src/blob.c
~
src/branch.c
~
src/builtin.c
~
src/cache.c
~
src/captcha.c
~
src/cgi.c
~
src/chat.c
~
src/checkin.c
~
src/checkout.c
~
src/color.c
~
src/comformat.c
~
src/config.h
~
src/configure.c
~
src/content.c
~
src/db.c
~
src/default.css
~
src/delta.c
~
src/descendants.c
~
src/diff.c
~
src/diffcmd.c
~
src/dispatch.c
~
src/event.c
~
src/export.c
~
src/file.c
~
src/fileedit.c
~
src/forum.c
~
src/fossil.confirmer.js
~
src/fossil.diff.js
~
src/fossil.dom.js
~
src/fossil.fetch.js
~
src/fossil.page.chat.js
~
src/fossil.page.fileedit.js
~
src/fossil.page.wikiedit.js
~
src/fossil.pikchr.js
~
src/fossil.wikiedit-wysiwyg.js
~
src/fshell.c
~
src/glob.c
~
src/graph.c
~
src/hname.c
~
src/hook.c
~
src/http.c
~
src/http_socket.c
~
src/http_ssl.c
~
src/json.c
~
src/json_report.c
~
src/json_status.c
~
src/login.c
~
src/main.c
~
src/manifest.c
~
src/markdown.c
~
src/markdown_html.c
~
src/match.c
~
src/merge.c
~
src/merge3.c
~
src/name.c
~
src/piechart.c
~
src/printf.c
~
src/purge.c
~
src/regexp.c
~
src/report.c
~
src/robot.c
~
src/schema.c
~
src/search.c
~
src/security_audit.c
~
src/setup.c
~
src/setupuser.c
~
src/sha1.c
~
src/skins.c
~
src/smtp.c
~
src/sqlcmd.c
~
src/stat.c
~
src/style.c
~
src/sync.c
~
src/tag.c
~
src/tar.c
~
src/terminal.c
~
src/th.c
~
src/th_main.c
~
src/timeline.c
~
src/tkt.c
~
src/tktsetup.c
~
src/undo.c
~
src/unicode.c
~
src/update.c
~
src/util.c
~
src/vfile.c
~
src/wiki.c
~
src/wikiformat.c
~
src/winhttp.c
~
src/xfer.c
~
src/xsystem.c
~
src/zip.c
~
tools/codecheck1.c
~
tools/mkindex.c
~
www/antibot.wiki
~
www/changes.wiki
~
www/customskin.md
~
www/serverext.wiki
+1027
-523
| --- extsrc/shell.c | ||
| +++ extsrc/shell.c | ||
| @@ -35,11 +35,11 @@ | ||
| 35 | 35 | ** ext/recover/sqlite3recover.h |
| 36 | 36 | ** src/shell.c.in |
| 37 | 37 | ** |
| 38 | 38 | ** To modify this program, get a copy of the canonical SQLite source tree, |
| 39 | 39 | ** edit the src/shell.c.in file and/or some of the other files that are |
| 40 | -** listed above, then rerun the rerun "make shell.c". | |
| 40 | +** listed above, then rerun the command "make shell.c". | |
| 41 | 41 | */ |
| 42 | 42 | /************************* Begin src/shell.c.in ******************/ |
| 43 | 43 | /* |
| 44 | 44 | ** 2001 September 15 |
| 45 | 45 | ** |
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | ** May you do good and not evil. |
| 50 | 50 | ** May you find forgiveness for yourself and forgive others. |
| 51 | 51 | ** May you share freely, never taking more than you give. |
| 52 | 52 | ** |
| 53 | 53 | ************************************************************************* |
| 54 | -** This file contains code to implement the "sqlite" command line | |
| 54 | +** This file contains code to implement the "sqlite3" command line | |
| 55 | 55 | ** utility for accessing SQLite databases. |
| 56 | 56 | */ |
| 57 | 57 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 58 | 58 | /* This needs to come before any includes for MSVC compiler */ |
| 59 | 59 | #define _CRT_SECURE_NO_WARNINGS |
| @@ -79,10 +79,14 @@ | ||
| 79 | 79 | ** should only be used when building the "fiddle" web application, as |
| 80 | 80 | ** the browser-mode build has much different user input requirements |
| 81 | 81 | ** and this build mode rewires the user input subsystem to account for |
| 82 | 82 | ** that. |
| 83 | 83 | */ |
| 84 | +#if defined(SQLITE_SHELL_FIDDLE) | |
| 85 | +# undef SQLITE_OMIT_LOAD_EXTENSION | |
| 86 | +# define SQLITE_OMIT_LOAD_EXTENSION 1 | |
| 87 | +#endif | |
| 84 | 88 | |
| 85 | 89 | /* |
| 86 | 90 | ** Warning pragmas copied from msvc.h in the core. |
| 87 | 91 | */ |
| 88 | 92 | #if defined(_MSC_VER) |
| @@ -872,10 +876,11 @@ | ||
| 872 | 876 | #ifndef SQLITE_QRF_H |
| 873 | 877 | #include "qrf.h" |
| 874 | 878 | #endif |
| 875 | 879 | #include <string.h> |
| 876 | 880 | #include <assert.h> |
| 881 | +#include <stdint.h> | |
| 877 | 882 | |
| 878 | 883 | /* typedef sqlite3_int64 i64; */ |
| 879 | 884 | |
| 880 | 885 | /* A single line in the EQP output */ |
| 881 | 886 | typedef struct qrfEQPGraphRow qrfEQPGraphRow; |
| @@ -889,11 +894,12 @@ | ||
| 889 | 894 | /* All EQP output is collected into an instance of the following */ |
| 890 | 895 | typedef struct qrfEQPGraph qrfEQPGraph; |
| 891 | 896 | struct qrfEQPGraph { |
| 892 | 897 | qrfEQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ |
| 893 | 898 | qrfEQPGraphRow *pLast; /* Last element of the pRow list */ |
| 894 | - char zPrefix[100]; /* Graph prefix */ | |
| 899 | + int nWidth; /* Width of the graph */ | |
| 900 | + char zPrefix[400]; /* Graph prefix */ | |
| 895 | 901 | }; |
| 896 | 902 | |
| 897 | 903 | /* |
| 898 | 904 | ** Private state information. Subject to change from one release to the |
| 899 | 905 | ** next. |
| @@ -995,10 +1001,19 @@ | ||
| 995 | 1001 | */ |
| 996 | 1002 | static void qrfOom(Qrf *p){ |
| 997 | 1003 | qrfError(p, SQLITE_NOMEM, "out of memory"); |
| 998 | 1004 | } |
| 999 | 1005 | |
| 1006 | +/* | |
| 1007 | +** Transfer any error in pStr over into p. | |
| 1008 | +*/ | |
| 1009 | +static void qrfStrErr(Qrf *p, sqlite3_str *pStr){ | |
| 1010 | + int rc = pStr ? sqlite3_str_errcode(pStr) : 0; | |
| 1011 | + if( rc ){ | |
| 1012 | + qrfError(p, rc, sqlite3_errstr(rc)); | |
| 1013 | + } | |
| 1014 | +} | |
| 1000 | 1015 | |
| 1001 | 1016 | |
| 1002 | 1017 | /* |
| 1003 | 1018 | ** Add a new entry to the EXPLAIN QUERY PLAN data |
| 1004 | 1019 | */ |
| @@ -1074,10 +1089,49 @@ | ||
| 1074 | 1089 | qrfEqpRenderLevel(p, pRow->iEqpId); |
| 1075 | 1090 | p->u.pGraph->zPrefix[n] = 0; |
| 1076 | 1091 | } |
| 1077 | 1092 | } |
| 1078 | 1093 | } |
| 1094 | + | |
| 1095 | +/* | |
| 1096 | +** Render the 64-bit value N in a more human-readable format into | |
| 1097 | +** pOut. | |
| 1098 | +** | |
| 1099 | +** + Only show the first three significant digits. | |
| 1100 | +** + Append suffixes K, M, G, T, P, and E for 1e3, 1e6, ... 1e18 | |
| 1101 | +*/ | |
| 1102 | +static void qrfApproxInt64(sqlite3_str *pOut, i64 N){ | |
| 1103 | + static const char aSuffix[] = { 'K', 'M', 'G', 'T', 'P', 'E' }; | |
| 1104 | + int i; | |
| 1105 | + if( N<0 ){ | |
| 1106 | + N = N==INT64_MIN ? INT64_MAX : -N; | |
| 1107 | + sqlite3_str_append(pOut, "-", 1); | |
| 1108 | + } | |
| 1109 | + if( N<10000 ){ | |
| 1110 | + sqlite3_str_appendf(pOut, "%4lld ", N); | |
| 1111 | + return; | |
| 1112 | + } | |
| 1113 | + for(i=1; i<=18; i++){ | |
| 1114 | + N = (N+5)/10; | |
| 1115 | + if( N<10000 ){ | |
| 1116 | + int n = (int)N; | |
| 1117 | + switch( i%3 ){ | |
| 1118 | + case 0: | |
| 1119 | + sqlite3_str_appendf(pOut, "%d.%02d", n/1000, (n%1000)/10); | |
| 1120 | + break; | |
| 1121 | + case 1: | |
| 1122 | + sqlite3_str_appendf(pOut, "%2d.%d", n/100, (n%100)/10); | |
| 1123 | + break; | |
| 1124 | + case 2: | |
| 1125 | + sqlite3_str_appendf(pOut, "%4d", n/10); | |
| 1126 | + break; | |
| 1127 | + } | |
| 1128 | + sqlite3_str_append(pOut, &aSuffix[i/3], 1); | |
| 1129 | + break; | |
| 1130 | + } | |
| 1131 | + } | |
| 1132 | +} | |
| 1079 | 1133 | |
| 1080 | 1134 | /* |
| 1081 | 1135 | ** Display and reset the EXPLAIN QUERY PLAN data |
| 1082 | 1136 | */ |
| 1083 | 1137 | static void qrfEqpRender(Qrf *p, i64 nCycle){ |
| @@ -1090,11 +1144,30 @@ | ||
| 1090 | 1144 | } |
| 1091 | 1145 | sqlite3_str_appendf(p->pOut, "%s\n", pRow->zText+3); |
| 1092 | 1146 | p->u.pGraph->pRow = pRow->pNext; |
| 1093 | 1147 | sqlite3_free(pRow); |
| 1094 | 1148 | }else if( nCycle>0 ){ |
| 1095 | - sqlite3_str_appendf(p->pOut, "QUERY PLAN (cycles=%lld [100%%])\n",nCycle); | |
| 1149 | + int nSp = p->u.pGraph->nWidth - 2; | |
| 1150 | + if( p->spec.eStyle==QRF_STYLE_StatsEst ){ | |
| 1151 | + sqlite3_str_appendchar(p->pOut, nSp, ' '); | |
| 1152 | + sqlite3_str_appendall(p->pOut, | |
| 1153 | + "Cycles Loops (est) Rows (est)\n"); | |
| 1154 | + sqlite3_str_appendchar(p->pOut, nSp, ' '); | |
| 1155 | + sqlite3_str_appendall(p->pOut, | |
| 1156 | + "---------- ------------ ------------\n"); | |
| 1157 | + }else{ | |
| 1158 | + sqlite3_str_appendchar(p->pOut, nSp, ' '); | |
| 1159 | + sqlite3_str_appendall(p->pOut, | |
| 1160 | + "Cycles Loops Rows \n"); | |
| 1161 | + sqlite3_str_appendchar(p->pOut, nSp, ' '); | |
| 1162 | + sqlite3_str_appendall(p->pOut, | |
| 1163 | + "---------- ----- -----\n"); | |
| 1164 | + } | |
| 1165 | + sqlite3_str_appendall(p->pOut, "QUERY PLAN"); | |
| 1166 | + sqlite3_str_appendchar(p->pOut, nSp - 10, ' '); | |
| 1167 | + qrfApproxInt64(p->pOut, nCycle); | |
| 1168 | + sqlite3_str_appendall(p->pOut, " 100%\n"); | |
| 1096 | 1169 | }else{ |
| 1097 | 1170 | sqlite3_str_appendall(p->pOut, "QUERY PLAN\n"); |
| 1098 | 1171 | } |
| 1099 | 1172 | p->u.pGraph->zPrefix[0] = 0; |
| 1100 | 1173 | qrfEqpRenderLevel(p, 0); |
| @@ -1145,10 +1218,12 @@ | ||
| 1145 | 1218 | static const int f = SQLITE_SCANSTAT_COMPLEX; |
| 1146 | 1219 | sqlite3_stmt *pS = p->pStmt; |
| 1147 | 1220 | int i = 0; |
| 1148 | 1221 | i64 nTotal = 0; |
| 1149 | 1222 | int nWidth = 0; |
| 1223 | + int prevPid = -1; /* Previous iPid */ | |
| 1224 | + double rEstCum = 1.0; /* Cumulative row estimate */ | |
| 1150 | 1225 | sqlite3_str *pLine = sqlite3_str_new(p->db); |
| 1151 | 1226 | sqlite3_str *pStats = sqlite3_str_new(p->db); |
| 1152 | 1227 | qrfEqpReset(p); |
| 1153 | 1228 | |
| 1154 | 1229 | for(i=0; 1; i++){ |
| @@ -1158,11 +1233,11 @@ | ||
| 1158 | 1233 | break; |
| 1159 | 1234 | } |
| 1160 | 1235 | n = (int)strlen(z) + qrfStatsHeight(pS,i)*3; |
| 1161 | 1236 | if( n>nWidth ) nWidth = n; |
| 1162 | 1237 | } |
| 1163 | - nWidth += 4; | |
| 1238 | + nWidth += 2; | |
| 1164 | 1239 | |
| 1165 | 1240 | sqlite3_stmt_scanstatus_v2(pS,-1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); |
| 1166 | 1241 | for(i=0; 1; i++){ |
| 1167 | 1242 | i64 nLoop = 0; |
| 1168 | 1243 | i64 nRow = 0; |
| @@ -1173,55 +1248,67 @@ | ||
| 1173 | 1248 | const char *zName = 0; |
| 1174 | 1249 | double rEst = 0.0; |
| 1175 | 1250 | |
| 1176 | 1251 | if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){ |
| 1177 | 1252 | break; |
| 1253 | + } | |
| 1254 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); | |
| 1255 | + if( iPid!=prevPid ){ | |
| 1256 | + prevPid = iPid; | |
| 1257 | + rEstCum = 1.0; | |
| 1178 | 1258 | } |
| 1179 | 1259 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_EST,f,(void*)&rEst); |
| 1260 | + rEstCum *= rEst; | |
| 1180 | 1261 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); |
| 1181 | 1262 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); |
| 1182 | 1263 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); |
| 1183 | 1264 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); |
| 1184 | - sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); | |
| 1185 | 1265 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NAME,f,(void*)&zName); |
| 1186 | 1266 | |
| 1187 | 1267 | if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ |
| 1188 | - const char *zSp = ""; | |
| 1189 | - double rpl; | |
| 1268 | + int nSp = 0; | |
| 1190 | 1269 | sqlite3_str_reset(pStats); |
| 1191 | 1270 | if( nCycle>=0 && nTotal>0 ){ |
| 1192 | - sqlite3_str_appendf(pStats, "cycles=%lld [%d%%]", | |
| 1193 | - nCycle, ((nCycle*100)+nTotal/2) / nTotal | |
| 1271 | + qrfApproxInt64(pStats, nCycle); | |
| 1272 | + sqlite3_str_appendf(pStats, " %3d%%", | |
| 1273 | + ((nCycle*100)+nTotal/2) / nTotal | |
| 1194 | 1274 | ); |
| 1195 | - zSp = " "; | |
| 1275 | + nSp = 2; | |
| 1196 | 1276 | } |
| 1197 | 1277 | if( nLoop>=0 ){ |
| 1198 | - sqlite3_str_appendf(pStats, "%sloops=%lld", zSp, nLoop); | |
| 1199 | - zSp = " "; | |
| 1278 | + if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' '); | |
| 1279 | + qrfApproxInt64(pStats, nLoop); | |
| 1280 | + nSp = 2; | |
| 1281 | + if( p->spec.eStyle==QRF_STYLE_StatsEst ){ | |
| 1282 | + sqlite3_str_appendf(pStats, " "); | |
| 1283 | + qrfApproxInt64(pStats, (i64)(rEstCum/rEst)); | |
| 1284 | + } | |
| 1200 | 1285 | } |
| 1201 | 1286 | if( nRow>=0 ){ |
| 1202 | - sqlite3_str_appendf(pStats, "%srows=%lld", zSp, nRow); | |
| 1203 | - zSp = " "; | |
| 1204 | - } | |
| 1205 | - | |
| 1206 | - if( p->spec.eStyle==QRF_STYLE_StatsEst ){ | |
| 1207 | - rpl = (double)nRow / (double)nLoop; | |
| 1208 | - sqlite3_str_appendf(pStats, "%srpl=%.1f est=%.1f", zSp, rpl, rEst); | |
| 1209 | - } | |
| 1210 | - | |
| 1287 | + if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' '); | |
| 1288 | + qrfApproxInt64(pStats, nRow); | |
| 1289 | + nSp = 2; | |
| 1290 | + if( p->spec.eStyle==QRF_STYLE_StatsEst ){ | |
| 1291 | + sqlite3_str_appendf(pStats, " "); | |
| 1292 | + qrfApproxInt64(pStats, (i64)rEstCum); | |
| 1293 | + } | |
| 1294 | + } | |
| 1211 | 1295 | sqlite3_str_appendf(pLine, |
| 1212 | - "% *s (%s)", -1*(nWidth-qrfStatsHeight(pS,i)*3), zo, | |
| 1296 | + "% *s %s", -1*(nWidth-qrfStatsHeight(pS,i)*3), zo, | |
| 1213 | 1297 | sqlite3_str_value(pStats) |
| 1214 | 1298 | ); |
| 1215 | 1299 | sqlite3_str_reset(pStats); |
| 1216 | 1300 | qrfEqpAppend(p, iId, iPid, sqlite3_str_value(pLine)); |
| 1217 | 1301 | sqlite3_str_reset(pLine); |
| 1218 | 1302 | }else{ |
| 1219 | 1303 | qrfEqpAppend(p, iId, iPid, zo); |
| 1220 | 1304 | } |
| 1221 | 1305 | } |
| 1306 | + if( p->u.pGraph ) p->u.pGraph->nWidth = nWidth; | |
| 1307 | + qrfStrErr(p, pLine); | |
| 1222 | 1308 | sqlite3_free(sqlite3_str_finish(pLine)); |
| 1309 | + qrfStrErr(p, pStats); | |
| 1223 | 1310 | sqlite3_free(sqlite3_str_finish(pStats)); |
| 1224 | 1311 | #endif |
| 1225 | 1312 | } |
| 1226 | 1313 | |
| 1227 | 1314 | |
| @@ -1581,13 +1668,15 @@ | ||
| 1581 | 1668 | ** (3) z[] does not looks like a numeric literal |
| 1582 | 1669 | */ |
| 1583 | 1670 | static int qrfRelaxable(Qrf *p, const char *z){ |
| 1584 | 1671 | size_t i, n; |
| 1585 | 1672 | if( z[0]=='\'' || qrfSpace(z[0]) ) return 0; |
| 1586 | - if( z[0]==0 && (p->spec.zNull==0 || p->spec.zNull[0]==0) ) return 0; | |
| 1673 | + if( z[0]==0 ){ | |
| 1674 | + return (p->spec.zNull!=0 && p->spec.zNull[0]!=0); | |
| 1675 | + } | |
| 1587 | 1676 | n = strlen(z); |
| 1588 | - if( z[n-1]=='\'' || qrfSpace(z[n-1]) ) return 0; | |
| 1677 | + if( n==0 || z[n-1]=='\'' || qrfSpace(z[n-1]) ) return 0; | |
| 1589 | 1678 | if( p->spec.zNull && strcmp(p->spec.zNull,z)==0 ) return 0; |
| 1590 | 1679 | i = (z[0]=='-' || z[0]=='+'); |
| 1591 | 1680 | if( strcmp(z+i,"Inf")==0 ) return 0; |
| 1592 | 1681 | if( !qrfDigit(z[i]) ) return 1; |
| 1593 | 1682 | i++; |
| @@ -2729,10 +2818,11 @@ | ||
| 2729 | 2818 | int nNL = 0; |
| 2730 | 2819 | int n, w; |
| 2731 | 2820 | pStr = sqlite3_str_new(p->db); |
| 2732 | 2821 | qrfEncodeText(p, pStr, z ? z : ""); |
| 2733 | 2822 | n = sqlite3_str_length(pStr); |
| 2823 | + qrfStrErr(p, pStr); | |
| 2734 | 2824 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2735 | 2825 | if( p->spec.nTitleLimit ){ |
| 2736 | 2826 | nNL = 0; |
| 2737 | 2827 | data.aiWth[data.n] = w = qrfTitleLimit(data.az[data.n], |
| 2738 | 2828 | p->spec.nTitleLimit ); |
| @@ -2756,10 +2846,11 @@ | ||
| 2756 | 2846 | int n, w; |
| 2757 | 2847 | int eType = sqlite3_column_type(p->pStmt,i); |
| 2758 | 2848 | pStr = sqlite3_str_new(p->db); |
| 2759 | 2849 | qrfRenderValue(p, pStr, i); |
| 2760 | 2850 | n = sqlite3_str_length(pStr); |
| 2851 | + qrfStrErr(p, pStr); | |
| 2761 | 2852 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2762 | 2853 | data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT; |
| 2763 | 2854 | data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2764 | 2855 | data.n++; |
| 2765 | 2856 | if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| @@ -2910,11 +3001,11 @@ | ||
| 2910 | 3001 | ){ |
| 2911 | 3002 | bRTrim = 1; |
| 2912 | 3003 | }else{ |
| 2913 | 3004 | bRTrim = 0; |
| 2914 | 3005 | } |
| 2915 | - for(i=0; i<data.n; i+=nColumn){ | |
| 3006 | + for(i=0; i<data.n && sqlite3_str_errcode(p->pOut)==SQLITE_OK; i+=nColumn){ | |
| 2916 | 3007 | int bMore; |
| 2917 | 3008 | int nRow = 0; |
| 2918 | 3009 | |
| 2919 | 3010 | /* Draw a single row of the table. This might be the title line |
| 2920 | 3011 | ** (if there is a title line) or a row in the body of the table. |
| @@ -3097,11 +3188,11 @@ | ||
| 3097 | 3188 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0), "addr" ) ); |
| 3098 | 3189 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1), "opcode" ) ); |
| 3099 | 3190 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2), "p1" ) ); |
| 3100 | 3191 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3), "p2" ) ); |
| 3101 | 3192 | |
| 3102 | - for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt); iOp++){ | |
| 3193 | + for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt) && !p->iErr; iOp++){ | |
| 3103 | 3194 | int iAddr = sqlite3_column_int(p->pStmt, 0); |
| 3104 | 3195 | const char *zOp = (const char*)sqlite3_column_text(p->pStmt, 1); |
| 3105 | 3196 | int p1 = sqlite3_column_int(p->pStmt, 2); |
| 3106 | 3197 | int p2 = sqlite3_column_int(p->pStmt, 3); |
| 3107 | 3198 | |
| @@ -3154,11 +3245,11 @@ | ||
| 3154 | 3245 | nWidth = sizeof(aScanExpWidth)/sizeof(int); |
| 3155 | 3246 | iIndent = 3; |
| 3156 | 3247 | } |
| 3157 | 3248 | if( nArg>nWidth ) nArg = nWidth; |
| 3158 | 3249 | |
| 3159 | - for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW; iOp++){ | |
| 3250 | + for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW && !p->iErr; iOp++){ | |
| 3160 | 3251 | /* If this is the first row seen, print out the headers */ |
| 3161 | 3252 | if( iOp==0 ){ |
| 3162 | 3253 | for(i=0; i<nArg; i++){ |
| 3163 | 3254 | const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]); |
| 3164 | 3255 | qrfWidthPrint(p,p->pOut, aWidth[i], zCol); |
| @@ -3410,10 +3501,11 @@ | ||
| 3410 | 3501 | sqlite3_str_append(p->pOut, "\n", 1); |
| 3411 | 3502 | zVal += iNext; |
| 3412 | 3503 | }while( zVal[0] ); |
| 3413 | 3504 | sqlite3_str_reset(pVal); |
| 3414 | 3505 | } |
| 3506 | + qrfStrErr(p, pVal); | |
| 3415 | 3507 | sqlite3_free(sqlite3_str_finish(pVal)); |
| 3416 | 3508 | qrfWrite(p); |
| 3417 | 3509 | break; |
| 3418 | 3510 | } |
| 3419 | 3511 | case QRF_STYLE_Eqp: { |
| @@ -3473,11 +3565,11 @@ | ||
| 3473 | 3565 | p->pOut = sqlite3_str_new(p->db); |
| 3474 | 3566 | if( p->pOut==0 ){ |
| 3475 | 3567 | qrfOom(p); |
| 3476 | 3568 | return; |
| 3477 | 3569 | } |
| 3478 | - p->iErr = 0; | |
| 3570 | + p->iErr = SQLITE_OK; | |
| 3479 | 3571 | p->nCol = sqlite3_column_count(p->pStmt); |
| 3480 | 3572 | p->nRow = 0; |
| 3481 | 3573 | sz = sizeof(sqlite3_qrf_spec); |
| 3482 | 3574 | memcpy(&p->spec, pSpec, sz); |
| 3483 | 3575 | if( p->spec.zNull==0 ) p->spec.zNull = ""; |
| @@ -3644,17 +3736,27 @@ | ||
| 3644 | 3736 | sqlite3_free(p->u.sLine.azCol); |
| 3645 | 3737 | } |
| 3646 | 3738 | break; |
| 3647 | 3739 | } |
| 3648 | 3740 | case QRF_STYLE_Stats: |
| 3649 | - case QRF_STYLE_StatsEst: | |
| 3741 | + case QRF_STYLE_StatsEst: { | |
| 3742 | + i64 nCycle = 0; | |
| 3743 | +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS | |
| 3744 | + sqlite3_stmt_scanstatus_v2(p->pStmt, -1, SQLITE_SCANSTAT_NCYCLE, | |
| 3745 | + SQLITE_SCANSTAT_COMPLEX, (void*)&nCycle); | |
| 3746 | +#endif | |
| 3747 | + qrfEqpRender(p, nCycle); | |
| 3748 | + qrfWrite(p); | |
| 3749 | + break; | |
| 3750 | + } | |
| 3650 | 3751 | case QRF_STYLE_Eqp: { |
| 3651 | 3752 | qrfEqpRender(p, 0); |
| 3652 | 3753 | qrfWrite(p); |
| 3653 | 3754 | break; |
| 3654 | 3755 | } |
| 3655 | 3756 | } |
| 3757 | + qrfStrErr(p, p->pOut); | |
| 3656 | 3758 | if( p->spec.pzOutput ){ |
| 3657 | 3759 | if( p->spec.pzOutput[0] ){ |
| 3658 | 3760 | sqlite3_int64 n, sz; |
| 3659 | 3761 | char *zCombined; |
| 3660 | 3762 | sz = strlen(p->spec.pzOutput[0]); |
| @@ -3815,13 +3917,10 @@ | ||
| 3815 | 3917 | |
| 3816 | 3918 | |
| 3817 | 3919 | #define eputz(z) cli_puts(z,stderr) |
| 3818 | 3920 | #define sputz(fp,z) cli_puts(z,fp) |
| 3819 | 3921 | |
| 3820 | -/* True if the timer is enabled */ | |
| 3821 | -static int enableTimer = 0; | |
| 3822 | - | |
| 3823 | 3922 | /* A version of strcmp() that works with NULL values */ |
| 3824 | 3923 | static int cli_strcmp(const char *a, const char *b){ |
| 3825 | 3924 | if( a==0 ) a = ""; |
| 3826 | 3925 | if( b==0 ) b = ""; |
| 3827 | 3926 | return strcmp(a,b); |
| @@ -3834,11 +3933,11 @@ | ||
| 3834 | 3933 | |
| 3835 | 3934 | /* Return the current wall-clock time in microseconds since the |
| 3836 | 3935 | ** Unix epoch (1970-01-01T00:00:00Z) |
| 3837 | 3936 | */ |
| 3838 | 3937 | static sqlite3_int64 timeOfDay(void){ |
| 3839 | -#if defined(_WIN64) | |
| 3938 | +#if defined(_WIN64) && _WIN32_WINNT >= _WIN32_WINNT_WIN8 | |
| 3840 | 3939 | sqlite3_uint64 t; |
| 3841 | 3940 | FILETIME tm; |
| 3842 | 3941 | GetSystemTimePreciseAsFileTime(&tm); |
| 3843 | 3942 | t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime; |
| 3844 | 3943 | t += 116444736000000000LL; |
| @@ -3862,154 +3961,10 @@ | ||
| 3862 | 3961 | (void)gettimeofday(&sNow,0); |
| 3863 | 3962 | return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec; |
| 3864 | 3963 | #endif |
| 3865 | 3964 | } |
| 3866 | 3965 | |
| 3867 | -#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) | |
| 3868 | -#include <sys/time.h> | |
| 3869 | -#include <sys/resource.h> | |
| 3870 | - | |
| 3871 | -/* VxWorks does not support getrusage() as far as we can determine */ | |
| 3872 | -#if defined(_WRS_KERNEL) || defined(__RTP__) | |
| 3873 | -struct rusage { | |
| 3874 | - struct timeval ru_utime; /* user CPU time used */ | |
| 3875 | - struct timeval ru_stime; /* system CPU time used */ | |
| 3876 | -}; | |
| 3877 | -#define getrusage(A,B) memset(B,0,sizeof(*B)) | |
| 3878 | -#endif | |
| 3879 | - | |
| 3880 | - | |
| 3881 | -/* Saved resource information for the beginning of an operation */ | |
| 3882 | -static struct rusage sBegin; /* CPU time at start */ | |
| 3883 | -static sqlite3_int64 iBegin; /* Wall-clock time at start */ | |
| 3884 | - | |
| 3885 | -/* | |
| 3886 | -** Begin timing an operation | |
| 3887 | -*/ | |
| 3888 | -static void beginTimer(void){ | |
| 3889 | - if( enableTimer ){ | |
| 3890 | - getrusage(RUSAGE_SELF, &sBegin); | |
| 3891 | - iBegin = timeOfDay(); | |
| 3892 | - } | |
| 3893 | -} | |
| 3894 | - | |
| 3895 | -/* Return the difference of two time_structs in seconds */ | |
| 3896 | -static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ | |
| 3897 | - return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + | |
| 3898 | - (double)(pEnd->tv_sec - pStart->tv_sec); | |
| 3899 | -} | |
| 3900 | - | |
| 3901 | -/* | |
| 3902 | -** Print the timing results. | |
| 3903 | -*/ | |
| 3904 | -static void endTimer(FILE *out){ | |
| 3905 | - if( enableTimer ){ | |
| 3906 | - sqlite3_int64 iEnd = timeOfDay(); | |
| 3907 | - struct rusage sEnd; | |
| 3908 | - getrusage(RUSAGE_SELF, &sEnd); | |
| 3909 | - cli_printf(out, "Run Time: real %.6f user %.6f sys %.6f\n", | |
| 3910 | - (iEnd - iBegin)*0.000001, | |
| 3911 | - timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), | |
| 3912 | - timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); | |
| 3913 | - } | |
| 3914 | -} | |
| 3915 | - | |
| 3916 | -#define BEGIN_TIMER beginTimer() | |
| 3917 | -#define END_TIMER(X) endTimer(X) | |
| 3918 | -#define HAS_TIMER 1 | |
| 3919 | - | |
| 3920 | -#elif (defined(_WIN32) || defined(WIN32)) | |
| 3921 | - | |
| 3922 | -/* Saved resource information for the beginning of an operation */ | |
| 3923 | -static HANDLE hProcess; | |
| 3924 | -static FILETIME ftKernelBegin; | |
| 3925 | -static FILETIME ftUserBegin; | |
| 3926 | -static sqlite3_int64 ftWallBegin; | |
| 3927 | -typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, | |
| 3928 | - LPFILETIME, LPFILETIME); | |
| 3929 | -static GETPROCTIMES getProcessTimesAddr = NULL; | |
| 3930 | - | |
| 3931 | -/* | |
| 3932 | -** Check to see if we have timer support. Return 1 if necessary | |
| 3933 | -** support found (or found previously). | |
| 3934 | -*/ | |
| 3935 | -static int hasTimer(void){ | |
| 3936 | - if( getProcessTimesAddr ){ | |
| 3937 | - return 1; | |
| 3938 | - } else { | |
| 3939 | - /* GetProcessTimes() isn't supported in WIN95 and some other Windows | |
| 3940 | - ** versions. See if the version we are running on has it, and if it | |
| 3941 | - ** does, save off a pointer to it and the current process handle. | |
| 3942 | - */ | |
| 3943 | - hProcess = GetCurrentProcess(); | |
| 3944 | - if( hProcess ){ | |
| 3945 | - HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); | |
| 3946 | - if( NULL != hinstLib ){ | |
| 3947 | - getProcessTimesAddr = | |
| 3948 | - (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); | |
| 3949 | - if( NULL != getProcessTimesAddr ){ | |
| 3950 | - return 1; | |
| 3951 | - } | |
| 3952 | - FreeLibrary(hinstLib); | |
| 3953 | - } | |
| 3954 | - } | |
| 3955 | - } | |
| 3956 | - return 0; | |
| 3957 | -} | |
| 3958 | - | |
| 3959 | -/* | |
| 3960 | -** Begin timing an operation | |
| 3961 | -*/ | |
| 3962 | -static void beginTimer(void){ | |
| 3963 | - if( enableTimer && getProcessTimesAddr ){ | |
| 3964 | - FILETIME ftCreation, ftExit; | |
| 3965 | - getProcessTimesAddr(hProcess,&ftCreation,&ftExit, | |
| 3966 | - &ftKernelBegin,&ftUserBegin); | |
| 3967 | - ftWallBegin = timeOfDay(); | |
| 3968 | - } | |
| 3969 | -} | |
| 3970 | - | |
| 3971 | -/* Return the difference of two FILETIME structs in seconds */ | |
| 3972 | -static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ | |
| 3973 | - sqlite_int64 i64Start = *((sqlite_int64 *) pStart); | |
| 3974 | - sqlite_int64 i64End = *((sqlite_int64 *) pEnd); | |
| 3975 | - return (double) ((i64End - i64Start) / 10000000.0); | |
| 3976 | -} | |
| 3977 | - | |
| 3978 | -/* | |
| 3979 | -** Print the timing results. | |
| 3980 | -*/ | |
| 3981 | -static void endTimer(FILE *out){ | |
| 3982 | - if( enableTimer && getProcessTimesAddr){ | |
| 3983 | - FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; | |
| 3984 | - sqlite3_int64 ftWallEnd = timeOfDay(); | |
| 3985 | - getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); | |
| 3986 | -#ifdef _WIN64 | |
| 3987 | - /* microsecond precision on 64-bit windows */ | |
| 3988 | - cli_printf(out, "Run Time: real %.6f user %f sys %f\n", | |
| 3989 | - (ftWallEnd - ftWallBegin)*0.000001, | |
| 3990 | - timeDiff(&ftUserBegin, &ftUserEnd), | |
| 3991 | - timeDiff(&ftKernelBegin, &ftKernelEnd)); | |
| 3992 | -#else | |
| 3993 | - /* millisecond precisino on 32-bit windows */ | |
| 3994 | - cli_printf(out, "Run Time: real %.3f user %.3f sys %.3f\n", | |
| 3995 | - (ftWallEnd - ftWallBegin)*0.000001, | |
| 3996 | - timeDiff(&ftUserBegin, &ftUserEnd), | |
| 3997 | - timeDiff(&ftKernelBegin, &ftKernelEnd)); | |
| 3998 | -#endif | |
| 3999 | - } | |
| 4000 | -} | |
| 4001 | - | |
| 4002 | -#define BEGIN_TIMER beginTimer() | |
| 4003 | -#define END_TIMER(X) endTimer(X) | |
| 4004 | -#define HAS_TIMER hasTimer() | |
| 4005 | - | |
| 4006 | -#else | |
| 4007 | -#define BEGIN_TIMER | |
| 4008 | -#define END_TIMER(X) /*no-op*/ | |
| 4009 | -#define HAS_TIMER 0 | |
| 4010 | -#endif | |
| 4011 | 3966 | |
| 4012 | 3967 | /* |
| 4013 | 3968 | ** Used to prevent warnings about unused parameters |
| 4014 | 3969 | */ |
| 4015 | 3970 | #define UNUSED_PARAMETER(x) (void)(x) |
| @@ -4068,10 +4023,18 @@ | ||
| 4068 | 4023 | #define PROMPT_LEN_MAX 128 |
| 4069 | 4024 | /* First line prompt. default: "sqlite> " */ |
| 4070 | 4025 | static char mainPrompt[PROMPT_LEN_MAX]; |
| 4071 | 4026 | /* Continuation prompt. default: " ...> " */ |
| 4072 | 4027 | static char continuePrompt[PROMPT_LEN_MAX]; |
| 4028 | + | |
| 4029 | +/* | |
| 4030 | +** Write I/O traces to the following stream. | |
| 4031 | +*/ | |
| 4032 | +#ifdef SQLITE_ENABLE_IOTRACE | |
| 4033 | +static FILE *iotrace = 0; | |
| 4034 | +#endif | |
| 4035 | + | |
| 4073 | 4036 | |
| 4074 | 4037 | /* This is variant of the standard-library strncpy() routine with the |
| 4075 | 4038 | ** one change that the destination string is always zero-terminated, even |
| 4076 | 4039 | ** if there is no zero-terminator in the first n-1 characters of the source |
| 4077 | 4040 | ** string. |
| @@ -4188,17 +4151,10 @@ | ||
| 4188 | 4151 | */ |
| 4189 | 4152 | static void shell_check_oom(const void *p){ |
| 4190 | 4153 | if( p==0 ) shell_out_of_memory(); |
| 4191 | 4154 | } |
| 4192 | 4155 | |
| 4193 | -/* | |
| 4194 | -** Write I/O traces to the following stream. | |
| 4195 | -*/ | |
| 4196 | -#ifdef SQLITE_ENABLE_IOTRACE | |
| 4197 | -static FILE *iotrace = 0; | |
| 4198 | -#endif | |
| 4199 | - | |
| 4200 | 4156 | /* |
| 4201 | 4157 | ** This routine works like printf in that its first argument is a |
| 4202 | 4158 | ** format string and subsequent arguments are values to be substituted |
| 4203 | 4159 | ** in place of % fields. The result of formatting this string |
| 4204 | 4160 | ** is written to iotrace. |
| @@ -8349,13 +8305,13 @@ | ||
| 8349 | 8305 | }else if( e<-10000 ){ |
| 8350 | 8306 | e = -10000; |
| 8351 | 8307 | } |
| 8352 | 8308 | |
| 8353 | 8309 | if( m<0 ){ |
| 8310 | + if( m<(-9223372036854775807LL) ) return; | |
| 8354 | 8311 | isNeg = 1; |
| 8355 | 8312 | m = -m; |
| 8356 | - if( m<0 ) return; | |
| 8357 | 8313 | }else if( m==0 && e>-1000 && e<1000 ){ |
| 8358 | 8314 | sqlite3_result_double(context, 0.0); |
| 8359 | 8315 | return; |
| 8360 | 8316 | } |
| 8361 | 8317 | while( (m>>32)&0xffe00000 ){ |
| @@ -9472,11 +9428,11 @@ | ||
| 9472 | 9428 | ** (X) match X |
| 9473 | 9429 | ** X|Y X or Y |
| 9474 | 9430 | ** ^X X occurring at the beginning of the string |
| 9475 | 9431 | ** X$ X occurring at the end of the string |
| 9476 | 9432 | ** . Match any single character |
| 9477 | -** \c Character c where c is one of \{}()[]|*+?. | |
| 9433 | +** \c Character c where c is one of \{}()[]|*+?-. | |
| 9478 | 9434 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 9479 | 9435 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 9480 | 9436 | ** \xXX Where XX is exactly 2 hex digits, unicode value XX |
| 9481 | 9437 | ** [abc] Any single character from the set abc |
| 9482 | 9438 | ** [^abc] Any single character not in the set abc |
| @@ -9857,11 +9813,11 @@ | ||
| 9857 | 9813 | |
| 9858 | 9814 | /* A backslash character has been seen, read the next character and |
| 9859 | 9815 | ** return its interpretation. |
| 9860 | 9816 | */ |
| 9861 | 9817 | static unsigned re_esc_char(ReCompiled *p){ |
| 9862 | - static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; | |
| 9818 | + static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-"; | |
| 9863 | 9819 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 9864 | 9820 | int i, v = 0; |
| 9865 | 9821 | char c; |
| 9866 | 9822 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 9867 | 9823 | c = p->sIn.z[p->sIn.i]; |
| @@ -10180,15 +10136,22 @@ | ||
| 10180 | 10136 | } |
| 10181 | 10137 | return pRe->zErr; |
| 10182 | 10138 | } |
| 10183 | 10139 | |
| 10184 | 10140 | /* |
| 10185 | -** Compute a reasonable limit on the length of the REGEXP NFA. | |
| 10141 | +** The value of LIMIT_MAX_PATTERN_LENGTH. | |
| 10186 | 10142 | */ |
| 10187 | 10143 | static int re_maxlen(sqlite3_context *context){ |
| 10188 | 10144 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 10189 | - return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2; | |
| 10145 | + return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1); | |
| 10146 | +} | |
| 10147 | + | |
| 10148 | +/* | |
| 10149 | +** Maximum NFA size given a maximum pattern length. | |
| 10150 | +*/ | |
| 10151 | +static int re_maxnfa(int mxlen){ | |
| 10152 | + return 75+mxlen/2; | |
| 10190 | 10153 | } |
| 10191 | 10154 | |
| 10192 | 10155 | /* |
| 10193 | 10156 | ** Implementation of the regexp() SQL function. This function implements |
| 10194 | 10157 | ** the build-in REGEXP operator. The first argument to the function is the |
| @@ -10210,14 +10173,21 @@ | ||
| 10210 | 10173 | int setAux = 0; /* True to invoke sqlite3_set_auxdata() */ |
| 10211 | 10174 | |
| 10212 | 10175 | (void)argc; /* Unused */ |
| 10213 | 10176 | pRe = sqlite3_get_auxdata(context, 0); |
| 10214 | 10177 | if( pRe==0 ){ |
| 10178 | + int mxLen = re_maxlen(context); | |
| 10179 | + int nPattern; | |
| 10215 | 10180 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10216 | 10181 | if( zPattern==0 ) return; |
| 10217 | - zErr = re_compile(&pRe, zPattern, re_maxlen(context), | |
| 10218 | - sqlite3_user_data(context)!=0); | |
| 10182 | + nPattern = sqlite3_value_bytes(argv[0]); | |
| 10183 | + if( nPattern>mxLen ){ | |
| 10184 | + zErr = "REGEXP pattern too big"; | |
| 10185 | + }else{ | |
| 10186 | + zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen), | |
| 10187 | + sqlite3_user_data(context)!=0); | |
| 10188 | + } | |
| 10219 | 10189 | if( zErr ){ |
| 10220 | 10190 | re_free(pRe); |
| 10221 | 10191 | sqlite3_result_error(context, zErr, -1); |
| 10222 | 10192 | return; |
| 10223 | 10193 | } |
| @@ -10279,11 +10249,11 @@ | ||
| 10279 | 10249 | "ATSTART", |
| 10280 | 10250 | }; |
| 10281 | 10251 | |
| 10282 | 10252 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10283 | 10253 | if( zPattern==0 ) return; |
| 10284 | - zErr = re_compile(&pRe, zPattern, re_maxlen(context), | |
| 10254 | + zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)), | |
| 10285 | 10255 | sqlite3_user_data(context)!=0); |
| 10286 | 10256 | if( zErr ){ |
| 10287 | 10257 | re_free(pRe); |
| 10288 | 10258 | sqlite3_result_error(context, zErr, -1); |
| 10289 | 10259 | return; |
| @@ -13067,11 +13037,11 @@ | ||
| 13067 | 13037 | nFile = (int)strlen(zFile)+1; |
| 13068 | 13038 | } |
| 13069 | 13039 | |
| 13070 | 13040 | rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA); |
| 13071 | 13041 | if( rc==SQLITE_OK ){ |
| 13072 | - pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile); | |
| 13042 | + pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile); | |
| 13073 | 13043 | if( pNew==0 ) return SQLITE_NOMEM; |
| 13074 | 13044 | memset(pNew, 0, nByte+nFile); |
| 13075 | 13045 | pNew->db = db; |
| 13076 | 13046 | pNew->aBuffer = (u8*)&pNew[1]; |
| 13077 | 13047 | if( zFile ){ |
| @@ -13213,18 +13183,19 @@ | ||
| 13213 | 13183 | ** sqlite3_free(). |
| 13214 | 13184 | */ |
| 13215 | 13185 | static int zipfileReadData( |
| 13216 | 13186 | FILE *pFile, /* Read from this file */ |
| 13217 | 13187 | u8 *aRead, /* Read into this buffer */ |
| 13218 | - int nRead, /* Number of bytes to read */ | |
| 13188 | + i64 nRead, /* Number of bytes to read */ | |
| 13219 | 13189 | i64 iOff, /* Offset to read from */ |
| 13220 | 13190 | char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */ |
| 13221 | 13191 | ){ |
| 13222 | 13192 | size_t n; |
| 13223 | 13193 | fseek(pFile, (long)iOff, SEEK_SET); |
| 13224 | - n = fread(aRead, 1, nRead, pFile); | |
| 13225 | - if( (int)n!=nRead ){ | |
| 13194 | + n = fread(aRead, 1, (long)nRead, pFile); | |
| 13195 | + if( n!=(size_t)nRead ){ | |
| 13196 | + sqlite3_free(*pzErrmsg); | |
| 13226 | 13197 | *pzErrmsg = sqlite3_mprintf("error in fread()"); |
| 13227 | 13198 | return SQLITE_ERROR; |
| 13228 | 13199 | } |
| 13229 | 13200 | return SQLITE_OK; |
| 13230 | 13201 | } |
| @@ -13237,11 +13208,11 @@ | ||
| 13237 | 13208 | if( nWrite>0 ){ |
| 13238 | 13209 | size_t n = nWrite; |
| 13239 | 13210 | fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET); |
| 13240 | 13211 | n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd); |
| 13241 | 13212 | if( (int)n!=nWrite ){ |
| 13242 | - pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()"); | |
| 13213 | + zipfileTableErr(pTab,"error in fwrite()"); | |
| 13243 | 13214 | return SQLITE_ERROR; |
| 13244 | 13215 | } |
| 13245 | 13216 | pTab->szCurrent += nWrite; |
| 13246 | 13217 | } |
| 13247 | 13218 | return SQLITE_OK; |
| @@ -13484,10 +13455,11 @@ | ||
| 13484 | 13455 | /* |
| 13485 | 13456 | ** Set (*pzErr) to point to a buffer from sqlite3_malloc() containing a |
| 13486 | 13457 | ** generic corruption message and return SQLITE_CORRUPT; |
| 13487 | 13458 | */ |
| 13488 | 13459 | static int zipfileCorrupt(char **pzErr){ |
| 13460 | + sqlite3_free(*pzErr); | |
| 13489 | 13461 | *pzErr = sqlite3_mprintf("zip archive is corrupt"); |
| 13490 | 13462 | return SQLITE_CORRUPT; |
| 13491 | 13463 | } |
| 13492 | 13464 | |
| 13493 | 13465 | /* |
| @@ -13502,11 +13474,11 @@ | ||
| 13502 | 13474 | ** final value of (*ppEntry) undefined. |
| 13503 | 13475 | */ |
| 13504 | 13476 | static int zipfileGetEntry( |
| 13505 | 13477 | ZipfileTab *pTab, /* Store any error message here */ |
| 13506 | 13478 | const u8 *aBlob, /* Pointer to in-memory file image */ |
| 13507 | - int nBlob, /* Size of aBlob[] in bytes */ | |
| 13479 | + i64 nBlob, /* Size of aBlob[] in bytes */ | |
| 13508 | 13480 | FILE *pFile, /* If aBlob==0, read from this file */ |
| 13509 | 13481 | i64 iOff, /* Offset of CDS record */ |
| 13510 | 13482 | ZipfileEntry **ppEntry /* OUT: Pointer to new object */ |
| 13511 | 13483 | ){ |
| 13512 | 13484 | u8 *aRead; |
| @@ -13542,18 +13514,18 @@ | ||
| 13542 | 13514 | rc = SQLITE_NOMEM; |
| 13543 | 13515 | }else{ |
| 13544 | 13516 | memset(pNew, 0, sizeof(ZipfileEntry)); |
| 13545 | 13517 | rc = zipfileReadCDS(aRead, &pNew->cds); |
| 13546 | 13518 | if( rc!=SQLITE_OK ){ |
| 13547 | - *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff); | |
| 13519 | + zipfileTableErr(pTab, "failed to read CDS at offset %lld", iOff); | |
| 13548 | 13520 | }else if( aBlob==0 ){ |
| 13549 | 13521 | rc = zipfileReadData( |
| 13550 | 13522 | pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr |
| 13551 | 13523 | ); |
| 13552 | 13524 | }else{ |
| 13553 | 13525 | aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ]; |
| 13554 | - if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){ | |
| 13526 | + if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){ | |
| 13555 | 13527 | rc = zipfileCorrupt(pzErr); |
| 13556 | 13528 | } |
| 13557 | 13529 | } |
| 13558 | 13530 | } |
| 13559 | 13531 | |
| @@ -13574,19 +13546,19 @@ | ||
| 13574 | 13546 | ZipfileLFH lfh; |
| 13575 | 13547 | if( pFile ){ |
| 13576 | 13548 | rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr); |
| 13577 | 13549 | }else{ |
| 13578 | 13550 | aRead = (u8*)&aBlob[pNew->cds.iOffset]; |
| 13579 | - if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>(unsigned)nBlob ){ | |
| 13551 | + if( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){ | |
| 13580 | 13552 | rc = zipfileCorrupt(pzErr); |
| 13581 | 13553 | } |
| 13582 | 13554 | } |
| 13583 | 13555 | |
| 13584 | 13556 | memset(&lfh, 0, sizeof(lfh)); |
| 13585 | 13557 | if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh); |
| 13586 | 13558 | if( rc==SQLITE_OK ){ |
| 13587 | - pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; | |
| 13559 | + pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; | |
| 13588 | 13560 | pNew->iDataOff += lfh.nFile + lfh.nExtra; |
| 13589 | 13561 | if( aBlob && pNew->cds.szCompressed ){ |
| 13590 | 13562 | if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){ |
| 13591 | 13563 | rc = zipfileCorrupt(pzErr); |
| 13592 | 13564 | }else{ |
| @@ -13593,11 +13565,11 @@ | ||
| 13593 | 13565 | pNew->aData = &pNew->aExtra[nExtra]; |
| 13594 | 13566 | memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed); |
| 13595 | 13567 | } |
| 13596 | 13568 | } |
| 13597 | 13569 | }else{ |
| 13598 | - *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", | |
| 13570 | + zipfileTableErr(pTab, "failed to read LFH at offset %d", | |
| 13599 | 13571 | (int)pNew->cds.iOffset |
| 13600 | 13572 | ); |
| 13601 | 13573 | } |
| 13602 | 13574 | } |
| 13603 | 13575 | |
| @@ -13617,11 +13589,11 @@ | ||
| 13617 | 13589 | static int zipfileNext(sqlite3_vtab_cursor *cur){ |
| 13618 | 13590 | ZipfileCsr *pCsr = (ZipfileCsr*)cur; |
| 13619 | 13591 | int rc = SQLITE_OK; |
| 13620 | 13592 | |
| 13621 | 13593 | if( pCsr->pFile ){ |
| 13622 | - i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize; | |
| 13594 | + i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize; | |
| 13623 | 13595 | zipfileEntryFree(pCsr->pCurrent); |
| 13624 | 13596 | pCsr->pCurrent = 0; |
| 13625 | 13597 | if( pCsr->iNextOff>=iEof ){ |
| 13626 | 13598 | pCsr->bEof = 1; |
| 13627 | 13599 | }else{ |
| @@ -13855,16 +13827,16 @@ | ||
| 13855 | 13827 | ** an English language error message may be left in virtual-table pTab. |
| 13856 | 13828 | */ |
| 13857 | 13829 | static int zipfileReadEOCD( |
| 13858 | 13830 | ZipfileTab *pTab, /* Return errors here */ |
| 13859 | 13831 | const u8 *aBlob, /* Pointer to in-memory file image */ |
| 13860 | - int nBlob, /* Size of aBlob[] in bytes */ | |
| 13832 | + i64 nBlob, /* Size of aBlob[] in bytes */ | |
| 13861 | 13833 | FILE *pFile, /* Read from this file if aBlob==0 */ |
| 13862 | 13834 | ZipfileEOCD *pEOCD /* Object to populate */ |
| 13863 | 13835 | ){ |
| 13864 | 13836 | u8 *aRead = pTab->aBuffer; /* Temporary buffer */ |
| 13865 | - int nRead; /* Bytes to read from file */ | |
| 13837 | + i64 nRead; /* Bytes to read from file */ | |
| 13866 | 13838 | int rc = SQLITE_OK; |
| 13867 | 13839 | |
| 13868 | 13840 | memset(pEOCD, 0, sizeof(ZipfileEOCD)); |
| 13869 | 13841 | if( aBlob==0 ){ |
| 13870 | 13842 | i64 iOff; /* Offset to read from */ |
| @@ -13881,11 +13853,11 @@ | ||
| 13881 | 13853 | nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE)); |
| 13882 | 13854 | aRead = (u8*)&aBlob[nBlob-nRead]; |
| 13883 | 13855 | } |
| 13884 | 13856 | |
| 13885 | 13857 | if( rc==SQLITE_OK ){ |
| 13886 | - int i; | |
| 13858 | + i64 i; | |
| 13887 | 13859 | |
| 13888 | 13860 | /* Scan backwards looking for the signature bytes */ |
| 13889 | 13861 | for(i=nRead-20; i>=0; i--){ |
| 13890 | 13862 | if( aRead[i]==0x50 && aRead[i+1]==0x4b |
| 13891 | 13863 | && aRead[i+2]==0x05 && aRead[i+3]==0x06 |
| @@ -13892,13 +13864,11 @@ | ||
| 13892 | 13864 | ){ |
| 13893 | 13865 | break; |
| 13894 | 13866 | } |
| 13895 | 13867 | } |
| 13896 | 13868 | if( i<0 ){ |
| 13897 | - pTab->base.zErrMsg = sqlite3_mprintf( | |
| 13898 | - "cannot find end of central directory record" | |
| 13899 | - ); | |
| 13869 | + zipfileTableErr(pTab, "cannot find end of central directory record"); | |
| 13900 | 13870 | return SQLITE_ERROR; |
| 13901 | 13871 | } |
| 13902 | 13872 | |
| 13903 | 13873 | aRead += i+4; |
| 13904 | 13874 | pEOCD->iDisk = zipfileRead16(aRead); |
| @@ -13939,11 +13909,11 @@ | ||
| 13939 | 13909 | pNew->pNext = pBefore; |
| 13940 | 13910 | *pp = pNew; |
| 13941 | 13911 | } |
| 13942 | 13912 | } |
| 13943 | 13913 | |
| 13944 | -static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){ | |
| 13914 | +static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){ | |
| 13945 | 13915 | ZipfileEOCD eocd; |
| 13946 | 13916 | int rc; |
| 13947 | 13917 | int i; |
| 13948 | 13918 | i64 iOff; |
| 13949 | 13919 | |
| @@ -13987,11 +13957,11 @@ | ||
| 13987 | 13957 | zipfileCursorErr(pCsr, "zipfile() function requires an argument"); |
| 13988 | 13958 | return SQLITE_ERROR; |
| 13989 | 13959 | }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ |
| 13990 | 13960 | static const u8 aEmptyBlob = 0; |
| 13991 | 13961 | const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]); |
| 13992 | - int nBlob = sqlite3_value_bytes(argv[0]); | |
| 13962 | + i64 nBlob = sqlite3_value_bytes(argv[0]); | |
| 13993 | 13963 | assert( pTab->pFirstEntry==0 ); |
| 13994 | 13964 | if( aBlob==0 ){ |
| 13995 | 13965 | aBlob = &aEmptyBlob; |
| 13996 | 13966 | nBlob = 0; |
| 13997 | 13967 | } |
| @@ -14185,23 +14155,23 @@ | ||
| 14185 | 14155 | ZipfileTab *pTab = (ZipfileTab*)pVtab; |
| 14186 | 14156 | int rc = SQLITE_OK; |
| 14187 | 14157 | |
| 14188 | 14158 | assert( pTab->pWriteFd==0 ); |
| 14189 | 14159 | if( pTab->zFile==0 || pTab->zFile[0]==0 ){ |
| 14190 | - pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename"); | |
| 14160 | + zipfileTableErr(pTab, "zipfile: missing filename"); | |
| 14191 | 14161 | return SQLITE_ERROR; |
| 14192 | 14162 | } |
| 14193 | 14163 | |
| 14194 | 14164 | /* Open a write fd on the file. Also load the entire central directory |
| 14195 | 14165 | ** structure into memory. During the transaction any new file data is |
| 14196 | 14166 | ** appended to the archive file, but the central directory is accumulated |
| 14197 | 14167 | ** in main-memory until the transaction is committed. */ |
| 14198 | 14168 | pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+"); |
| 14199 | 14169 | if( pTab->pWriteFd==0 ){ |
| 14200 | - pTab->base.zErrMsg = sqlite3_mprintf( | |
| 14201 | - "zipfile: failed to open file %s for writing", pTab->zFile | |
| 14202 | - ); | |
| 14170 | + zipfileTableErr(pTab, | |
| 14171 | + "zipfile: failed to open file %s for writing", pTab->zFile | |
| 14172 | + ); | |
| 14203 | 14173 | rc = SQLITE_ERROR; |
| 14204 | 14174 | }else{ |
| 14205 | 14175 | fseek(pTab->pWriteFd, 0, SEEK_END); |
| 14206 | 14176 | pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); |
| 14207 | 14177 | rc = zipfileLoadDirectory(pTab, 0, 0); |
| @@ -14662,11 +14632,11 @@ | ||
| 14662 | 14632 | int nEntry; |
| 14663 | 14633 | ZipfileBuffer body; |
| 14664 | 14634 | ZipfileBuffer cds; |
| 14665 | 14635 | }; |
| 14666 | 14636 | |
| 14667 | -static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){ | |
| 14637 | +static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){ | |
| 14668 | 14638 | if( pBuf->n+nByte>pBuf->nAlloc ){ |
| 14669 | 14639 | u8 *aNew; |
| 14670 | 14640 | sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512; |
| 14671 | 14641 | int nReq = pBuf->n + nByte; |
| 14672 | 14642 | |
| @@ -14711,11 +14681,11 @@ | ||
| 14711 | 14681 | u32 iCrc32 = 0; /* crc32 of uncompressed data */ |
| 14712 | 14682 | |
| 14713 | 14683 | char *zName = 0; /* Path (name) of new entry */ |
| 14714 | 14684 | int nName = 0; /* Size of zName in bytes */ |
| 14715 | 14685 | char *zFree = 0; /* Free this before returning */ |
| 14716 | - int nByte; | |
| 14686 | + i64 nByte; | |
| 14717 | 14687 | |
| 14718 | 14688 | memset(&e, 0, sizeof(e)); |
| 14719 | 14689 | p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); |
| 14720 | 14690 | if( p==0 ) return; |
| 14721 | 14691 | |
| @@ -24161,28 +24131,28 @@ | ||
| 24161 | 24131 | u8 autoExplain; /* Automatically turn on .explain mode */ |
| 24162 | 24132 | u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ |
| 24163 | 24133 | u8 autoEQPtrace; /* autoEQP is in trace mode */ |
| 24164 | 24134 | u8 scanstatsOn; /* True to display scan stats before each finalize */ |
| 24165 | 24135 | u8 bAutoScreenWidth; /* Using the TTY to determine screen width */ |
| 24166 | - u8 mFlags; /* MFLG_ECHO and/or MFLG_CRLF */ | |
| 24136 | + u8 mFlags; /* MFLG_ECHO, MFLG_CRLF, etc. */ | |
| 24167 | 24137 | u8 eMode; /* One of the MODE_ values */ |
| 24168 | 24138 | sqlite3_qrf_spec spec; /* Spec to be passed into QRF */ |
| 24169 | 24139 | } Mode; |
| 24170 | 24140 | |
| 24171 | 24141 | /* Flags for Mode.mFlags */ |
| 24172 | 24142 | #define MFLG_ECHO 0x01 /* Echo inputs to output */ |
| 24173 | 24143 | #define MFLG_CRLF 0x02 /* Use CR/LF output line endings */ |
| 24144 | +#define MFLG_HDR 0x04 /* .header used to change headers on/off */ | |
| 24174 | 24145 | |
| 24175 | 24146 | |
| 24176 | 24147 | /* |
| 24177 | 24148 | ** State information about the database connection is contained in an |
| 24178 | 24149 | ** instance of the following structure. |
| 24179 | 24150 | */ |
| 24180 | 24151 | typedef struct ShellState ShellState; |
| 24181 | 24152 | struct ShellState { |
| 24182 | 24153 | sqlite3 *db; /* The database */ |
| 24183 | - int iCompat; /* Compatibility date YYYYMMDD */ | |
| 24184 | 24154 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ |
| 24185 | 24155 | u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ |
| 24186 | 24156 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 24187 | 24157 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 24188 | 24158 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| @@ -24190,11 +24160,14 @@ | ||
| 24190 | 24160 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 24191 | 24161 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 24192 | 24162 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 24193 | 24163 | u8 nPopOutput; /* Revert .output settings when reaching zero */ |
| 24194 | 24164 | u8 nPopMode; /* Revert .mode settings when reaching zero */ |
| 24165 | + u8 enableTimer; /* Enable the timer. 2: permanently 1: only once */ | |
| 24195 | 24166 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 24167 | + double prevTimer; /* Last reported timer value */ | |
| 24168 | + double tmProgress; /* --timeout option for .progress */ | |
| 24196 | 24169 | i64 lineno; /* Line number of last line read from in */ |
| 24197 | 24170 | const char *zInFile; /* Name of the input file */ |
| 24198 | 24171 | int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ |
| 24199 | 24172 | FILE *in; /* Read commands from this stream */ |
| 24200 | 24173 | FILE *out; /* Write results here */ |
| @@ -24286,10 +24259,11 @@ | ||
| 24286 | 24259 | #define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */ |
| 24287 | 24260 | #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress |
| 24288 | 24261 | ** callback limit is reached, and for each |
| 24289 | 24262 | ** top-level SQL statement */ |
| 24290 | 24263 | #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ |
| 24264 | +#define SHELL_PROGRESS_TMOUT 0x08 /* Stop after tmProgress seconds */ | |
| 24291 | 24265 | |
| 24292 | 24266 | /* Names of values for Mode.spec.eEsc and Mode.spec.eText |
| 24293 | 24267 | */ |
| 24294 | 24268 | static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; |
| 24295 | 24269 | static const char *qrfQuoteNames[] = |
| @@ -24447,10 +24421,181 @@ | ||
| 24447 | 24421 | ** Limit input nesting via .read or any other input redirect. |
| 24448 | 24422 | ** It's not too expensive, so a generous allowance can be made. |
| 24449 | 24423 | */ |
| 24450 | 24424 | #define MAX_INPUT_NESTING 25 |
| 24451 | 24425 | |
| 24426 | +/************************* BEGIN PERFORMANCE TIMER *****************************/ | |
| 24427 | +#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) | |
| 24428 | +#include <sys/time.h> | |
| 24429 | +#include <sys/resource.h> | |
| 24430 | +/* VxWorks does not support getrusage() as far as we can determine */ | |
| 24431 | +#if defined(_WRS_KERNEL) || defined(__RTP__) | |
| 24432 | +struct rusage { | |
| 24433 | + struct timeval ru_utime; /* user CPU time used */ | |
| 24434 | + struct timeval ru_stime; /* system CPU time used */ | |
| 24435 | +}; | |
| 24436 | +#define getrusage(A,B) memset(B,0,sizeof(*B)) | |
| 24437 | +#endif | |
| 24438 | + | |
| 24439 | +/* Saved resource information for the beginning of an operation */ | |
| 24440 | +static struct rusage sBegin; /* CPU time at start */ | |
| 24441 | +static sqlite3_int64 iBegin; /* Wall-clock time at start */ | |
| 24442 | + | |
| 24443 | +/* | |
| 24444 | +** Begin timing an operation | |
| 24445 | +*/ | |
| 24446 | +static void beginTimer(ShellState *p){ | |
| 24447 | + if( p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0 ){ | |
| 24448 | + getrusage(RUSAGE_SELF, &sBegin); | |
| 24449 | + iBegin = timeOfDay(); | |
| 24450 | + } | |
| 24451 | +} | |
| 24452 | + | |
| 24453 | +/* Return the difference of two time_structs in seconds */ | |
| 24454 | +static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ | |
| 24455 | + return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + | |
| 24456 | + (double)(pEnd->tv_sec - pStart->tv_sec); | |
| 24457 | +} | |
| 24458 | + | |
| 24459 | +/* Return the time since the start of the timer in | |
| 24460 | +** seconds. */ | |
| 24461 | +static double elapseTime(ShellState *NotUsed){ | |
| 24462 | + (void)NotUsed; | |
| 24463 | + if( iBegin==0 ) return 0.0; | |
| 24464 | + return (timeOfDay() - iBegin)*0.000001; | |
| 24465 | +} | |
| 24466 | + | |
| 24467 | +/* | |
| 24468 | +** Print the timing results. | |
| 24469 | +*/ | |
| 24470 | +static void endTimer(ShellState *p){ | |
| 24471 | + if( p->enableTimer ){ | |
| 24472 | + sqlite3_int64 iEnd = timeOfDay(); | |
| 24473 | + struct rusage sEnd; | |
| 24474 | + getrusage(RUSAGE_SELF, &sEnd); | |
| 24475 | + p->prevTimer = (iEnd - iBegin)*0.000001; | |
| 24476 | + cli_printf(p->out, "Run Time: real %.6f user %.6f sys %.6f\n", | |
| 24477 | + p->prevTimer, | |
| 24478 | + timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), | |
| 24479 | + timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); | |
| 24480 | + if( p->enableTimer==1 ) p->enableTimer = 0; | |
| 24481 | + iBegin = 0; | |
| 24482 | + } | |
| 24483 | +} | |
| 24484 | + | |
| 24485 | +#define BEGIN_TIMER(X) beginTimer(X) | |
| 24486 | +#define END_TIMER(X) endTimer(X) | |
| 24487 | +#define ELAPSE_TIME(X) elapseTime(X) | |
| 24488 | +#define HAS_TIMER 1 | |
| 24489 | + | |
| 24490 | +#elif (defined(_WIN32) || defined(WIN32)) | |
| 24491 | + | |
| 24492 | +/* Saved resource information for the beginning of an operation */ | |
| 24493 | +static HANDLE hProcess; | |
| 24494 | +static FILETIME ftKernelBegin; | |
| 24495 | +static FILETIME ftUserBegin; | |
| 24496 | +static sqlite3_int64 ftWallBegin; | |
| 24497 | +typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, | |
| 24498 | + LPFILETIME, LPFILETIME); | |
| 24499 | +static GETPROCTIMES getProcessTimesAddr = NULL; | |
| 24500 | + | |
| 24501 | +/* | |
| 24502 | +** Check to see if we have timer support. Return 1 if necessary | |
| 24503 | +** support found (or found previously). | |
| 24504 | +*/ | |
| 24505 | +static int hasTimer(void){ | |
| 24506 | + if( getProcessTimesAddr ){ | |
| 24507 | + return 1; | |
| 24508 | + } else { | |
| 24509 | + /* GetProcessTimes() isn't supported in WIN95 and some other Windows | |
| 24510 | + ** versions. See if the version we are running on has it, and if it | |
| 24511 | + ** does, save off a pointer to it and the current process handle. | |
| 24512 | + */ | |
| 24513 | + hProcess = GetCurrentProcess(); | |
| 24514 | + if( hProcess ){ | |
| 24515 | + HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); | |
| 24516 | + if( NULL != hinstLib ){ | |
| 24517 | + getProcessTimesAddr = | |
| 24518 | + (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); | |
| 24519 | + if( NULL != getProcessTimesAddr ){ | |
| 24520 | + return 1; | |
| 24521 | + } | |
| 24522 | + FreeLibrary(hinstLib); | |
| 24523 | + } | |
| 24524 | + } | |
| 24525 | + } | |
| 24526 | + return 0; | |
| 24527 | +} | |
| 24528 | + | |
| 24529 | +/* | |
| 24530 | +** Begin timing an operation | |
| 24531 | +*/ | |
| 24532 | +static void beginTimer(ShellState *p){ | |
| 24533 | + if( (p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0) | |
| 24534 | + && getProcessTimesAddr | |
| 24535 | + ){ | |
| 24536 | + FILETIME ftCreation, ftExit; | |
| 24537 | + getProcessTimesAddr(hProcess,&ftCreation,&ftExit, | |
| 24538 | + &ftKernelBegin,&ftUserBegin); | |
| 24539 | + ftWallBegin = timeOfDay(); | |
| 24540 | + } | |
| 24541 | +} | |
| 24542 | + | |
| 24543 | +/* Return the difference of two FILETIME structs in seconds */ | |
| 24544 | +static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ | |
| 24545 | + sqlite_int64 i64Start = *((sqlite_int64 *) pStart); | |
| 24546 | + sqlite_int64 i64End = *((sqlite_int64 *) pEnd); | |
| 24547 | + return (double) ((i64End - i64Start) / 10000000.0); | |
| 24548 | +} | |
| 24549 | + | |
| 24550 | +/* Return the time since the start of the timer in | |
| 24551 | +** seconds. */ | |
| 24552 | +static double elapseTime(ShellState *NotUsed){ | |
| 24553 | + (void)NotUsed; | |
| 24554 | + if( ftWallBegin==0 ) return 0.0; | |
| 24555 | + return (timeOfDay() - ftWallBegin)*0.000001; | |
| 24556 | +} | |
| 24557 | + | |
| 24558 | +/* | |
| 24559 | +** Print the timing results. | |
| 24560 | +*/ | |
| 24561 | +static void endTimer(ShellState *p){ | |
| 24562 | + if( p->enableTimer && getProcessTimesAddr){ | |
| 24563 | + FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; | |
| 24564 | + sqlite3_int64 ftWallEnd = timeOfDay(); | |
| 24565 | + getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); | |
| 24566 | + p->prevTimer = (ftWallEnd - ftWallBegin)*0.000001; | |
| 24567 | +#ifdef _WIN64 | |
| 24568 | + /* microsecond precision on 64-bit windows */ | |
| 24569 | + cli_printf(p->out, "Run Time: real %.6f user %f sys %f\n", | |
| 24570 | + p->prevTimer, | |
| 24571 | + timeDiff(&ftUserBegin, &ftUserEnd), | |
| 24572 | + timeDiff(&ftKernelBegin, &ftKernelEnd)); | |
| 24573 | +#else | |
| 24574 | + /* millisecond precisino on 32-bit windows */ | |
| 24575 | + cli_printf(p->out, "Run Time: real %.3f user %.3f sys %.3f\n", | |
| 24576 | + p->prevTimer, | |
| 24577 | + timeDiff(&ftUserBegin, &ftUserEnd), | |
| 24578 | + timeDiff(&ftKernelBegin, &ftKernelEnd)); | |
| 24579 | +#endif | |
| 24580 | + if( p->enableTimer==1 ) p->enableTimer = 0; | |
| 24581 | + ftWallBegin = 0; | |
| 24582 | + } | |
| 24583 | +} | |
| 24584 | + | |
| 24585 | +#define BEGIN_TIMER(X) beginTimer(X) | |
| 24586 | +#define ELAPSE_TIME(X) elapseTime(X) | |
| 24587 | +#define END_TIMER(X) endTimer(X) | |
| 24588 | +#define HAS_TIMER hasTimer() | |
| 24589 | + | |
| 24590 | +#else | |
| 24591 | +#define BEGIN_TIMER(X) /* no-op */ | |
| 24592 | +#define ELAPSE_TIME(X) 0.0 | |
| 24593 | +#define END_TIMER(X) /*no-op*/ | |
| 24594 | +#define HAS_TIMER 0 | |
| 24595 | +#endif | |
| 24596 | +/************************* END PERFORMANCE TIMER ******************************/ | |
| 24452 | 24597 | |
| 24453 | 24598 | /* |
| 24454 | 24599 | ** Clear a display mode, freeing any allocated memory that it |
| 24455 | 24600 | ** contains. |
| 24456 | 24601 | */ |
| @@ -24534,11 +24679,13 @@ | ||
| 24534 | 24679 | if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]); |
| 24535 | 24680 | if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]); |
| 24536 | 24681 | if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]); |
| 24537 | 24682 | pM->spec.eText = pI->eText; |
| 24538 | 24683 | pM->spec.eBlob = pI->eBlob; |
| 24539 | - pM->spec.bTitles = pI->bHdr; | |
| 24684 | + if( (pM->mFlags & MFLG_HDR)==0 ){ | |
| 24685 | + pM->spec.bTitles = pI->bHdr; | |
| 24686 | + } | |
| 24540 | 24687 | pM->spec.eTitle = pI->eHdr; |
| 24541 | 24688 | if( pI->mFlg & 0x01 ){ |
| 24542 | 24689 | pM->spec.bBorder = QRF_No; |
| 24543 | 24690 | }else{ |
| 24544 | 24691 | pM->spec.bBorder = QRF_Auto; |
| @@ -24570,18 +24717,17 @@ | ||
| 24570 | 24717 | p->mode.mFlags = mFlags; |
| 24571 | 24718 | } |
| 24572 | 24719 | } |
| 24573 | 24720 | |
| 24574 | 24721 | /* |
| 24575 | -** Set the mode to the default according to p->iCompat. It assumed | |
| 24576 | -** that the mode has already been freed and zeroed prior to calling | |
| 24577 | -** this routine. | |
| 24722 | +** Set the mode to the default. It assumed that the mode has | |
| 24723 | +** already been freed and zeroed prior to calling this routine. | |
| 24578 | 24724 | */ |
| 24579 | 24725 | static void modeDefault(ShellState *p){ |
| 24580 | 24726 | p->mode.spec.iVersion = 1; |
| 24581 | 24727 | p->mode.autoExplain = 1; |
| 24582 | - if( p->iCompat>=20251115 && (stdin_is_interactive || stdout_is_console) ){ | |
| 24728 | + if( stdin_is_interactive || stdout_is_console ){ | |
| 24583 | 24729 | modeChange(p, MODE_TTY); |
| 24584 | 24730 | }else{ |
| 24585 | 24731 | modeChange(p, MODE_BATCH); |
| 24586 | 24732 | } |
| 24587 | 24733 | } |
| @@ -25371,10 +25517,17 @@ | ||
| 25371 | 25517 | ** Progress handler callback. |
| 25372 | 25518 | */ |
| 25373 | 25519 | static int progress_handler(void *pClientData) { |
| 25374 | 25520 | ShellState *p = (ShellState*)pClientData; |
| 25375 | 25521 | p->nProgress++; |
| 25522 | + if( (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0 | |
| 25523 | + && ELAPSE_TIME(p)>=p->tmProgress | |
| 25524 | + ){ | |
| 25525 | + cli_printf(p->out, "Progress timeout after %.6f seconds\n", | |
| 25526 | + ELAPSE_TIME(p)); | |
| 25527 | + return 1; | |
| 25528 | + } | |
| 25376 | 25529 | if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ |
| 25377 | 25530 | cli_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); |
| 25378 | 25531 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 25379 | 25532 | if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; |
| 25380 | 25533 | return 1; |
| @@ -25908,10 +26061,12 @@ | ||
| 25908 | 26061 | char *zBuf = sqlite3_malloc64( szVar-5 ); |
| 25909 | 26062 | if( zBuf ){ |
| 25910 | 26063 | memcpy(zBuf, &zVar[6], szVar-5); |
| 25911 | 26064 | sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8); |
| 25912 | 26065 | } |
| 26066 | + }else if( strcmp(zVar, "$TIMER")==0 ){ | |
| 26067 | + sqlite3_bind_double(pStmt, i, pArg->prevTimer); | |
| 25913 | 26068 | #ifdef SQLITE_ENABLE_CARRAY |
| 25914 | 26069 | }else if( strncmp(zVar, "$carray_", 8)==0 ){ |
| 25915 | 26070 | static char *azColorNames[] = { |
| 25916 | 26071 | "azure", "black", "blue", "brown", "cyan", "fuchsia", "gold", |
| 25917 | 26072 | "gray", "green", "indigo", "khaki", "lime", "magenta", "maroon", |
| @@ -26167,27 +26322,36 @@ | ||
| 26167 | 26322 | pArg->pStmt = pStmt; |
| 26168 | 26323 | } |
| 26169 | 26324 | |
| 26170 | 26325 | /* Show the EXPLAIN QUERY PLAN if .eqp is on */ |
| 26171 | 26326 | isExplain = sqlite3_stmt_isexplain(pStmt); |
| 26172 | - if( pArg && pArg->mode.autoEQP && isExplain==0 ){ | |
| 26327 | + if( pArg && pArg->mode.autoEQP && isExplain==0 && pArg->dot.nArg==0 ){ | |
| 26173 | 26328 | int triggerEQP = 0; |
| 26329 | + u8 savedEnableTimer = pArg->enableTimer; | |
| 26330 | + pArg->enableTimer = 0; | |
| 26174 | 26331 | disable_debug_trace_modes(); |
| 26175 | 26332 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); |
| 26176 | 26333 | if( pArg->mode.autoEQP>=AUTOEQP_trigger ){ |
| 26177 | 26334 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); |
| 26178 | 26335 | } |
| 26179 | 26336 | sqlite3_reset(pStmt); |
| 26180 | 26337 | spec.eStyle = QRF_STYLE_Auto; |
| 26181 | - sqlite3_stmt_explain(pStmt, 2-(pArg->mode.autoEQP>=AUTOEQP_full)); | |
| 26338 | + sqlite3_stmt_explain(pStmt, 2); | |
| 26182 | 26339 | sqlite3_format_query_result(pStmt, &spec, 0); |
| 26340 | + if( pArg->mode.autoEQP>=AUTOEQP_full ){ | |
| 26341 | + sqlite3_reset(pStmt); | |
| 26342 | + sqlite3_stmt_explain(pStmt, 1); | |
| 26343 | + sqlite3_format_query_result(pStmt, &spec, 0); | |
| 26344 | + } | |
| 26345 | + | |
| 26183 | 26346 | if( pArg->mode.autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ |
| 26184 | 26347 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); |
| 26185 | 26348 | } |
| 26186 | 26349 | sqlite3_reset(pStmt); |
| 26187 | 26350 | sqlite3_stmt_explain(pStmt, 0); |
| 26188 | 26351 | restore_debug_trace_modes(); |
| 26352 | + pArg->enableTimer = savedEnableTimer; | |
| 26189 | 26353 | } |
| 26190 | 26354 | |
| 26191 | 26355 | bind_prepared_stmt(pArg, pStmt); |
| 26192 | 26356 | if( isExplain && pArg->mode.autoExplain ){ |
| 26193 | 26357 | spec.eStyle = isExplain==1 ? QRF_STYLE_Explain : QRF_STYLE_Eqp; |
| @@ -26604,12 +26768,12 @@ | ||
| 26604 | 26768 | ".bail on|off Stop after hitting an error. Default OFF", |
| 26605 | 26769 | #ifndef SQLITE_SHELL_FIDDLE |
| 26606 | 26770 | ".cd DIRECTORY Change the working directory to DIRECTORY", |
| 26607 | 26771 | #endif |
| 26608 | 26772 | ".changes on|off Show number of rows changed by SQL", |
| 26773 | + ".check OPTIONS ... Verify the results of a .testcase", | |
| 26609 | 26774 | #ifndef SQLITE_SHELL_FIDDLE |
| 26610 | - ".check GLOB Fail if output since .testcase does not match", | |
| 26611 | 26775 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 26612 | 26776 | #endif |
| 26613 | 26777 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 26614 | 26778 | ".crlf ?on|off? Whether or not to use \\r\\n line endings", |
| 26615 | 26779 | ".databases List names and files of attached databases", |
| @@ -26721,10 +26885,11 @@ | ||
| 26721 | 26885 | ".progress N Invoke progress handler after every N opcodes", |
| 26722 | 26886 | " --limit N Interrupt after N progress callbacks", |
| 26723 | 26887 | " --once Do no more than one progress interrupt", |
| 26724 | 26888 | " --quiet|-q No output except at interrupts", |
| 26725 | 26889 | " --reset Reset the count for each input and interrupt", |
| 26890 | + " --timeout S Halt after running for S seconds", | |
| 26726 | 26891 | #endif |
| 26727 | 26892 | ".prompt MAIN CONTINUE Replace the standard prompts", |
| 26728 | 26893 | #ifndef SQLITE_SHELL_FIDDLE |
| 26729 | 26894 | ".quit Stop interpreting input stream, exit if primary.", |
| 26730 | 26895 | ".read FILE Read input from FILE or command output", |
| @@ -26785,17 +26950,15 @@ | ||
| 26785 | 26950 | " vmstep Show the virtual machine step count only", |
| 26786 | 26951 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 26787 | 26952 | ".system CMD ARGS... Run CMD ARGS... in a system shell", |
| 26788 | 26953 | #endif |
| 26789 | 26954 | ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", |
| 26790 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 26791 | - ",testcase NAME Begin redirecting output to 'testcase-out.txt'", | |
| 26792 | -#endif | |
| 26955 | + ".testcase NAME Begin a test case.", | |
| 26793 | 26956 | ",testctrl CMD ... Run various sqlite3_test_control() operations", |
| 26794 | 26957 | " Run \".testctrl\" with no arguments for details", |
| 26795 | 26958 | ".timeout MS Try opening locked tables for MS milliseconds", |
| 26796 | - ".timer on|off Turn SQL timer on or off", | |
| 26959 | + ".timer on|off|once Turn SQL timer on or off.", | |
| 26797 | 26960 | #ifndef SQLITE_OMIT_TRACE |
| 26798 | 26961 | ".trace ?OPTIONS? Output each SQL statement as it is run", |
| 26799 | 26962 | " FILE Send output to FILE", |
| 26800 | 26963 | " stdout Send output to stdout", |
| 26801 | 26964 | " stderr Send output to stderr", |
| @@ -26836,19 +26999,33 @@ | ||
| 26836 | 26999 | "USAGE: .import [OPTIONS] FILE TABLE\n" |
| 26837 | 27000 | "\n" |
| 26838 | 27001 | "Import CSV or similar text from FILE into TABLE. If TABLE does\n" |
| 26839 | 27002 | "not exist, it is created using the first row of FILE as the column\n" |
| 26840 | 27003 | "names. If FILE begins with \"|\" then it is a command that is run\n" |
| 26841 | -"and the output from the command is used as the input data.\n" | |
| 27004 | +"and the output from the command is used as the input data. If\n" | |
| 27005 | +"FILE begins with \"<<\" followed by a label, then content is read from\n" | |
| 27006 | +"the script until the first line that matches the label.\n" | |
| 27007 | +"\n" | |
| 27008 | +"The content of FILE is interpreted using RFC-4180 (\"CSV\") quoting\n" | |
| 27009 | +"rules unless the current mode is \"ascii\" or \"tabs\" or unless one\n" | |
| 27010 | +"the --ascii option is used.\n" | |
| 26842 | 27011 | "\n" |
| 26843 | -"FILE is assumed to be in a CSV format, unless the current mode\n" | |
| 26844 | -"is \"ascii\" or \"tabs\" or unless one of the options below specify\n" | |
| 26845 | -"an alternative.\n" | |
| 27012 | +"The column and row separators must be single ASCII characters. If\n" | |
| 27013 | +"multiple characters or a Unicode character are specified for the\n" | |
| 27014 | +"separators, then only the first byte of the separator is used. Except,\n" | |
| 27015 | +"if the row separator is \\n and the mode is not --ascii, then \\r\\n is\n" | |
| 27016 | +"understood as a row separator too.\n" | |
| 26846 | 27017 | "\n" |
| 26847 | 27018 | "Options:\n" |
| 26848 | -" --ascii Use \\037 and \\036 as column and row separators on input\n" | |
| 27019 | +" --ascii Do not use RFC-4180 quoting. Use \\037 and \\036\n" | |
| 27020 | +" as column and row separators on input, unless other\n" | |
| 27021 | +" delimiters are specified using --colsep and/or --rowsep\n" | |
| 27022 | +" --colsep CHAR Use CHAR as the column separator.\n" | |
| 26849 | 27023 | " --csv Input is standard RFC-4180 CSV.\n" |
| 27024 | +" --esc CHAR Use CHAR as an escape character in unquoted CSV inputs.\n" | |
| 27025 | +" --qesc CHAR Use CHAR as an escape character in quoted CSV inputs.\n" | |
| 27026 | +" --rowsep CHAR Use CHAR as the row separator.\n" | |
| 26850 | 27027 | " --schema S When creating TABLE, put it in schema S\n" |
| 26851 | 27028 | " --skip N Ignore the first N rows of input\n" |
| 26852 | 27029 | " -v Verbose mode\n" |
| 26853 | 27030 | }, |
| 26854 | 27031 | { ".mode", |
| @@ -26930,23 +27107,17 @@ | ||
| 26930 | 27107 | " --bom Prepend a byte-order mark to the output\n" |
| 26931 | 27108 | " -e Accumulate output in a temporary text file then\n" |
| 26932 | 27109 | " launch a text editor when the redirection ends.\n" |
| 26933 | 27110 | " --error-prefix X Use X as the left-margin prefix for error messages.\n" |
| 26934 | 27111 | " Set to an empty string to restore the default.\n" |
| 26935 | -" --glob GLOB Raise an error if the memory buffer does not match\n" | |
| 26936 | -" the GLOB pattern.\n" | |
| 26937 | -" --keep Continue using the same \"memory\" buffer. Do not\n" | |
| 26938 | -" reset it or delete it. Useful in combination with\n" | |
| 26939 | -" --glob, --not-glob, and/or --verify.\n" | |
| 26940 | -" ---notglob GLOB Raise an error if the memory buffer does not match\n" | |
| 26941 | -" the GLOB pattern.\n" | |
| 27112 | +" --keep Keep redirecting output to its current destination.\n" | |
| 27113 | +" Use this option in combination with --show or\n" | |
| 27114 | +" with --error-prefix when you do not want to stop\n" | |
| 27115 | +" a current redirection.\n" | |
| 26942 | 27116 | " --plain Use plain text rather than HTML tables with -w\n" |
| 26943 | -" --show Write the memory buffer to the screen, for debugging.\n" | |
| 26944 | -" --verify ENDMARK Read subsequent lines of text until the first line\n" | |
| 26945 | -" that matches ENDMARK. Discard the ENDMARK. Compare\n" | |
| 26946 | -" the text against the accumulated output in memory and\n" | |
| 26947 | -" raise an error if there are any differences.\n" | |
| 27117 | +" --show Show output text captured by .testcase or by\n" | |
| 27118 | +" redirecting to \"memory\".\n" | |
| 26948 | 27119 | " -w Show the output in a web browser. Output is\n" |
| 26949 | 27120 | " written into a temporary HTML file until the\n" |
| 26950 | 27121 | " redirect ends, then the web browser is launched.\n" |
| 26951 | 27122 | " Query results are shown as HTML tables, unless\n" |
| 26952 | 27123 | " the --plain is used too.\n" |
| @@ -26970,10 +27141,39 @@ | ||
| 26970 | 27141 | " file in a web browser\n" |
| 26971 | 27142 | " -x Show the output in a spreadsheet. Output is\n" |
| 26972 | 27143 | " written to a temp file as CSV then the spreadsheet\n" |
| 26973 | 27144 | " is launched when\n" |
| 26974 | 27145 | }, |
| 27146 | + { ".check", | |
| 27147 | +"USAGE: .check [OPTIONS] PATTERN\n" | |
| 27148 | +"\n" | |
| 27149 | +"Verify results of commands since the most recent .testcase command.\n" | |
| 27150 | +"Restore output to the console, unless --keep is used.\n" | |
| 27151 | +"\n" | |
| 27152 | +"If PATTERN starts with \"<<ENDMARK\" then the actual pattern is taken from\n" | |
| 27153 | +"subsequent lines of text up to the first line that begins with ENDMARK.\n" | |
| 27154 | +"All pattern lines and the ENDMARK are discarded.\n" | |
| 27155 | +"\n" | |
| 27156 | +"Options:\n" | |
| 27157 | +" --exact Do an exact comparison including leading and\n" | |
| 27158 | +" trailing whitespace.\n" | |
| 27159 | +" --glob Treat PATTERN as a GLOB\n" | |
| 27160 | +" --keep Do not reset the testcase. More .check commands\n" | |
| 27161 | +" will follow.\n" | |
| 27162 | +" --notglob Output should not match PATTERN\n" | |
| 27163 | +" --show Write testcase output to the screen, for debugging.\n" | |
| 27164 | + }, | |
| 27165 | + { ".testcase", | |
| 27166 | +"USAGE: .testcase [OPTIONS] NAME\n" | |
| 27167 | +"\n" | |
| 27168 | +"Start a new test case identified by NAME. All output\n" | |
| 27169 | +"through the next \".check\" command is captured for comparison. See the\n" | |
| 27170 | +"\".check\" commandn for additional informatioon.\n" | |
| 27171 | +"\n" | |
| 27172 | +"Options:\n" | |
| 27173 | +" --error-prefix TEXT Change error message prefix text to TEXT\n" | |
| 27174 | + }, | |
| 26975 | 27175 | }; |
| 26976 | 27176 | |
| 26977 | 27177 | /* |
| 26978 | 27178 | ** Return a pointer to usage text for zCmd, or NULL if none exists. |
| 26979 | 27179 | */ |
| @@ -27193,10 +27393,56 @@ | ||
| 27193 | 27393 | if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; |
| 27194 | 27394 | } |
| 27195 | 27395 | return 1; |
| 27196 | 27396 | } |
| 27197 | 27397 | #endif |
| 27398 | + | |
| 27399 | +/* | |
| 27400 | +** Return the size of the named file in bytes. Or return a negative | |
| 27401 | +** number if the file does not exist. | |
| 27402 | +*/ | |
| 27403 | +static sqlite3_int64 fileSize(const char *zFile){ | |
| 27404 | +#if defined(_WIN32) || defined(WIN32) | |
| 27405 | + struct _stat64 x; | |
| 27406 | + if( _stat64(zFile, &x)!=0 ) return -1; | |
| 27407 | + return (sqlite3_int64)x.st_size; | |
| 27408 | +#else | |
| 27409 | + struct stat x; | |
| 27410 | + if( stat(zFile, &x)!=0 ) return -1; | |
| 27411 | + return (sqlite3_int64)x.st_size; | |
| 27412 | +#endif | |
| 27413 | +} | |
| 27414 | + | |
| 27415 | +/* | |
| 27416 | +** Return true if zFile is an SQLite database. | |
| 27417 | +** | |
| 27418 | +** Algorithm: | |
| 27419 | +** * If the file does not exist -> return false | |
| 27420 | +** * If the size of the file is not a multiple of 512 -> return false | |
| 27421 | +** * If sqlite3_open() fails -> return false | |
| 27422 | +** * if sqlite3_prepare() or sqlite3_step() fails -> return false | |
| 27423 | +** * Otherwise -> return true | |
| 27424 | +*/ | |
| 27425 | +static int isDatabaseFile(const char *zFile, int openFlags){ | |
| 27426 | + sqlite3 *db = 0; | |
| 27427 | + sqlite3_stmt *pStmt = 0; | |
| 27428 | + int rc; | |
| 27429 | + sqlite3_int64 sz = fileSize(zFile); | |
| 27430 | + if( sz<512 || (sz%512)!=0 ) return 0; | |
| 27431 | + if( sqlite3_open_v2(zFile, &db, openFlags, 0)==SQLITE_OK | |
| 27432 | + && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) | |
| 27433 | + ==SQLITE_OK | |
| 27434 | + && sqlite3_step(pStmt)==SQLITE_ROW | |
| 27435 | + ){ | |
| 27436 | + rc = 1; | |
| 27437 | + }else{ | |
| 27438 | + rc = 0; | |
| 27439 | + } | |
| 27440 | + sqlite3_finalize(pStmt); | |
| 27441 | + sqlite3_close(db); | |
| 27442 | + return rc; | |
| 27443 | +} | |
| 27198 | 27444 | |
| 27199 | 27445 | /* |
| 27200 | 27446 | ** Try to deduce the type of file for zName based on its content. Return |
| 27201 | 27447 | ** one of the SHELL_OPEN_* constants. |
| 27202 | 27448 | ** |
| @@ -27206,24 +27452,16 @@ | ||
| 27206 | 27452 | ** the type cannot be determined from content. |
| 27207 | 27453 | */ |
| 27208 | 27454 | int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){ |
| 27209 | 27455 | FILE *f; |
| 27210 | 27456 | size_t n; |
| 27211 | - sqlite3 *db = 0; | |
| 27212 | - sqlite3_stmt *pStmt = 0; | |
| 27213 | 27457 | int rc = SHELL_OPEN_UNSPEC; |
| 27214 | 27458 | char zBuf[100]; |
| 27215 | 27459 | if( access(zName,0)!=0 ) goto database_type_by_name; |
| 27216 | - if( sqlite3_open_v2(zName, &db, openFlags, 0)==SQLITE_OK | |
| 27217 | - && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) | |
| 27218 | - ==SQLITE_OK | |
| 27219 | - && sqlite3_step(pStmt)==SQLITE_ROW | |
| 27220 | - ){ | |
| 27460 | + if( isDatabaseFile(zName, openFlags) ){ | |
| 27221 | 27461 | rc = SHELL_OPEN_NORMAL; |
| 27222 | 27462 | } |
| 27223 | - sqlite3_finalize(pStmt); | |
| 27224 | - sqlite3_close(db); | |
| 27225 | 27463 | if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL; |
| 27226 | 27464 | f = sqlite3_fopen(zName, "rb"); |
| 27227 | 27465 | if( f==0 ) goto database_type_by_name; |
| 27228 | 27466 | n = fread(zBuf, 16, 1, f); |
| 27229 | 27467 | if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){ |
| @@ -27253,10 +27491,39 @@ | ||
| 27253 | 27491 | }else{ |
| 27254 | 27492 | rc = SHELL_OPEN_NORMAL; |
| 27255 | 27493 | } |
| 27256 | 27494 | return rc; |
| 27257 | 27495 | } |
| 27496 | + | |
| 27497 | +/* | |
| 27498 | +** If the text in z[] is the name of a readable file and that file appears | |
| 27499 | +** to contain SQL text and/or dot-commands, then return true. If z[] is | |
| 27500 | +** not a file, or if the file is unreadable, or if the file is a database | |
| 27501 | +** or anything else that is not SQL text and dot-commands, then return false. | |
| 27502 | +** | |
| 27503 | +** If the bLeaveUninit flag is set, then be sure to leave SQLite in an | |
| 27504 | +** uninitialized state. This means invoking sqlite3_shutdown() after any | |
| 27505 | +** SQLite API is used. | |
| 27506 | +** | |
| 27507 | +** Some amount of guesswork is involved in this decision. | |
| 27508 | +*/ | |
| 27509 | +static int isScriptFile(const char *z, int bLeaveUninit){ | |
| 27510 | + sqlite3_int64 sz = fileSize(z); | |
| 27511 | + if( sz<=0 ) return 0; | |
| 27512 | + if( (sz%512)==0 ){ | |
| 27513 | + int rc; | |
| 27514 | + sqlite3_initialize(); | |
| 27515 | + rc = isDatabaseFile(z, SQLITE_OPEN_READONLY); | |
| 27516 | + if( bLeaveUninit ){ | |
| 27517 | + sqlite3_shutdown(); | |
| 27518 | + } | |
| 27519 | + if( rc ) return 0; /* Is a database */ | |
| 27520 | + } | |
| 27521 | + if( sqlite3_strlike("%.sql",z,0)==0 ) return 1; | |
| 27522 | + if( sqlite3_strlike("%.txt",z,0)==0 ) return 1; | |
| 27523 | + return 0; | |
| 27524 | +} | |
| 27258 | 27525 | |
| 27259 | 27526 | #ifndef SQLITE_OMIT_DESERIALIZE |
| 27260 | 27527 | /* |
| 27261 | 27528 | ** Reconstruct an in-memory database using the output from the "dbtotxt" |
| 27262 | 27529 | ** program. Read content from the file in p->aAuxDb[].zDbFilename. |
| @@ -27899,20 +28166,24 @@ | ||
| 27899 | 28166 | typedef struct ImportCtx ImportCtx; |
| 27900 | 28167 | struct ImportCtx { |
| 27901 | 28168 | const char *zFile; /* Name of the input file */ |
| 27902 | 28169 | FILE *in; /* Read the CSV text from this input stream */ |
| 27903 | 28170 | int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */ |
| 28171 | + char *zIn; /* Input text */ | |
| 27904 | 28172 | char *z; /* Accumulated text for a field */ |
| 28173 | + i64 nUsed; /* Bytes of zIn[] used so far */ | |
| 27905 | 28174 | i64 n; /* Number of bytes in z */ |
| 27906 | 28175 | i64 nAlloc; /* Space allocated for z[] */ |
| 27907 | 28176 | int nLine; /* Current line number */ |
| 27908 | 28177 | int nRow; /* Number of rows imported */ |
| 27909 | 28178 | int nErr; /* Number of errors encountered */ |
| 27910 | 28179 | int bNotFirst; /* True if one or more bytes already read */ |
| 27911 | 28180 | int cTerm; /* Character that terminated the most recent field */ |
| 27912 | 28181 | int cColSep; /* The column separator character. (Usually ",") */ |
| 27913 | 28182 | int cRowSep; /* The row separator character. (Usually "\n") */ |
| 28183 | + int cQEscape; /* Escape character with "...". 0 for none */ | |
| 28184 | + int cUQEscape; /* Escape character not with "...". 0 for none */ | |
| 27914 | 28185 | }; |
| 27915 | 28186 | |
| 27916 | 28187 | /* Clean up resourced used by an ImportCtx */ |
| 27917 | 28188 | static void import_cleanup(ImportCtx *p){ |
| 27918 | 28189 | if( p->in!=0 && p->xCloser!=0 ){ |
| @@ -27919,13 +28190,32 @@ | ||
| 27919 | 28190 | p->xCloser(p->in); |
| 27920 | 28191 | p->in = 0; |
| 27921 | 28192 | } |
| 27922 | 28193 | sqlite3_free(p->z); |
| 27923 | 28194 | p->z = 0; |
| 28195 | + if( p->zIn ){ | |
| 28196 | + sqlite3_free(p->zIn); | |
| 28197 | + p->zIn = 0; | |
| 28198 | + } | |
| 27924 | 28199 | } |
| 27925 | 28200 | |
| 27926 | -/* Append a single byte to z[] */ | |
| 28201 | +/* Read a single character of the .import input text. Return EOF | |
| 28202 | +** at end-of-file. | |
| 28203 | +*/ | |
| 28204 | +static int import_getc(ImportCtx *p){ | |
| 28205 | + if( p->in ){ | |
| 28206 | + return fgetc(p->in); | |
| 28207 | + }else if( p->zIn && p->zIn[p->nUsed]!=0 ){ | |
| 28208 | + return p->zIn[p->nUsed++]; | |
| 28209 | + }else{ | |
| 28210 | + return EOF; | |
| 28211 | + } | |
| 28212 | +} | |
| 28213 | + | |
| 28214 | +/* Append a single byte to the field value begin constructed | |
| 28215 | +** in the p->z[] buffer | |
| 28216 | +*/ | |
| 27927 | 28217 | static void import_append_char(ImportCtx *p, int c){ |
| 27928 | 28218 | if( p->n+1>=p->nAlloc ){ |
| 27929 | 28219 | p->nAlloc += p->nAlloc + 100; |
| 27930 | 28220 | p->z = sqlite3_realloc64(p->z, p->nAlloc); |
| 27931 | 28221 | shell_check_oom(p->z); |
| @@ -27937,12 +28227,12 @@ | ||
| 27937 | 28227 | ** with the option of having a separator other than ",". |
| 27938 | 28228 | ** |
| 27939 | 28229 | ** + Input comes from p->in. |
| 27940 | 28230 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 27941 | 28231 | ** from sqlite3_malloc64(). |
| 27942 | -** + Use p->cSep as the column separator. The default is ",". | |
| 27943 | -** + Use p->rSep as the row separator. The default is "\n". | |
| 28232 | +** + Use p->cColSep as the column separator. The default is ",". | |
| 28233 | +** + Use p->cRowSep as the row separator. The default is "\n". | |
| 27944 | 28234 | ** + Keep track of the line number in p->nLine. |
| 27945 | 28235 | ** + Store the character that terminates the field in p->cTerm. Store |
| 27946 | 28236 | ** EOF on end-of-file. |
| 27947 | 28237 | ** + Report syntax errors on stderr |
| 27948 | 28238 | */ |
| @@ -27949,23 +28239,30 @@ | ||
| 27949 | 28239 | static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ |
| 27950 | 28240 | int c; |
| 27951 | 28241 | int cSep = (u8)p->cColSep; |
| 27952 | 28242 | int rSep = (u8)p->cRowSep; |
| 27953 | 28243 | p->n = 0; |
| 27954 | - c = fgetc(p->in); | |
| 28244 | + c = import_getc(p); | |
| 27955 | 28245 | if( c==EOF || seenInterrupt ){ |
| 27956 | 28246 | p->cTerm = EOF; |
| 27957 | 28247 | return 0; |
| 27958 | 28248 | } |
| 27959 | 28249 | if( c=='"' ){ |
| 27960 | 28250 | int pc, ppc; |
| 27961 | 28251 | int startLine = p->nLine; |
| 27962 | 28252 | int cQuote = c; |
| 28253 | + int cEsc = (u8)p->cQEscape; | |
| 27963 | 28254 | pc = ppc = 0; |
| 27964 | 28255 | while( 1 ){ |
| 27965 | - c = fgetc(p->in); | |
| 28256 | + c = import_getc(p); | |
| 27966 | 28257 | if( c==rSep ) p->nLine++; |
| 28258 | + if( c==cEsc && cEsc!=0 ){ | |
| 28259 | + c = import_getc(p); | |
| 28260 | + import_append_char(p, c); | |
| 28261 | + ppc = pc = 0; | |
| 28262 | + continue; | |
| 28263 | + } | |
| 27967 | 28264 | if( c==cQuote ){ |
| 27968 | 28265 | if( pc==cQuote ){ |
| 27969 | 28266 | pc = 0; |
| 27970 | 28267 | continue; |
| 27971 | 28268 | } |
| @@ -27979,11 +28276,11 @@ | ||
| 27979 | 28276 | p->cTerm = c; |
| 27980 | 28277 | break; |
| 27981 | 28278 | } |
| 27982 | 28279 | if( pc==cQuote && c!='\r' ){ |
| 27983 | 28280 | cli_printf(stderr,"%s:%d: unescaped %c character\n", |
| 27984 | - p->zFile, p->nLine, cQuote); | |
| 28281 | + p->zFile, p->nLine, cQuote); | |
| 27985 | 28282 | } |
| 27986 | 28283 | if( c==EOF ){ |
| 27987 | 28284 | cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n", |
| 27988 | 28285 | p->zFile, startLine, cQuote); |
| 27989 | 28286 | p->cTerm = c; |
| @@ -27994,26 +28291,28 @@ | ||
| 27994 | 28291 | pc = c; |
| 27995 | 28292 | } |
| 27996 | 28293 | }else{ |
| 27997 | 28294 | /* If this is the first field being parsed and it begins with the |
| 27998 | 28295 | ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ |
| 28296 | + int cEsc = p->cUQEscape; | |
| 27999 | 28297 | if( (c&0xff)==0xef && p->bNotFirst==0 ){ |
| 28000 | 28298 | import_append_char(p, c); |
| 28001 | - c = fgetc(p->in); | |
| 28299 | + c = import_getc(p); | |
| 28002 | 28300 | if( (c&0xff)==0xbb ){ |
| 28003 | 28301 | import_append_char(p, c); |
| 28004 | - c = fgetc(p->in); | |
| 28302 | + c = import_getc(p); | |
| 28005 | 28303 | if( (c&0xff)==0xbf ){ |
| 28006 | 28304 | p->bNotFirst = 1; |
| 28007 | 28305 | p->n = 0; |
| 28008 | 28306 | return csv_read_one_field(p); |
| 28009 | 28307 | } |
| 28010 | 28308 | } |
| 28011 | 28309 | } |
| 28012 | 28310 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 28311 | + if( c==cEsc && cEsc!=0 ) c = import_getc(p); | |
| 28013 | 28312 | import_append_char(p, c); |
| 28014 | - c = fgetc(p->in); | |
| 28313 | + c = import_getc(p); | |
| 28015 | 28314 | } |
| 28016 | 28315 | if( c==rSep ){ |
| 28017 | 28316 | p->nLine++; |
| 28018 | 28317 | if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
| 28019 | 28318 | } |
| @@ -28027,12 +28326,12 @@ | ||
| 28027 | 28326 | /* Read a single field of ASCII delimited text. |
| 28028 | 28327 | ** |
| 28029 | 28328 | ** + Input comes from p->in. |
| 28030 | 28329 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 28031 | 28330 | ** from sqlite3_malloc64(). |
| 28032 | -** + Use p->cSep as the column separator. The default is "\x1F". | |
| 28033 | -** + Use p->rSep as the row separator. The default is "\x1E". | |
| 28331 | +** + Use p->cColSep as the column separator. The default is "\x1F". | |
| 28332 | +** + Use p->cRowSep as the row separator. The default is "\x1E". | |
| 28034 | 28333 | ** + Keep track of the row number in p->nLine. |
| 28035 | 28334 | ** + Store the character that terminates the field in p->cTerm. Store |
| 28036 | 28335 | ** EOF on end-of-file. |
| 28037 | 28336 | ** + Report syntax errors on stderr |
| 28038 | 28337 | */ |
| @@ -28039,18 +28338,18 @@ | ||
| 28039 | 28338 | static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ |
| 28040 | 28339 | int c; |
| 28041 | 28340 | int cSep = (u8)p->cColSep; |
| 28042 | 28341 | int rSep = (u8)p->cRowSep; |
| 28043 | 28342 | p->n = 0; |
| 28044 | - c = fgetc(p->in); | |
| 28343 | + c = import_getc(p); | |
| 28045 | 28344 | if( c==EOF || seenInterrupt ){ |
| 28046 | 28345 | p->cTerm = EOF; |
| 28047 | 28346 | return 0; |
| 28048 | 28347 | } |
| 28049 | 28348 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 28050 | 28349 | import_append_char(p, c); |
| 28051 | - c = fgetc(p->in); | |
| 28350 | + c = import_getc(p); | |
| 28052 | 28351 | } |
| 28053 | 28352 | if( c==rSep ){ |
| 28054 | 28353 | p->nLine++; |
| 28055 | 28354 | } |
| 28056 | 28355 | p->cTerm = c; |
| @@ -30151,11 +30450,11 @@ | ||
| 30151 | 30450 | ; |
| 30152 | 30451 | static const char * const zCollectVar = "\ |
| 30153 | 30452 | SELECT\ |
| 30154 | 30453 | '('||x'0a'\ |
| 30155 | 30454 | || group_concat(\ |
| 30156 | - cname||' TEXT',\ | |
| 30455 | + cname||' ANY',\ | |
| 30157 | 30456 | ','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\ |
| 30158 | 30457 | ||')' AS ColsSpec \ |
| 30159 | 30458 | FROM (\ |
| 30160 | 30459 | SELECT cpos, printf('\"%w\"',printf('%!.*s%s', nlen-chop,name,suff)) AS cname \ |
| 30161 | 30460 | FROM ColNames ORDER BY cpos\ |
| @@ -30351,19 +30650,33 @@ | ||
| 30351 | 30650 | ** USAGE: .import [OPTIONS] FILE TABLE |
| 30352 | 30651 | ** |
| 30353 | 30652 | ** Import CSV or similar text from FILE into TABLE. If TABLE does |
| 30354 | 30653 | ** not exist, it is created using the first row of FILE as the column |
| 30355 | 30654 | ** names. If FILE begins with "|" then it is a command that is run |
| 30356 | -** and the output from the command is used as the input data. | |
| 30655 | +** and the output from the command is used as the input data. If | |
| 30656 | +** FILE begins with "<<" followed by a label, then content is read from | |
| 30657 | +** the script until the first line that matches the label. | |
| 30658 | +** | |
| 30659 | +** The content of FILE is interpreted using RFC-4180 ("CSV") quoting | |
| 30660 | +** rules unless the current mode is "ascii" or "tabs" or unless one | |
| 30661 | +** the --ascii option is used. | |
| 30357 | 30662 | ** |
| 30358 | -** FILE is assumed to be in a CSV format, unless the current mode | |
| 30359 | -** is "ascii" or "tabs" or unless one of the options below specify | |
| 30360 | -** an alternative. | |
| 30663 | +** The column and row separators must be single ASCII characters. If | |
| 30664 | +** multiple characters or a Unicode character are specified for the | |
| 30665 | +** separators, then only the first byte of the separator is used. Except, | |
| 30666 | +** if the row separator is \n and the mode is not --ascii, then \r\n is | |
| 30667 | +** understood as a row separator too. | |
| 30361 | 30668 | ** |
| 30362 | 30669 | ** Options: |
| 30363 | -** --ascii Use \037 and \036 as column and row separators on input | |
| 30670 | +** --ascii Do not use RFC-4180 quoting. Use \037 and \036 | |
| 30671 | +** as column and row separators on input, unless other | |
| 30672 | +** delimiters are specified using --colsep and/or --rowsep | |
| 30673 | +** --colsep CHAR Use CHAR as the column separator. | |
| 30364 | 30674 | ** --csv Input is standard RFC-4180 CSV. |
| 30675 | +** --esc CHAR Use CHAR as an escape character in unquoted CSV inputs. | |
| 30676 | +** --qesc CHAR Use CHAR as an escape character in quoted CSV inputs. | |
| 30677 | +** --rowsep CHAR Use CHAR as the row separator. | |
| 30365 | 30678 | ** --schema S When creating TABLE, put it in schema S |
| 30366 | 30679 | ** --skip N Ignore the first N rows of input |
| 30367 | 30680 | ** -v Verbose mode |
| 30368 | 30681 | */ |
| 30369 | 30682 | static int dotCmdImport(ShellState *p){ |
| @@ -30375,17 +30688,16 @@ | ||
| 30375 | 30688 | sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 30376 | 30689 | int nCol; /* Number of columns in the table */ |
| 30377 | 30690 | i64 nByte; /* Number of bytes in an SQL string */ |
| 30378 | 30691 | int i, j; /* Loop counters */ |
| 30379 | 30692 | int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 30380 | - int nSep; /* Number of bytes in spec.zColumnSep */ | |
| 30381 | 30693 | char *zSql = 0; /* An SQL statement */ |
| 30382 | 30694 | ImportCtx sCtx; /* Reader context */ |
| 30383 | 30695 | char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ |
| 30384 | 30696 | int eVerbose = 0; /* Larger for more console output */ |
| 30385 | 30697 | i64 nSkip = 0; /* Initial lines to skip */ |
| 30386 | - int useOutputMode = 1; /* Use output mode to determine separators */ | |
| 30698 | + i64 iLineOffset = 0; /* Offset to the first line of input */ | |
| 30387 | 30699 | char *zCreate = 0; /* CREATE TABLE statement text */ |
| 30388 | 30700 | int rc; /* Result code */ |
| 30389 | 30701 | |
| 30390 | 30702 | failIfSafeMode(p, "cannot run .import in safe mode"); |
| 30391 | 30703 | memset(&sCtx, 0, sizeof(sCtx)); |
| @@ -30411,70 +30723,71 @@ | ||
| 30411 | 30723 | }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){ |
| 30412 | 30724 | zSchema = azArg[++i]; |
| 30413 | 30725 | }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){ |
| 30414 | 30726 | nSkip = integerValue(azArg[++i]); |
| 30415 | 30727 | }else if( cli_strcmp(z,"-ascii")==0 ){ |
| 30416 | - sCtx.cColSep = SEP_Unit[0]; | |
| 30417 | - sCtx.cRowSep = SEP_Record[0]; | |
| 30728 | + if( sCtx.cColSep==0 ) sCtx.cColSep = SEP_Unit[0]; | |
| 30729 | + if( sCtx.cRowSep==0 ) sCtx.cRowSep = SEP_Record[0]; | |
| 30418 | 30730 | xRead = ascii_read_one_field; |
| 30419 | - useOutputMode = 0; | |
| 30420 | 30731 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 30421 | - sCtx.cColSep = ','; | |
| 30422 | - sCtx.cRowSep = '\n'; | |
| 30732 | + if( sCtx.cColSep==0 ) sCtx.cColSep = ','; | |
| 30733 | + if( sCtx.cRowSep==0 ) sCtx.cRowSep = '\n'; | |
| 30423 | 30734 | xRead = csv_read_one_field; |
| 30424 | - useOutputMode = 0; | |
| 30735 | + }else if( cli_strcmp(z,"-esc")==0 ){ | |
| 30736 | + sCtx.cUQEscape = azArg[++i][0]; | |
| 30737 | + }else if( cli_strcmp(z,"-qesc")==0 ){ | |
| 30738 | + sCtx.cQEscape = azArg[++i][0]; | |
| 30739 | + }else if( cli_strcmp(z,"-colsep")==0 ){ | |
| 30740 | + if( i==nArg-1 ){ | |
| 30741 | + dotCmdError(p, i, "missing argument", 0); | |
| 30742 | + return 1; | |
| 30743 | + } | |
| 30744 | + i++; | |
| 30745 | + sCtx.cColSep = azArg[i][0]; | |
| 30746 | + }else if( cli_strcmp(z,"-rowsep")==0 ){ | |
| 30747 | + if( i==nArg-1 ){ | |
| 30748 | + dotCmdError(p, i, "missing argument", 0); | |
| 30749 | + return 1; | |
| 30750 | + } | |
| 30751 | + i++; | |
| 30752 | + sCtx.cRowSep = azArg[i][0]; | |
| 30425 | 30753 | }else{ |
| 30426 | 30754 | dotCmdError(p, i, "unknown option", 0); |
| 30427 | 30755 | return 1; |
| 30428 | 30756 | } |
| 30429 | 30757 | } |
| 30430 | 30758 | if( zTable==0 ){ |
| 30431 | - cli_printf(p->out, "ERROR: missing %s argument\n", | |
| 30759 | + dotCmdError(p, nArg, 0, "Missing %s argument\n", | |
| 30432 | 30760 | zFile==0 ? "FILE" : "TABLE"); |
| 30433 | 30761 | return 1; |
| 30434 | 30762 | } |
| 30435 | 30763 | seenInterrupt = 0; |
| 30436 | 30764 | open_db(p, 0); |
| 30437 | - if( useOutputMode ){ | |
| 30438 | - /* If neither the --csv or --ascii options are specified, then set | |
| 30439 | - ** the column and row separator characters from the output mode. */ | |
| 30440 | - if( p->mode.spec.zColumnSep==0 ){ | |
| 30441 | - modeSetStr(&p->mode.spec.zColumnSep, ","); | |
| 30442 | - nSep = 1; | |
| 30443 | - }else if( (nSep = strlen30(p->mode.spec.zColumnSep))==0 ){ | |
| 30444 | - eputz("Error: non-null column separator required for import\n"); | |
| 30445 | - return 1; | |
| 30446 | - } | |
| 30447 | - if( nSep>1 ){ | |
| 30448 | - eputz("Error: multi-character column separators not allowed" | |
| 30449 | - " for import\n"); | |
| 30450 | - return 1; | |
| 30451 | - } | |
| 30452 | - if( p->mode.spec.zRowSep==0 ){ | |
| 30453 | - modeSetStr(&p->mode.spec.zRowSep, "\n"); | |
| 30454 | - nSep = 1; | |
| 30455 | - }else if( (nSep = strlen30(p->mode.spec.zRowSep))==0 ){ | |
| 30456 | - eputz("Error: non-null row separator required for import\n"); | |
| 30457 | - return 1; | |
| 30458 | - } | |
| 30459 | - if( nSep==2 && p->mode.eMode==MODE_Csv | |
| 30460 | - && cli_strcmp(p->mode.spec.zRowSep,SEP_CrLf)==0 | |
| 30461 | - ){ | |
| 30462 | - /* When importing CSV (only), if the row separator is set to the | |
| 30463 | - ** default output row separator, change it to the default input | |
| 30464 | - ** row separator. This avoids having to maintain different input | |
| 30465 | - ** and output row separators. */ | |
| 30466 | - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); | |
| 30467 | - nSep = strlen30(p->mode.spec.zRowSep); | |
| 30468 | - } | |
| 30469 | - if( nSep>1 ){ | |
| 30470 | - eputz("Error: multi-character row separators not allowed" | |
| 30471 | - " for import\n"); | |
| 30472 | - return 1; | |
| 30473 | - } | |
| 30474 | - sCtx.cColSep = (u8)p->mode.spec.zColumnSep[0]; | |
| 30475 | - sCtx.cRowSep = (u8)p->mode.spec.zRowSep[0]; | |
| 30765 | + if( sCtx.cColSep==0 ){ | |
| 30766 | + if( p->mode.spec.zColumnSep && p->mode.spec.zColumnSep[0]!=0 ){ | |
| 30767 | + sCtx.cColSep = p->mode.spec.zColumnSep[0]; | |
| 30768 | + }else{ | |
| 30769 | + sCtx.cColSep = ','; | |
| 30770 | + } | |
| 30771 | + } | |
| 30772 | + if( (sCtx.cColSep & 0x80)!=0 ){ | |
| 30773 | + eputz("Error: .import column separator must be ASCII\n"); | |
| 30774 | + return 1; | |
| 30775 | + } | |
| 30776 | + if( sCtx.cRowSep==0 ){ | |
| 30777 | + if( p->mode.spec.zRowSep && p->mode.spec.zRowSep[0]!=0 ){ | |
| 30778 | + sCtx.cRowSep = p->mode.spec.zRowSep[0]; | |
| 30779 | + }else{ | |
| 30780 | + sCtx.cRowSep = '\n'; | |
| 30781 | + } | |
| 30782 | + } | |
| 30783 | + if( sCtx.cRowSep=='\r' && xRead!=ascii_read_one_field ){ | |
| 30784 | + sCtx.cRowSep = '\n'; | |
| 30785 | + } | |
| 30786 | + if( (sCtx.cRowSep & 0x80)!=0 ){ | |
| 30787 | + eputz("Error: .import row separator must be ASCII\n"); | |
| 30788 | + return 1; | |
| 30476 | 30789 | } |
| 30477 | 30790 | sCtx.zFile = zFile; |
| 30478 | 30791 | sCtx.nLine = 1; |
| 30479 | 30792 | if( sCtx.zFile[0]=='|' ){ |
| 30480 | 30793 | #ifdef SQLITE_OMIT_POPEN |
| @@ -30483,19 +30796,58 @@ | ||
| 30483 | 30796 | #else |
| 30484 | 30797 | sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
| 30485 | 30798 | sCtx.zFile = "<pipe>"; |
| 30486 | 30799 | sCtx.xCloser = pclose; |
| 30487 | 30800 | #endif |
| 30801 | + }else if( sCtx.zFile[0]=='<' && sCtx.zFile[1]=='<' && sCtx.zFile[2]!=0 ){ | |
| 30802 | + /* Input text comes from subsequent lines of script until the zFile | |
| 30803 | + ** delimiter */ | |
| 30804 | + int nEndMark = strlen30(zFile)-2; | |
| 30805 | + char *zEndMark = &zFile[2]; | |
| 30806 | + sqlite3_str *pContent = sqlite3_str_new(p->db); | |
| 30807 | + int ckEnd = 1; | |
| 30808 | + i64 iStart = p->lineno; | |
| 30809 | + char zLine[2000]; | |
| 30810 | + sCtx.zFile = p->zInFile; | |
| 30811 | + sCtx.nLine = p->lineno+1; | |
| 30812 | + iLineOffset = p->lineno; | |
| 30813 | + while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ | |
| 30814 | + if( ckEnd && cli_strncmp(zLine,zEndMark,nEndMark)==0 ){ | |
| 30815 | + ckEnd = 2; | |
| 30816 | + if( strchr(zLine,'\n') ) p->lineno++; | |
| 30817 | + break; | |
| 30818 | + } | |
| 30819 | + if( strchr(zLine,'\n') ){ | |
| 30820 | + p->lineno++; | |
| 30821 | + ckEnd = 1; | |
| 30822 | + }else{ | |
| 30823 | + ckEnd = 0; | |
| 30824 | + } | |
| 30825 | + sqlite3_str_appendall(pContent, zLine); | |
| 30826 | + } | |
| 30827 | + sCtx.zIn = sqlite3_str_finish(pContent); | |
| 30828 | + if( sCtx.zIn==0 ){ | |
| 30829 | + sCtx.zIn = sqlite3_mprintf(""); | |
| 30830 | + } | |
| 30831 | + if( ckEnd<2 ){ | |
| 30832 | + i64 savedLn = p->lineno; | |
| 30833 | + p->lineno = iStart; | |
| 30834 | + dotCmdError(p, 0, 0,"Content terminator \"%s\" not found.",zEndMark); | |
| 30835 | + p->lineno = savedLn; | |
| 30836 | + import_cleanup(&sCtx); | |
| 30837 | + return 1; | |
| 30838 | + } | |
| 30488 | 30839 | }else{ |
| 30489 | 30840 | sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
| 30490 | 30841 | sCtx.xCloser = fclose; |
| 30491 | 30842 | } |
| 30492 | - if( sCtx.in==0 ){ | |
| 30493 | - cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); | |
| 30843 | + if( sCtx.in==0 && sCtx.zIn==0 ){ | |
| 30844 | + dotCmdError(p, 0, 0, "cannot open \"%s\"", zFile); | |
| 30845 | + import_cleanup(&sCtx); | |
| 30494 | 30846 | return 1; |
| 30495 | 30847 | } |
| 30496 | - if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ | |
| 30848 | + if( eVerbose>=1 ){ | |
| 30497 | 30849 | char zSep[2]; |
| 30498 | 30850 | zSep[1] = 0; |
| 30499 | 30851 | zSep[0] = sCtx.cColSep; |
| 30500 | 30852 | cli_puts("Column separator ", p->out); |
| 30501 | 30853 | output_c_string(p->out, zSep); |
| @@ -30648,10 +31000,14 @@ | ||
| 30648 | 31000 | if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 30649 | 31001 | z = ""; |
| 30650 | 31002 | } |
| 30651 | 31003 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 30652 | 31004 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 31005 | + if( i==0 && (strcmp(z,"\n")==0 || strcmp(z,"\r\n")==0) ){ | |
| 31006 | + /* Ignore trailing \n or \r\n when some other row separator */ | |
| 31007 | + break; | |
| 31008 | + } | |
| 30653 | 31009 | cli_printf(stderr,"%s:%d: expected %d columns but found %d" |
| 30654 | 31010 | " - filling the rest with NULL\n", |
| 30655 | 31011 | sCtx.zFile, startLine, nCol, i+1); |
| 30656 | 31012 | i += 2; |
| 30657 | 31013 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| @@ -30671,10 +31027,11 @@ | ||
| 30671 | 31027 | rc = sqlite3_reset(pStmt); |
| 30672 | 31028 | if( rc!=SQLITE_OK ){ |
| 30673 | 31029 | cli_printf(stderr,"%s:%d: INSERT failed: %s\n", |
| 30674 | 31030 | sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 30675 | 31031 | sCtx.nErr++; |
| 31032 | + if( bail_on_error ) break; | |
| 30676 | 31033 | }else{ |
| 30677 | 31034 | sCtx.nRow++; |
| 30678 | 31035 | } |
| 30679 | 31036 | } |
| 30680 | 31037 | }while( sCtx.cTerm!=EOF ); |
| @@ -30683,13 +31040,13 @@ | ||
| 30683 | 31040 | sqlite3_finalize(pStmt); |
| 30684 | 31041 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 30685 | 31042 | if( eVerbose>0 ){ |
| 30686 | 31043 | cli_printf(p->out, |
| 30687 | 31044 | "Added %d rows with %d errors using %d lines of input\n", |
| 30688 | - sCtx.nRow, sCtx.nErr, sCtx.nLine-1); | |
| 31045 | + sCtx.nRow, sCtx.nErr, sCtx.nLine-1-iLineOffset); | |
| 30689 | 31046 | } |
| 30690 | - return 0; | |
| 31047 | + return sCtx.nErr ? 1 : 0; | |
| 30691 | 31048 | } |
| 30692 | 31049 | |
| 30693 | 31050 | |
| 30694 | 31051 | /* |
| 30695 | 31052 | ** This function computes what to show the user about the configured |
| @@ -31100,10 +31457,11 @@ | ||
| 31100 | 31457 | if( k<0 ){ |
| 31101 | 31458 | dotCmdError(p, i, "bad --titles value","%z", zErr); |
| 31102 | 31459 | return 1; |
| 31103 | 31460 | } |
| 31104 | 31461 | p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; |
| 31462 | + p->mode.mFlags &= ~MFLG_HDR; | |
| 31105 | 31463 | p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; |
| 31106 | 31464 | chng = 1; |
| 31107 | 31465 | }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ |
| 31108 | 31466 | int nWidth = 0; |
| 31109 | 31467 | short int *aWidth; |
| @@ -31328,23 +31686,17 @@ | ||
| 31328 | 31686 | ** --bom Prepend a byte-order mark to the output |
| 31329 | 31687 | ** -e Accumulate output in a temporary text file then |
| 31330 | 31688 | ** launch a text editor when the redirection ends. |
| 31331 | 31689 | ** --error-prefix X Use X as the left-margin prefix for error messages. |
| 31332 | 31690 | ** Set to an empty string to restore the default. |
| 31333 | -** --glob GLOB Raise an error if the memory buffer does not match | |
| 31334 | -** the GLOB pattern. | |
| 31335 | -** --keep Continue using the same "memory" buffer. Do not | |
| 31336 | -** reset it or delete it. Useful in combination with | |
| 31337 | -** --glob, --not-glob, and/or --verify. | |
| 31338 | -** ---notglob GLOB Raise an error if the memory buffer does not match | |
| 31339 | -** the GLOB pattern. | |
| 31691 | +** --keep Keep redirecting output to its current destination. | |
| 31692 | +** Use this option in combination with --show or | |
| 31693 | +** with --error-prefix when you do not want to stop | |
| 31694 | +** a current redirection. | |
| 31340 | 31695 | ** --plain Use plain text rather than HTML tables with -w |
| 31341 | -** --show Write the memory buffer to the screen, for debugging. | |
| 31342 | -** --verify ENDMARK Read subsequent lines of text until the first line | |
| 31343 | -** that matches ENDMARK. Discard the ENDMARK. Compare | |
| 31344 | -** the text against the accumulated output in memory and | |
| 31345 | -** raise an error if there are any differences. | |
| 31696 | +** --show Show output text captured by .testcase or by | |
| 31697 | +** redirecting to "memory". | |
| 31346 | 31698 | ** -w Show the output in a web browser. Output is |
| 31347 | 31699 | ** written into a temporary HTML file until the |
| 31348 | 31700 | ** redirect ends, then the web browser is launched. |
| 31349 | 31701 | ** Query results are shown as HTML tables, unless |
| 31350 | 31702 | ** the --plain is used too. |
| @@ -31382,13 +31734,11 @@ | ||
| 31382 | 31734 | char *zFile = 0; /* The FILE argument */ |
| 31383 | 31735 | int i; /* Loop counter */ |
| 31384 | 31736 | int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ |
| 31385 | 31737 | int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
| 31386 | 31738 | int bPlain = 0; /* --plain option */ |
| 31387 | - int bKeep = 0; /* --keep option */ | |
| 31388 | - char *zCheck = 0; /* Argument to --glob, --notglob, --verify */ | |
| 31389 | - int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --verify */ | |
| 31739 | + int bKeep = 0; /* Keep redirecting */ | |
| 31390 | 31740 | static const char *zBomUtf8 = "\357\273\277"; |
| 31391 | 31741 | const char *zBom = 0; |
| 31392 | 31742 | char c = azArg[0][0]; |
| 31393 | 31743 | int n = strlen30(azArg[0]); |
| 31394 | 31744 | |
| @@ -31410,36 +31760,21 @@ | ||
| 31410 | 31760 | zBom = zBomUtf8; |
| 31411 | 31761 | }else if( cli_strcmp(z,"-plain")==0 ){ |
| 31412 | 31762 | bPlain = 1; |
| 31413 | 31763 | }else if( c=='o' && z[0]=='1' && z[1]!=0 && z[2]==0 |
| 31414 | 31764 | && (z[1]=='x' || z[1]=='e' || z[1]=='w') ){ |
| 31415 | - if( bKeep || eMode || eCheck ){ | |
| 31765 | + if( bKeep || eMode ){ | |
| 31416 | 31766 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31417 | 31767 | goto dotCmdOutput_error; |
| 31418 | 31768 | } |
| 31419 | 31769 | eMode = z[1]; |
| 31420 | - }else if( cli_strcmp(z,"-keep")==0 ){ | |
| 31421 | - bKeep = 1; | |
| 31422 | 31770 | }else if( cli_strcmp(z,"-show")==0 ){ |
| 31423 | 31771 | if( cli_output_capture ){ |
| 31424 | 31772 | sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
| 31425 | 31773 | } |
| 31774 | + }else if( cli_strcmp(z,"-keep")==0 ){ | |
| 31426 | 31775 | bKeep = 1; |
| 31427 | - }else if( cli_strcmp(z,"-glob")==0 | |
| 31428 | - || cli_strcmp(z,"-notglob")==0 | |
| 31429 | - || cli_strcmp(z,"-verify")==0 | |
| 31430 | - ){ | |
| 31431 | - if( eCheck || eMode ){ | |
| 31432 | - dotCmdError(p, i, "incompatible with prior options",0); | |
| 31433 | - goto dotCmdOutput_error; | |
| 31434 | - } | |
| 31435 | - if( i+1>=nArg ){ | |
| 31436 | - dotCmdError(p, i, "missing argument", 0); | |
| 31437 | - goto dotCmdOutput_error; | |
| 31438 | - } | |
| 31439 | - zCheck = azArg[++i]; | |
| 31440 | - eCheck = z[1]=='g' ? 1 : z[1]=='n' ? 2 : 3; | |
| 31441 | 31776 | }else if( optionMatch(z,"error-prefix") ){ |
| 31442 | 31777 | if( i+1>=nArg ){ |
| 31443 | 31778 | dotCmdError(p, i, "missing argument", 0); |
| 31444 | 31779 | return 1; |
| 31445 | 31780 | } |
| @@ -31450,11 +31785,11 @@ | ||
| 31450 | 31785 | dotCmdError(p, i, "unknown option", 0); |
| 31451 | 31786 | sqlite3_free(zFile); |
| 31452 | 31787 | return 1; |
| 31453 | 31788 | } |
| 31454 | 31789 | }else if( zFile==0 && eMode==0 ){ |
| 31455 | - if( bKeep || eCheck ){ | |
| 31790 | + if( bKeep ){ | |
| 31456 | 31791 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31457 | 31792 | goto dotCmdOutput_error; |
| 31458 | 31793 | } |
| 31459 | 31794 | if( cli_strcmp(z, "memory")==0 && bOnce ){ |
| 31460 | 31795 | dotCmdError(p, 0, "cannot redirect to \"memory\"", 0); |
| @@ -31486,53 +31821,10 @@ | ||
| 31486 | 31821 | if( bOnce ){ |
| 31487 | 31822 | p->nPopOutput = 2; |
| 31488 | 31823 | }else{ |
| 31489 | 31824 | p->nPopOutput = 0; |
| 31490 | 31825 | } |
| 31491 | - if( eCheck ){ | |
| 31492 | - char *zTest; | |
| 31493 | - if( cli_output_capture ){ | |
| 31494 | - zTest = sqlite3_str_value(cli_output_capture); | |
| 31495 | - }else{ | |
| 31496 | - zTest = ""; | |
| 31497 | - } | |
| 31498 | - p->nTestRun++; | |
| 31499 | - if( eCheck==3 ){ | |
| 31500 | - int nCheck = strlen30(zCheck); | |
| 31501 | - sqlite3_str *pPattern = sqlite3_str_new(p->db); | |
| 31502 | - char *zPattern; | |
| 31503 | - sqlite3_int64 iStart = p->lineno; | |
| 31504 | - char zLine[2000]; | |
| 31505 | - while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ | |
| 31506 | - if( strchr(zLine,'\n') ) p->lineno++; | |
| 31507 | - if( cli_strncmp(zCheck,zLine,nCheck)==0 ) break; | |
| 31508 | - sqlite3_str_appendall(pPattern, zLine); | |
| 31509 | - } | |
| 31510 | - zPattern = sqlite3_str_finish(pPattern); | |
| 31511 | - if( cli_strcmp(zPattern,zTest)!=0 ){ | |
| 31512 | - sqlite3_fprintf(stderr, | |
| 31513 | - "%s:%lld: --verify does matches prior output\n", | |
| 31514 | - p->zInFile, iStart); | |
| 31515 | - p->nTestErr++; | |
| 31516 | - } | |
| 31517 | - sqlite3_free(zPattern); | |
| 31518 | - }else{ | |
| 31519 | - char *zGlob = sqlite3_mprintf("*%s*", zCheck); | |
| 31520 | - if( eCheck==1 && sqlite3_strglob(zGlob, zTest)!=0 ){ | |
| 31521 | - sqlite3_fprintf(stderr, | |
| 31522 | - "%s:%lld: --glob \"%s\" does not match prior output\n", | |
| 31523 | - p->zInFile, p->lineno, zCheck); | |
| 31524 | - p->nTestErr++; | |
| 31525 | - }else if( eCheck==2 && sqlite3_strglob(zGlob, zTest)==0 ){ | |
| 31526 | - sqlite3_fprintf(stderr, | |
| 31527 | - "%s:%lld: --notglob \"%s\" matches prior output\n", | |
| 31528 | - p->zInFile, p->lineno, zCheck); | |
| 31529 | - p->nTestErr++; | |
| 31530 | - } | |
| 31531 | - sqlite3_free(zGlob); | |
| 31532 | - } | |
| 31533 | - } | |
| 31534 | 31826 | if( !bKeep ) output_reset(p); |
| 31535 | 31827 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 31536 | 31828 | if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
| 31537 | 31829 | p->doXdgOpen = 1; |
| 31538 | 31830 | modePush(p); |
| @@ -31609,10 +31901,199 @@ | ||
| 31609 | 31901 | |
| 31610 | 31902 | dotCmdOutput_error: |
| 31611 | 31903 | sqlite3_free(zFile); |
| 31612 | 31904 | return 1; |
| 31613 | 31905 | } |
| 31906 | + | |
| 31907 | +/* | |
| 31908 | +** DOT-COMMAND: .check | |
| 31909 | +** USAGE: .check [OPTIONS] PATTERN | |
| 31910 | +** | |
| 31911 | +** Verify results of commands since the most recent .testcase command. | |
| 31912 | +** Restore output to the console, unless --keep is used. | |
| 31913 | +** | |
| 31914 | +** If PATTERN starts with "<<ENDMARK" then the actual pattern is taken from | |
| 31915 | +** subsequent lines of text up to the first line that begins with ENDMARK. | |
| 31916 | +** All pattern lines and the ENDMARK are discarded. | |
| 31917 | +** | |
| 31918 | +** Options: | |
| 31919 | +** --exact Do an exact comparison including leading and | |
| 31920 | +** trailing whitespace. | |
| 31921 | +** --glob Treat PATTERN as a GLOB | |
| 31922 | +** --keep Do not reset the testcase. More .check commands | |
| 31923 | +** will follow. | |
| 31924 | +** --notglob Output should not match PATTERN | |
| 31925 | +** --show Write testcase output to the screen, for debugging. | |
| 31926 | +*/ | |
| 31927 | +static int dotCmdCheck(ShellState *p){ | |
| 31928 | + int nArg = p->dot.nArg; /* Number of arguments */ | |
| 31929 | + char **azArg = p->dot.azArg; /* Text of the arguments */ | |
| 31930 | + int i; /* Loop counter */ | |
| 31931 | + int k; /* Result of pickStr() */ | |
| 31932 | + char *zTest; /* Textcase result */ | |
| 31933 | + int bKeep = 0; /* --keep option */ | |
| 31934 | + char *zCheck = 0; /* PATTERN argument */ | |
| 31935 | + char *zPattern = 0; /* Actual test pattern */ | |
| 31936 | + int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --exact */ | |
| 31937 | + int isOk; /* True if results are OK */ | |
| 31938 | + sqlite3_int64 iStart = p->lineno; /* Line number of .check statement */ | |
| 31939 | + | |
| 31940 | + if( p->zTestcase[0]==0 ){ | |
| 31941 | + dotCmdError(p, 0, "no .testcase is active", 0); | |
| 31942 | + return 1; | |
| 31943 | + } | |
| 31944 | + for(i=1; i<nArg; i++){ | |
| 31945 | + char *z = azArg[i]; | |
| 31946 | + if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; | |
| 31947 | + if( cli_strcmp(z,"-keep")==0 ){ | |
| 31948 | + bKeep = 1; | |
| 31949 | + }else if( cli_strcmp(z,"-show")==0 ){ | |
| 31950 | + if( cli_output_capture ){ | |
| 31951 | + sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); | |
| 31952 | + } | |
| 31953 | + bKeep = 1; | |
| 31954 | + }else if( z[0]=='-' | |
| 31955 | + && (k = pickStr(&z[1],0,"glob","notglob","exact",""))>=0 | |
| 31956 | + ){ | |
| 31957 | + if( eCheck && eCheck!=k+1 ){ | |
| 31958 | + dotCmdError(p, i, "incompatible with prior options",0); | |
| 31959 | + return 1; | |
| 31960 | + } | |
| 31961 | + eCheck = k+1; | |
| 31962 | + }else if( zCheck ){ | |
| 31963 | + dotCmdError(p, i, "unknown option", 0); | |
| 31964 | + return 1; | |
| 31965 | + }else{ | |
| 31966 | + zCheck = azArg[i]; | |
| 31967 | + } | |
| 31968 | + } | |
| 31969 | + if( zCheck==0 ){ | |
| 31970 | + dotCmdError(p, 0, "no PATTERN specified", 0); | |
| 31971 | + return 1; | |
| 31972 | + } | |
| 31973 | + if( cli_output_capture && sqlite3_str_length(cli_output_capture) ){ | |
| 31974 | + zTest = sqlite3_str_value(cli_output_capture); | |
| 31975 | + shell_check_oom(zTest); | |
| 31976 | + }else{ | |
| 31977 | + zTest = ""; | |
| 31978 | + } | |
| 31979 | + p->nTestRun++; | |
| 31980 | + if( zCheck[0]=='<' && zCheck[1]=='<' && zCheck[2]!=0 ){ | |
| 31981 | + int nCheck = strlen30(zCheck); | |
| 31982 | + sqlite3_str *pPattern = sqlite3_str_new(p->db); | |
| 31983 | + char zLine[2000]; | |
| 31984 | + while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ | |
| 31985 | + if( strchr(zLine,'\n') ) p->lineno++; | |
| 31986 | + if( cli_strncmp(&zCheck[2],zLine,nCheck-2)==0 ) break; | |
| 31987 | + sqlite3_str_appendall(pPattern, zLine); | |
| 31988 | + } | |
| 31989 | + zPattern = sqlite3_str_finish(pPattern); | |
| 31990 | + if( zPattern==0 ){ | |
| 31991 | + zPattern = sqlite3_mprintf(""); | |
| 31992 | + } | |
| 31993 | + }else{ | |
| 31994 | + zPattern = zCheck; | |
| 31995 | + } | |
| 31996 | + shell_check_oom(zPattern); | |
| 31997 | + switch( eCheck ){ | |
| 31998 | + case 1: { | |
| 31999 | + char *zGlob = sqlite3_mprintf("*%s*", zPattern); | |
| 32000 | + isOk = testcase_glob(zGlob, zTest)!=0; | |
| 32001 | + sqlite3_free(zGlob); | |
| 32002 | + break; | |
| 32003 | + } | |
| 32004 | + case 2: { | |
| 32005 | + char *zGlob = sqlite3_mprintf("*%s*", zPattern); | |
| 32006 | + isOk = testcase_glob(zGlob, zTest)==0; | |
| 32007 | + sqlite3_free(zGlob); | |
| 32008 | + break; | |
| 32009 | + } | |
| 32010 | + case 3: { | |
| 32011 | + isOk = cli_strcmp(zTest,zPattern)==0; | |
| 32012 | + break; | |
| 32013 | + } | |
| 32014 | + default: { | |
| 32015 | + /* Skip leading and trailing \n and \r on both pattern and test output */ | |
| 32016 | + const char *z1 = zPattern; | |
| 32017 | + const char *z2 = zTest; | |
| 32018 | + size_t n1, n2; | |
| 32019 | + while( z1[0]=='\n' || z1[0]=='\r' ) z1++; | |
| 32020 | + n1 = strlen(z1); | |
| 32021 | + while( n1>0 && (z1[n1-1]=='\n' || z1[n1-1]=='\r') ) n1--; | |
| 32022 | + while( z2[0]=='\n' || z2[0]=='\r' ) z2++; | |
| 32023 | + n2 = strlen(z2); | |
| 32024 | + while( n2>0 && (z2[n2-1]=='\n' || z2[n2-1]=='\r') ) n2--; | |
| 32025 | + isOk = n1==n2 && memcmp(z1,z2,n1)==0; | |
| 32026 | + break; | |
| 32027 | + } | |
| 32028 | + } | |
| 32029 | + if( !isOk ){ | |
| 32030 | + sqlite3_fprintf(stderr, | |
| 32031 | + "%s:%lld: .check failed for testcase %s\n", | |
| 32032 | + p->zInFile, iStart, p->zTestcase); | |
| 32033 | + p->nTestErr++; | |
| 32034 | + sqlite3_fprintf(stderr, "Expected: [%s]\n", zPattern); | |
| 32035 | + sqlite3_fprintf(stderr, "Got: [%s]\n", zTest); | |
| 32036 | + } | |
| 32037 | + if( zPattern!=zCheck ){ | |
| 32038 | + sqlite3_free(zPattern); | |
| 32039 | + } | |
| 32040 | + if( !bKeep ){ | |
| 32041 | + output_reset(p); | |
| 32042 | + p->zTestcase[0] = 0; | |
| 32043 | + } | |
| 32044 | + return 0; | |
| 32045 | +} | |
| 32046 | + | |
| 32047 | +/* | |
| 32048 | +** DOT-COMMAND: .testcase | |
| 32049 | +** USAGE: .testcase [OPTIONS] NAME | |
| 32050 | +** | |
| 32051 | +** Start a new test case identified by NAME. All output | |
| 32052 | +** through the next ".check" command is captured for comparison. See the | |
| 32053 | +** ".check" commandn for additional informatioon. | |
| 32054 | +** | |
| 32055 | +** Options: | |
| 32056 | +** --error-prefix TEXT Change error message prefix text to TEXT | |
| 32057 | +*/ | |
| 32058 | +static int dotCmdTestcase(ShellState *p){ | |
| 32059 | + int nArg = p->dot.nArg; /* Number of arguments */ | |
| 32060 | + char **azArg = p->dot.azArg; /* Text of the arguments */ | |
| 32061 | + int i; /* Loop counter */ | |
| 32062 | + const char *zName = 0; /* Testcase name */ | |
| 32063 | + | |
| 32064 | + for(i=1; i<nArg; i++){ | |
| 32065 | + char *z = azArg[i]; | |
| 32066 | + if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; | |
| 32067 | + if( optionMatch(z,"error-prefix") ){ | |
| 32068 | + if( i+1>=nArg ){ | |
| 32069 | + dotCmdError(p, i, "missing argument", 0); | |
| 32070 | + return 1; | |
| 32071 | + } | |
| 32072 | + free(p->zErrPrefix); | |
| 32073 | + i++; | |
| 32074 | + p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]); | |
| 32075 | + }else if( zName ){ | |
| 32076 | + dotCmdError(p, i, "unknown option", 0); | |
| 32077 | + return 1; | |
| 32078 | + }else{ | |
| 32079 | + zName = azArg[i]; | |
| 32080 | + } | |
| 32081 | + } | |
| 32082 | + output_reset(p); | |
| 32083 | + if( cli_output_capture ){ | |
| 32084 | + sqlite3_str_free(cli_output_capture); | |
| 32085 | + } | |
| 32086 | + cli_output_capture = sqlite3_str_new(0); | |
| 32087 | + if( zName ){ | |
| 32088 | + sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", zName); | |
| 32089 | + }else{ | |
| 32090 | + sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s:%lld", | |
| 32091 | + p->zInFile, p->lineno); | |
| 32092 | + } | |
| 32093 | + return 0; | |
| 32094 | +} | |
| 31614 | 32095 | |
| 31615 | 32096 | /* |
| 31616 | 32097 | ** Enlarge the space allocated in p->dot so that it can hold more |
| 31617 | 32098 | ** than nArg parsed command-line arguments. |
| 31618 | 32099 | */ |
| @@ -31641,11 +32122,11 @@ | ||
| 31641 | 32122 | free(p->dot.zCopy); |
| 31642 | 32123 | z = p->dot.zCopy = strdup(zLine); |
| 31643 | 32124 | shell_check_oom(z); |
| 31644 | 32125 | szLine = strlen(z); |
| 31645 | 32126 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 31646 | - if( szLine>0 && z[szLine-1]==';' && p->iCompat>=20251115 ){ | |
| 32127 | + if( szLine>0 && z[szLine-1]==';' ){ | |
| 31647 | 32128 | szLine--; |
| 31648 | 32129 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 31649 | 32130 | } |
| 31650 | 32131 | z[szLine] = 0; |
| 31651 | 32132 | parseDotRealloc(p, 2); |
| @@ -31861,35 +32342,17 @@ | ||
| 31861 | 32342 | eputz("Usage: .changes on|off\n"); |
| 31862 | 32343 | rc = 1; |
| 31863 | 32344 | } |
| 31864 | 32345 | }else |
| 31865 | 32346 | |
| 31866 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 31867 | 32347 | /* Cancel output redirection, if it is currently set (by .testcase) |
| 31868 | 32348 | ** Then read the content of the testcase-out.txt file and compare against |
| 31869 | 32349 | ** azArg[1]. If there are differences, report an error and exit. |
| 31870 | 32350 | */ |
| 31871 | 32351 | if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){ |
| 31872 | - char *zRes = 0; | |
| 31873 | - output_reset(p); | |
| 31874 | - if( nArg!=2 ){ | |
| 31875 | - eputz("Usage: .check GLOB-PATTERN\n"); | |
| 31876 | - rc = 2; | |
| 31877 | - }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ | |
| 31878 | - rc = 2; | |
| 31879 | - }else if( testcase_glob(azArg[1],zRes)==0 ){ | |
| 31880 | - cli_printf(stderr, | |
| 31881 | - "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", | |
| 31882 | - p->zTestcase, azArg[1], zRes); | |
| 31883 | - rc = 1; | |
| 31884 | - }else{ | |
| 31885 | - cli_printf(p->out, "testcase-%s ok\n", p->zTestcase); | |
| 31886 | - p->nCheck++; | |
| 31887 | - } | |
| 31888 | - sqlite3_free(zRes); | |
| 32352 | + rc = dotCmdCheck(p); | |
| 31889 | 32353 | }else |
| 31890 | -#endif /* !defined(SQLITE_SHELL_FIDDLE) */ | |
| 31891 | 32354 | |
| 31892 | 32355 | #ifndef SQLITE_SHELL_FIDDLE |
| 31893 | 32356 | if( c=='c' && cli_strncmp(azArg[0], "clone", n)==0 ){ |
| 31894 | 32357 | failIfSafeMode(p, "cannot run .clone in safe mode"); |
| 31895 | 32358 | if( nArg==2 ){ |
| @@ -32468,10 +32931,11 @@ | ||
| 32468 | 32931 | }else |
| 32469 | 32932 | |
| 32470 | 32933 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 32471 | 32934 | if( nArg==2 ){ |
| 32472 | 32935 | p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
| 32936 | + p->mode.mFlags |= MFLG_HDR; | |
| 32473 | 32937 | p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
| 32474 | 32938 | }else{ |
| 32475 | 32939 | eputz("Usage: .headers on|off\n"); |
| 32476 | 32940 | rc = 1; |
| 32477 | 32941 | } |
| @@ -32521,14 +32985,14 @@ | ||
| 32521 | 32985 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); |
| 32522 | 32986 | goto meta_command_exit; |
| 32523 | 32987 | } |
| 32524 | 32988 | zSql = sqlite3_mprintf( |
| 32525 | 32989 | "SELECT rootpage, 0 FROM sqlite_schema" |
| 32526 | - " WHERE name='%q' AND type='index'" | |
| 32990 | + " WHERE type='index' AND lower(name)=lower('%q')" | |
| 32527 | 32991 | "UNION ALL " |
| 32528 | 32992 | "SELECT rootpage, 1 FROM sqlite_schema" |
| 32529 | - " WHERE name='%q' AND type='table'" | |
| 32993 | + " WHERE type='table' AND lower(name)=lower('%q')" | |
| 32530 | 32994 | " AND sql LIKE '%%without%%rowid%%'", |
| 32531 | 32995 | azArg[1], azArg[1] |
| 32532 | 32996 | ); |
| 32533 | 32997 | sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 32534 | 32998 | sqlite3_free(zSql); |
| @@ -32682,13 +33146,14 @@ | ||
| 32682 | 33146 | goto meta_command_exit; |
| 32683 | 33147 | } |
| 32684 | 33148 | if( nArg==3 ){ |
| 32685 | 33149 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 32686 | 33150 | (int)integerValue(azArg[2])); |
| 33151 | + }else{ | |
| 33152 | + cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, | |
| 33153 | + sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); | |
| 32687 | 33154 | } |
| 32688 | - cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, | |
| 32689 | - sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); | |
| 32690 | 33155 | } |
| 32691 | 33156 | }else |
| 32692 | 33157 | |
| 32693 | 33158 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| 32694 | 33159 | open_db(p, 0); |
| @@ -33027,10 +33492,23 @@ | ||
| 33027 | 33492 | continue; |
| 33028 | 33493 | } |
| 33029 | 33494 | if( cli_strcmp(z,"once")==0 ){ |
| 33030 | 33495 | p->flgProgress |= SHELL_PROGRESS_ONCE; |
| 33031 | 33496 | continue; |
| 33497 | + } | |
| 33498 | + if( cli_strcmp(z,"timeout")==0 ){ | |
| 33499 | + if( i==nArg-1 ){ | |
| 33500 | + dotCmdError(p, i, "missing argument", 0); | |
| 33501 | + return 1; | |
| 33502 | + } | |
| 33503 | + i++; | |
| 33504 | + p->tmProgress = atof(azArg[i]); | |
| 33505 | + if( p->tmProgress>0.0 ){ | |
| 33506 | + p->flgProgress = SHELL_PROGRESS_QUIET|SHELL_PROGRESS_TMOUT; | |
| 33507 | + if( nn==0 ) nn = 100; | |
| 33508 | + } | |
| 33509 | + continue; | |
| 33032 | 33510 | } |
| 33033 | 33511 | if( cli_strcmp(z,"limit")==0 ){ |
| 33034 | 33512 | if( i+1>=nArg ){ |
| 33035 | 33513 | eputz("Error: missing argument on --limit\n"); |
| 33036 | 33514 | rc = 1; |
| @@ -33233,21 +33711,22 @@ | ||
| 33233 | 33711 | || sqlite3_strlike(zName, "sqlite_schema", '\\')==0 |
| 33234 | 33712 | || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 |
| 33235 | 33713 | || sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0; |
| 33236 | 33714 | if( isSchema ){ |
| 33237 | 33715 | cli_printf(p->out, |
| 33238 | - "CREATE TABLE %s (\n" | |
| 33716 | + "CREATE TABLE %ssqlite_schema (\n" | |
| 33239 | 33717 | " type text,\n" |
| 33240 | 33718 | " name text,\n" |
| 33241 | 33719 | " tbl_name text,\n" |
| 33242 | 33720 | " rootpage integer,\n" |
| 33243 | 33721 | " sql text\n" |
| 33244 | - ");\n", zName); | |
| 33722 | + ");\n", | |
| 33723 | + sqlite3_strlike("sqlite_t%",zName,0)==0 ? "temp." : "" | |
| 33724 | + ); | |
| 33245 | 33725 | } |
| 33246 | 33726 | } |
| 33247 | - rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", | |
| 33248 | - -1, &pStmt, 0); | |
| 33727 | + rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); | |
| 33249 | 33728 | if( rc ){ |
| 33250 | 33729 | shellDatabaseError(p->db); |
| 33251 | 33730 | sqlite3_finalize(pStmt); |
| 33252 | 33731 | |
| 33253 | 33732 | rc = 1; |
| @@ -33255,11 +33734,11 @@ | ||
| 33255 | 33734 | } |
| 33256 | 33735 | pSql = sqlite3_str_new(p->db); |
| 33257 | 33736 | sqlite3_str_appendf(pSql, "SELECT sql FROM", 0); |
| 33258 | 33737 | iSchema = 0; |
| 33259 | 33738 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 33260 | - const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); | |
| 33739 | + const char *zDb = (const char*)sqlite3_column_text(pStmt, 1); | |
| 33261 | 33740 | char zScNum[30]; |
| 33262 | 33741 | sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); |
| 33263 | 33742 | sqlite3_str_appendall(pSql, zDiv); |
| 33264 | 33743 | zDiv = " UNION ALL "; |
| 33265 | 33744 | if( sqlite3_stricmp(zDb, "main")==0 ){ |
| @@ -33275,11 +33754,12 @@ | ||
| 33275 | 33754 | " AS sql, type, tbl_name, name, rowid, %d AS snum, %Q as sname", |
| 33276 | 33755 | ++iSchema, zDb); |
| 33277 | 33756 | sqlite3_str_appendf(pSql," FROM \"%w\".sqlite_schema", zDb); |
| 33278 | 33757 | } |
| 33279 | 33758 | sqlite3_finalize(pStmt); |
| 33280 | -#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS | |
| 33759 | +#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) \ | |
| 33760 | + && !defined(SQLITE_OMIT_VIRTUALTABLE) | |
| 33281 | 33761 | if( zName ){ |
| 33282 | 33762 | sqlite3_str_appendall(pSql, |
| 33283 | 33763 | " UNION ALL SELECT shell_module_schema(name)," |
| 33284 | 33764 | " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list"); |
| 33285 | 33765 | } |
| @@ -33299,11 +33779,11 @@ | ||
| 33299 | 33779 | }else{ |
| 33300 | 33780 | sqlite3_str_appendf(pSql, " LIKE %Q ESCAPE '\\' AND ", zName); |
| 33301 | 33781 | } |
| 33302 | 33782 | } |
| 33303 | 33783 | if( bNoSystemTabs ){ |
| 33304 | - sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCALE '_' AND "); | |
| 33784 | + sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCAPE '_' AND "); | |
| 33305 | 33785 | } |
| 33306 | 33786 | sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid"); |
| 33307 | 33787 | if( bDebug ){ |
| 33308 | 33788 | cli_printf(p->out, "SQL: %s;\n", sqlite3_str_value(pSql)); |
| 33309 | 33789 | }else{ |
| @@ -34017,25 +34497,15 @@ | ||
| 34017 | 34497 | sqlite3_str_free(pSql); |
| 34018 | 34498 | modePop(p); |
| 34019 | 34499 | if( rc ) return shellDatabaseError(p->db); |
| 34020 | 34500 | }else |
| 34021 | 34501 | |
| 34022 | -#ifndef SQLITE_SHELL_FIDDLE | |
| 34023 | - /* Begin redirecting output to the file "testcase-out.txt" */ | |
| 34502 | + /* Set the p->zTestcase name and begin redirecting output into | |
| 34503 | + ** the cli_output_capture sqlite3_str */ | |
| 34024 | 34504 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 34025 | - output_reset(p); | |
| 34026 | - p->out = output_file_open(p, "testcase-out.txt"); | |
| 34027 | - if( p->out==0 ){ | |
| 34028 | - eputz("Error: cannot open 'testcase-out.txt'\n"); | |
| 34029 | - } | |
| 34030 | - if( nArg>=2 ){ | |
| 34031 | - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); | |
| 34032 | - }else{ | |
| 34033 | - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); | |
| 34034 | - } | |
| 34505 | + rc = dotCmdTestcase(p); | |
| 34035 | 34506 | }else |
| 34036 | -#endif /* !defined(SQLITE_SHELL_FIDDLE) */ | |
| 34037 | 34507 | |
| 34038 | 34508 | #ifndef SQLITE_UNTESTABLE |
| 34039 | 34509 | if( c=='t' && n>=8 && cli_strncmp(azArg[0], "testctrl", n)==0 ){ |
| 34040 | 34510 | static const struct { |
| 34041 | 34511 | const char *zCtrlName; /* Name of a test-control option */ |
| @@ -34527,17 +34997,21 @@ | ||
| 34527 | 34997 | sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); |
| 34528 | 34998 | }else |
| 34529 | 34999 | |
| 34530 | 35000 | if( c=='t' && n>=5 && cli_strncmp(azArg[0], "timer", n)==0 ){ |
| 34531 | 35001 | if( nArg==2 ){ |
| 34532 | - enableTimer = booleanValue(azArg[1]); | |
| 34533 | - if( enableTimer && !HAS_TIMER ){ | |
| 35002 | + if( cli_strcmp(azArg[1],"once")==0 ){ | |
| 35003 | + p->enableTimer = 1; | |
| 35004 | + }else{ | |
| 35005 | + p->enableTimer = 2*booleanValue(azArg[1]); | |
| 35006 | + } | |
| 35007 | + if( p->enableTimer && !HAS_TIMER ){ | |
| 34534 | 35008 | eputz("Error: timer not available on this system.\n"); |
| 34535 | - enableTimer = 0; | |
| 35009 | + p->enableTimer = 0; | |
| 34536 | 35010 | } |
| 34537 | 35011 | }else{ |
| 34538 | - eputz("Usage: .timer on|off\n"); | |
| 35012 | + eputz("Usage: .timer on|off|once\n"); | |
| 34539 | 35013 | rc = 1; |
| 34540 | 35014 | } |
| 34541 | 35015 | }else |
| 34542 | 35016 | |
| 34543 | 35017 | #ifndef SQLITE_OMIT_TRACE |
| @@ -34708,10 +35182,11 @@ | ||
| 34708 | 35182 | if( p->nPopOutput ){ |
| 34709 | 35183 | p->nPopOutput--; |
| 34710 | 35184 | if( p->nPopOutput==0 ) output_reset(p); |
| 34711 | 35185 | } |
| 34712 | 35186 | p->bSafeMode = p->bSafeModePersist; |
| 35187 | + p->dot.nArg = 0; | |
| 34713 | 35188 | return rc; |
| 34714 | 35189 | } |
| 34715 | 35190 | |
| 34716 | 35191 | /* Line scan result and intermediate states (supporting scan resumption) |
| 34717 | 35192 | */ |
| @@ -34944,13 +35419,13 @@ | ||
| 34944 | 35419 | char *zErrMsg = 0; |
| 34945 | 35420 | |
| 34946 | 35421 | open_db(p, 0); |
| 34947 | 35422 | if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); |
| 34948 | 35423 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 34949 | - BEGIN_TIMER; | |
| 35424 | + BEGIN_TIMER(p); | |
| 34950 | 35425 | rc = shell_exec(p, zSql, &zErrMsg); |
| 34951 | - END_TIMER(p->out); | |
| 35426 | + END_TIMER(p); | |
| 34952 | 35427 | if( rc || zErrMsg ){ |
| 34953 | 35428 | char zPrefix[100]; |
| 34954 | 35429 | const char *zErrorTail; |
| 34955 | 35430 | const char *zErrorType; |
| 34956 | 35431 | if( zErrMsg==0 ){ |
| @@ -35351,11 +35826,10 @@ | ||
| 35351 | 35826 | " -bail stop after hitting an error\n" |
| 35352 | 35827 | " -batch force batch I/O\n" |
| 35353 | 35828 | " -box set output mode to 'box'\n" |
| 35354 | 35829 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 35355 | 35830 | " -column set output mode to 'column'\n" |
| 35356 | - " -compat YYYYMMDD set default options for date YYYYMMDD\n" | |
| 35357 | 35831 | " -csv set output mode to 'csv'\n" |
| 35358 | 35832 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 35359 | 35833 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 35360 | 35834 | #endif |
| 35361 | 35835 | " -echo print inputs before execution\n" |
| @@ -35435,15 +35909,10 @@ | ||
| 35435 | 35909 | /* |
| 35436 | 35910 | ** Initialize the state information in data |
| 35437 | 35911 | */ |
| 35438 | 35912 | static void main_init(ShellState *p) { |
| 35439 | 35913 | memset(p, 0, sizeof(*p)); |
| 35440 | -#if defined(COMPATIBILITY_DATE) | |
| 35441 | - p->iCompat = COMPATIBILITY_DATE; | |
| 35442 | -#else | |
| 35443 | - p->iCompat = 20251116; | |
| 35444 | -#endif | |
| 35445 | 35914 | p->pAuxDb = &p->aAuxDb[0]; |
| 35446 | 35915 | p->shellFlgs = SHFLG_Lookaside; |
| 35447 | 35916 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p); |
| 35448 | 35917 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 35449 | 35918 | verify_uninitialized(); |
| @@ -35498,29 +35967,65 @@ | ||
| 35498 | 35967 | cli_puts(z, p->out); |
| 35499 | 35968 | fflush(p->out); |
| 35500 | 35969 | return 1; |
| 35501 | 35970 | } |
| 35502 | 35971 | |
| 35503 | -#ifndef SQLITE_SHELL_IS_UTF8 | |
| 35504 | -# if (defined(_WIN32) || defined(WIN32)) \ | |
| 35505 | - && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) | |
| 35506 | -# define SQLITE_SHELL_IS_UTF8 (0) | |
| 35507 | -# else | |
| 35508 | -# define SQLITE_SHELL_IS_UTF8 (1) | |
| 35509 | -# endif | |
| 35510 | -#endif | |
| 35511 | - | |
| 35972 | +/* Alternative name to the entry point for Fiddle */ | |
| 35512 | 35973 | #ifdef SQLITE_SHELL_FIDDLE |
| 35513 | 35974 | # define main fiddle_main |
| 35514 | 35975 | #endif |
| 35515 | 35976 | |
| 35516 | -#if SQLITE_SHELL_IS_UTF8 | |
| 35517 | -int SQLITE_CDECL main(int argc, char **argv){ | |
| 35518 | -#else | |
| 35977 | +/* Use the wmain() entry point on Windows. Translate arguments to | |
| 35978 | +** UTF8, then invoke the traditional main() entry point which is | |
| 35979 | +** renamed using a #define to utf8_main() . | |
| 35980 | +*/ | |
| 35981 | +#if defined(_WIN32) && !defined(main) | |
| 35982 | +# define main utf8_main /* Rename entry point to utf_main() */ | |
| 35983 | +int SQLITE_CDECL utf8_main(int,char**); /* Forward declaration */ | |
| 35519 | 35984 | int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ |
| 35520 | - char **argv; | |
| 35521 | -#endif | |
| 35985 | + int rc, i; | |
| 35986 | + char **argv = malloc( sizeof(char*) * (argc+1) ); | |
| 35987 | + char **orig = argv; | |
| 35988 | + if( argv==0 ){ | |
| 35989 | + fprintf(stderr, "malloc failed\n"); | |
| 35990 | + exit(1); | |
| 35991 | + } | |
| 35992 | + for(i=0; i<argc; i++){ | |
| 35993 | + int nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, 0, 0, 0, 0); | |
| 35994 | + if( nByte==0 ){ | |
| 35995 | + argv[i] = 0; | |
| 35996 | + }else{ | |
| 35997 | + argv[i] = malloc( nByte ); | |
| 35998 | + if( argv[i]==0 ){ | |
| 35999 | + fprintf(stderr, "malloc failed\n"); | |
| 36000 | + exit(1); | |
| 36001 | + } | |
| 36002 | + nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i],nByte,0,0); | |
| 36003 | + if( nByte==0 ){ | |
| 36004 | + free(argv[i]); | |
| 36005 | + argv[i] = 0; | |
| 36006 | + } | |
| 36007 | + } | |
| 36008 | + } | |
| 36009 | + argv[argc] = 0; | |
| 36010 | + rc = utf8_main(argc, argv); | |
| 36011 | + for(i=0; i<argc; i++) free(orig[i]); | |
| 36012 | + free(argv); | |
| 36013 | + return rc; | |
| 36014 | +} | |
| 36015 | +#endif /* WIN32 */ | |
| 36016 | + | |
| 36017 | +/* | |
| 36018 | +** This is the main entry point for the process. Everything starts here. | |
| 36019 | +** | |
| 36020 | +** The "main" identifier may have been #defined to something else: | |
| 36021 | +** | |
| 36022 | +** utf8_main On Windows | |
| 36023 | +** fiddle_main In Fiddle | |
| 36024 | +** sqlite3_shell Other projects that use shell.c as a subroutine | |
| 36025 | +*/ | |
| 36026 | +int SQLITE_CDECL main(int argc, char **argv){ | |
| 35522 | 36027 | #ifdef SQLITE_DEBUG |
| 35523 | 36028 | sqlite3_int64 mem_main_enter = 0; |
| 35524 | 36029 | #endif |
| 35525 | 36030 | char *zErrMsg = 0; |
| 35526 | 36031 | #ifdef SQLITE_SHELL_FIDDLE |
| @@ -35538,14 +36043,10 @@ | ||
| 35538 | 36043 | int nOptsEnd = argc; |
| 35539 | 36044 | int bEnableVfstrace = 0; |
| 35540 | 36045 | char **azCmd = 0; |
| 35541 | 36046 | int *aiCmd = 0; |
| 35542 | 36047 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 35543 | -#if !SQLITE_SHELL_IS_UTF8 | |
| 35544 | - char **argvToFree = 0; | |
| 35545 | - int argcToFree = 0; | |
| 35546 | -#endif | |
| 35547 | 36048 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 35548 | 36049 | |
| 35549 | 36050 | #ifdef SQLITE_SHELL_FIDDLE |
| 35550 | 36051 | stdin_is_interactive = 0; |
| 35551 | 36052 | stdout_is_console = 1; |
| @@ -35596,36 +36097,10 @@ | ||
| 35596 | 36097 | exit(1); |
| 35597 | 36098 | } |
| 35598 | 36099 | #endif |
| 35599 | 36100 | main_init(&data); |
| 35600 | 36101 | |
| 35601 | - /* On Windows, we must translate command-line arguments into UTF-8. | |
| 35602 | - ** The SQLite memory allocator subsystem has to be enabled in order to | |
| 35603 | - ** do this. But we want to run an sqlite3_shutdown() afterwards so that | |
| 35604 | - ** subsequent sqlite3_config() calls will work. So copy all results into | |
| 35605 | - ** memory that does not come from the SQLite memory allocator. | |
| 35606 | - */ | |
| 35607 | -#if !SQLITE_SHELL_IS_UTF8 | |
| 35608 | - sqlite3_initialize(); | |
| 35609 | - argvToFree = malloc(sizeof(argv[0])*argc*2); | |
| 35610 | - shell_check_oom(argvToFree); | |
| 35611 | - argcToFree = argc; | |
| 35612 | - argv = argvToFree + argc; | |
| 35613 | - for(i=0; i<argc; i++){ | |
| 35614 | - char *z = sqlite3_win32_unicode_to_utf8(wargv[i]); | |
| 35615 | - i64 n; | |
| 35616 | - shell_check_oom(z); | |
| 35617 | - n = strlen(z); | |
| 35618 | - argv[i] = malloc( n+1 ); | |
| 35619 | - shell_check_oom(argv[i]); | |
| 35620 | - memcpy(argv[i], z, n+1); | |
| 35621 | - argvToFree[i] = argv[i]; | |
| 35622 | - sqlite3_free(z); | |
| 35623 | - } | |
| 35624 | - sqlite3_shutdown(); | |
| 35625 | -#endif | |
| 35626 | - | |
| 35627 | 36102 | assert( argc>=1 && argv && argv[0] ); |
| 35628 | 36103 | Argv0 = argv[0]; |
| 35629 | 36104 | |
| 35630 | 36105 | #ifdef SQLITE_SHELL_DBNAME_PROC |
| 35631 | 36106 | { |
| @@ -35637,11 +36112,11 @@ | ||
| 35637 | 36112 | SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename); |
| 35638 | 36113 | warnInmemoryDb = 0; |
| 35639 | 36114 | } |
| 35640 | 36115 | #endif |
| 35641 | 36116 | |
| 35642 | - /* Do an initial pass through the command-line argument to locate | |
| 36117 | + /* Do an initial pass through the command-line arguments to locate | |
| 35643 | 36118 | ** the name of the database file, the name of the initialization file, |
| 35644 | 36119 | ** the size of the alternative malloc heap, options affecting commands |
| 35645 | 36120 | ** or SQL run from the command line, and the first command to execute. |
| 35646 | 36121 | */ |
| 35647 | 36122 | #ifndef SQLITE_SHELL_FIDDLE |
| @@ -35649,11 +36124,11 @@ | ||
| 35649 | 36124 | #endif |
| 35650 | 36125 | for(i=1; i<argc; i++){ |
| 35651 | 36126 | char *z; |
| 35652 | 36127 | z = argv[i]; |
| 35653 | 36128 | if( z[0]!='-' || i>nOptsEnd ){ |
| 35654 | - if( data.aAuxDb->zDbFilename==0 ){ | |
| 36129 | + if( data.aAuxDb->zDbFilename==0 && !isScriptFile(z,1) ){ | |
| 35655 | 36130 | data.aAuxDb->zDbFilename = z; |
| 35656 | 36131 | }else{ |
| 35657 | 36132 | /* Excess arguments are interpreted as SQL (or dot-commands) and |
| 35658 | 36133 | ** mean that nothing is read from stdin */ |
| 35659 | 36134 | readStdin = 0; |
| @@ -35694,15 +36169,10 @@ | ||
| 35694 | 36169 | if( n<2 ){ |
| 35695 | 36170 | sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n"); |
| 35696 | 36171 | exit(1); |
| 35697 | 36172 | } |
| 35698 | 36173 | stdout_tty_width = n; |
| 35699 | - }else if( cli_strcmp(z,"-compat")==0 ){ | |
| 35700 | - data.iCompat = atoi(cmdline_option_value(argc, argv, ++i)); | |
| 35701 | - modeFree(&data.mode); | |
| 35702 | - memset(&data.mode, 0, sizeof(data.mode)); | |
| 35703 | - modeDefault(&data); | |
| 35704 | 36174 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 35705 | 36175 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 35706 | 36176 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| 35707 | 36177 | int val = 0; |
| 35708 | 36178 | sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val); |
| @@ -35822,10 +36292,20 @@ | ||
| 35822 | 36292 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 35823 | 36293 | /* no-op - catch this on the second pass */ |
| 35824 | 36294 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| 35825 | 36295 | /* skip over the argument */ |
| 35826 | 36296 | i++; |
| 36297 | + }else if( cli_strcmp(z,"-test-argv")==0 ){ | |
| 36298 | + /* Undocumented test option. Print the values in argv[] and exit. | |
| 36299 | + ** Use this to verify that any translation of the argv[], for example | |
| 36300 | + ** on Windows that receives wargv[] from the OS and must convert | |
| 36301 | + ** to UTF8 prior to calling this routine. */ | |
| 36302 | + int kk; | |
| 36303 | + for(kk=0; kk<argc; kk++){ | |
| 36304 | + sqlite3_fprintf(stdout,"argv[%d] = \"%s\"\n", kk, argv[kk]); | |
| 36305 | + } | |
| 36306 | + return 0; | |
| 35827 | 36307 | } |
| 35828 | 36308 | } |
| 35829 | 36309 | #ifndef SQLITE_SHELL_FIDDLE |
| 35830 | 36310 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 35831 | 36311 | #endif |
| @@ -35848,11 +36328,30 @@ | ||
| 35848 | 36328 | |
| 35849 | 36329 | if( zVfs ){ |
| 35850 | 36330 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 35851 | 36331 | if( pVfs ){ |
| 35852 | 36332 | sqlite3_vfs_register(pVfs, 1); |
| 35853 | - }else{ | |
| 36333 | + } | |
| 36334 | +#if !defined(SQLITE_OMIT_LOAD_EXTENSION) | |
| 36335 | + else if( access(zVfs,0)==0 ){ | |
| 36336 | + /* If the VFS name is not the name of an existing VFS, but it is | |
| 36337 | + ** the name of a file, then try to load that file as an extension. | |
| 36338 | + ** Presumably the extension implements the desired VFS. */ | |
| 36339 | + sqlite3 *db = 0; | |
| 36340 | + char *zErr = 0; | |
| 36341 | + sqlite3_open(":memory:", &db); | |
| 36342 | + sqlite3_enable_load_extension(db, 1); | |
| 36343 | + rc = sqlite3_load_extension(db, zVfs, 0, &zErr); | |
| 36344 | + sqlite3_close(db); | |
| 36345 | + if( (rc&0xff)!=SQLITE_OK ){ | |
| 36346 | + cli_printf(stderr, "could not load extension VFS \"%s\": %s\n", | |
| 36347 | + zVfs, zErr); | |
| 36348 | + exit(1); | |
| 36349 | + } | |
| 36350 | + } | |
| 36351 | +#endif | |
| 36352 | + else{ | |
| 35854 | 36353 | cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs); |
| 35855 | 36354 | exit(1); |
| 35856 | 36355 | } |
| 35857 | 36356 | } |
| 35858 | 36357 | |
| @@ -35889,11 +36388,11 @@ | ||
| 35889 | 36388 | ** is given on the command line, look for a file named ~/.sqliterc and |
| 35890 | 36389 | ** try to process it. |
| 35891 | 36390 | */ |
| 35892 | 36391 | if( !noInit ) process_sqliterc(&data,zInitFile); |
| 35893 | 36392 | |
| 35894 | - /* Make a second pass through the command-line argument and set | |
| 36393 | + /* Make a second pass through the command-line arguments and set | |
| 35895 | 36394 | ** options. This second pass is delayed until after the initialization |
| 35896 | 36395 | ** file is processed so that the command-line arguments will override |
| 35897 | 36396 | ** settings in the initialization file. |
| 35898 | 36397 | */ |
| 35899 | 36398 | for(i=1; i<argc; i++){ |
| @@ -36015,12 +36514,10 @@ | ||
| 36015 | 36514 | stdin_is_interactive = 1; |
| 36016 | 36515 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 36017 | 36516 | /* already handled */ |
| 36018 | 36517 | }else if( cli_strcmp(z,"-screenwidth")==0 ){ |
| 36019 | 36518 | i++; |
| 36020 | - }else if( cli_strcmp(z,"-compat")==0 ){ | |
| 36021 | - i++; | |
| 36022 | 36519 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 36023 | 36520 | /* already handled */ |
| 36024 | 36521 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 36025 | 36522 | /* already handled */ |
| 36026 | 36523 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| @@ -36116,11 +36613,22 @@ | ||
| 36116 | 36613 | ** command-line inputs, except for the argToSkip argument which contains |
| 36117 | 36614 | ** the database filename. |
| 36118 | 36615 | */ |
| 36119 | 36616 | for(i=0; i<nCmd; i++){ |
| 36120 | 36617 | echo_group_input(&data, azCmd[i]); |
| 36121 | - if( azCmd[i][0]=='.' ){ | |
| 36618 | + if( isScriptFile(azCmd[i],0) ){ | |
| 36619 | + FILE *inSaved = data.in; | |
| 36620 | + i64 savedLineno = data.lineno; | |
| 36621 | + int res = 1; | |
| 36622 | + if( (data.in = openChrSource(azCmd[i]))!=0 ){ | |
| 36623 | + res = process_input(&data, azCmd[i]); | |
| 36624 | + fclose(data.in); | |
| 36625 | + } | |
| 36626 | + data.in = inSaved; | |
| 36627 | + data.lineno = savedLineno; | |
| 36628 | + if( res ) i = nCmd; | |
| 36629 | + }else if( azCmd[i][0]=='.' ){ | |
| 36122 | 36630 | char *zErrCtx = malloc( 64 ); |
| 36123 | 36631 | shell_check_oom(zErrCtx); |
| 36124 | 36632 | sqlite3_snprintf(64,zErrCtx,"argv[%i]:",aiCmd[i]); |
| 36125 | 36633 | data.zInFile = "<cmdline>"; |
| 36126 | 36634 | data.zErrPrefix = zErrCtx; |
| @@ -36231,14 +36739,10 @@ | ||
| 36231 | 36739 | } |
| 36232 | 36740 | find_home_dir(1); |
| 36233 | 36741 | output_reset(&data); |
| 36234 | 36742 | data.doXdgOpen = 0; |
| 36235 | 36743 | clearTempFile(&data); |
| 36236 | -#if !SQLITE_SHELL_IS_UTF8 | |
| 36237 | - for(i=0; i<argcToFree; i++) free(argvToFree[i]); | |
| 36238 | - free(argvToFree); | |
| 36239 | -#endif | |
| 36240 | 36744 | modeFree(&data.mode); |
| 36241 | 36745 | if( data.nSavedModes ){ |
| 36242 | 36746 | int ii; |
| 36243 | 36747 | for(ii=0; ii<data.nSavedModes; ii++){ |
| 36244 | 36748 | modeFree(&data.aSavedModes[ii].mode); |
| 36245 | 36749 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -35,11 +35,11 @@ | |
| 35 | ** ext/recover/sqlite3recover.h |
| 36 | ** src/shell.c.in |
| 37 | ** |
| 38 | ** To modify this program, get a copy of the canonical SQLite source tree, |
| 39 | ** edit the src/shell.c.in file and/or some of the other files that are |
| 40 | ** listed above, then rerun the rerun "make shell.c". |
| 41 | */ |
| 42 | /************************* Begin src/shell.c.in ******************/ |
| 43 | /* |
| 44 | ** 2001 September 15 |
| 45 | ** |
| @@ -49,11 +49,11 @@ | |
| 49 | ** May you do good and not evil. |
| 50 | ** May you find forgiveness for yourself and forgive others. |
| 51 | ** May you share freely, never taking more than you give. |
| 52 | ** |
| 53 | ************************************************************************* |
| 54 | ** This file contains code to implement the "sqlite" command line |
| 55 | ** utility for accessing SQLite databases. |
| 56 | */ |
| 57 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 58 | /* This needs to come before any includes for MSVC compiler */ |
| 59 | #define _CRT_SECURE_NO_WARNINGS |
| @@ -79,10 +79,14 @@ | |
| 79 | ** should only be used when building the "fiddle" web application, as |
| 80 | ** the browser-mode build has much different user input requirements |
| 81 | ** and this build mode rewires the user input subsystem to account for |
| 82 | ** that. |
| 83 | */ |
| 84 | |
| 85 | /* |
| 86 | ** Warning pragmas copied from msvc.h in the core. |
| 87 | */ |
| 88 | #if defined(_MSC_VER) |
| @@ -872,10 +876,11 @@ | |
| 872 | #ifndef SQLITE_QRF_H |
| 873 | #include "qrf.h" |
| 874 | #endif |
| 875 | #include <string.h> |
| 876 | #include <assert.h> |
| 877 | |
| 878 | /* typedef sqlite3_int64 i64; */ |
| 879 | |
| 880 | /* A single line in the EQP output */ |
| 881 | typedef struct qrfEQPGraphRow qrfEQPGraphRow; |
| @@ -889,11 +894,12 @@ | |
| 889 | /* All EQP output is collected into an instance of the following */ |
| 890 | typedef struct qrfEQPGraph qrfEQPGraph; |
| 891 | struct qrfEQPGraph { |
| 892 | qrfEQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ |
| 893 | qrfEQPGraphRow *pLast; /* Last element of the pRow list */ |
| 894 | char zPrefix[100]; /* Graph prefix */ |
| 895 | }; |
| 896 | |
| 897 | /* |
| 898 | ** Private state information. Subject to change from one release to the |
| 899 | ** next. |
| @@ -995,10 +1001,19 @@ | |
| 995 | */ |
| 996 | static void qrfOom(Qrf *p){ |
| 997 | qrfError(p, SQLITE_NOMEM, "out of memory"); |
| 998 | } |
| 999 | |
| 1000 | |
| 1001 | |
| 1002 | /* |
| 1003 | ** Add a new entry to the EXPLAIN QUERY PLAN data |
| 1004 | */ |
| @@ -1074,10 +1089,49 @@ | |
| 1074 | qrfEqpRenderLevel(p, pRow->iEqpId); |
| 1075 | p->u.pGraph->zPrefix[n] = 0; |
| 1076 | } |
| 1077 | } |
| 1078 | } |
| 1079 | |
| 1080 | /* |
| 1081 | ** Display and reset the EXPLAIN QUERY PLAN data |
| 1082 | */ |
| 1083 | static void qrfEqpRender(Qrf *p, i64 nCycle){ |
| @@ -1090,11 +1144,30 @@ | |
| 1090 | } |
| 1091 | sqlite3_str_appendf(p->pOut, "%s\n", pRow->zText+3); |
| 1092 | p->u.pGraph->pRow = pRow->pNext; |
| 1093 | sqlite3_free(pRow); |
| 1094 | }else if( nCycle>0 ){ |
| 1095 | sqlite3_str_appendf(p->pOut, "QUERY PLAN (cycles=%lld [100%%])\n",nCycle); |
| 1096 | }else{ |
| 1097 | sqlite3_str_appendall(p->pOut, "QUERY PLAN\n"); |
| 1098 | } |
| 1099 | p->u.pGraph->zPrefix[0] = 0; |
| 1100 | qrfEqpRenderLevel(p, 0); |
| @@ -1145,10 +1218,12 @@ | |
| 1145 | static const int f = SQLITE_SCANSTAT_COMPLEX; |
| 1146 | sqlite3_stmt *pS = p->pStmt; |
| 1147 | int i = 0; |
| 1148 | i64 nTotal = 0; |
| 1149 | int nWidth = 0; |
| 1150 | sqlite3_str *pLine = sqlite3_str_new(p->db); |
| 1151 | sqlite3_str *pStats = sqlite3_str_new(p->db); |
| 1152 | qrfEqpReset(p); |
| 1153 | |
| 1154 | for(i=0; 1; i++){ |
| @@ -1158,11 +1233,11 @@ | |
| 1158 | break; |
| 1159 | } |
| 1160 | n = (int)strlen(z) + qrfStatsHeight(pS,i)*3; |
| 1161 | if( n>nWidth ) nWidth = n; |
| 1162 | } |
| 1163 | nWidth += 4; |
| 1164 | |
| 1165 | sqlite3_stmt_scanstatus_v2(pS,-1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); |
| 1166 | for(i=0; 1; i++){ |
| 1167 | i64 nLoop = 0; |
| 1168 | i64 nRow = 0; |
| @@ -1173,55 +1248,67 @@ | |
| 1173 | const char *zName = 0; |
| 1174 | double rEst = 0.0; |
| 1175 | |
| 1176 | if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){ |
| 1177 | break; |
| 1178 | } |
| 1179 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_EST,f,(void*)&rEst); |
| 1180 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); |
| 1181 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); |
| 1182 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); |
| 1183 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); |
| 1184 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); |
| 1185 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NAME,f,(void*)&zName); |
| 1186 | |
| 1187 | if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ |
| 1188 | const char *zSp = ""; |
| 1189 | double rpl; |
| 1190 | sqlite3_str_reset(pStats); |
| 1191 | if( nCycle>=0 && nTotal>0 ){ |
| 1192 | sqlite3_str_appendf(pStats, "cycles=%lld [%d%%]", |
| 1193 | nCycle, ((nCycle*100)+nTotal/2) / nTotal |
| 1194 | ); |
| 1195 | zSp = " "; |
| 1196 | } |
| 1197 | if( nLoop>=0 ){ |
| 1198 | sqlite3_str_appendf(pStats, "%sloops=%lld", zSp, nLoop); |
| 1199 | zSp = " "; |
| 1200 | } |
| 1201 | if( nRow>=0 ){ |
| 1202 | sqlite3_str_appendf(pStats, "%srows=%lld", zSp, nRow); |
| 1203 | zSp = " "; |
| 1204 | } |
| 1205 | |
| 1206 | if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
| 1207 | rpl = (double)nRow / (double)nLoop; |
| 1208 | sqlite3_str_appendf(pStats, "%srpl=%.1f est=%.1f", zSp, rpl, rEst); |
| 1209 | } |
| 1210 | |
| 1211 | sqlite3_str_appendf(pLine, |
| 1212 | "% *s (%s)", -1*(nWidth-qrfStatsHeight(pS,i)*3), zo, |
| 1213 | sqlite3_str_value(pStats) |
| 1214 | ); |
| 1215 | sqlite3_str_reset(pStats); |
| 1216 | qrfEqpAppend(p, iId, iPid, sqlite3_str_value(pLine)); |
| 1217 | sqlite3_str_reset(pLine); |
| 1218 | }else{ |
| 1219 | qrfEqpAppend(p, iId, iPid, zo); |
| 1220 | } |
| 1221 | } |
| 1222 | sqlite3_free(sqlite3_str_finish(pLine)); |
| 1223 | sqlite3_free(sqlite3_str_finish(pStats)); |
| 1224 | #endif |
| 1225 | } |
| 1226 | |
| 1227 | |
| @@ -1581,13 +1668,15 @@ | |
| 1581 | ** (3) z[] does not looks like a numeric literal |
| 1582 | */ |
| 1583 | static int qrfRelaxable(Qrf *p, const char *z){ |
| 1584 | size_t i, n; |
| 1585 | if( z[0]=='\'' || qrfSpace(z[0]) ) return 0; |
| 1586 | if( z[0]==0 && (p->spec.zNull==0 || p->spec.zNull[0]==0) ) return 0; |
| 1587 | n = strlen(z); |
| 1588 | if( z[n-1]=='\'' || qrfSpace(z[n-1]) ) return 0; |
| 1589 | if( p->spec.zNull && strcmp(p->spec.zNull,z)==0 ) return 0; |
| 1590 | i = (z[0]=='-' || z[0]=='+'); |
| 1591 | if( strcmp(z+i,"Inf")==0 ) return 0; |
| 1592 | if( !qrfDigit(z[i]) ) return 1; |
| 1593 | i++; |
| @@ -2729,10 +2818,11 @@ | |
| 2729 | int nNL = 0; |
| 2730 | int n, w; |
| 2731 | pStr = sqlite3_str_new(p->db); |
| 2732 | qrfEncodeText(p, pStr, z ? z : ""); |
| 2733 | n = sqlite3_str_length(pStr); |
| 2734 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2735 | if( p->spec.nTitleLimit ){ |
| 2736 | nNL = 0; |
| 2737 | data.aiWth[data.n] = w = qrfTitleLimit(data.az[data.n], |
| 2738 | p->spec.nTitleLimit ); |
| @@ -2756,10 +2846,11 @@ | |
| 2756 | int n, w; |
| 2757 | int eType = sqlite3_column_type(p->pStmt,i); |
| 2758 | pStr = sqlite3_str_new(p->db); |
| 2759 | qrfRenderValue(p, pStr, i); |
| 2760 | n = sqlite3_str_length(pStr); |
| 2761 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2762 | data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT; |
| 2763 | data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2764 | data.n++; |
| 2765 | if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| @@ -2910,11 +3001,11 @@ | |
| 2910 | ){ |
| 2911 | bRTrim = 1; |
| 2912 | }else{ |
| 2913 | bRTrim = 0; |
| 2914 | } |
| 2915 | for(i=0; i<data.n; i+=nColumn){ |
| 2916 | int bMore; |
| 2917 | int nRow = 0; |
| 2918 | |
| 2919 | /* Draw a single row of the table. This might be the title line |
| 2920 | ** (if there is a title line) or a row in the body of the table. |
| @@ -3097,11 +3188,11 @@ | |
| 3097 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0), "addr" ) ); |
| 3098 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1), "opcode" ) ); |
| 3099 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2), "p1" ) ); |
| 3100 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3), "p2" ) ); |
| 3101 | |
| 3102 | for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt); iOp++){ |
| 3103 | int iAddr = sqlite3_column_int(p->pStmt, 0); |
| 3104 | const char *zOp = (const char*)sqlite3_column_text(p->pStmt, 1); |
| 3105 | int p1 = sqlite3_column_int(p->pStmt, 2); |
| 3106 | int p2 = sqlite3_column_int(p->pStmt, 3); |
| 3107 | |
| @@ -3154,11 +3245,11 @@ | |
| 3154 | nWidth = sizeof(aScanExpWidth)/sizeof(int); |
| 3155 | iIndent = 3; |
| 3156 | } |
| 3157 | if( nArg>nWidth ) nArg = nWidth; |
| 3158 | |
| 3159 | for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW; iOp++){ |
| 3160 | /* If this is the first row seen, print out the headers */ |
| 3161 | if( iOp==0 ){ |
| 3162 | for(i=0; i<nArg; i++){ |
| 3163 | const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]); |
| 3164 | qrfWidthPrint(p,p->pOut, aWidth[i], zCol); |
| @@ -3410,10 +3501,11 @@ | |
| 3410 | sqlite3_str_append(p->pOut, "\n", 1); |
| 3411 | zVal += iNext; |
| 3412 | }while( zVal[0] ); |
| 3413 | sqlite3_str_reset(pVal); |
| 3414 | } |
| 3415 | sqlite3_free(sqlite3_str_finish(pVal)); |
| 3416 | qrfWrite(p); |
| 3417 | break; |
| 3418 | } |
| 3419 | case QRF_STYLE_Eqp: { |
| @@ -3473,11 +3565,11 @@ | |
| 3473 | p->pOut = sqlite3_str_new(p->db); |
| 3474 | if( p->pOut==0 ){ |
| 3475 | qrfOom(p); |
| 3476 | return; |
| 3477 | } |
| 3478 | p->iErr = 0; |
| 3479 | p->nCol = sqlite3_column_count(p->pStmt); |
| 3480 | p->nRow = 0; |
| 3481 | sz = sizeof(sqlite3_qrf_spec); |
| 3482 | memcpy(&p->spec, pSpec, sz); |
| 3483 | if( p->spec.zNull==0 ) p->spec.zNull = ""; |
| @@ -3644,17 +3736,27 @@ | |
| 3644 | sqlite3_free(p->u.sLine.azCol); |
| 3645 | } |
| 3646 | break; |
| 3647 | } |
| 3648 | case QRF_STYLE_Stats: |
| 3649 | case QRF_STYLE_StatsEst: |
| 3650 | case QRF_STYLE_Eqp: { |
| 3651 | qrfEqpRender(p, 0); |
| 3652 | qrfWrite(p); |
| 3653 | break; |
| 3654 | } |
| 3655 | } |
| 3656 | if( p->spec.pzOutput ){ |
| 3657 | if( p->spec.pzOutput[0] ){ |
| 3658 | sqlite3_int64 n, sz; |
| 3659 | char *zCombined; |
| 3660 | sz = strlen(p->spec.pzOutput[0]); |
| @@ -3815,13 +3917,10 @@ | |
| 3815 | |
| 3816 | |
| 3817 | #define eputz(z) cli_puts(z,stderr) |
| 3818 | #define sputz(fp,z) cli_puts(z,fp) |
| 3819 | |
| 3820 | /* True if the timer is enabled */ |
| 3821 | static int enableTimer = 0; |
| 3822 | |
| 3823 | /* A version of strcmp() that works with NULL values */ |
| 3824 | static int cli_strcmp(const char *a, const char *b){ |
| 3825 | if( a==0 ) a = ""; |
| 3826 | if( b==0 ) b = ""; |
| 3827 | return strcmp(a,b); |
| @@ -3834,11 +3933,11 @@ | |
| 3834 | |
| 3835 | /* Return the current wall-clock time in microseconds since the |
| 3836 | ** Unix epoch (1970-01-01T00:00:00Z) |
| 3837 | */ |
| 3838 | static sqlite3_int64 timeOfDay(void){ |
| 3839 | #if defined(_WIN64) |
| 3840 | sqlite3_uint64 t; |
| 3841 | FILETIME tm; |
| 3842 | GetSystemTimePreciseAsFileTime(&tm); |
| 3843 | t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime; |
| 3844 | t += 116444736000000000LL; |
| @@ -3862,154 +3961,10 @@ | |
| 3862 | (void)gettimeofday(&sNow,0); |
| 3863 | return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec; |
| 3864 | #endif |
| 3865 | } |
| 3866 | |
| 3867 | #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) |
| 3868 | #include <sys/time.h> |
| 3869 | #include <sys/resource.h> |
| 3870 | |
| 3871 | /* VxWorks does not support getrusage() as far as we can determine */ |
| 3872 | #if defined(_WRS_KERNEL) || defined(__RTP__) |
| 3873 | struct rusage { |
| 3874 | struct timeval ru_utime; /* user CPU time used */ |
| 3875 | struct timeval ru_stime; /* system CPU time used */ |
| 3876 | }; |
| 3877 | #define getrusage(A,B) memset(B,0,sizeof(*B)) |
| 3878 | #endif |
| 3879 | |
| 3880 | |
| 3881 | /* Saved resource information for the beginning of an operation */ |
| 3882 | static struct rusage sBegin; /* CPU time at start */ |
| 3883 | static sqlite3_int64 iBegin; /* Wall-clock time at start */ |
| 3884 | |
| 3885 | /* |
| 3886 | ** Begin timing an operation |
| 3887 | */ |
| 3888 | static void beginTimer(void){ |
| 3889 | if( enableTimer ){ |
| 3890 | getrusage(RUSAGE_SELF, &sBegin); |
| 3891 | iBegin = timeOfDay(); |
| 3892 | } |
| 3893 | } |
| 3894 | |
| 3895 | /* Return the difference of two time_structs in seconds */ |
| 3896 | static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ |
| 3897 | return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + |
| 3898 | (double)(pEnd->tv_sec - pStart->tv_sec); |
| 3899 | } |
| 3900 | |
| 3901 | /* |
| 3902 | ** Print the timing results. |
| 3903 | */ |
| 3904 | static void endTimer(FILE *out){ |
| 3905 | if( enableTimer ){ |
| 3906 | sqlite3_int64 iEnd = timeOfDay(); |
| 3907 | struct rusage sEnd; |
| 3908 | getrusage(RUSAGE_SELF, &sEnd); |
| 3909 | cli_printf(out, "Run Time: real %.6f user %.6f sys %.6f\n", |
| 3910 | (iEnd - iBegin)*0.000001, |
| 3911 | timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
| 3912 | timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
| 3913 | } |
| 3914 | } |
| 3915 | |
| 3916 | #define BEGIN_TIMER beginTimer() |
| 3917 | #define END_TIMER(X) endTimer(X) |
| 3918 | #define HAS_TIMER 1 |
| 3919 | |
| 3920 | #elif (defined(_WIN32) || defined(WIN32)) |
| 3921 | |
| 3922 | /* Saved resource information for the beginning of an operation */ |
| 3923 | static HANDLE hProcess; |
| 3924 | static FILETIME ftKernelBegin; |
| 3925 | static FILETIME ftUserBegin; |
| 3926 | static sqlite3_int64 ftWallBegin; |
| 3927 | typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, |
| 3928 | LPFILETIME, LPFILETIME); |
| 3929 | static GETPROCTIMES getProcessTimesAddr = NULL; |
| 3930 | |
| 3931 | /* |
| 3932 | ** Check to see if we have timer support. Return 1 if necessary |
| 3933 | ** support found (or found previously). |
| 3934 | */ |
| 3935 | static int hasTimer(void){ |
| 3936 | if( getProcessTimesAddr ){ |
| 3937 | return 1; |
| 3938 | } else { |
| 3939 | /* GetProcessTimes() isn't supported in WIN95 and some other Windows |
| 3940 | ** versions. See if the version we are running on has it, and if it |
| 3941 | ** does, save off a pointer to it and the current process handle. |
| 3942 | */ |
| 3943 | hProcess = GetCurrentProcess(); |
| 3944 | if( hProcess ){ |
| 3945 | HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); |
| 3946 | if( NULL != hinstLib ){ |
| 3947 | getProcessTimesAddr = |
| 3948 | (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); |
| 3949 | if( NULL != getProcessTimesAddr ){ |
| 3950 | return 1; |
| 3951 | } |
| 3952 | FreeLibrary(hinstLib); |
| 3953 | } |
| 3954 | } |
| 3955 | } |
| 3956 | return 0; |
| 3957 | } |
| 3958 | |
| 3959 | /* |
| 3960 | ** Begin timing an operation |
| 3961 | */ |
| 3962 | static void beginTimer(void){ |
| 3963 | if( enableTimer && getProcessTimesAddr ){ |
| 3964 | FILETIME ftCreation, ftExit; |
| 3965 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit, |
| 3966 | &ftKernelBegin,&ftUserBegin); |
| 3967 | ftWallBegin = timeOfDay(); |
| 3968 | } |
| 3969 | } |
| 3970 | |
| 3971 | /* Return the difference of two FILETIME structs in seconds */ |
| 3972 | static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ |
| 3973 | sqlite_int64 i64Start = *((sqlite_int64 *) pStart); |
| 3974 | sqlite_int64 i64End = *((sqlite_int64 *) pEnd); |
| 3975 | return (double) ((i64End - i64Start) / 10000000.0); |
| 3976 | } |
| 3977 | |
| 3978 | /* |
| 3979 | ** Print the timing results. |
| 3980 | */ |
| 3981 | static void endTimer(FILE *out){ |
| 3982 | if( enableTimer && getProcessTimesAddr){ |
| 3983 | FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
| 3984 | sqlite3_int64 ftWallEnd = timeOfDay(); |
| 3985 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
| 3986 | #ifdef _WIN64 |
| 3987 | /* microsecond precision on 64-bit windows */ |
| 3988 | cli_printf(out, "Run Time: real %.6f user %f sys %f\n", |
| 3989 | (ftWallEnd - ftWallBegin)*0.000001, |
| 3990 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 3991 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 3992 | #else |
| 3993 | /* millisecond precisino on 32-bit windows */ |
| 3994 | cli_printf(out, "Run Time: real %.3f user %.3f sys %.3f\n", |
| 3995 | (ftWallEnd - ftWallBegin)*0.000001, |
| 3996 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 3997 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 3998 | #endif |
| 3999 | } |
| 4000 | } |
| 4001 | |
| 4002 | #define BEGIN_TIMER beginTimer() |
| 4003 | #define END_TIMER(X) endTimer(X) |
| 4004 | #define HAS_TIMER hasTimer() |
| 4005 | |
| 4006 | #else |
| 4007 | #define BEGIN_TIMER |
| 4008 | #define END_TIMER(X) /*no-op*/ |
| 4009 | #define HAS_TIMER 0 |
| 4010 | #endif |
| 4011 | |
| 4012 | /* |
| 4013 | ** Used to prevent warnings about unused parameters |
| 4014 | */ |
| 4015 | #define UNUSED_PARAMETER(x) (void)(x) |
| @@ -4068,10 +4023,18 @@ | |
| 4068 | #define PROMPT_LEN_MAX 128 |
| 4069 | /* First line prompt. default: "sqlite> " */ |
| 4070 | static char mainPrompt[PROMPT_LEN_MAX]; |
| 4071 | /* Continuation prompt. default: " ...> " */ |
| 4072 | static char continuePrompt[PROMPT_LEN_MAX]; |
| 4073 | |
| 4074 | /* This is variant of the standard-library strncpy() routine with the |
| 4075 | ** one change that the destination string is always zero-terminated, even |
| 4076 | ** if there is no zero-terminator in the first n-1 characters of the source |
| 4077 | ** string. |
| @@ -4188,17 +4151,10 @@ | |
| 4188 | */ |
| 4189 | static void shell_check_oom(const void *p){ |
| 4190 | if( p==0 ) shell_out_of_memory(); |
| 4191 | } |
| 4192 | |
| 4193 | /* |
| 4194 | ** Write I/O traces to the following stream. |
| 4195 | */ |
| 4196 | #ifdef SQLITE_ENABLE_IOTRACE |
| 4197 | static FILE *iotrace = 0; |
| 4198 | #endif |
| 4199 | |
| 4200 | /* |
| 4201 | ** This routine works like printf in that its first argument is a |
| 4202 | ** format string and subsequent arguments are values to be substituted |
| 4203 | ** in place of % fields. The result of formatting this string |
| 4204 | ** is written to iotrace. |
| @@ -8349,13 +8305,13 @@ | |
| 8349 | }else if( e<-10000 ){ |
| 8350 | e = -10000; |
| 8351 | } |
| 8352 | |
| 8353 | if( m<0 ){ |
| 8354 | isNeg = 1; |
| 8355 | m = -m; |
| 8356 | if( m<0 ) return; |
| 8357 | }else if( m==0 && e>-1000 && e<1000 ){ |
| 8358 | sqlite3_result_double(context, 0.0); |
| 8359 | return; |
| 8360 | } |
| 8361 | while( (m>>32)&0xffe00000 ){ |
| @@ -9472,11 +9428,11 @@ | |
| 9472 | ** (X) match X |
| 9473 | ** X|Y X or Y |
| 9474 | ** ^X X occurring at the beginning of the string |
| 9475 | ** X$ X occurring at the end of the string |
| 9476 | ** . Match any single character |
| 9477 | ** \c Character c where c is one of \{}()[]|*+?. |
| 9478 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 9479 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 9480 | ** \xXX Where XX is exactly 2 hex digits, unicode value XX |
| 9481 | ** [abc] Any single character from the set abc |
| 9482 | ** [^abc] Any single character not in the set abc |
| @@ -9857,11 +9813,11 @@ | |
| 9857 | |
| 9858 | /* A backslash character has been seen, read the next character and |
| 9859 | ** return its interpretation. |
| 9860 | */ |
| 9861 | static unsigned re_esc_char(ReCompiled *p){ |
| 9862 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 9863 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 9864 | int i, v = 0; |
| 9865 | char c; |
| 9866 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 9867 | c = p->sIn.z[p->sIn.i]; |
| @@ -10180,15 +10136,22 @@ | |
| 10180 | } |
| 10181 | return pRe->zErr; |
| 10182 | } |
| 10183 | |
| 10184 | /* |
| 10185 | ** Compute a reasonable limit on the length of the REGEXP NFA. |
| 10186 | */ |
| 10187 | static int re_maxlen(sqlite3_context *context){ |
| 10188 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 10189 | return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2; |
| 10190 | } |
| 10191 | |
| 10192 | /* |
| 10193 | ** Implementation of the regexp() SQL function. This function implements |
| 10194 | ** the build-in REGEXP operator. The first argument to the function is the |
| @@ -10210,14 +10173,21 @@ | |
| 10210 | int setAux = 0; /* True to invoke sqlite3_set_auxdata() */ |
| 10211 | |
| 10212 | (void)argc; /* Unused */ |
| 10213 | pRe = sqlite3_get_auxdata(context, 0); |
| 10214 | if( pRe==0 ){ |
| 10215 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10216 | if( zPattern==0 ) return; |
| 10217 | zErr = re_compile(&pRe, zPattern, re_maxlen(context), |
| 10218 | sqlite3_user_data(context)!=0); |
| 10219 | if( zErr ){ |
| 10220 | re_free(pRe); |
| 10221 | sqlite3_result_error(context, zErr, -1); |
| 10222 | return; |
| 10223 | } |
| @@ -10279,11 +10249,11 @@ | |
| 10279 | "ATSTART", |
| 10280 | }; |
| 10281 | |
| 10282 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10283 | if( zPattern==0 ) return; |
| 10284 | zErr = re_compile(&pRe, zPattern, re_maxlen(context), |
| 10285 | sqlite3_user_data(context)!=0); |
| 10286 | if( zErr ){ |
| 10287 | re_free(pRe); |
| 10288 | sqlite3_result_error(context, zErr, -1); |
| 10289 | return; |
| @@ -13067,11 +13037,11 @@ | |
| 13067 | nFile = (int)strlen(zFile)+1; |
| 13068 | } |
| 13069 | |
| 13070 | rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA); |
| 13071 | if( rc==SQLITE_OK ){ |
| 13072 | pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile); |
| 13073 | if( pNew==0 ) return SQLITE_NOMEM; |
| 13074 | memset(pNew, 0, nByte+nFile); |
| 13075 | pNew->db = db; |
| 13076 | pNew->aBuffer = (u8*)&pNew[1]; |
| 13077 | if( zFile ){ |
| @@ -13213,18 +13183,19 @@ | |
| 13213 | ** sqlite3_free(). |
| 13214 | */ |
| 13215 | static int zipfileReadData( |
| 13216 | FILE *pFile, /* Read from this file */ |
| 13217 | u8 *aRead, /* Read into this buffer */ |
| 13218 | int nRead, /* Number of bytes to read */ |
| 13219 | i64 iOff, /* Offset to read from */ |
| 13220 | char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */ |
| 13221 | ){ |
| 13222 | size_t n; |
| 13223 | fseek(pFile, (long)iOff, SEEK_SET); |
| 13224 | n = fread(aRead, 1, nRead, pFile); |
| 13225 | if( (int)n!=nRead ){ |
| 13226 | *pzErrmsg = sqlite3_mprintf("error in fread()"); |
| 13227 | return SQLITE_ERROR; |
| 13228 | } |
| 13229 | return SQLITE_OK; |
| 13230 | } |
| @@ -13237,11 +13208,11 @@ | |
| 13237 | if( nWrite>0 ){ |
| 13238 | size_t n = nWrite; |
| 13239 | fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET); |
| 13240 | n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd); |
| 13241 | if( (int)n!=nWrite ){ |
| 13242 | pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()"); |
| 13243 | return SQLITE_ERROR; |
| 13244 | } |
| 13245 | pTab->szCurrent += nWrite; |
| 13246 | } |
| 13247 | return SQLITE_OK; |
| @@ -13484,10 +13455,11 @@ | |
| 13484 | /* |
| 13485 | ** Set (*pzErr) to point to a buffer from sqlite3_malloc() containing a |
| 13486 | ** generic corruption message and return SQLITE_CORRUPT; |
| 13487 | */ |
| 13488 | static int zipfileCorrupt(char **pzErr){ |
| 13489 | *pzErr = sqlite3_mprintf("zip archive is corrupt"); |
| 13490 | return SQLITE_CORRUPT; |
| 13491 | } |
| 13492 | |
| 13493 | /* |
| @@ -13502,11 +13474,11 @@ | |
| 13502 | ** final value of (*ppEntry) undefined. |
| 13503 | */ |
| 13504 | static int zipfileGetEntry( |
| 13505 | ZipfileTab *pTab, /* Store any error message here */ |
| 13506 | const u8 *aBlob, /* Pointer to in-memory file image */ |
| 13507 | int nBlob, /* Size of aBlob[] in bytes */ |
| 13508 | FILE *pFile, /* If aBlob==0, read from this file */ |
| 13509 | i64 iOff, /* Offset of CDS record */ |
| 13510 | ZipfileEntry **ppEntry /* OUT: Pointer to new object */ |
| 13511 | ){ |
| 13512 | u8 *aRead; |
| @@ -13542,18 +13514,18 @@ | |
| 13542 | rc = SQLITE_NOMEM; |
| 13543 | }else{ |
| 13544 | memset(pNew, 0, sizeof(ZipfileEntry)); |
| 13545 | rc = zipfileReadCDS(aRead, &pNew->cds); |
| 13546 | if( rc!=SQLITE_OK ){ |
| 13547 | *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff); |
| 13548 | }else if( aBlob==0 ){ |
| 13549 | rc = zipfileReadData( |
| 13550 | pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr |
| 13551 | ); |
| 13552 | }else{ |
| 13553 | aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ]; |
| 13554 | if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){ |
| 13555 | rc = zipfileCorrupt(pzErr); |
| 13556 | } |
| 13557 | } |
| 13558 | } |
| 13559 | |
| @@ -13574,19 +13546,19 @@ | |
| 13574 | ZipfileLFH lfh; |
| 13575 | if( pFile ){ |
| 13576 | rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr); |
| 13577 | }else{ |
| 13578 | aRead = (u8*)&aBlob[pNew->cds.iOffset]; |
| 13579 | if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>(unsigned)nBlob ){ |
| 13580 | rc = zipfileCorrupt(pzErr); |
| 13581 | } |
| 13582 | } |
| 13583 | |
| 13584 | memset(&lfh, 0, sizeof(lfh)); |
| 13585 | if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh); |
| 13586 | if( rc==SQLITE_OK ){ |
| 13587 | pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; |
| 13588 | pNew->iDataOff += lfh.nFile + lfh.nExtra; |
| 13589 | if( aBlob && pNew->cds.szCompressed ){ |
| 13590 | if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){ |
| 13591 | rc = zipfileCorrupt(pzErr); |
| 13592 | }else{ |
| @@ -13593,11 +13565,11 @@ | |
| 13593 | pNew->aData = &pNew->aExtra[nExtra]; |
| 13594 | memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed); |
| 13595 | } |
| 13596 | } |
| 13597 | }else{ |
| 13598 | *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", |
| 13599 | (int)pNew->cds.iOffset |
| 13600 | ); |
| 13601 | } |
| 13602 | } |
| 13603 | |
| @@ -13617,11 +13589,11 @@ | |
| 13617 | static int zipfileNext(sqlite3_vtab_cursor *cur){ |
| 13618 | ZipfileCsr *pCsr = (ZipfileCsr*)cur; |
| 13619 | int rc = SQLITE_OK; |
| 13620 | |
| 13621 | if( pCsr->pFile ){ |
| 13622 | i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize; |
| 13623 | zipfileEntryFree(pCsr->pCurrent); |
| 13624 | pCsr->pCurrent = 0; |
| 13625 | if( pCsr->iNextOff>=iEof ){ |
| 13626 | pCsr->bEof = 1; |
| 13627 | }else{ |
| @@ -13855,16 +13827,16 @@ | |
| 13855 | ** an English language error message may be left in virtual-table pTab. |
| 13856 | */ |
| 13857 | static int zipfileReadEOCD( |
| 13858 | ZipfileTab *pTab, /* Return errors here */ |
| 13859 | const u8 *aBlob, /* Pointer to in-memory file image */ |
| 13860 | int nBlob, /* Size of aBlob[] in bytes */ |
| 13861 | FILE *pFile, /* Read from this file if aBlob==0 */ |
| 13862 | ZipfileEOCD *pEOCD /* Object to populate */ |
| 13863 | ){ |
| 13864 | u8 *aRead = pTab->aBuffer; /* Temporary buffer */ |
| 13865 | int nRead; /* Bytes to read from file */ |
| 13866 | int rc = SQLITE_OK; |
| 13867 | |
| 13868 | memset(pEOCD, 0, sizeof(ZipfileEOCD)); |
| 13869 | if( aBlob==0 ){ |
| 13870 | i64 iOff; /* Offset to read from */ |
| @@ -13881,11 +13853,11 @@ | |
| 13881 | nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE)); |
| 13882 | aRead = (u8*)&aBlob[nBlob-nRead]; |
| 13883 | } |
| 13884 | |
| 13885 | if( rc==SQLITE_OK ){ |
| 13886 | int i; |
| 13887 | |
| 13888 | /* Scan backwards looking for the signature bytes */ |
| 13889 | for(i=nRead-20; i>=0; i--){ |
| 13890 | if( aRead[i]==0x50 && aRead[i+1]==0x4b |
| 13891 | && aRead[i+2]==0x05 && aRead[i+3]==0x06 |
| @@ -13892,13 +13864,11 @@ | |
| 13892 | ){ |
| 13893 | break; |
| 13894 | } |
| 13895 | } |
| 13896 | if( i<0 ){ |
| 13897 | pTab->base.zErrMsg = sqlite3_mprintf( |
| 13898 | "cannot find end of central directory record" |
| 13899 | ); |
| 13900 | return SQLITE_ERROR; |
| 13901 | } |
| 13902 | |
| 13903 | aRead += i+4; |
| 13904 | pEOCD->iDisk = zipfileRead16(aRead); |
| @@ -13939,11 +13909,11 @@ | |
| 13939 | pNew->pNext = pBefore; |
| 13940 | *pp = pNew; |
| 13941 | } |
| 13942 | } |
| 13943 | |
| 13944 | static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){ |
| 13945 | ZipfileEOCD eocd; |
| 13946 | int rc; |
| 13947 | int i; |
| 13948 | i64 iOff; |
| 13949 | |
| @@ -13987,11 +13957,11 @@ | |
| 13987 | zipfileCursorErr(pCsr, "zipfile() function requires an argument"); |
| 13988 | return SQLITE_ERROR; |
| 13989 | }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ |
| 13990 | static const u8 aEmptyBlob = 0; |
| 13991 | const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]); |
| 13992 | int nBlob = sqlite3_value_bytes(argv[0]); |
| 13993 | assert( pTab->pFirstEntry==0 ); |
| 13994 | if( aBlob==0 ){ |
| 13995 | aBlob = &aEmptyBlob; |
| 13996 | nBlob = 0; |
| 13997 | } |
| @@ -14185,23 +14155,23 @@ | |
| 14185 | ZipfileTab *pTab = (ZipfileTab*)pVtab; |
| 14186 | int rc = SQLITE_OK; |
| 14187 | |
| 14188 | assert( pTab->pWriteFd==0 ); |
| 14189 | if( pTab->zFile==0 || pTab->zFile[0]==0 ){ |
| 14190 | pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename"); |
| 14191 | return SQLITE_ERROR; |
| 14192 | } |
| 14193 | |
| 14194 | /* Open a write fd on the file. Also load the entire central directory |
| 14195 | ** structure into memory. During the transaction any new file data is |
| 14196 | ** appended to the archive file, but the central directory is accumulated |
| 14197 | ** in main-memory until the transaction is committed. */ |
| 14198 | pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+"); |
| 14199 | if( pTab->pWriteFd==0 ){ |
| 14200 | pTab->base.zErrMsg = sqlite3_mprintf( |
| 14201 | "zipfile: failed to open file %s for writing", pTab->zFile |
| 14202 | ); |
| 14203 | rc = SQLITE_ERROR; |
| 14204 | }else{ |
| 14205 | fseek(pTab->pWriteFd, 0, SEEK_END); |
| 14206 | pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); |
| 14207 | rc = zipfileLoadDirectory(pTab, 0, 0); |
| @@ -14662,11 +14632,11 @@ | |
| 14662 | int nEntry; |
| 14663 | ZipfileBuffer body; |
| 14664 | ZipfileBuffer cds; |
| 14665 | }; |
| 14666 | |
| 14667 | static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){ |
| 14668 | if( pBuf->n+nByte>pBuf->nAlloc ){ |
| 14669 | u8 *aNew; |
| 14670 | sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512; |
| 14671 | int nReq = pBuf->n + nByte; |
| 14672 | |
| @@ -14711,11 +14681,11 @@ | |
| 14711 | u32 iCrc32 = 0; /* crc32 of uncompressed data */ |
| 14712 | |
| 14713 | char *zName = 0; /* Path (name) of new entry */ |
| 14714 | int nName = 0; /* Size of zName in bytes */ |
| 14715 | char *zFree = 0; /* Free this before returning */ |
| 14716 | int nByte; |
| 14717 | |
| 14718 | memset(&e, 0, sizeof(e)); |
| 14719 | p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); |
| 14720 | if( p==0 ) return; |
| 14721 | |
| @@ -24161,28 +24131,28 @@ | |
| 24161 | u8 autoExplain; /* Automatically turn on .explain mode */ |
| 24162 | u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ |
| 24163 | u8 autoEQPtrace; /* autoEQP is in trace mode */ |
| 24164 | u8 scanstatsOn; /* True to display scan stats before each finalize */ |
| 24165 | u8 bAutoScreenWidth; /* Using the TTY to determine screen width */ |
| 24166 | u8 mFlags; /* MFLG_ECHO and/or MFLG_CRLF */ |
| 24167 | u8 eMode; /* One of the MODE_ values */ |
| 24168 | sqlite3_qrf_spec spec; /* Spec to be passed into QRF */ |
| 24169 | } Mode; |
| 24170 | |
| 24171 | /* Flags for Mode.mFlags */ |
| 24172 | #define MFLG_ECHO 0x01 /* Echo inputs to output */ |
| 24173 | #define MFLG_CRLF 0x02 /* Use CR/LF output line endings */ |
| 24174 | |
| 24175 | |
| 24176 | /* |
| 24177 | ** State information about the database connection is contained in an |
| 24178 | ** instance of the following structure. |
| 24179 | */ |
| 24180 | typedef struct ShellState ShellState; |
| 24181 | struct ShellState { |
| 24182 | sqlite3 *db; /* The database */ |
| 24183 | int iCompat; /* Compatibility date YYYYMMDD */ |
| 24184 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ |
| 24185 | u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ |
| 24186 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 24187 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 24188 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| @@ -24190,11 +24160,14 @@ | |
| 24190 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 24191 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 24192 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 24193 | u8 nPopOutput; /* Revert .output settings when reaching zero */ |
| 24194 | u8 nPopMode; /* Revert .mode settings when reaching zero */ |
| 24195 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 24196 | i64 lineno; /* Line number of last line read from in */ |
| 24197 | const char *zInFile; /* Name of the input file */ |
| 24198 | int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ |
| 24199 | FILE *in; /* Read commands from this stream */ |
| 24200 | FILE *out; /* Write results here */ |
| @@ -24286,10 +24259,11 @@ | |
| 24286 | #define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */ |
| 24287 | #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress |
| 24288 | ** callback limit is reached, and for each |
| 24289 | ** top-level SQL statement */ |
| 24290 | #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ |
| 24291 | |
| 24292 | /* Names of values for Mode.spec.eEsc and Mode.spec.eText |
| 24293 | */ |
| 24294 | static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; |
| 24295 | static const char *qrfQuoteNames[] = |
| @@ -24447,10 +24421,181 @@ | |
| 24447 | ** Limit input nesting via .read or any other input redirect. |
| 24448 | ** It's not too expensive, so a generous allowance can be made. |
| 24449 | */ |
| 24450 | #define MAX_INPUT_NESTING 25 |
| 24451 | |
| 24452 | |
| 24453 | /* |
| 24454 | ** Clear a display mode, freeing any allocated memory that it |
| 24455 | ** contains. |
| 24456 | */ |
| @@ -24534,11 +24679,13 @@ | |
| 24534 | if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]); |
| 24535 | if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]); |
| 24536 | if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]); |
| 24537 | pM->spec.eText = pI->eText; |
| 24538 | pM->spec.eBlob = pI->eBlob; |
| 24539 | pM->spec.bTitles = pI->bHdr; |
| 24540 | pM->spec.eTitle = pI->eHdr; |
| 24541 | if( pI->mFlg & 0x01 ){ |
| 24542 | pM->spec.bBorder = QRF_No; |
| 24543 | }else{ |
| 24544 | pM->spec.bBorder = QRF_Auto; |
| @@ -24570,18 +24717,17 @@ | |
| 24570 | p->mode.mFlags = mFlags; |
| 24571 | } |
| 24572 | } |
| 24573 | |
| 24574 | /* |
| 24575 | ** Set the mode to the default according to p->iCompat. It assumed |
| 24576 | ** that the mode has already been freed and zeroed prior to calling |
| 24577 | ** this routine. |
| 24578 | */ |
| 24579 | static void modeDefault(ShellState *p){ |
| 24580 | p->mode.spec.iVersion = 1; |
| 24581 | p->mode.autoExplain = 1; |
| 24582 | if( p->iCompat>=20251115 && (stdin_is_interactive || stdout_is_console) ){ |
| 24583 | modeChange(p, MODE_TTY); |
| 24584 | }else{ |
| 24585 | modeChange(p, MODE_BATCH); |
| 24586 | } |
| 24587 | } |
| @@ -25371,10 +25517,17 @@ | |
| 25371 | ** Progress handler callback. |
| 25372 | */ |
| 25373 | static int progress_handler(void *pClientData) { |
| 25374 | ShellState *p = (ShellState*)pClientData; |
| 25375 | p->nProgress++; |
| 25376 | if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ |
| 25377 | cli_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); |
| 25378 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 25379 | if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; |
| 25380 | return 1; |
| @@ -25908,10 +26061,12 @@ | |
| 25908 | char *zBuf = sqlite3_malloc64( szVar-5 ); |
| 25909 | if( zBuf ){ |
| 25910 | memcpy(zBuf, &zVar[6], szVar-5); |
| 25911 | sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8); |
| 25912 | } |
| 25913 | #ifdef SQLITE_ENABLE_CARRAY |
| 25914 | }else if( strncmp(zVar, "$carray_", 8)==0 ){ |
| 25915 | static char *azColorNames[] = { |
| 25916 | "azure", "black", "blue", "brown", "cyan", "fuchsia", "gold", |
| 25917 | "gray", "green", "indigo", "khaki", "lime", "magenta", "maroon", |
| @@ -26167,27 +26322,36 @@ | |
| 26167 | pArg->pStmt = pStmt; |
| 26168 | } |
| 26169 | |
| 26170 | /* Show the EXPLAIN QUERY PLAN if .eqp is on */ |
| 26171 | isExplain = sqlite3_stmt_isexplain(pStmt); |
| 26172 | if( pArg && pArg->mode.autoEQP && isExplain==0 ){ |
| 26173 | int triggerEQP = 0; |
| 26174 | disable_debug_trace_modes(); |
| 26175 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); |
| 26176 | if( pArg->mode.autoEQP>=AUTOEQP_trigger ){ |
| 26177 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); |
| 26178 | } |
| 26179 | sqlite3_reset(pStmt); |
| 26180 | spec.eStyle = QRF_STYLE_Auto; |
| 26181 | sqlite3_stmt_explain(pStmt, 2-(pArg->mode.autoEQP>=AUTOEQP_full)); |
| 26182 | sqlite3_format_query_result(pStmt, &spec, 0); |
| 26183 | if( pArg->mode.autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ |
| 26184 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); |
| 26185 | } |
| 26186 | sqlite3_reset(pStmt); |
| 26187 | sqlite3_stmt_explain(pStmt, 0); |
| 26188 | restore_debug_trace_modes(); |
| 26189 | } |
| 26190 | |
| 26191 | bind_prepared_stmt(pArg, pStmt); |
| 26192 | if( isExplain && pArg->mode.autoExplain ){ |
| 26193 | spec.eStyle = isExplain==1 ? QRF_STYLE_Explain : QRF_STYLE_Eqp; |
| @@ -26604,12 +26768,12 @@ | |
| 26604 | ".bail on|off Stop after hitting an error. Default OFF", |
| 26605 | #ifndef SQLITE_SHELL_FIDDLE |
| 26606 | ".cd DIRECTORY Change the working directory to DIRECTORY", |
| 26607 | #endif |
| 26608 | ".changes on|off Show number of rows changed by SQL", |
| 26609 | #ifndef SQLITE_SHELL_FIDDLE |
| 26610 | ".check GLOB Fail if output since .testcase does not match", |
| 26611 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 26612 | #endif |
| 26613 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 26614 | ".crlf ?on|off? Whether or not to use \\r\\n line endings", |
| 26615 | ".databases List names and files of attached databases", |
| @@ -26721,10 +26885,11 @@ | |
| 26721 | ".progress N Invoke progress handler after every N opcodes", |
| 26722 | " --limit N Interrupt after N progress callbacks", |
| 26723 | " --once Do no more than one progress interrupt", |
| 26724 | " --quiet|-q No output except at interrupts", |
| 26725 | " --reset Reset the count for each input and interrupt", |
| 26726 | #endif |
| 26727 | ".prompt MAIN CONTINUE Replace the standard prompts", |
| 26728 | #ifndef SQLITE_SHELL_FIDDLE |
| 26729 | ".quit Stop interpreting input stream, exit if primary.", |
| 26730 | ".read FILE Read input from FILE or command output", |
| @@ -26785,17 +26950,15 @@ | |
| 26785 | " vmstep Show the virtual machine step count only", |
| 26786 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 26787 | ".system CMD ARGS... Run CMD ARGS... in a system shell", |
| 26788 | #endif |
| 26789 | ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", |
| 26790 | #ifndef SQLITE_SHELL_FIDDLE |
| 26791 | ",testcase NAME Begin redirecting output to 'testcase-out.txt'", |
| 26792 | #endif |
| 26793 | ",testctrl CMD ... Run various sqlite3_test_control() operations", |
| 26794 | " Run \".testctrl\" with no arguments for details", |
| 26795 | ".timeout MS Try opening locked tables for MS milliseconds", |
| 26796 | ".timer on|off Turn SQL timer on or off", |
| 26797 | #ifndef SQLITE_OMIT_TRACE |
| 26798 | ".trace ?OPTIONS? Output each SQL statement as it is run", |
| 26799 | " FILE Send output to FILE", |
| 26800 | " stdout Send output to stdout", |
| 26801 | " stderr Send output to stderr", |
| @@ -26836,19 +26999,33 @@ | |
| 26836 | "USAGE: .import [OPTIONS] FILE TABLE\n" |
| 26837 | "\n" |
| 26838 | "Import CSV or similar text from FILE into TABLE. If TABLE does\n" |
| 26839 | "not exist, it is created using the first row of FILE as the column\n" |
| 26840 | "names. If FILE begins with \"|\" then it is a command that is run\n" |
| 26841 | "and the output from the command is used as the input data.\n" |
| 26842 | "\n" |
| 26843 | "FILE is assumed to be in a CSV format, unless the current mode\n" |
| 26844 | "is \"ascii\" or \"tabs\" or unless one of the options below specify\n" |
| 26845 | "an alternative.\n" |
| 26846 | "\n" |
| 26847 | "Options:\n" |
| 26848 | " --ascii Use \\037 and \\036 as column and row separators on input\n" |
| 26849 | " --csv Input is standard RFC-4180 CSV.\n" |
| 26850 | " --schema S When creating TABLE, put it in schema S\n" |
| 26851 | " --skip N Ignore the first N rows of input\n" |
| 26852 | " -v Verbose mode\n" |
| 26853 | }, |
| 26854 | { ".mode", |
| @@ -26930,23 +27107,17 @@ | |
| 26930 | " --bom Prepend a byte-order mark to the output\n" |
| 26931 | " -e Accumulate output in a temporary text file then\n" |
| 26932 | " launch a text editor when the redirection ends.\n" |
| 26933 | " --error-prefix X Use X as the left-margin prefix for error messages.\n" |
| 26934 | " Set to an empty string to restore the default.\n" |
| 26935 | " --glob GLOB Raise an error if the memory buffer does not match\n" |
| 26936 | " the GLOB pattern.\n" |
| 26937 | " --keep Continue using the same \"memory\" buffer. Do not\n" |
| 26938 | " reset it or delete it. Useful in combination with\n" |
| 26939 | " --glob, --not-glob, and/or --verify.\n" |
| 26940 | " ---notglob GLOB Raise an error if the memory buffer does not match\n" |
| 26941 | " the GLOB pattern.\n" |
| 26942 | " --plain Use plain text rather than HTML tables with -w\n" |
| 26943 | " --show Write the memory buffer to the screen, for debugging.\n" |
| 26944 | " --verify ENDMARK Read subsequent lines of text until the first line\n" |
| 26945 | " that matches ENDMARK. Discard the ENDMARK. Compare\n" |
| 26946 | " the text against the accumulated output in memory and\n" |
| 26947 | " raise an error if there are any differences.\n" |
| 26948 | " -w Show the output in a web browser. Output is\n" |
| 26949 | " written into a temporary HTML file until the\n" |
| 26950 | " redirect ends, then the web browser is launched.\n" |
| 26951 | " Query results are shown as HTML tables, unless\n" |
| 26952 | " the --plain is used too.\n" |
| @@ -26970,10 +27141,39 @@ | |
| 26970 | " file in a web browser\n" |
| 26971 | " -x Show the output in a spreadsheet. Output is\n" |
| 26972 | " written to a temp file as CSV then the spreadsheet\n" |
| 26973 | " is launched when\n" |
| 26974 | }, |
| 26975 | }; |
| 26976 | |
| 26977 | /* |
| 26978 | ** Return a pointer to usage text for zCmd, or NULL if none exists. |
| 26979 | */ |
| @@ -27193,10 +27393,56 @@ | |
| 27193 | if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; |
| 27194 | } |
| 27195 | return 1; |
| 27196 | } |
| 27197 | #endif |
| 27198 | |
| 27199 | /* |
| 27200 | ** Try to deduce the type of file for zName based on its content. Return |
| 27201 | ** one of the SHELL_OPEN_* constants. |
| 27202 | ** |
| @@ -27206,24 +27452,16 @@ | |
| 27206 | ** the type cannot be determined from content. |
| 27207 | */ |
| 27208 | int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){ |
| 27209 | FILE *f; |
| 27210 | size_t n; |
| 27211 | sqlite3 *db = 0; |
| 27212 | sqlite3_stmt *pStmt = 0; |
| 27213 | int rc = SHELL_OPEN_UNSPEC; |
| 27214 | char zBuf[100]; |
| 27215 | if( access(zName,0)!=0 ) goto database_type_by_name; |
| 27216 | if( sqlite3_open_v2(zName, &db, openFlags, 0)==SQLITE_OK |
| 27217 | && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) |
| 27218 | ==SQLITE_OK |
| 27219 | && sqlite3_step(pStmt)==SQLITE_ROW |
| 27220 | ){ |
| 27221 | rc = SHELL_OPEN_NORMAL; |
| 27222 | } |
| 27223 | sqlite3_finalize(pStmt); |
| 27224 | sqlite3_close(db); |
| 27225 | if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL; |
| 27226 | f = sqlite3_fopen(zName, "rb"); |
| 27227 | if( f==0 ) goto database_type_by_name; |
| 27228 | n = fread(zBuf, 16, 1, f); |
| 27229 | if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){ |
| @@ -27253,10 +27491,39 @@ | |
| 27253 | }else{ |
| 27254 | rc = SHELL_OPEN_NORMAL; |
| 27255 | } |
| 27256 | return rc; |
| 27257 | } |
| 27258 | |
| 27259 | #ifndef SQLITE_OMIT_DESERIALIZE |
| 27260 | /* |
| 27261 | ** Reconstruct an in-memory database using the output from the "dbtotxt" |
| 27262 | ** program. Read content from the file in p->aAuxDb[].zDbFilename. |
| @@ -27899,20 +28166,24 @@ | |
| 27899 | typedef struct ImportCtx ImportCtx; |
| 27900 | struct ImportCtx { |
| 27901 | const char *zFile; /* Name of the input file */ |
| 27902 | FILE *in; /* Read the CSV text from this input stream */ |
| 27903 | int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */ |
| 27904 | char *z; /* Accumulated text for a field */ |
| 27905 | i64 n; /* Number of bytes in z */ |
| 27906 | i64 nAlloc; /* Space allocated for z[] */ |
| 27907 | int nLine; /* Current line number */ |
| 27908 | int nRow; /* Number of rows imported */ |
| 27909 | int nErr; /* Number of errors encountered */ |
| 27910 | int bNotFirst; /* True if one or more bytes already read */ |
| 27911 | int cTerm; /* Character that terminated the most recent field */ |
| 27912 | int cColSep; /* The column separator character. (Usually ",") */ |
| 27913 | int cRowSep; /* The row separator character. (Usually "\n") */ |
| 27914 | }; |
| 27915 | |
| 27916 | /* Clean up resourced used by an ImportCtx */ |
| 27917 | static void import_cleanup(ImportCtx *p){ |
| 27918 | if( p->in!=0 && p->xCloser!=0 ){ |
| @@ -27919,13 +28190,32 @@ | |
| 27919 | p->xCloser(p->in); |
| 27920 | p->in = 0; |
| 27921 | } |
| 27922 | sqlite3_free(p->z); |
| 27923 | p->z = 0; |
| 27924 | } |
| 27925 | |
| 27926 | /* Append a single byte to z[] */ |
| 27927 | static void import_append_char(ImportCtx *p, int c){ |
| 27928 | if( p->n+1>=p->nAlloc ){ |
| 27929 | p->nAlloc += p->nAlloc + 100; |
| 27930 | p->z = sqlite3_realloc64(p->z, p->nAlloc); |
| 27931 | shell_check_oom(p->z); |
| @@ -27937,12 +28227,12 @@ | |
| 27937 | ** with the option of having a separator other than ",". |
| 27938 | ** |
| 27939 | ** + Input comes from p->in. |
| 27940 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 27941 | ** from sqlite3_malloc64(). |
| 27942 | ** + Use p->cSep as the column separator. The default is ",". |
| 27943 | ** + Use p->rSep as the row separator. The default is "\n". |
| 27944 | ** + Keep track of the line number in p->nLine. |
| 27945 | ** + Store the character that terminates the field in p->cTerm. Store |
| 27946 | ** EOF on end-of-file. |
| 27947 | ** + Report syntax errors on stderr |
| 27948 | */ |
| @@ -27949,23 +28239,30 @@ | |
| 27949 | static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ |
| 27950 | int c; |
| 27951 | int cSep = (u8)p->cColSep; |
| 27952 | int rSep = (u8)p->cRowSep; |
| 27953 | p->n = 0; |
| 27954 | c = fgetc(p->in); |
| 27955 | if( c==EOF || seenInterrupt ){ |
| 27956 | p->cTerm = EOF; |
| 27957 | return 0; |
| 27958 | } |
| 27959 | if( c=='"' ){ |
| 27960 | int pc, ppc; |
| 27961 | int startLine = p->nLine; |
| 27962 | int cQuote = c; |
| 27963 | pc = ppc = 0; |
| 27964 | while( 1 ){ |
| 27965 | c = fgetc(p->in); |
| 27966 | if( c==rSep ) p->nLine++; |
| 27967 | if( c==cQuote ){ |
| 27968 | if( pc==cQuote ){ |
| 27969 | pc = 0; |
| 27970 | continue; |
| 27971 | } |
| @@ -27979,11 +28276,11 @@ | |
| 27979 | p->cTerm = c; |
| 27980 | break; |
| 27981 | } |
| 27982 | if( pc==cQuote && c!='\r' ){ |
| 27983 | cli_printf(stderr,"%s:%d: unescaped %c character\n", |
| 27984 | p->zFile, p->nLine, cQuote); |
| 27985 | } |
| 27986 | if( c==EOF ){ |
| 27987 | cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n", |
| 27988 | p->zFile, startLine, cQuote); |
| 27989 | p->cTerm = c; |
| @@ -27994,26 +28291,28 @@ | |
| 27994 | pc = c; |
| 27995 | } |
| 27996 | }else{ |
| 27997 | /* If this is the first field being parsed and it begins with the |
| 27998 | ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ |
| 27999 | if( (c&0xff)==0xef && p->bNotFirst==0 ){ |
| 28000 | import_append_char(p, c); |
| 28001 | c = fgetc(p->in); |
| 28002 | if( (c&0xff)==0xbb ){ |
| 28003 | import_append_char(p, c); |
| 28004 | c = fgetc(p->in); |
| 28005 | if( (c&0xff)==0xbf ){ |
| 28006 | p->bNotFirst = 1; |
| 28007 | p->n = 0; |
| 28008 | return csv_read_one_field(p); |
| 28009 | } |
| 28010 | } |
| 28011 | } |
| 28012 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 28013 | import_append_char(p, c); |
| 28014 | c = fgetc(p->in); |
| 28015 | } |
| 28016 | if( c==rSep ){ |
| 28017 | p->nLine++; |
| 28018 | if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
| 28019 | } |
| @@ -28027,12 +28326,12 @@ | |
| 28027 | /* Read a single field of ASCII delimited text. |
| 28028 | ** |
| 28029 | ** + Input comes from p->in. |
| 28030 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 28031 | ** from sqlite3_malloc64(). |
| 28032 | ** + Use p->cSep as the column separator. The default is "\x1F". |
| 28033 | ** + Use p->rSep as the row separator. The default is "\x1E". |
| 28034 | ** + Keep track of the row number in p->nLine. |
| 28035 | ** + Store the character that terminates the field in p->cTerm. Store |
| 28036 | ** EOF on end-of-file. |
| 28037 | ** + Report syntax errors on stderr |
| 28038 | */ |
| @@ -28039,18 +28338,18 @@ | |
| 28039 | static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ |
| 28040 | int c; |
| 28041 | int cSep = (u8)p->cColSep; |
| 28042 | int rSep = (u8)p->cRowSep; |
| 28043 | p->n = 0; |
| 28044 | c = fgetc(p->in); |
| 28045 | if( c==EOF || seenInterrupt ){ |
| 28046 | p->cTerm = EOF; |
| 28047 | return 0; |
| 28048 | } |
| 28049 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 28050 | import_append_char(p, c); |
| 28051 | c = fgetc(p->in); |
| 28052 | } |
| 28053 | if( c==rSep ){ |
| 28054 | p->nLine++; |
| 28055 | } |
| 28056 | p->cTerm = c; |
| @@ -30151,11 +30450,11 @@ | |
| 30151 | ; |
| 30152 | static const char * const zCollectVar = "\ |
| 30153 | SELECT\ |
| 30154 | '('||x'0a'\ |
| 30155 | || group_concat(\ |
| 30156 | cname||' TEXT',\ |
| 30157 | ','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\ |
| 30158 | ||')' AS ColsSpec \ |
| 30159 | FROM (\ |
| 30160 | SELECT cpos, printf('\"%w\"',printf('%!.*s%s', nlen-chop,name,suff)) AS cname \ |
| 30161 | FROM ColNames ORDER BY cpos\ |
| @@ -30351,19 +30650,33 @@ | |
| 30351 | ** USAGE: .import [OPTIONS] FILE TABLE |
| 30352 | ** |
| 30353 | ** Import CSV or similar text from FILE into TABLE. If TABLE does |
| 30354 | ** not exist, it is created using the first row of FILE as the column |
| 30355 | ** names. If FILE begins with "|" then it is a command that is run |
| 30356 | ** and the output from the command is used as the input data. |
| 30357 | ** |
| 30358 | ** FILE is assumed to be in a CSV format, unless the current mode |
| 30359 | ** is "ascii" or "tabs" or unless one of the options below specify |
| 30360 | ** an alternative. |
| 30361 | ** |
| 30362 | ** Options: |
| 30363 | ** --ascii Use \037 and \036 as column and row separators on input |
| 30364 | ** --csv Input is standard RFC-4180 CSV. |
| 30365 | ** --schema S When creating TABLE, put it in schema S |
| 30366 | ** --skip N Ignore the first N rows of input |
| 30367 | ** -v Verbose mode |
| 30368 | */ |
| 30369 | static int dotCmdImport(ShellState *p){ |
| @@ -30375,17 +30688,16 @@ | |
| 30375 | sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 30376 | int nCol; /* Number of columns in the table */ |
| 30377 | i64 nByte; /* Number of bytes in an SQL string */ |
| 30378 | int i, j; /* Loop counters */ |
| 30379 | int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 30380 | int nSep; /* Number of bytes in spec.zColumnSep */ |
| 30381 | char *zSql = 0; /* An SQL statement */ |
| 30382 | ImportCtx sCtx; /* Reader context */ |
| 30383 | char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ |
| 30384 | int eVerbose = 0; /* Larger for more console output */ |
| 30385 | i64 nSkip = 0; /* Initial lines to skip */ |
| 30386 | int useOutputMode = 1; /* Use output mode to determine separators */ |
| 30387 | char *zCreate = 0; /* CREATE TABLE statement text */ |
| 30388 | int rc; /* Result code */ |
| 30389 | |
| 30390 | failIfSafeMode(p, "cannot run .import in safe mode"); |
| 30391 | memset(&sCtx, 0, sizeof(sCtx)); |
| @@ -30411,70 +30723,71 @@ | |
| 30411 | }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){ |
| 30412 | zSchema = azArg[++i]; |
| 30413 | }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){ |
| 30414 | nSkip = integerValue(azArg[++i]); |
| 30415 | }else if( cli_strcmp(z,"-ascii")==0 ){ |
| 30416 | sCtx.cColSep = SEP_Unit[0]; |
| 30417 | sCtx.cRowSep = SEP_Record[0]; |
| 30418 | xRead = ascii_read_one_field; |
| 30419 | useOutputMode = 0; |
| 30420 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 30421 | sCtx.cColSep = ','; |
| 30422 | sCtx.cRowSep = '\n'; |
| 30423 | xRead = csv_read_one_field; |
| 30424 | useOutputMode = 0; |
| 30425 | }else{ |
| 30426 | dotCmdError(p, i, "unknown option", 0); |
| 30427 | return 1; |
| 30428 | } |
| 30429 | } |
| 30430 | if( zTable==0 ){ |
| 30431 | cli_printf(p->out, "ERROR: missing %s argument\n", |
| 30432 | zFile==0 ? "FILE" : "TABLE"); |
| 30433 | return 1; |
| 30434 | } |
| 30435 | seenInterrupt = 0; |
| 30436 | open_db(p, 0); |
| 30437 | if( useOutputMode ){ |
| 30438 | /* If neither the --csv or --ascii options are specified, then set |
| 30439 | ** the column and row separator characters from the output mode. */ |
| 30440 | if( p->mode.spec.zColumnSep==0 ){ |
| 30441 | modeSetStr(&p->mode.spec.zColumnSep, ","); |
| 30442 | nSep = 1; |
| 30443 | }else if( (nSep = strlen30(p->mode.spec.zColumnSep))==0 ){ |
| 30444 | eputz("Error: non-null column separator required for import\n"); |
| 30445 | return 1; |
| 30446 | } |
| 30447 | if( nSep>1 ){ |
| 30448 | eputz("Error: multi-character column separators not allowed" |
| 30449 | " for import\n"); |
| 30450 | return 1; |
| 30451 | } |
| 30452 | if( p->mode.spec.zRowSep==0 ){ |
| 30453 | modeSetStr(&p->mode.spec.zRowSep, "\n"); |
| 30454 | nSep = 1; |
| 30455 | }else if( (nSep = strlen30(p->mode.spec.zRowSep))==0 ){ |
| 30456 | eputz("Error: non-null row separator required for import\n"); |
| 30457 | return 1; |
| 30458 | } |
| 30459 | if( nSep==2 && p->mode.eMode==MODE_Csv |
| 30460 | && cli_strcmp(p->mode.spec.zRowSep,SEP_CrLf)==0 |
| 30461 | ){ |
| 30462 | /* When importing CSV (only), if the row separator is set to the |
| 30463 | ** default output row separator, change it to the default input |
| 30464 | ** row separator. This avoids having to maintain different input |
| 30465 | ** and output row separators. */ |
| 30466 | modeSetStr(&p->mode.spec.zRowSep, SEP_Row); |
| 30467 | nSep = strlen30(p->mode.spec.zRowSep); |
| 30468 | } |
| 30469 | if( nSep>1 ){ |
| 30470 | eputz("Error: multi-character row separators not allowed" |
| 30471 | " for import\n"); |
| 30472 | return 1; |
| 30473 | } |
| 30474 | sCtx.cColSep = (u8)p->mode.spec.zColumnSep[0]; |
| 30475 | sCtx.cRowSep = (u8)p->mode.spec.zRowSep[0]; |
| 30476 | } |
| 30477 | sCtx.zFile = zFile; |
| 30478 | sCtx.nLine = 1; |
| 30479 | if( sCtx.zFile[0]=='|' ){ |
| 30480 | #ifdef SQLITE_OMIT_POPEN |
| @@ -30483,19 +30796,58 @@ | |
| 30483 | #else |
| 30484 | sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
| 30485 | sCtx.zFile = "<pipe>"; |
| 30486 | sCtx.xCloser = pclose; |
| 30487 | #endif |
| 30488 | }else{ |
| 30489 | sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
| 30490 | sCtx.xCloser = fclose; |
| 30491 | } |
| 30492 | if( sCtx.in==0 ){ |
| 30493 | cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 30494 | return 1; |
| 30495 | } |
| 30496 | if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ |
| 30497 | char zSep[2]; |
| 30498 | zSep[1] = 0; |
| 30499 | zSep[0] = sCtx.cColSep; |
| 30500 | cli_puts("Column separator ", p->out); |
| 30501 | output_c_string(p->out, zSep); |
| @@ -30648,10 +31000,14 @@ | |
| 30648 | if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 30649 | z = ""; |
| 30650 | } |
| 30651 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 30652 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 30653 | cli_printf(stderr,"%s:%d: expected %d columns but found %d" |
| 30654 | " - filling the rest with NULL\n", |
| 30655 | sCtx.zFile, startLine, nCol, i+1); |
| 30656 | i += 2; |
| 30657 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| @@ -30671,10 +31027,11 @@ | |
| 30671 | rc = sqlite3_reset(pStmt); |
| 30672 | if( rc!=SQLITE_OK ){ |
| 30673 | cli_printf(stderr,"%s:%d: INSERT failed: %s\n", |
| 30674 | sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 30675 | sCtx.nErr++; |
| 30676 | }else{ |
| 30677 | sCtx.nRow++; |
| 30678 | } |
| 30679 | } |
| 30680 | }while( sCtx.cTerm!=EOF ); |
| @@ -30683,13 +31040,13 @@ | |
| 30683 | sqlite3_finalize(pStmt); |
| 30684 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 30685 | if( eVerbose>0 ){ |
| 30686 | cli_printf(p->out, |
| 30687 | "Added %d rows with %d errors using %d lines of input\n", |
| 30688 | sCtx.nRow, sCtx.nErr, sCtx.nLine-1); |
| 30689 | } |
| 30690 | return 0; |
| 30691 | } |
| 30692 | |
| 30693 | |
| 30694 | /* |
| 30695 | ** This function computes what to show the user about the configured |
| @@ -31100,10 +31457,11 @@ | |
| 31100 | if( k<0 ){ |
| 31101 | dotCmdError(p, i, "bad --titles value","%z", zErr); |
| 31102 | return 1; |
| 31103 | } |
| 31104 | p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; |
| 31105 | p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; |
| 31106 | chng = 1; |
| 31107 | }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ |
| 31108 | int nWidth = 0; |
| 31109 | short int *aWidth; |
| @@ -31328,23 +31686,17 @@ | |
| 31328 | ** --bom Prepend a byte-order mark to the output |
| 31329 | ** -e Accumulate output in a temporary text file then |
| 31330 | ** launch a text editor when the redirection ends. |
| 31331 | ** --error-prefix X Use X as the left-margin prefix for error messages. |
| 31332 | ** Set to an empty string to restore the default. |
| 31333 | ** --glob GLOB Raise an error if the memory buffer does not match |
| 31334 | ** the GLOB pattern. |
| 31335 | ** --keep Continue using the same "memory" buffer. Do not |
| 31336 | ** reset it or delete it. Useful in combination with |
| 31337 | ** --glob, --not-glob, and/or --verify. |
| 31338 | ** ---notglob GLOB Raise an error if the memory buffer does not match |
| 31339 | ** the GLOB pattern. |
| 31340 | ** --plain Use plain text rather than HTML tables with -w |
| 31341 | ** --show Write the memory buffer to the screen, for debugging. |
| 31342 | ** --verify ENDMARK Read subsequent lines of text until the first line |
| 31343 | ** that matches ENDMARK. Discard the ENDMARK. Compare |
| 31344 | ** the text against the accumulated output in memory and |
| 31345 | ** raise an error if there are any differences. |
| 31346 | ** -w Show the output in a web browser. Output is |
| 31347 | ** written into a temporary HTML file until the |
| 31348 | ** redirect ends, then the web browser is launched. |
| 31349 | ** Query results are shown as HTML tables, unless |
| 31350 | ** the --plain is used too. |
| @@ -31382,13 +31734,11 @@ | |
| 31382 | char *zFile = 0; /* The FILE argument */ |
| 31383 | int i; /* Loop counter */ |
| 31384 | int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ |
| 31385 | int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
| 31386 | int bPlain = 0; /* --plain option */ |
| 31387 | int bKeep = 0; /* --keep option */ |
| 31388 | char *zCheck = 0; /* Argument to --glob, --notglob, --verify */ |
| 31389 | int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --verify */ |
| 31390 | static const char *zBomUtf8 = "\357\273\277"; |
| 31391 | const char *zBom = 0; |
| 31392 | char c = azArg[0][0]; |
| 31393 | int n = strlen30(azArg[0]); |
| 31394 | |
| @@ -31410,36 +31760,21 @@ | |
| 31410 | zBom = zBomUtf8; |
| 31411 | }else if( cli_strcmp(z,"-plain")==0 ){ |
| 31412 | bPlain = 1; |
| 31413 | }else if( c=='o' && z[0]=='1' && z[1]!=0 && z[2]==0 |
| 31414 | && (z[1]=='x' || z[1]=='e' || z[1]=='w') ){ |
| 31415 | if( bKeep || eMode || eCheck ){ |
| 31416 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31417 | goto dotCmdOutput_error; |
| 31418 | } |
| 31419 | eMode = z[1]; |
| 31420 | }else if( cli_strcmp(z,"-keep")==0 ){ |
| 31421 | bKeep = 1; |
| 31422 | }else if( cli_strcmp(z,"-show")==0 ){ |
| 31423 | if( cli_output_capture ){ |
| 31424 | sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
| 31425 | } |
| 31426 | bKeep = 1; |
| 31427 | }else if( cli_strcmp(z,"-glob")==0 |
| 31428 | || cli_strcmp(z,"-notglob")==0 |
| 31429 | || cli_strcmp(z,"-verify")==0 |
| 31430 | ){ |
| 31431 | if( eCheck || eMode ){ |
| 31432 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31433 | goto dotCmdOutput_error; |
| 31434 | } |
| 31435 | if( i+1>=nArg ){ |
| 31436 | dotCmdError(p, i, "missing argument", 0); |
| 31437 | goto dotCmdOutput_error; |
| 31438 | } |
| 31439 | zCheck = azArg[++i]; |
| 31440 | eCheck = z[1]=='g' ? 1 : z[1]=='n' ? 2 : 3; |
| 31441 | }else if( optionMatch(z,"error-prefix") ){ |
| 31442 | if( i+1>=nArg ){ |
| 31443 | dotCmdError(p, i, "missing argument", 0); |
| 31444 | return 1; |
| 31445 | } |
| @@ -31450,11 +31785,11 @@ | |
| 31450 | dotCmdError(p, i, "unknown option", 0); |
| 31451 | sqlite3_free(zFile); |
| 31452 | return 1; |
| 31453 | } |
| 31454 | }else if( zFile==0 && eMode==0 ){ |
| 31455 | if( bKeep || eCheck ){ |
| 31456 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31457 | goto dotCmdOutput_error; |
| 31458 | } |
| 31459 | if( cli_strcmp(z, "memory")==0 && bOnce ){ |
| 31460 | dotCmdError(p, 0, "cannot redirect to \"memory\"", 0); |
| @@ -31486,53 +31821,10 @@ | |
| 31486 | if( bOnce ){ |
| 31487 | p->nPopOutput = 2; |
| 31488 | }else{ |
| 31489 | p->nPopOutput = 0; |
| 31490 | } |
| 31491 | if( eCheck ){ |
| 31492 | char *zTest; |
| 31493 | if( cli_output_capture ){ |
| 31494 | zTest = sqlite3_str_value(cli_output_capture); |
| 31495 | }else{ |
| 31496 | zTest = ""; |
| 31497 | } |
| 31498 | p->nTestRun++; |
| 31499 | if( eCheck==3 ){ |
| 31500 | int nCheck = strlen30(zCheck); |
| 31501 | sqlite3_str *pPattern = sqlite3_str_new(p->db); |
| 31502 | char *zPattern; |
| 31503 | sqlite3_int64 iStart = p->lineno; |
| 31504 | char zLine[2000]; |
| 31505 | while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
| 31506 | if( strchr(zLine,'\n') ) p->lineno++; |
| 31507 | if( cli_strncmp(zCheck,zLine,nCheck)==0 ) break; |
| 31508 | sqlite3_str_appendall(pPattern, zLine); |
| 31509 | } |
| 31510 | zPattern = sqlite3_str_finish(pPattern); |
| 31511 | if( cli_strcmp(zPattern,zTest)!=0 ){ |
| 31512 | sqlite3_fprintf(stderr, |
| 31513 | "%s:%lld: --verify does matches prior output\n", |
| 31514 | p->zInFile, iStart); |
| 31515 | p->nTestErr++; |
| 31516 | } |
| 31517 | sqlite3_free(zPattern); |
| 31518 | }else{ |
| 31519 | char *zGlob = sqlite3_mprintf("*%s*", zCheck); |
| 31520 | if( eCheck==1 && sqlite3_strglob(zGlob, zTest)!=0 ){ |
| 31521 | sqlite3_fprintf(stderr, |
| 31522 | "%s:%lld: --glob \"%s\" does not match prior output\n", |
| 31523 | p->zInFile, p->lineno, zCheck); |
| 31524 | p->nTestErr++; |
| 31525 | }else if( eCheck==2 && sqlite3_strglob(zGlob, zTest)==0 ){ |
| 31526 | sqlite3_fprintf(stderr, |
| 31527 | "%s:%lld: --notglob \"%s\" matches prior output\n", |
| 31528 | p->zInFile, p->lineno, zCheck); |
| 31529 | p->nTestErr++; |
| 31530 | } |
| 31531 | sqlite3_free(zGlob); |
| 31532 | } |
| 31533 | } |
| 31534 | if( !bKeep ) output_reset(p); |
| 31535 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 31536 | if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
| 31537 | p->doXdgOpen = 1; |
| 31538 | modePush(p); |
| @@ -31609,10 +31901,199 @@ | |
| 31609 | |
| 31610 | dotCmdOutput_error: |
| 31611 | sqlite3_free(zFile); |
| 31612 | return 1; |
| 31613 | } |
| 31614 | |
| 31615 | /* |
| 31616 | ** Enlarge the space allocated in p->dot so that it can hold more |
| 31617 | ** than nArg parsed command-line arguments. |
| 31618 | */ |
| @@ -31641,11 +32122,11 @@ | |
| 31641 | free(p->dot.zCopy); |
| 31642 | z = p->dot.zCopy = strdup(zLine); |
| 31643 | shell_check_oom(z); |
| 31644 | szLine = strlen(z); |
| 31645 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 31646 | if( szLine>0 && z[szLine-1]==';' && p->iCompat>=20251115 ){ |
| 31647 | szLine--; |
| 31648 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 31649 | } |
| 31650 | z[szLine] = 0; |
| 31651 | parseDotRealloc(p, 2); |
| @@ -31861,35 +32342,17 @@ | |
| 31861 | eputz("Usage: .changes on|off\n"); |
| 31862 | rc = 1; |
| 31863 | } |
| 31864 | }else |
| 31865 | |
| 31866 | #ifndef SQLITE_SHELL_FIDDLE |
| 31867 | /* Cancel output redirection, if it is currently set (by .testcase) |
| 31868 | ** Then read the content of the testcase-out.txt file and compare against |
| 31869 | ** azArg[1]. If there are differences, report an error and exit. |
| 31870 | */ |
| 31871 | if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){ |
| 31872 | char *zRes = 0; |
| 31873 | output_reset(p); |
| 31874 | if( nArg!=2 ){ |
| 31875 | eputz("Usage: .check GLOB-PATTERN\n"); |
| 31876 | rc = 2; |
| 31877 | }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ |
| 31878 | rc = 2; |
| 31879 | }else if( testcase_glob(azArg[1],zRes)==0 ){ |
| 31880 | cli_printf(stderr, |
| 31881 | "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", |
| 31882 | p->zTestcase, azArg[1], zRes); |
| 31883 | rc = 1; |
| 31884 | }else{ |
| 31885 | cli_printf(p->out, "testcase-%s ok\n", p->zTestcase); |
| 31886 | p->nCheck++; |
| 31887 | } |
| 31888 | sqlite3_free(zRes); |
| 31889 | }else |
| 31890 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 31891 | |
| 31892 | #ifndef SQLITE_SHELL_FIDDLE |
| 31893 | if( c=='c' && cli_strncmp(azArg[0], "clone", n)==0 ){ |
| 31894 | failIfSafeMode(p, "cannot run .clone in safe mode"); |
| 31895 | if( nArg==2 ){ |
| @@ -32468,10 +32931,11 @@ | |
| 32468 | }else |
| 32469 | |
| 32470 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 32471 | if( nArg==2 ){ |
| 32472 | p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
| 32473 | p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
| 32474 | }else{ |
| 32475 | eputz("Usage: .headers on|off\n"); |
| 32476 | rc = 1; |
| 32477 | } |
| @@ -32521,14 +32985,14 @@ | |
| 32521 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); |
| 32522 | goto meta_command_exit; |
| 32523 | } |
| 32524 | zSql = sqlite3_mprintf( |
| 32525 | "SELECT rootpage, 0 FROM sqlite_schema" |
| 32526 | " WHERE name='%q' AND type='index'" |
| 32527 | "UNION ALL " |
| 32528 | "SELECT rootpage, 1 FROM sqlite_schema" |
| 32529 | " WHERE name='%q' AND type='table'" |
| 32530 | " AND sql LIKE '%%without%%rowid%%'", |
| 32531 | azArg[1], azArg[1] |
| 32532 | ); |
| 32533 | sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 32534 | sqlite3_free(zSql); |
| @@ -32682,13 +33146,14 @@ | |
| 32682 | goto meta_command_exit; |
| 32683 | } |
| 32684 | if( nArg==3 ){ |
| 32685 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 32686 | (int)integerValue(azArg[2])); |
| 32687 | } |
| 32688 | cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 32689 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 32690 | } |
| 32691 | }else |
| 32692 | |
| 32693 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| 32694 | open_db(p, 0); |
| @@ -33027,10 +33492,23 @@ | |
| 33027 | continue; |
| 33028 | } |
| 33029 | if( cli_strcmp(z,"once")==0 ){ |
| 33030 | p->flgProgress |= SHELL_PROGRESS_ONCE; |
| 33031 | continue; |
| 33032 | } |
| 33033 | if( cli_strcmp(z,"limit")==0 ){ |
| 33034 | if( i+1>=nArg ){ |
| 33035 | eputz("Error: missing argument on --limit\n"); |
| 33036 | rc = 1; |
| @@ -33233,21 +33711,22 @@ | |
| 33233 | || sqlite3_strlike(zName, "sqlite_schema", '\\')==0 |
| 33234 | || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 |
| 33235 | || sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0; |
| 33236 | if( isSchema ){ |
| 33237 | cli_printf(p->out, |
| 33238 | "CREATE TABLE %s (\n" |
| 33239 | " type text,\n" |
| 33240 | " name text,\n" |
| 33241 | " tbl_name text,\n" |
| 33242 | " rootpage integer,\n" |
| 33243 | " sql text\n" |
| 33244 | ");\n", zName); |
| 33245 | } |
| 33246 | } |
| 33247 | rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", |
| 33248 | -1, &pStmt, 0); |
| 33249 | if( rc ){ |
| 33250 | shellDatabaseError(p->db); |
| 33251 | sqlite3_finalize(pStmt); |
| 33252 | |
| 33253 | rc = 1; |
| @@ -33255,11 +33734,11 @@ | |
| 33255 | } |
| 33256 | pSql = sqlite3_str_new(p->db); |
| 33257 | sqlite3_str_appendf(pSql, "SELECT sql FROM", 0); |
| 33258 | iSchema = 0; |
| 33259 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 33260 | const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); |
| 33261 | char zScNum[30]; |
| 33262 | sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); |
| 33263 | sqlite3_str_appendall(pSql, zDiv); |
| 33264 | zDiv = " UNION ALL "; |
| 33265 | if( sqlite3_stricmp(zDb, "main")==0 ){ |
| @@ -33275,11 +33754,12 @@ | |
| 33275 | " AS sql, type, tbl_name, name, rowid, %d AS snum, %Q as sname", |
| 33276 | ++iSchema, zDb); |
| 33277 | sqlite3_str_appendf(pSql," FROM \"%w\".sqlite_schema", zDb); |
| 33278 | } |
| 33279 | sqlite3_finalize(pStmt); |
| 33280 | #ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS |
| 33281 | if( zName ){ |
| 33282 | sqlite3_str_appendall(pSql, |
| 33283 | " UNION ALL SELECT shell_module_schema(name)," |
| 33284 | " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list"); |
| 33285 | } |
| @@ -33299,11 +33779,11 @@ | |
| 33299 | }else{ |
| 33300 | sqlite3_str_appendf(pSql, " LIKE %Q ESCAPE '\\' AND ", zName); |
| 33301 | } |
| 33302 | } |
| 33303 | if( bNoSystemTabs ){ |
| 33304 | sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCALE '_' AND "); |
| 33305 | } |
| 33306 | sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid"); |
| 33307 | if( bDebug ){ |
| 33308 | cli_printf(p->out, "SQL: %s;\n", sqlite3_str_value(pSql)); |
| 33309 | }else{ |
| @@ -34017,25 +34497,15 @@ | |
| 34017 | sqlite3_str_free(pSql); |
| 34018 | modePop(p); |
| 34019 | if( rc ) return shellDatabaseError(p->db); |
| 34020 | }else |
| 34021 | |
| 34022 | #ifndef SQLITE_SHELL_FIDDLE |
| 34023 | /* Begin redirecting output to the file "testcase-out.txt" */ |
| 34024 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 34025 | output_reset(p); |
| 34026 | p->out = output_file_open(p, "testcase-out.txt"); |
| 34027 | if( p->out==0 ){ |
| 34028 | eputz("Error: cannot open 'testcase-out.txt'\n"); |
| 34029 | } |
| 34030 | if( nArg>=2 ){ |
| 34031 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); |
| 34032 | }else{ |
| 34033 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); |
| 34034 | } |
| 34035 | }else |
| 34036 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 34037 | |
| 34038 | #ifndef SQLITE_UNTESTABLE |
| 34039 | if( c=='t' && n>=8 && cli_strncmp(azArg[0], "testctrl", n)==0 ){ |
| 34040 | static const struct { |
| 34041 | const char *zCtrlName; /* Name of a test-control option */ |
| @@ -34527,17 +34997,21 @@ | |
| 34527 | sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); |
| 34528 | }else |
| 34529 | |
| 34530 | if( c=='t' && n>=5 && cli_strncmp(azArg[0], "timer", n)==0 ){ |
| 34531 | if( nArg==2 ){ |
| 34532 | enableTimer = booleanValue(azArg[1]); |
| 34533 | if( enableTimer && !HAS_TIMER ){ |
| 34534 | eputz("Error: timer not available on this system.\n"); |
| 34535 | enableTimer = 0; |
| 34536 | } |
| 34537 | }else{ |
| 34538 | eputz("Usage: .timer on|off\n"); |
| 34539 | rc = 1; |
| 34540 | } |
| 34541 | }else |
| 34542 | |
| 34543 | #ifndef SQLITE_OMIT_TRACE |
| @@ -34708,10 +35182,11 @@ | |
| 34708 | if( p->nPopOutput ){ |
| 34709 | p->nPopOutput--; |
| 34710 | if( p->nPopOutput==0 ) output_reset(p); |
| 34711 | } |
| 34712 | p->bSafeMode = p->bSafeModePersist; |
| 34713 | return rc; |
| 34714 | } |
| 34715 | |
| 34716 | /* Line scan result and intermediate states (supporting scan resumption) |
| 34717 | */ |
| @@ -34944,13 +35419,13 @@ | |
| 34944 | char *zErrMsg = 0; |
| 34945 | |
| 34946 | open_db(p, 0); |
| 34947 | if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); |
| 34948 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 34949 | BEGIN_TIMER; |
| 34950 | rc = shell_exec(p, zSql, &zErrMsg); |
| 34951 | END_TIMER(p->out); |
| 34952 | if( rc || zErrMsg ){ |
| 34953 | char zPrefix[100]; |
| 34954 | const char *zErrorTail; |
| 34955 | const char *zErrorType; |
| 34956 | if( zErrMsg==0 ){ |
| @@ -35351,11 +35826,10 @@ | |
| 35351 | " -bail stop after hitting an error\n" |
| 35352 | " -batch force batch I/O\n" |
| 35353 | " -box set output mode to 'box'\n" |
| 35354 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 35355 | " -column set output mode to 'column'\n" |
| 35356 | " -compat YYYYMMDD set default options for date YYYYMMDD\n" |
| 35357 | " -csv set output mode to 'csv'\n" |
| 35358 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 35359 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 35360 | #endif |
| 35361 | " -echo print inputs before execution\n" |
| @@ -35435,15 +35909,10 @@ | |
| 35435 | /* |
| 35436 | ** Initialize the state information in data |
| 35437 | */ |
| 35438 | static void main_init(ShellState *p) { |
| 35439 | memset(p, 0, sizeof(*p)); |
| 35440 | #if defined(COMPATIBILITY_DATE) |
| 35441 | p->iCompat = COMPATIBILITY_DATE; |
| 35442 | #else |
| 35443 | p->iCompat = 20251116; |
| 35444 | #endif |
| 35445 | p->pAuxDb = &p->aAuxDb[0]; |
| 35446 | p->shellFlgs = SHFLG_Lookaside; |
| 35447 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p); |
| 35448 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 35449 | verify_uninitialized(); |
| @@ -35498,29 +35967,65 @@ | |
| 35498 | cli_puts(z, p->out); |
| 35499 | fflush(p->out); |
| 35500 | return 1; |
| 35501 | } |
| 35502 | |
| 35503 | #ifndef SQLITE_SHELL_IS_UTF8 |
| 35504 | # if (defined(_WIN32) || defined(WIN32)) \ |
| 35505 | && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 35506 | # define SQLITE_SHELL_IS_UTF8 (0) |
| 35507 | # else |
| 35508 | # define SQLITE_SHELL_IS_UTF8 (1) |
| 35509 | # endif |
| 35510 | #endif |
| 35511 | |
| 35512 | #ifdef SQLITE_SHELL_FIDDLE |
| 35513 | # define main fiddle_main |
| 35514 | #endif |
| 35515 | |
| 35516 | #if SQLITE_SHELL_IS_UTF8 |
| 35517 | int SQLITE_CDECL main(int argc, char **argv){ |
| 35518 | #else |
| 35519 | int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ |
| 35520 | char **argv; |
| 35521 | #endif |
| 35522 | #ifdef SQLITE_DEBUG |
| 35523 | sqlite3_int64 mem_main_enter = 0; |
| 35524 | #endif |
| 35525 | char *zErrMsg = 0; |
| 35526 | #ifdef SQLITE_SHELL_FIDDLE |
| @@ -35538,14 +36043,10 @@ | |
| 35538 | int nOptsEnd = argc; |
| 35539 | int bEnableVfstrace = 0; |
| 35540 | char **azCmd = 0; |
| 35541 | int *aiCmd = 0; |
| 35542 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 35543 | #if !SQLITE_SHELL_IS_UTF8 |
| 35544 | char **argvToFree = 0; |
| 35545 | int argcToFree = 0; |
| 35546 | #endif |
| 35547 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 35548 | |
| 35549 | #ifdef SQLITE_SHELL_FIDDLE |
| 35550 | stdin_is_interactive = 0; |
| 35551 | stdout_is_console = 1; |
| @@ -35596,36 +36097,10 @@ | |
| 35596 | exit(1); |
| 35597 | } |
| 35598 | #endif |
| 35599 | main_init(&data); |
| 35600 | |
| 35601 | /* On Windows, we must translate command-line arguments into UTF-8. |
| 35602 | ** The SQLite memory allocator subsystem has to be enabled in order to |
| 35603 | ** do this. But we want to run an sqlite3_shutdown() afterwards so that |
| 35604 | ** subsequent sqlite3_config() calls will work. So copy all results into |
| 35605 | ** memory that does not come from the SQLite memory allocator. |
| 35606 | */ |
| 35607 | #if !SQLITE_SHELL_IS_UTF8 |
| 35608 | sqlite3_initialize(); |
| 35609 | argvToFree = malloc(sizeof(argv[0])*argc*2); |
| 35610 | shell_check_oom(argvToFree); |
| 35611 | argcToFree = argc; |
| 35612 | argv = argvToFree + argc; |
| 35613 | for(i=0; i<argc; i++){ |
| 35614 | char *z = sqlite3_win32_unicode_to_utf8(wargv[i]); |
| 35615 | i64 n; |
| 35616 | shell_check_oom(z); |
| 35617 | n = strlen(z); |
| 35618 | argv[i] = malloc( n+1 ); |
| 35619 | shell_check_oom(argv[i]); |
| 35620 | memcpy(argv[i], z, n+1); |
| 35621 | argvToFree[i] = argv[i]; |
| 35622 | sqlite3_free(z); |
| 35623 | } |
| 35624 | sqlite3_shutdown(); |
| 35625 | #endif |
| 35626 | |
| 35627 | assert( argc>=1 && argv && argv[0] ); |
| 35628 | Argv0 = argv[0]; |
| 35629 | |
| 35630 | #ifdef SQLITE_SHELL_DBNAME_PROC |
| 35631 | { |
| @@ -35637,11 +36112,11 @@ | |
| 35637 | SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename); |
| 35638 | warnInmemoryDb = 0; |
| 35639 | } |
| 35640 | #endif |
| 35641 | |
| 35642 | /* Do an initial pass through the command-line argument to locate |
| 35643 | ** the name of the database file, the name of the initialization file, |
| 35644 | ** the size of the alternative malloc heap, options affecting commands |
| 35645 | ** or SQL run from the command line, and the first command to execute. |
| 35646 | */ |
| 35647 | #ifndef SQLITE_SHELL_FIDDLE |
| @@ -35649,11 +36124,11 @@ | |
| 35649 | #endif |
| 35650 | for(i=1; i<argc; i++){ |
| 35651 | char *z; |
| 35652 | z = argv[i]; |
| 35653 | if( z[0]!='-' || i>nOptsEnd ){ |
| 35654 | if( data.aAuxDb->zDbFilename==0 ){ |
| 35655 | data.aAuxDb->zDbFilename = z; |
| 35656 | }else{ |
| 35657 | /* Excess arguments are interpreted as SQL (or dot-commands) and |
| 35658 | ** mean that nothing is read from stdin */ |
| 35659 | readStdin = 0; |
| @@ -35694,15 +36169,10 @@ | |
| 35694 | if( n<2 ){ |
| 35695 | sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n"); |
| 35696 | exit(1); |
| 35697 | } |
| 35698 | stdout_tty_width = n; |
| 35699 | }else if( cli_strcmp(z,"-compat")==0 ){ |
| 35700 | data.iCompat = atoi(cmdline_option_value(argc, argv, ++i)); |
| 35701 | modeFree(&data.mode); |
| 35702 | memset(&data.mode, 0, sizeof(data.mode)); |
| 35703 | modeDefault(&data); |
| 35704 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 35705 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 35706 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| 35707 | int val = 0; |
| 35708 | sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val); |
| @@ -35822,10 +36292,20 @@ | |
| 35822 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 35823 | /* no-op - catch this on the second pass */ |
| 35824 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| 35825 | /* skip over the argument */ |
| 35826 | i++; |
| 35827 | } |
| 35828 | } |
| 35829 | #ifndef SQLITE_SHELL_FIDDLE |
| 35830 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 35831 | #endif |
| @@ -35848,11 +36328,30 @@ | |
| 35848 | |
| 35849 | if( zVfs ){ |
| 35850 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 35851 | if( pVfs ){ |
| 35852 | sqlite3_vfs_register(pVfs, 1); |
| 35853 | }else{ |
| 35854 | cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs); |
| 35855 | exit(1); |
| 35856 | } |
| 35857 | } |
| 35858 | |
| @@ -35889,11 +36388,11 @@ | |
| 35889 | ** is given on the command line, look for a file named ~/.sqliterc and |
| 35890 | ** try to process it. |
| 35891 | */ |
| 35892 | if( !noInit ) process_sqliterc(&data,zInitFile); |
| 35893 | |
| 35894 | /* Make a second pass through the command-line argument and set |
| 35895 | ** options. This second pass is delayed until after the initialization |
| 35896 | ** file is processed so that the command-line arguments will override |
| 35897 | ** settings in the initialization file. |
| 35898 | */ |
| 35899 | for(i=1; i<argc; i++){ |
| @@ -36015,12 +36514,10 @@ | |
| 36015 | stdin_is_interactive = 1; |
| 36016 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 36017 | /* already handled */ |
| 36018 | }else if( cli_strcmp(z,"-screenwidth")==0 ){ |
| 36019 | i++; |
| 36020 | }else if( cli_strcmp(z,"-compat")==0 ){ |
| 36021 | i++; |
| 36022 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 36023 | /* already handled */ |
| 36024 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 36025 | /* already handled */ |
| 36026 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| @@ -36116,11 +36613,22 @@ | |
| 36116 | ** command-line inputs, except for the argToSkip argument which contains |
| 36117 | ** the database filename. |
| 36118 | */ |
| 36119 | for(i=0; i<nCmd; i++){ |
| 36120 | echo_group_input(&data, azCmd[i]); |
| 36121 | if( azCmd[i][0]=='.' ){ |
| 36122 | char *zErrCtx = malloc( 64 ); |
| 36123 | shell_check_oom(zErrCtx); |
| 36124 | sqlite3_snprintf(64,zErrCtx,"argv[%i]:",aiCmd[i]); |
| 36125 | data.zInFile = "<cmdline>"; |
| 36126 | data.zErrPrefix = zErrCtx; |
| @@ -36231,14 +36739,10 @@ | |
| 36231 | } |
| 36232 | find_home_dir(1); |
| 36233 | output_reset(&data); |
| 36234 | data.doXdgOpen = 0; |
| 36235 | clearTempFile(&data); |
| 36236 | #if !SQLITE_SHELL_IS_UTF8 |
| 36237 | for(i=0; i<argcToFree; i++) free(argvToFree[i]); |
| 36238 | free(argvToFree); |
| 36239 | #endif |
| 36240 | modeFree(&data.mode); |
| 36241 | if( data.nSavedModes ){ |
| 36242 | int ii; |
| 36243 | for(ii=0; ii<data.nSavedModes; ii++){ |
| 36244 | modeFree(&data.aSavedModes[ii].mode); |
| 36245 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -35,11 +35,11 @@ | |
| 35 | ** ext/recover/sqlite3recover.h |
| 36 | ** src/shell.c.in |
| 37 | ** |
| 38 | ** To modify this program, get a copy of the canonical SQLite source tree, |
| 39 | ** edit the src/shell.c.in file and/or some of the other files that are |
| 40 | ** listed above, then rerun the command "make shell.c". |
| 41 | */ |
| 42 | /************************* Begin src/shell.c.in ******************/ |
| 43 | /* |
| 44 | ** 2001 September 15 |
| 45 | ** |
| @@ -49,11 +49,11 @@ | |
| 49 | ** May you do good and not evil. |
| 50 | ** May you find forgiveness for yourself and forgive others. |
| 51 | ** May you share freely, never taking more than you give. |
| 52 | ** |
| 53 | ************************************************************************* |
| 54 | ** This file contains code to implement the "sqlite3" command line |
| 55 | ** utility for accessing SQLite databases. |
| 56 | */ |
| 57 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 58 | /* This needs to come before any includes for MSVC compiler */ |
| 59 | #define _CRT_SECURE_NO_WARNINGS |
| @@ -79,10 +79,14 @@ | |
| 79 | ** should only be used when building the "fiddle" web application, as |
| 80 | ** the browser-mode build has much different user input requirements |
| 81 | ** and this build mode rewires the user input subsystem to account for |
| 82 | ** that. |
| 83 | */ |
| 84 | #if defined(SQLITE_SHELL_FIDDLE) |
| 85 | # undef SQLITE_OMIT_LOAD_EXTENSION |
| 86 | # define SQLITE_OMIT_LOAD_EXTENSION 1 |
| 87 | #endif |
| 88 | |
| 89 | /* |
| 90 | ** Warning pragmas copied from msvc.h in the core. |
| 91 | */ |
| 92 | #if defined(_MSC_VER) |
| @@ -872,10 +876,11 @@ | |
| 876 | #ifndef SQLITE_QRF_H |
| 877 | #include "qrf.h" |
| 878 | #endif |
| 879 | #include <string.h> |
| 880 | #include <assert.h> |
| 881 | #include <stdint.h> |
| 882 | |
| 883 | /* typedef sqlite3_int64 i64; */ |
| 884 | |
| 885 | /* A single line in the EQP output */ |
| 886 | typedef struct qrfEQPGraphRow qrfEQPGraphRow; |
| @@ -889,11 +894,12 @@ | |
| 894 | /* All EQP output is collected into an instance of the following */ |
| 895 | typedef struct qrfEQPGraph qrfEQPGraph; |
| 896 | struct qrfEQPGraph { |
| 897 | qrfEQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ |
| 898 | qrfEQPGraphRow *pLast; /* Last element of the pRow list */ |
| 899 | int nWidth; /* Width of the graph */ |
| 900 | char zPrefix[400]; /* Graph prefix */ |
| 901 | }; |
| 902 | |
| 903 | /* |
| 904 | ** Private state information. Subject to change from one release to the |
| 905 | ** next. |
| @@ -995,10 +1001,19 @@ | |
| 1001 | */ |
| 1002 | static void qrfOom(Qrf *p){ |
| 1003 | qrfError(p, SQLITE_NOMEM, "out of memory"); |
| 1004 | } |
| 1005 | |
| 1006 | /* |
| 1007 | ** Transfer any error in pStr over into p. |
| 1008 | */ |
| 1009 | static void qrfStrErr(Qrf *p, sqlite3_str *pStr){ |
| 1010 | int rc = pStr ? sqlite3_str_errcode(pStr) : 0; |
| 1011 | if( rc ){ |
| 1012 | qrfError(p, rc, sqlite3_errstr(rc)); |
| 1013 | } |
| 1014 | } |
| 1015 | |
| 1016 | |
| 1017 | /* |
| 1018 | ** Add a new entry to the EXPLAIN QUERY PLAN data |
| 1019 | */ |
| @@ -1074,10 +1089,49 @@ | |
| 1089 | qrfEqpRenderLevel(p, pRow->iEqpId); |
| 1090 | p->u.pGraph->zPrefix[n] = 0; |
| 1091 | } |
| 1092 | } |
| 1093 | } |
| 1094 | |
| 1095 | /* |
| 1096 | ** Render the 64-bit value N in a more human-readable format into |
| 1097 | ** pOut. |
| 1098 | ** |
| 1099 | ** + Only show the first three significant digits. |
| 1100 | ** + Append suffixes K, M, G, T, P, and E for 1e3, 1e6, ... 1e18 |
| 1101 | */ |
| 1102 | static void qrfApproxInt64(sqlite3_str *pOut, i64 N){ |
| 1103 | static const char aSuffix[] = { 'K', 'M', 'G', 'T', 'P', 'E' }; |
| 1104 | int i; |
| 1105 | if( N<0 ){ |
| 1106 | N = N==INT64_MIN ? INT64_MAX : -N; |
| 1107 | sqlite3_str_append(pOut, "-", 1); |
| 1108 | } |
| 1109 | if( N<10000 ){ |
| 1110 | sqlite3_str_appendf(pOut, "%4lld ", N); |
| 1111 | return; |
| 1112 | } |
| 1113 | for(i=1; i<=18; i++){ |
| 1114 | N = (N+5)/10; |
| 1115 | if( N<10000 ){ |
| 1116 | int n = (int)N; |
| 1117 | switch( i%3 ){ |
| 1118 | case 0: |
| 1119 | sqlite3_str_appendf(pOut, "%d.%02d", n/1000, (n%1000)/10); |
| 1120 | break; |
| 1121 | case 1: |
| 1122 | sqlite3_str_appendf(pOut, "%2d.%d", n/100, (n%100)/10); |
| 1123 | break; |
| 1124 | case 2: |
| 1125 | sqlite3_str_appendf(pOut, "%4d", n/10); |
| 1126 | break; |
| 1127 | } |
| 1128 | sqlite3_str_append(pOut, &aSuffix[i/3], 1); |
| 1129 | break; |
| 1130 | } |
| 1131 | } |
| 1132 | } |
| 1133 | |
| 1134 | /* |
| 1135 | ** Display and reset the EXPLAIN QUERY PLAN data |
| 1136 | */ |
| 1137 | static void qrfEqpRender(Qrf *p, i64 nCycle){ |
| @@ -1090,11 +1144,30 @@ | |
| 1144 | } |
| 1145 | sqlite3_str_appendf(p->pOut, "%s\n", pRow->zText+3); |
| 1146 | p->u.pGraph->pRow = pRow->pNext; |
| 1147 | sqlite3_free(pRow); |
| 1148 | }else if( nCycle>0 ){ |
| 1149 | int nSp = p->u.pGraph->nWidth - 2; |
| 1150 | if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
| 1151 | sqlite3_str_appendchar(p->pOut, nSp, ' '); |
| 1152 | sqlite3_str_appendall(p->pOut, |
| 1153 | "Cycles Loops (est) Rows (est)\n"); |
| 1154 | sqlite3_str_appendchar(p->pOut, nSp, ' '); |
| 1155 | sqlite3_str_appendall(p->pOut, |
| 1156 | "---------- ------------ ------------\n"); |
| 1157 | }else{ |
| 1158 | sqlite3_str_appendchar(p->pOut, nSp, ' '); |
| 1159 | sqlite3_str_appendall(p->pOut, |
| 1160 | "Cycles Loops Rows \n"); |
| 1161 | sqlite3_str_appendchar(p->pOut, nSp, ' '); |
| 1162 | sqlite3_str_appendall(p->pOut, |
| 1163 | "---------- ----- -----\n"); |
| 1164 | } |
| 1165 | sqlite3_str_appendall(p->pOut, "QUERY PLAN"); |
| 1166 | sqlite3_str_appendchar(p->pOut, nSp - 10, ' '); |
| 1167 | qrfApproxInt64(p->pOut, nCycle); |
| 1168 | sqlite3_str_appendall(p->pOut, " 100%\n"); |
| 1169 | }else{ |
| 1170 | sqlite3_str_appendall(p->pOut, "QUERY PLAN\n"); |
| 1171 | } |
| 1172 | p->u.pGraph->zPrefix[0] = 0; |
| 1173 | qrfEqpRenderLevel(p, 0); |
| @@ -1145,10 +1218,12 @@ | |
| 1218 | static const int f = SQLITE_SCANSTAT_COMPLEX; |
| 1219 | sqlite3_stmt *pS = p->pStmt; |
| 1220 | int i = 0; |
| 1221 | i64 nTotal = 0; |
| 1222 | int nWidth = 0; |
| 1223 | int prevPid = -1; /* Previous iPid */ |
| 1224 | double rEstCum = 1.0; /* Cumulative row estimate */ |
| 1225 | sqlite3_str *pLine = sqlite3_str_new(p->db); |
| 1226 | sqlite3_str *pStats = sqlite3_str_new(p->db); |
| 1227 | qrfEqpReset(p); |
| 1228 | |
| 1229 | for(i=0; 1; i++){ |
| @@ -1158,11 +1233,11 @@ | |
| 1233 | break; |
| 1234 | } |
| 1235 | n = (int)strlen(z) + qrfStatsHeight(pS,i)*3; |
| 1236 | if( n>nWidth ) nWidth = n; |
| 1237 | } |
| 1238 | nWidth += 2; |
| 1239 | |
| 1240 | sqlite3_stmt_scanstatus_v2(pS,-1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); |
| 1241 | for(i=0; 1; i++){ |
| 1242 | i64 nLoop = 0; |
| 1243 | i64 nRow = 0; |
| @@ -1173,55 +1248,67 @@ | |
| 1248 | const char *zName = 0; |
| 1249 | double rEst = 0.0; |
| 1250 | |
| 1251 | if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){ |
| 1252 | break; |
| 1253 | } |
| 1254 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); |
| 1255 | if( iPid!=prevPid ){ |
| 1256 | prevPid = iPid; |
| 1257 | rEstCum = 1.0; |
| 1258 | } |
| 1259 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_EST,f,(void*)&rEst); |
| 1260 | rEstCum *= rEst; |
| 1261 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); |
| 1262 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); |
| 1263 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); |
| 1264 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); |
| 1265 | sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NAME,f,(void*)&zName); |
| 1266 | |
| 1267 | if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ |
| 1268 | int nSp = 0; |
| 1269 | sqlite3_str_reset(pStats); |
| 1270 | if( nCycle>=0 && nTotal>0 ){ |
| 1271 | qrfApproxInt64(pStats, nCycle); |
| 1272 | sqlite3_str_appendf(pStats, " %3d%%", |
| 1273 | ((nCycle*100)+nTotal/2) / nTotal |
| 1274 | ); |
| 1275 | nSp = 2; |
| 1276 | } |
| 1277 | if( nLoop>=0 ){ |
| 1278 | if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' '); |
| 1279 | qrfApproxInt64(pStats, nLoop); |
| 1280 | nSp = 2; |
| 1281 | if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
| 1282 | sqlite3_str_appendf(pStats, " "); |
| 1283 | qrfApproxInt64(pStats, (i64)(rEstCum/rEst)); |
| 1284 | } |
| 1285 | } |
| 1286 | if( nRow>=0 ){ |
| 1287 | if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' '); |
| 1288 | qrfApproxInt64(pStats, nRow); |
| 1289 | nSp = 2; |
| 1290 | if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
| 1291 | sqlite3_str_appendf(pStats, " "); |
| 1292 | qrfApproxInt64(pStats, (i64)rEstCum); |
| 1293 | } |
| 1294 | } |
| 1295 | sqlite3_str_appendf(pLine, |
| 1296 | "% *s %s", -1*(nWidth-qrfStatsHeight(pS,i)*3), zo, |
| 1297 | sqlite3_str_value(pStats) |
| 1298 | ); |
| 1299 | sqlite3_str_reset(pStats); |
| 1300 | qrfEqpAppend(p, iId, iPid, sqlite3_str_value(pLine)); |
| 1301 | sqlite3_str_reset(pLine); |
| 1302 | }else{ |
| 1303 | qrfEqpAppend(p, iId, iPid, zo); |
| 1304 | } |
| 1305 | } |
| 1306 | if( p->u.pGraph ) p->u.pGraph->nWidth = nWidth; |
| 1307 | qrfStrErr(p, pLine); |
| 1308 | sqlite3_free(sqlite3_str_finish(pLine)); |
| 1309 | qrfStrErr(p, pStats); |
| 1310 | sqlite3_free(sqlite3_str_finish(pStats)); |
| 1311 | #endif |
| 1312 | } |
| 1313 | |
| 1314 | |
| @@ -1581,13 +1668,15 @@ | |
| 1668 | ** (3) z[] does not looks like a numeric literal |
| 1669 | */ |
| 1670 | static int qrfRelaxable(Qrf *p, const char *z){ |
| 1671 | size_t i, n; |
| 1672 | if( z[0]=='\'' || qrfSpace(z[0]) ) return 0; |
| 1673 | if( z[0]==0 ){ |
| 1674 | return (p->spec.zNull!=0 && p->spec.zNull[0]!=0); |
| 1675 | } |
| 1676 | n = strlen(z); |
| 1677 | if( n==0 || z[n-1]=='\'' || qrfSpace(z[n-1]) ) return 0; |
| 1678 | if( p->spec.zNull && strcmp(p->spec.zNull,z)==0 ) return 0; |
| 1679 | i = (z[0]=='-' || z[0]=='+'); |
| 1680 | if( strcmp(z+i,"Inf")==0 ) return 0; |
| 1681 | if( !qrfDigit(z[i]) ) return 1; |
| 1682 | i++; |
| @@ -2729,10 +2818,11 @@ | |
| 2818 | int nNL = 0; |
| 2819 | int n, w; |
| 2820 | pStr = sqlite3_str_new(p->db); |
| 2821 | qrfEncodeText(p, pStr, z ? z : ""); |
| 2822 | n = sqlite3_str_length(pStr); |
| 2823 | qrfStrErr(p, pStr); |
| 2824 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2825 | if( p->spec.nTitleLimit ){ |
| 2826 | nNL = 0; |
| 2827 | data.aiWth[data.n] = w = qrfTitleLimit(data.az[data.n], |
| 2828 | p->spec.nTitleLimit ); |
| @@ -2756,10 +2846,11 @@ | |
| 2846 | int n, w; |
| 2847 | int eType = sqlite3_column_type(p->pStmt,i); |
| 2848 | pStr = sqlite3_str_new(p->db); |
| 2849 | qrfRenderValue(p, pStr, i); |
| 2850 | n = sqlite3_str_length(pStr); |
| 2851 | qrfStrErr(p, pStr); |
| 2852 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2853 | data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT; |
| 2854 | data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2855 | data.n++; |
| 2856 | if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| @@ -2910,11 +3001,11 @@ | |
| 3001 | ){ |
| 3002 | bRTrim = 1; |
| 3003 | }else{ |
| 3004 | bRTrim = 0; |
| 3005 | } |
| 3006 | for(i=0; i<data.n && sqlite3_str_errcode(p->pOut)==SQLITE_OK; i+=nColumn){ |
| 3007 | int bMore; |
| 3008 | int nRow = 0; |
| 3009 | |
| 3010 | /* Draw a single row of the table. This might be the title line |
| 3011 | ** (if there is a title line) or a row in the body of the table. |
| @@ -3097,11 +3188,11 @@ | |
| 3188 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0), "addr" ) ); |
| 3189 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1), "opcode" ) ); |
| 3190 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2), "p1" ) ); |
| 3191 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3), "p2" ) ); |
| 3192 | |
| 3193 | for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt) && !p->iErr; iOp++){ |
| 3194 | int iAddr = sqlite3_column_int(p->pStmt, 0); |
| 3195 | const char *zOp = (const char*)sqlite3_column_text(p->pStmt, 1); |
| 3196 | int p1 = sqlite3_column_int(p->pStmt, 2); |
| 3197 | int p2 = sqlite3_column_int(p->pStmt, 3); |
| 3198 | |
| @@ -3154,11 +3245,11 @@ | |
| 3245 | nWidth = sizeof(aScanExpWidth)/sizeof(int); |
| 3246 | iIndent = 3; |
| 3247 | } |
| 3248 | if( nArg>nWidth ) nArg = nWidth; |
| 3249 | |
| 3250 | for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW && !p->iErr; iOp++){ |
| 3251 | /* If this is the first row seen, print out the headers */ |
| 3252 | if( iOp==0 ){ |
| 3253 | for(i=0; i<nArg; i++){ |
| 3254 | const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]); |
| 3255 | qrfWidthPrint(p,p->pOut, aWidth[i], zCol); |
| @@ -3410,10 +3501,11 @@ | |
| 3501 | sqlite3_str_append(p->pOut, "\n", 1); |
| 3502 | zVal += iNext; |
| 3503 | }while( zVal[0] ); |
| 3504 | sqlite3_str_reset(pVal); |
| 3505 | } |
| 3506 | qrfStrErr(p, pVal); |
| 3507 | sqlite3_free(sqlite3_str_finish(pVal)); |
| 3508 | qrfWrite(p); |
| 3509 | break; |
| 3510 | } |
| 3511 | case QRF_STYLE_Eqp: { |
| @@ -3473,11 +3565,11 @@ | |
| 3565 | p->pOut = sqlite3_str_new(p->db); |
| 3566 | if( p->pOut==0 ){ |
| 3567 | qrfOom(p); |
| 3568 | return; |
| 3569 | } |
| 3570 | p->iErr = SQLITE_OK; |
| 3571 | p->nCol = sqlite3_column_count(p->pStmt); |
| 3572 | p->nRow = 0; |
| 3573 | sz = sizeof(sqlite3_qrf_spec); |
| 3574 | memcpy(&p->spec, pSpec, sz); |
| 3575 | if( p->spec.zNull==0 ) p->spec.zNull = ""; |
| @@ -3644,17 +3736,27 @@ | |
| 3736 | sqlite3_free(p->u.sLine.azCol); |
| 3737 | } |
| 3738 | break; |
| 3739 | } |
| 3740 | case QRF_STYLE_Stats: |
| 3741 | case QRF_STYLE_StatsEst: { |
| 3742 | i64 nCycle = 0; |
| 3743 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 3744 | sqlite3_stmt_scanstatus_v2(p->pStmt, -1, SQLITE_SCANSTAT_NCYCLE, |
| 3745 | SQLITE_SCANSTAT_COMPLEX, (void*)&nCycle); |
| 3746 | #endif |
| 3747 | qrfEqpRender(p, nCycle); |
| 3748 | qrfWrite(p); |
| 3749 | break; |
| 3750 | } |
| 3751 | case QRF_STYLE_Eqp: { |
| 3752 | qrfEqpRender(p, 0); |
| 3753 | qrfWrite(p); |
| 3754 | break; |
| 3755 | } |
| 3756 | } |
| 3757 | qrfStrErr(p, p->pOut); |
| 3758 | if( p->spec.pzOutput ){ |
| 3759 | if( p->spec.pzOutput[0] ){ |
| 3760 | sqlite3_int64 n, sz; |
| 3761 | char *zCombined; |
| 3762 | sz = strlen(p->spec.pzOutput[0]); |
| @@ -3815,13 +3917,10 @@ | |
| 3917 | |
| 3918 | |
| 3919 | #define eputz(z) cli_puts(z,stderr) |
| 3920 | #define sputz(fp,z) cli_puts(z,fp) |
| 3921 | |
| 3922 | /* A version of strcmp() that works with NULL values */ |
| 3923 | static int cli_strcmp(const char *a, const char *b){ |
| 3924 | if( a==0 ) a = ""; |
| 3925 | if( b==0 ) b = ""; |
| 3926 | return strcmp(a,b); |
| @@ -3834,11 +3933,11 @@ | |
| 3933 | |
| 3934 | /* Return the current wall-clock time in microseconds since the |
| 3935 | ** Unix epoch (1970-01-01T00:00:00Z) |
| 3936 | */ |
| 3937 | static sqlite3_int64 timeOfDay(void){ |
| 3938 | #if defined(_WIN64) && _WIN32_WINNT >= _WIN32_WINNT_WIN8 |
| 3939 | sqlite3_uint64 t; |
| 3940 | FILETIME tm; |
| 3941 | GetSystemTimePreciseAsFileTime(&tm); |
| 3942 | t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime; |
| 3943 | t += 116444736000000000LL; |
| @@ -3862,154 +3961,10 @@ | |
| 3961 | (void)gettimeofday(&sNow,0); |
| 3962 | return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec; |
| 3963 | #endif |
| 3964 | } |
| 3965 | |
| 3966 | |
| 3967 | /* |
| 3968 | ** Used to prevent warnings about unused parameters |
| 3969 | */ |
| 3970 | #define UNUSED_PARAMETER(x) (void)(x) |
| @@ -4068,10 +4023,18 @@ | |
| 4023 | #define PROMPT_LEN_MAX 128 |
| 4024 | /* First line prompt. default: "sqlite> " */ |
| 4025 | static char mainPrompt[PROMPT_LEN_MAX]; |
| 4026 | /* Continuation prompt. default: " ...> " */ |
| 4027 | static char continuePrompt[PROMPT_LEN_MAX]; |
| 4028 | |
| 4029 | /* |
| 4030 | ** Write I/O traces to the following stream. |
| 4031 | */ |
| 4032 | #ifdef SQLITE_ENABLE_IOTRACE |
| 4033 | static FILE *iotrace = 0; |
| 4034 | #endif |
| 4035 | |
| 4036 | |
| 4037 | /* This is variant of the standard-library strncpy() routine with the |
| 4038 | ** one change that the destination string is always zero-terminated, even |
| 4039 | ** if there is no zero-terminator in the first n-1 characters of the source |
| 4040 | ** string. |
| @@ -4188,17 +4151,10 @@ | |
| 4151 | */ |
| 4152 | static void shell_check_oom(const void *p){ |
| 4153 | if( p==0 ) shell_out_of_memory(); |
| 4154 | } |
| 4155 | |
| 4156 | /* |
| 4157 | ** This routine works like printf in that its first argument is a |
| 4158 | ** format string and subsequent arguments are values to be substituted |
| 4159 | ** in place of % fields. The result of formatting this string |
| 4160 | ** is written to iotrace. |
| @@ -8349,13 +8305,13 @@ | |
| 8305 | }else if( e<-10000 ){ |
| 8306 | e = -10000; |
| 8307 | } |
| 8308 | |
| 8309 | if( m<0 ){ |
| 8310 | if( m<(-9223372036854775807LL) ) return; |
| 8311 | isNeg = 1; |
| 8312 | m = -m; |
| 8313 | }else if( m==0 && e>-1000 && e<1000 ){ |
| 8314 | sqlite3_result_double(context, 0.0); |
| 8315 | return; |
| 8316 | } |
| 8317 | while( (m>>32)&0xffe00000 ){ |
| @@ -9472,11 +9428,11 @@ | |
| 9428 | ** (X) match X |
| 9429 | ** X|Y X or Y |
| 9430 | ** ^X X occurring at the beginning of the string |
| 9431 | ** X$ X occurring at the end of the string |
| 9432 | ** . Match any single character |
| 9433 | ** \c Character c where c is one of \{}()[]|*+?-. |
| 9434 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 9435 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 9436 | ** \xXX Where XX is exactly 2 hex digits, unicode value XX |
| 9437 | ** [abc] Any single character from the set abc |
| 9438 | ** [^abc] Any single character not in the set abc |
| @@ -9857,11 +9813,11 @@ | |
| 9813 | |
| 9814 | /* A backslash character has been seen, read the next character and |
| 9815 | ** return its interpretation. |
| 9816 | */ |
| 9817 | static unsigned re_esc_char(ReCompiled *p){ |
| 9818 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-"; |
| 9819 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 9820 | int i, v = 0; |
| 9821 | char c; |
| 9822 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 9823 | c = p->sIn.z[p->sIn.i]; |
| @@ -10180,15 +10136,22 @@ | |
| 10136 | } |
| 10137 | return pRe->zErr; |
| 10138 | } |
| 10139 | |
| 10140 | /* |
| 10141 | ** The value of LIMIT_MAX_PATTERN_LENGTH. |
| 10142 | */ |
| 10143 | static int re_maxlen(sqlite3_context *context){ |
| 10144 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 10145 | return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1); |
| 10146 | } |
| 10147 | |
| 10148 | /* |
| 10149 | ** Maximum NFA size given a maximum pattern length. |
| 10150 | */ |
| 10151 | static int re_maxnfa(int mxlen){ |
| 10152 | return 75+mxlen/2; |
| 10153 | } |
| 10154 | |
| 10155 | /* |
| 10156 | ** Implementation of the regexp() SQL function. This function implements |
| 10157 | ** the build-in REGEXP operator. The first argument to the function is the |
| @@ -10210,14 +10173,21 @@ | |
| 10173 | int setAux = 0; /* True to invoke sqlite3_set_auxdata() */ |
| 10174 | |
| 10175 | (void)argc; /* Unused */ |
| 10176 | pRe = sqlite3_get_auxdata(context, 0); |
| 10177 | if( pRe==0 ){ |
| 10178 | int mxLen = re_maxlen(context); |
| 10179 | int nPattern; |
| 10180 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10181 | if( zPattern==0 ) return; |
| 10182 | nPattern = sqlite3_value_bytes(argv[0]); |
| 10183 | if( nPattern>mxLen ){ |
| 10184 | zErr = "REGEXP pattern too big"; |
| 10185 | }else{ |
| 10186 | zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen), |
| 10187 | sqlite3_user_data(context)!=0); |
| 10188 | } |
| 10189 | if( zErr ){ |
| 10190 | re_free(pRe); |
| 10191 | sqlite3_result_error(context, zErr, -1); |
| 10192 | return; |
| 10193 | } |
| @@ -10279,11 +10249,11 @@ | |
| 10249 | "ATSTART", |
| 10250 | }; |
| 10251 | |
| 10252 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10253 | if( zPattern==0 ) return; |
| 10254 | zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)), |
| 10255 | sqlite3_user_data(context)!=0); |
| 10256 | if( zErr ){ |
| 10257 | re_free(pRe); |
| 10258 | sqlite3_result_error(context, zErr, -1); |
| 10259 | return; |
| @@ -13067,11 +13037,11 @@ | |
| 13037 | nFile = (int)strlen(zFile)+1; |
| 13038 | } |
| 13039 | |
| 13040 | rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA); |
| 13041 | if( rc==SQLITE_OK ){ |
| 13042 | pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile); |
| 13043 | if( pNew==0 ) return SQLITE_NOMEM; |
| 13044 | memset(pNew, 0, nByte+nFile); |
| 13045 | pNew->db = db; |
| 13046 | pNew->aBuffer = (u8*)&pNew[1]; |
| 13047 | if( zFile ){ |
| @@ -13213,18 +13183,19 @@ | |
| 13183 | ** sqlite3_free(). |
| 13184 | */ |
| 13185 | static int zipfileReadData( |
| 13186 | FILE *pFile, /* Read from this file */ |
| 13187 | u8 *aRead, /* Read into this buffer */ |
| 13188 | i64 nRead, /* Number of bytes to read */ |
| 13189 | i64 iOff, /* Offset to read from */ |
| 13190 | char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */ |
| 13191 | ){ |
| 13192 | size_t n; |
| 13193 | fseek(pFile, (long)iOff, SEEK_SET); |
| 13194 | n = fread(aRead, 1, (long)nRead, pFile); |
| 13195 | if( n!=(size_t)nRead ){ |
| 13196 | sqlite3_free(*pzErrmsg); |
| 13197 | *pzErrmsg = sqlite3_mprintf("error in fread()"); |
| 13198 | return SQLITE_ERROR; |
| 13199 | } |
| 13200 | return SQLITE_OK; |
| 13201 | } |
| @@ -13237,11 +13208,11 @@ | |
| 13208 | if( nWrite>0 ){ |
| 13209 | size_t n = nWrite; |
| 13210 | fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET); |
| 13211 | n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd); |
| 13212 | if( (int)n!=nWrite ){ |
| 13213 | zipfileTableErr(pTab,"error in fwrite()"); |
| 13214 | return SQLITE_ERROR; |
| 13215 | } |
| 13216 | pTab->szCurrent += nWrite; |
| 13217 | } |
| 13218 | return SQLITE_OK; |
| @@ -13484,10 +13455,11 @@ | |
| 13455 | /* |
| 13456 | ** Set (*pzErr) to point to a buffer from sqlite3_malloc() containing a |
| 13457 | ** generic corruption message and return SQLITE_CORRUPT; |
| 13458 | */ |
| 13459 | static int zipfileCorrupt(char **pzErr){ |
| 13460 | sqlite3_free(*pzErr); |
| 13461 | *pzErr = sqlite3_mprintf("zip archive is corrupt"); |
| 13462 | return SQLITE_CORRUPT; |
| 13463 | } |
| 13464 | |
| 13465 | /* |
| @@ -13502,11 +13474,11 @@ | |
| 13474 | ** final value of (*ppEntry) undefined. |
| 13475 | */ |
| 13476 | static int zipfileGetEntry( |
| 13477 | ZipfileTab *pTab, /* Store any error message here */ |
| 13478 | const u8 *aBlob, /* Pointer to in-memory file image */ |
| 13479 | i64 nBlob, /* Size of aBlob[] in bytes */ |
| 13480 | FILE *pFile, /* If aBlob==0, read from this file */ |
| 13481 | i64 iOff, /* Offset of CDS record */ |
| 13482 | ZipfileEntry **ppEntry /* OUT: Pointer to new object */ |
| 13483 | ){ |
| 13484 | u8 *aRead; |
| @@ -13542,18 +13514,18 @@ | |
| 13514 | rc = SQLITE_NOMEM; |
| 13515 | }else{ |
| 13516 | memset(pNew, 0, sizeof(ZipfileEntry)); |
| 13517 | rc = zipfileReadCDS(aRead, &pNew->cds); |
| 13518 | if( rc!=SQLITE_OK ){ |
| 13519 | zipfileTableErr(pTab, "failed to read CDS at offset %lld", iOff); |
| 13520 | }else if( aBlob==0 ){ |
| 13521 | rc = zipfileReadData( |
| 13522 | pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr |
| 13523 | ); |
| 13524 | }else{ |
| 13525 | aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ]; |
| 13526 | if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){ |
| 13527 | rc = zipfileCorrupt(pzErr); |
| 13528 | } |
| 13529 | } |
| 13530 | } |
| 13531 | |
| @@ -13574,19 +13546,19 @@ | |
| 13546 | ZipfileLFH lfh; |
| 13547 | if( pFile ){ |
| 13548 | rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr); |
| 13549 | }else{ |
| 13550 | aRead = (u8*)&aBlob[pNew->cds.iOffset]; |
| 13551 | if( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){ |
| 13552 | rc = zipfileCorrupt(pzErr); |
| 13553 | } |
| 13554 | } |
| 13555 | |
| 13556 | memset(&lfh, 0, sizeof(lfh)); |
| 13557 | if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh); |
| 13558 | if( rc==SQLITE_OK ){ |
| 13559 | pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; |
| 13560 | pNew->iDataOff += lfh.nFile + lfh.nExtra; |
| 13561 | if( aBlob && pNew->cds.szCompressed ){ |
| 13562 | if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){ |
| 13563 | rc = zipfileCorrupt(pzErr); |
| 13564 | }else{ |
| @@ -13593,11 +13565,11 @@ | |
| 13565 | pNew->aData = &pNew->aExtra[nExtra]; |
| 13566 | memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed); |
| 13567 | } |
| 13568 | } |
| 13569 | }else{ |
| 13570 | zipfileTableErr(pTab, "failed to read LFH at offset %d", |
| 13571 | (int)pNew->cds.iOffset |
| 13572 | ); |
| 13573 | } |
| 13574 | } |
| 13575 | |
| @@ -13617,11 +13589,11 @@ | |
| 13589 | static int zipfileNext(sqlite3_vtab_cursor *cur){ |
| 13590 | ZipfileCsr *pCsr = (ZipfileCsr*)cur; |
| 13591 | int rc = SQLITE_OK; |
| 13592 | |
| 13593 | if( pCsr->pFile ){ |
| 13594 | i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize; |
| 13595 | zipfileEntryFree(pCsr->pCurrent); |
| 13596 | pCsr->pCurrent = 0; |
| 13597 | if( pCsr->iNextOff>=iEof ){ |
| 13598 | pCsr->bEof = 1; |
| 13599 | }else{ |
| @@ -13855,16 +13827,16 @@ | |
| 13827 | ** an English language error message may be left in virtual-table pTab. |
| 13828 | */ |
| 13829 | static int zipfileReadEOCD( |
| 13830 | ZipfileTab *pTab, /* Return errors here */ |
| 13831 | const u8 *aBlob, /* Pointer to in-memory file image */ |
| 13832 | i64 nBlob, /* Size of aBlob[] in bytes */ |
| 13833 | FILE *pFile, /* Read from this file if aBlob==0 */ |
| 13834 | ZipfileEOCD *pEOCD /* Object to populate */ |
| 13835 | ){ |
| 13836 | u8 *aRead = pTab->aBuffer; /* Temporary buffer */ |
| 13837 | i64 nRead; /* Bytes to read from file */ |
| 13838 | int rc = SQLITE_OK; |
| 13839 | |
| 13840 | memset(pEOCD, 0, sizeof(ZipfileEOCD)); |
| 13841 | if( aBlob==0 ){ |
| 13842 | i64 iOff; /* Offset to read from */ |
| @@ -13881,11 +13853,11 @@ | |
| 13853 | nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE)); |
| 13854 | aRead = (u8*)&aBlob[nBlob-nRead]; |
| 13855 | } |
| 13856 | |
| 13857 | if( rc==SQLITE_OK ){ |
| 13858 | i64 i; |
| 13859 | |
| 13860 | /* Scan backwards looking for the signature bytes */ |
| 13861 | for(i=nRead-20; i>=0; i--){ |
| 13862 | if( aRead[i]==0x50 && aRead[i+1]==0x4b |
| 13863 | && aRead[i+2]==0x05 && aRead[i+3]==0x06 |
| @@ -13892,13 +13864,11 @@ | |
| 13864 | ){ |
| 13865 | break; |
| 13866 | } |
| 13867 | } |
| 13868 | if( i<0 ){ |
| 13869 | zipfileTableErr(pTab, "cannot find end of central directory record"); |
| 13870 | return SQLITE_ERROR; |
| 13871 | } |
| 13872 | |
| 13873 | aRead += i+4; |
| 13874 | pEOCD->iDisk = zipfileRead16(aRead); |
| @@ -13939,11 +13909,11 @@ | |
| 13909 | pNew->pNext = pBefore; |
| 13910 | *pp = pNew; |
| 13911 | } |
| 13912 | } |
| 13913 | |
| 13914 | static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){ |
| 13915 | ZipfileEOCD eocd; |
| 13916 | int rc; |
| 13917 | int i; |
| 13918 | i64 iOff; |
| 13919 | |
| @@ -13987,11 +13957,11 @@ | |
| 13957 | zipfileCursorErr(pCsr, "zipfile() function requires an argument"); |
| 13958 | return SQLITE_ERROR; |
| 13959 | }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ |
| 13960 | static const u8 aEmptyBlob = 0; |
| 13961 | const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]); |
| 13962 | i64 nBlob = sqlite3_value_bytes(argv[0]); |
| 13963 | assert( pTab->pFirstEntry==0 ); |
| 13964 | if( aBlob==0 ){ |
| 13965 | aBlob = &aEmptyBlob; |
| 13966 | nBlob = 0; |
| 13967 | } |
| @@ -14185,23 +14155,23 @@ | |
| 14155 | ZipfileTab *pTab = (ZipfileTab*)pVtab; |
| 14156 | int rc = SQLITE_OK; |
| 14157 | |
| 14158 | assert( pTab->pWriteFd==0 ); |
| 14159 | if( pTab->zFile==0 || pTab->zFile[0]==0 ){ |
| 14160 | zipfileTableErr(pTab, "zipfile: missing filename"); |
| 14161 | return SQLITE_ERROR; |
| 14162 | } |
| 14163 | |
| 14164 | /* Open a write fd on the file. Also load the entire central directory |
| 14165 | ** structure into memory. During the transaction any new file data is |
| 14166 | ** appended to the archive file, but the central directory is accumulated |
| 14167 | ** in main-memory until the transaction is committed. */ |
| 14168 | pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+"); |
| 14169 | if( pTab->pWriteFd==0 ){ |
| 14170 | zipfileTableErr(pTab, |
| 14171 | "zipfile: failed to open file %s for writing", pTab->zFile |
| 14172 | ); |
| 14173 | rc = SQLITE_ERROR; |
| 14174 | }else{ |
| 14175 | fseek(pTab->pWriteFd, 0, SEEK_END); |
| 14176 | pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); |
| 14177 | rc = zipfileLoadDirectory(pTab, 0, 0); |
| @@ -14662,11 +14632,11 @@ | |
| 14632 | int nEntry; |
| 14633 | ZipfileBuffer body; |
| 14634 | ZipfileBuffer cds; |
| 14635 | }; |
| 14636 | |
| 14637 | static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){ |
| 14638 | if( pBuf->n+nByte>pBuf->nAlloc ){ |
| 14639 | u8 *aNew; |
| 14640 | sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512; |
| 14641 | int nReq = pBuf->n + nByte; |
| 14642 | |
| @@ -14711,11 +14681,11 @@ | |
| 14681 | u32 iCrc32 = 0; /* crc32 of uncompressed data */ |
| 14682 | |
| 14683 | char *zName = 0; /* Path (name) of new entry */ |
| 14684 | int nName = 0; /* Size of zName in bytes */ |
| 14685 | char *zFree = 0; /* Free this before returning */ |
| 14686 | i64 nByte; |
| 14687 | |
| 14688 | memset(&e, 0, sizeof(e)); |
| 14689 | p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); |
| 14690 | if( p==0 ) return; |
| 14691 | |
| @@ -24161,28 +24131,28 @@ | |
| 24131 | u8 autoExplain; /* Automatically turn on .explain mode */ |
| 24132 | u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ |
| 24133 | u8 autoEQPtrace; /* autoEQP is in trace mode */ |
| 24134 | u8 scanstatsOn; /* True to display scan stats before each finalize */ |
| 24135 | u8 bAutoScreenWidth; /* Using the TTY to determine screen width */ |
| 24136 | u8 mFlags; /* MFLG_ECHO, MFLG_CRLF, etc. */ |
| 24137 | u8 eMode; /* One of the MODE_ values */ |
| 24138 | sqlite3_qrf_spec spec; /* Spec to be passed into QRF */ |
| 24139 | } Mode; |
| 24140 | |
| 24141 | /* Flags for Mode.mFlags */ |
| 24142 | #define MFLG_ECHO 0x01 /* Echo inputs to output */ |
| 24143 | #define MFLG_CRLF 0x02 /* Use CR/LF output line endings */ |
| 24144 | #define MFLG_HDR 0x04 /* .header used to change headers on/off */ |
| 24145 | |
| 24146 | |
| 24147 | /* |
| 24148 | ** State information about the database connection is contained in an |
| 24149 | ** instance of the following structure. |
| 24150 | */ |
| 24151 | typedef struct ShellState ShellState; |
| 24152 | struct ShellState { |
| 24153 | sqlite3 *db; /* The database */ |
| 24154 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ |
| 24155 | u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ |
| 24156 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 24157 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 24158 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| @@ -24190,11 +24160,14 @@ | |
| 24160 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 24161 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 24162 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 24163 | u8 nPopOutput; /* Revert .output settings when reaching zero */ |
| 24164 | u8 nPopMode; /* Revert .mode settings when reaching zero */ |
| 24165 | u8 enableTimer; /* Enable the timer. 2: permanently 1: only once */ |
| 24166 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 24167 | double prevTimer; /* Last reported timer value */ |
| 24168 | double tmProgress; /* --timeout option for .progress */ |
| 24169 | i64 lineno; /* Line number of last line read from in */ |
| 24170 | const char *zInFile; /* Name of the input file */ |
| 24171 | int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ |
| 24172 | FILE *in; /* Read commands from this stream */ |
| 24173 | FILE *out; /* Write results here */ |
| @@ -24286,10 +24259,11 @@ | |
| 24259 | #define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */ |
| 24260 | #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress |
| 24261 | ** callback limit is reached, and for each |
| 24262 | ** top-level SQL statement */ |
| 24263 | #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ |
| 24264 | #define SHELL_PROGRESS_TMOUT 0x08 /* Stop after tmProgress seconds */ |
| 24265 | |
| 24266 | /* Names of values for Mode.spec.eEsc and Mode.spec.eText |
| 24267 | */ |
| 24268 | static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; |
| 24269 | static const char *qrfQuoteNames[] = |
| @@ -24447,10 +24421,181 @@ | |
| 24421 | ** Limit input nesting via .read or any other input redirect. |
| 24422 | ** It's not too expensive, so a generous allowance can be made. |
| 24423 | */ |
| 24424 | #define MAX_INPUT_NESTING 25 |
| 24425 | |
| 24426 | /************************* BEGIN PERFORMANCE TIMER *****************************/ |
| 24427 | #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) |
| 24428 | #include <sys/time.h> |
| 24429 | #include <sys/resource.h> |
| 24430 | /* VxWorks does not support getrusage() as far as we can determine */ |
| 24431 | #if defined(_WRS_KERNEL) || defined(__RTP__) |
| 24432 | struct rusage { |
| 24433 | struct timeval ru_utime; /* user CPU time used */ |
| 24434 | struct timeval ru_stime; /* system CPU time used */ |
| 24435 | }; |
| 24436 | #define getrusage(A,B) memset(B,0,sizeof(*B)) |
| 24437 | #endif |
| 24438 | |
| 24439 | /* Saved resource information for the beginning of an operation */ |
| 24440 | static struct rusage sBegin; /* CPU time at start */ |
| 24441 | static sqlite3_int64 iBegin; /* Wall-clock time at start */ |
| 24442 | |
| 24443 | /* |
| 24444 | ** Begin timing an operation |
| 24445 | */ |
| 24446 | static void beginTimer(ShellState *p){ |
| 24447 | if( p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0 ){ |
| 24448 | getrusage(RUSAGE_SELF, &sBegin); |
| 24449 | iBegin = timeOfDay(); |
| 24450 | } |
| 24451 | } |
| 24452 | |
| 24453 | /* Return the difference of two time_structs in seconds */ |
| 24454 | static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ |
| 24455 | return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + |
| 24456 | (double)(pEnd->tv_sec - pStart->tv_sec); |
| 24457 | } |
| 24458 | |
| 24459 | /* Return the time since the start of the timer in |
| 24460 | ** seconds. */ |
| 24461 | static double elapseTime(ShellState *NotUsed){ |
| 24462 | (void)NotUsed; |
| 24463 | if( iBegin==0 ) return 0.0; |
| 24464 | return (timeOfDay() - iBegin)*0.000001; |
| 24465 | } |
| 24466 | |
| 24467 | /* |
| 24468 | ** Print the timing results. |
| 24469 | */ |
| 24470 | static void endTimer(ShellState *p){ |
| 24471 | if( p->enableTimer ){ |
| 24472 | sqlite3_int64 iEnd = timeOfDay(); |
| 24473 | struct rusage sEnd; |
| 24474 | getrusage(RUSAGE_SELF, &sEnd); |
| 24475 | p->prevTimer = (iEnd - iBegin)*0.000001; |
| 24476 | cli_printf(p->out, "Run Time: real %.6f user %.6f sys %.6f\n", |
| 24477 | p->prevTimer, |
| 24478 | timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
| 24479 | timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
| 24480 | if( p->enableTimer==1 ) p->enableTimer = 0; |
| 24481 | iBegin = 0; |
| 24482 | } |
| 24483 | } |
| 24484 | |
| 24485 | #define BEGIN_TIMER(X) beginTimer(X) |
| 24486 | #define END_TIMER(X) endTimer(X) |
| 24487 | #define ELAPSE_TIME(X) elapseTime(X) |
| 24488 | #define HAS_TIMER 1 |
| 24489 | |
| 24490 | #elif (defined(_WIN32) || defined(WIN32)) |
| 24491 | |
| 24492 | /* Saved resource information for the beginning of an operation */ |
| 24493 | static HANDLE hProcess; |
| 24494 | static FILETIME ftKernelBegin; |
| 24495 | static FILETIME ftUserBegin; |
| 24496 | static sqlite3_int64 ftWallBegin; |
| 24497 | typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, |
| 24498 | LPFILETIME, LPFILETIME); |
| 24499 | static GETPROCTIMES getProcessTimesAddr = NULL; |
| 24500 | |
| 24501 | /* |
| 24502 | ** Check to see if we have timer support. Return 1 if necessary |
| 24503 | ** support found (or found previously). |
| 24504 | */ |
| 24505 | static int hasTimer(void){ |
| 24506 | if( getProcessTimesAddr ){ |
| 24507 | return 1; |
| 24508 | } else { |
| 24509 | /* GetProcessTimes() isn't supported in WIN95 and some other Windows |
| 24510 | ** versions. See if the version we are running on has it, and if it |
| 24511 | ** does, save off a pointer to it and the current process handle. |
| 24512 | */ |
| 24513 | hProcess = GetCurrentProcess(); |
| 24514 | if( hProcess ){ |
| 24515 | HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); |
| 24516 | if( NULL != hinstLib ){ |
| 24517 | getProcessTimesAddr = |
| 24518 | (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); |
| 24519 | if( NULL != getProcessTimesAddr ){ |
| 24520 | return 1; |
| 24521 | } |
| 24522 | FreeLibrary(hinstLib); |
| 24523 | } |
| 24524 | } |
| 24525 | } |
| 24526 | return 0; |
| 24527 | } |
| 24528 | |
| 24529 | /* |
| 24530 | ** Begin timing an operation |
| 24531 | */ |
| 24532 | static void beginTimer(ShellState *p){ |
| 24533 | if( (p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0) |
| 24534 | && getProcessTimesAddr |
| 24535 | ){ |
| 24536 | FILETIME ftCreation, ftExit; |
| 24537 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit, |
| 24538 | &ftKernelBegin,&ftUserBegin); |
| 24539 | ftWallBegin = timeOfDay(); |
| 24540 | } |
| 24541 | } |
| 24542 | |
| 24543 | /* Return the difference of two FILETIME structs in seconds */ |
| 24544 | static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ |
| 24545 | sqlite_int64 i64Start = *((sqlite_int64 *) pStart); |
| 24546 | sqlite_int64 i64End = *((sqlite_int64 *) pEnd); |
| 24547 | return (double) ((i64End - i64Start) / 10000000.0); |
| 24548 | } |
| 24549 | |
| 24550 | /* Return the time since the start of the timer in |
| 24551 | ** seconds. */ |
| 24552 | static double elapseTime(ShellState *NotUsed){ |
| 24553 | (void)NotUsed; |
| 24554 | if( ftWallBegin==0 ) return 0.0; |
| 24555 | return (timeOfDay() - ftWallBegin)*0.000001; |
| 24556 | } |
| 24557 | |
| 24558 | /* |
| 24559 | ** Print the timing results. |
| 24560 | */ |
| 24561 | static void endTimer(ShellState *p){ |
| 24562 | if( p->enableTimer && getProcessTimesAddr){ |
| 24563 | FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
| 24564 | sqlite3_int64 ftWallEnd = timeOfDay(); |
| 24565 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
| 24566 | p->prevTimer = (ftWallEnd - ftWallBegin)*0.000001; |
| 24567 | #ifdef _WIN64 |
| 24568 | /* microsecond precision on 64-bit windows */ |
| 24569 | cli_printf(p->out, "Run Time: real %.6f user %f sys %f\n", |
| 24570 | p->prevTimer, |
| 24571 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 24572 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 24573 | #else |
| 24574 | /* millisecond precisino on 32-bit windows */ |
| 24575 | cli_printf(p->out, "Run Time: real %.3f user %.3f sys %.3f\n", |
| 24576 | p->prevTimer, |
| 24577 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 24578 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 24579 | #endif |
| 24580 | if( p->enableTimer==1 ) p->enableTimer = 0; |
| 24581 | ftWallBegin = 0; |
| 24582 | } |
| 24583 | } |
| 24584 | |
| 24585 | #define BEGIN_TIMER(X) beginTimer(X) |
| 24586 | #define ELAPSE_TIME(X) elapseTime(X) |
| 24587 | #define END_TIMER(X) endTimer(X) |
| 24588 | #define HAS_TIMER hasTimer() |
| 24589 | |
| 24590 | #else |
| 24591 | #define BEGIN_TIMER(X) /* no-op */ |
| 24592 | #define ELAPSE_TIME(X) 0.0 |
| 24593 | #define END_TIMER(X) /*no-op*/ |
| 24594 | #define HAS_TIMER 0 |
| 24595 | #endif |
| 24596 | /************************* END PERFORMANCE TIMER ******************************/ |
| 24597 | |
| 24598 | /* |
| 24599 | ** Clear a display mode, freeing any allocated memory that it |
| 24600 | ** contains. |
| 24601 | */ |
| @@ -24534,11 +24679,13 @@ | |
| 24679 | if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]); |
| 24680 | if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]); |
| 24681 | if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]); |
| 24682 | pM->spec.eText = pI->eText; |
| 24683 | pM->spec.eBlob = pI->eBlob; |
| 24684 | if( (pM->mFlags & MFLG_HDR)==0 ){ |
| 24685 | pM->spec.bTitles = pI->bHdr; |
| 24686 | } |
| 24687 | pM->spec.eTitle = pI->eHdr; |
| 24688 | if( pI->mFlg & 0x01 ){ |
| 24689 | pM->spec.bBorder = QRF_No; |
| 24690 | }else{ |
| 24691 | pM->spec.bBorder = QRF_Auto; |
| @@ -24570,18 +24717,17 @@ | |
| 24717 | p->mode.mFlags = mFlags; |
| 24718 | } |
| 24719 | } |
| 24720 | |
| 24721 | /* |
| 24722 | ** Set the mode to the default. It assumed that the mode has |
| 24723 | ** already been freed and zeroed prior to calling this routine. |
| 24724 | */ |
| 24725 | static void modeDefault(ShellState *p){ |
| 24726 | p->mode.spec.iVersion = 1; |
| 24727 | p->mode.autoExplain = 1; |
| 24728 | if( stdin_is_interactive || stdout_is_console ){ |
| 24729 | modeChange(p, MODE_TTY); |
| 24730 | }else{ |
| 24731 | modeChange(p, MODE_BATCH); |
| 24732 | } |
| 24733 | } |
| @@ -25371,10 +25517,17 @@ | |
| 25517 | ** Progress handler callback. |
| 25518 | */ |
| 25519 | static int progress_handler(void *pClientData) { |
| 25520 | ShellState *p = (ShellState*)pClientData; |
| 25521 | p->nProgress++; |
| 25522 | if( (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0 |
| 25523 | && ELAPSE_TIME(p)>=p->tmProgress |
| 25524 | ){ |
| 25525 | cli_printf(p->out, "Progress timeout after %.6f seconds\n", |
| 25526 | ELAPSE_TIME(p)); |
| 25527 | return 1; |
| 25528 | } |
| 25529 | if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ |
| 25530 | cli_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); |
| 25531 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 25532 | if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; |
| 25533 | return 1; |
| @@ -25908,10 +26061,12 @@ | |
| 26061 | char *zBuf = sqlite3_malloc64( szVar-5 ); |
| 26062 | if( zBuf ){ |
| 26063 | memcpy(zBuf, &zVar[6], szVar-5); |
| 26064 | sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8); |
| 26065 | } |
| 26066 | }else if( strcmp(zVar, "$TIMER")==0 ){ |
| 26067 | sqlite3_bind_double(pStmt, i, pArg->prevTimer); |
| 26068 | #ifdef SQLITE_ENABLE_CARRAY |
| 26069 | }else if( strncmp(zVar, "$carray_", 8)==0 ){ |
| 26070 | static char *azColorNames[] = { |
| 26071 | "azure", "black", "blue", "brown", "cyan", "fuchsia", "gold", |
| 26072 | "gray", "green", "indigo", "khaki", "lime", "magenta", "maroon", |
| @@ -26167,27 +26322,36 @@ | |
| 26322 | pArg->pStmt = pStmt; |
| 26323 | } |
| 26324 | |
| 26325 | /* Show the EXPLAIN QUERY PLAN if .eqp is on */ |
| 26326 | isExplain = sqlite3_stmt_isexplain(pStmt); |
| 26327 | if( pArg && pArg->mode.autoEQP && isExplain==0 && pArg->dot.nArg==0 ){ |
| 26328 | int triggerEQP = 0; |
| 26329 | u8 savedEnableTimer = pArg->enableTimer; |
| 26330 | pArg->enableTimer = 0; |
| 26331 | disable_debug_trace_modes(); |
| 26332 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); |
| 26333 | if( pArg->mode.autoEQP>=AUTOEQP_trigger ){ |
| 26334 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); |
| 26335 | } |
| 26336 | sqlite3_reset(pStmt); |
| 26337 | spec.eStyle = QRF_STYLE_Auto; |
| 26338 | sqlite3_stmt_explain(pStmt, 2); |
| 26339 | sqlite3_format_query_result(pStmt, &spec, 0); |
| 26340 | if( pArg->mode.autoEQP>=AUTOEQP_full ){ |
| 26341 | sqlite3_reset(pStmt); |
| 26342 | sqlite3_stmt_explain(pStmt, 1); |
| 26343 | sqlite3_format_query_result(pStmt, &spec, 0); |
| 26344 | } |
| 26345 | |
| 26346 | if( pArg->mode.autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ |
| 26347 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); |
| 26348 | } |
| 26349 | sqlite3_reset(pStmt); |
| 26350 | sqlite3_stmt_explain(pStmt, 0); |
| 26351 | restore_debug_trace_modes(); |
| 26352 | pArg->enableTimer = savedEnableTimer; |
| 26353 | } |
| 26354 | |
| 26355 | bind_prepared_stmt(pArg, pStmt); |
| 26356 | if( isExplain && pArg->mode.autoExplain ){ |
| 26357 | spec.eStyle = isExplain==1 ? QRF_STYLE_Explain : QRF_STYLE_Eqp; |
| @@ -26604,12 +26768,12 @@ | |
| 26768 | ".bail on|off Stop after hitting an error. Default OFF", |
| 26769 | #ifndef SQLITE_SHELL_FIDDLE |
| 26770 | ".cd DIRECTORY Change the working directory to DIRECTORY", |
| 26771 | #endif |
| 26772 | ".changes on|off Show number of rows changed by SQL", |
| 26773 | ".check OPTIONS ... Verify the results of a .testcase", |
| 26774 | #ifndef SQLITE_SHELL_FIDDLE |
| 26775 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 26776 | #endif |
| 26777 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 26778 | ".crlf ?on|off? Whether or not to use \\r\\n line endings", |
| 26779 | ".databases List names and files of attached databases", |
| @@ -26721,10 +26885,11 @@ | |
| 26885 | ".progress N Invoke progress handler after every N opcodes", |
| 26886 | " --limit N Interrupt after N progress callbacks", |
| 26887 | " --once Do no more than one progress interrupt", |
| 26888 | " --quiet|-q No output except at interrupts", |
| 26889 | " --reset Reset the count for each input and interrupt", |
| 26890 | " --timeout S Halt after running for S seconds", |
| 26891 | #endif |
| 26892 | ".prompt MAIN CONTINUE Replace the standard prompts", |
| 26893 | #ifndef SQLITE_SHELL_FIDDLE |
| 26894 | ".quit Stop interpreting input stream, exit if primary.", |
| 26895 | ".read FILE Read input from FILE or command output", |
| @@ -26785,17 +26950,15 @@ | |
| 26950 | " vmstep Show the virtual machine step count only", |
| 26951 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 26952 | ".system CMD ARGS... Run CMD ARGS... in a system shell", |
| 26953 | #endif |
| 26954 | ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", |
| 26955 | ".testcase NAME Begin a test case.", |
| 26956 | ",testctrl CMD ... Run various sqlite3_test_control() operations", |
| 26957 | " Run \".testctrl\" with no arguments for details", |
| 26958 | ".timeout MS Try opening locked tables for MS milliseconds", |
| 26959 | ".timer on|off|once Turn SQL timer on or off.", |
| 26960 | #ifndef SQLITE_OMIT_TRACE |
| 26961 | ".trace ?OPTIONS? Output each SQL statement as it is run", |
| 26962 | " FILE Send output to FILE", |
| 26963 | " stdout Send output to stdout", |
| 26964 | " stderr Send output to stderr", |
| @@ -26836,19 +26999,33 @@ | |
| 26999 | "USAGE: .import [OPTIONS] FILE TABLE\n" |
| 27000 | "\n" |
| 27001 | "Import CSV or similar text from FILE into TABLE. If TABLE does\n" |
| 27002 | "not exist, it is created using the first row of FILE as the column\n" |
| 27003 | "names. If FILE begins with \"|\" then it is a command that is run\n" |
| 27004 | "and the output from the command is used as the input data. If\n" |
| 27005 | "FILE begins with \"<<\" followed by a label, then content is read from\n" |
| 27006 | "the script until the first line that matches the label.\n" |
| 27007 | "\n" |
| 27008 | "The content of FILE is interpreted using RFC-4180 (\"CSV\") quoting\n" |
| 27009 | "rules unless the current mode is \"ascii\" or \"tabs\" or unless one\n" |
| 27010 | "the --ascii option is used.\n" |
| 27011 | "\n" |
| 27012 | "The column and row separators must be single ASCII characters. If\n" |
| 27013 | "multiple characters or a Unicode character are specified for the\n" |
| 27014 | "separators, then only the first byte of the separator is used. Except,\n" |
| 27015 | "if the row separator is \\n and the mode is not --ascii, then \\r\\n is\n" |
| 27016 | "understood as a row separator too.\n" |
| 27017 | "\n" |
| 27018 | "Options:\n" |
| 27019 | " --ascii Do not use RFC-4180 quoting. Use \\037 and \\036\n" |
| 27020 | " as column and row separators on input, unless other\n" |
| 27021 | " delimiters are specified using --colsep and/or --rowsep\n" |
| 27022 | " --colsep CHAR Use CHAR as the column separator.\n" |
| 27023 | " --csv Input is standard RFC-4180 CSV.\n" |
| 27024 | " --esc CHAR Use CHAR as an escape character in unquoted CSV inputs.\n" |
| 27025 | " --qesc CHAR Use CHAR as an escape character in quoted CSV inputs.\n" |
| 27026 | " --rowsep CHAR Use CHAR as the row separator.\n" |
| 27027 | " --schema S When creating TABLE, put it in schema S\n" |
| 27028 | " --skip N Ignore the first N rows of input\n" |
| 27029 | " -v Verbose mode\n" |
| 27030 | }, |
| 27031 | { ".mode", |
| @@ -26930,23 +27107,17 @@ | |
| 27107 | " --bom Prepend a byte-order mark to the output\n" |
| 27108 | " -e Accumulate output in a temporary text file then\n" |
| 27109 | " launch a text editor when the redirection ends.\n" |
| 27110 | " --error-prefix X Use X as the left-margin prefix for error messages.\n" |
| 27111 | " Set to an empty string to restore the default.\n" |
| 27112 | " --keep Keep redirecting output to its current destination.\n" |
| 27113 | " Use this option in combination with --show or\n" |
| 27114 | " with --error-prefix when you do not want to stop\n" |
| 27115 | " a current redirection.\n" |
| 27116 | " --plain Use plain text rather than HTML tables with -w\n" |
| 27117 | " --show Show output text captured by .testcase or by\n" |
| 27118 | " redirecting to \"memory\".\n" |
| 27119 | " -w Show the output in a web browser. Output is\n" |
| 27120 | " written into a temporary HTML file until the\n" |
| 27121 | " redirect ends, then the web browser is launched.\n" |
| 27122 | " Query results are shown as HTML tables, unless\n" |
| 27123 | " the --plain is used too.\n" |
| @@ -26970,10 +27141,39 @@ | |
| 27141 | " file in a web browser\n" |
| 27142 | " -x Show the output in a spreadsheet. Output is\n" |
| 27143 | " written to a temp file as CSV then the spreadsheet\n" |
| 27144 | " is launched when\n" |
| 27145 | }, |
| 27146 | { ".check", |
| 27147 | "USAGE: .check [OPTIONS] PATTERN\n" |
| 27148 | "\n" |
| 27149 | "Verify results of commands since the most recent .testcase command.\n" |
| 27150 | "Restore output to the console, unless --keep is used.\n" |
| 27151 | "\n" |
| 27152 | "If PATTERN starts with \"<<ENDMARK\" then the actual pattern is taken from\n" |
| 27153 | "subsequent lines of text up to the first line that begins with ENDMARK.\n" |
| 27154 | "All pattern lines and the ENDMARK are discarded.\n" |
| 27155 | "\n" |
| 27156 | "Options:\n" |
| 27157 | " --exact Do an exact comparison including leading and\n" |
| 27158 | " trailing whitespace.\n" |
| 27159 | " --glob Treat PATTERN as a GLOB\n" |
| 27160 | " --keep Do not reset the testcase. More .check commands\n" |
| 27161 | " will follow.\n" |
| 27162 | " --notglob Output should not match PATTERN\n" |
| 27163 | " --show Write testcase output to the screen, for debugging.\n" |
| 27164 | }, |
| 27165 | { ".testcase", |
| 27166 | "USAGE: .testcase [OPTIONS] NAME\n" |
| 27167 | "\n" |
| 27168 | "Start a new test case identified by NAME. All output\n" |
| 27169 | "through the next \".check\" command is captured for comparison. See the\n" |
| 27170 | "\".check\" commandn for additional informatioon.\n" |
| 27171 | "\n" |
| 27172 | "Options:\n" |
| 27173 | " --error-prefix TEXT Change error message prefix text to TEXT\n" |
| 27174 | }, |
| 27175 | }; |
| 27176 | |
| 27177 | /* |
| 27178 | ** Return a pointer to usage text for zCmd, or NULL if none exists. |
| 27179 | */ |
| @@ -27193,10 +27393,56 @@ | |
| 27393 | if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; |
| 27394 | } |
| 27395 | return 1; |
| 27396 | } |
| 27397 | #endif |
| 27398 | |
| 27399 | /* |
| 27400 | ** Return the size of the named file in bytes. Or return a negative |
| 27401 | ** number if the file does not exist. |
| 27402 | */ |
| 27403 | static sqlite3_int64 fileSize(const char *zFile){ |
| 27404 | #if defined(_WIN32) || defined(WIN32) |
| 27405 | struct _stat64 x; |
| 27406 | if( _stat64(zFile, &x)!=0 ) return -1; |
| 27407 | return (sqlite3_int64)x.st_size; |
| 27408 | #else |
| 27409 | struct stat x; |
| 27410 | if( stat(zFile, &x)!=0 ) return -1; |
| 27411 | return (sqlite3_int64)x.st_size; |
| 27412 | #endif |
| 27413 | } |
| 27414 | |
| 27415 | /* |
| 27416 | ** Return true if zFile is an SQLite database. |
| 27417 | ** |
| 27418 | ** Algorithm: |
| 27419 | ** * If the file does not exist -> return false |
| 27420 | ** * If the size of the file is not a multiple of 512 -> return false |
| 27421 | ** * If sqlite3_open() fails -> return false |
| 27422 | ** * if sqlite3_prepare() or sqlite3_step() fails -> return false |
| 27423 | ** * Otherwise -> return true |
| 27424 | */ |
| 27425 | static int isDatabaseFile(const char *zFile, int openFlags){ |
| 27426 | sqlite3 *db = 0; |
| 27427 | sqlite3_stmt *pStmt = 0; |
| 27428 | int rc; |
| 27429 | sqlite3_int64 sz = fileSize(zFile); |
| 27430 | if( sz<512 || (sz%512)!=0 ) return 0; |
| 27431 | if( sqlite3_open_v2(zFile, &db, openFlags, 0)==SQLITE_OK |
| 27432 | && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) |
| 27433 | ==SQLITE_OK |
| 27434 | && sqlite3_step(pStmt)==SQLITE_ROW |
| 27435 | ){ |
| 27436 | rc = 1; |
| 27437 | }else{ |
| 27438 | rc = 0; |
| 27439 | } |
| 27440 | sqlite3_finalize(pStmt); |
| 27441 | sqlite3_close(db); |
| 27442 | return rc; |
| 27443 | } |
| 27444 | |
| 27445 | /* |
| 27446 | ** Try to deduce the type of file for zName based on its content. Return |
| 27447 | ** one of the SHELL_OPEN_* constants. |
| 27448 | ** |
| @@ -27206,24 +27452,16 @@ | |
| 27452 | ** the type cannot be determined from content. |
| 27453 | */ |
| 27454 | int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){ |
| 27455 | FILE *f; |
| 27456 | size_t n; |
| 27457 | int rc = SHELL_OPEN_UNSPEC; |
| 27458 | char zBuf[100]; |
| 27459 | if( access(zName,0)!=0 ) goto database_type_by_name; |
| 27460 | if( isDatabaseFile(zName, openFlags) ){ |
| 27461 | rc = SHELL_OPEN_NORMAL; |
| 27462 | } |
| 27463 | if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL; |
| 27464 | f = sqlite3_fopen(zName, "rb"); |
| 27465 | if( f==0 ) goto database_type_by_name; |
| 27466 | n = fread(zBuf, 16, 1, f); |
| 27467 | if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){ |
| @@ -27253,10 +27491,39 @@ | |
| 27491 | }else{ |
| 27492 | rc = SHELL_OPEN_NORMAL; |
| 27493 | } |
| 27494 | return rc; |
| 27495 | } |
| 27496 | |
| 27497 | /* |
| 27498 | ** If the text in z[] is the name of a readable file and that file appears |
| 27499 | ** to contain SQL text and/or dot-commands, then return true. If z[] is |
| 27500 | ** not a file, or if the file is unreadable, or if the file is a database |
| 27501 | ** or anything else that is not SQL text and dot-commands, then return false. |
| 27502 | ** |
| 27503 | ** If the bLeaveUninit flag is set, then be sure to leave SQLite in an |
| 27504 | ** uninitialized state. This means invoking sqlite3_shutdown() after any |
| 27505 | ** SQLite API is used. |
| 27506 | ** |
| 27507 | ** Some amount of guesswork is involved in this decision. |
| 27508 | */ |
| 27509 | static int isScriptFile(const char *z, int bLeaveUninit){ |
| 27510 | sqlite3_int64 sz = fileSize(z); |
| 27511 | if( sz<=0 ) return 0; |
| 27512 | if( (sz%512)==0 ){ |
| 27513 | int rc; |
| 27514 | sqlite3_initialize(); |
| 27515 | rc = isDatabaseFile(z, SQLITE_OPEN_READONLY); |
| 27516 | if( bLeaveUninit ){ |
| 27517 | sqlite3_shutdown(); |
| 27518 | } |
| 27519 | if( rc ) return 0; /* Is a database */ |
| 27520 | } |
| 27521 | if( sqlite3_strlike("%.sql",z,0)==0 ) return 1; |
| 27522 | if( sqlite3_strlike("%.txt",z,0)==0 ) return 1; |
| 27523 | return 0; |
| 27524 | } |
| 27525 | |
| 27526 | #ifndef SQLITE_OMIT_DESERIALIZE |
| 27527 | /* |
| 27528 | ** Reconstruct an in-memory database using the output from the "dbtotxt" |
| 27529 | ** program. Read content from the file in p->aAuxDb[].zDbFilename. |
| @@ -27899,20 +28166,24 @@ | |
| 28166 | typedef struct ImportCtx ImportCtx; |
| 28167 | struct ImportCtx { |
| 28168 | const char *zFile; /* Name of the input file */ |
| 28169 | FILE *in; /* Read the CSV text from this input stream */ |
| 28170 | int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */ |
| 28171 | char *zIn; /* Input text */ |
| 28172 | char *z; /* Accumulated text for a field */ |
| 28173 | i64 nUsed; /* Bytes of zIn[] used so far */ |
| 28174 | i64 n; /* Number of bytes in z */ |
| 28175 | i64 nAlloc; /* Space allocated for z[] */ |
| 28176 | int nLine; /* Current line number */ |
| 28177 | int nRow; /* Number of rows imported */ |
| 28178 | int nErr; /* Number of errors encountered */ |
| 28179 | int bNotFirst; /* True if one or more bytes already read */ |
| 28180 | int cTerm; /* Character that terminated the most recent field */ |
| 28181 | int cColSep; /* The column separator character. (Usually ",") */ |
| 28182 | int cRowSep; /* The row separator character. (Usually "\n") */ |
| 28183 | int cQEscape; /* Escape character with "...". 0 for none */ |
| 28184 | int cUQEscape; /* Escape character not with "...". 0 for none */ |
| 28185 | }; |
| 28186 | |
| 28187 | /* Clean up resourced used by an ImportCtx */ |
| 28188 | static void import_cleanup(ImportCtx *p){ |
| 28189 | if( p->in!=0 && p->xCloser!=0 ){ |
| @@ -27919,13 +28190,32 @@ | |
| 28190 | p->xCloser(p->in); |
| 28191 | p->in = 0; |
| 28192 | } |
| 28193 | sqlite3_free(p->z); |
| 28194 | p->z = 0; |
| 28195 | if( p->zIn ){ |
| 28196 | sqlite3_free(p->zIn); |
| 28197 | p->zIn = 0; |
| 28198 | } |
| 28199 | } |
| 28200 | |
| 28201 | /* Read a single character of the .import input text. Return EOF |
| 28202 | ** at end-of-file. |
| 28203 | */ |
| 28204 | static int import_getc(ImportCtx *p){ |
| 28205 | if( p->in ){ |
| 28206 | return fgetc(p->in); |
| 28207 | }else if( p->zIn && p->zIn[p->nUsed]!=0 ){ |
| 28208 | return p->zIn[p->nUsed++]; |
| 28209 | }else{ |
| 28210 | return EOF; |
| 28211 | } |
| 28212 | } |
| 28213 | |
| 28214 | /* Append a single byte to the field value begin constructed |
| 28215 | ** in the p->z[] buffer |
| 28216 | */ |
| 28217 | static void import_append_char(ImportCtx *p, int c){ |
| 28218 | if( p->n+1>=p->nAlloc ){ |
| 28219 | p->nAlloc += p->nAlloc + 100; |
| 28220 | p->z = sqlite3_realloc64(p->z, p->nAlloc); |
| 28221 | shell_check_oom(p->z); |
| @@ -27937,12 +28227,12 @@ | |
| 28227 | ** with the option of having a separator other than ",". |
| 28228 | ** |
| 28229 | ** + Input comes from p->in. |
| 28230 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 28231 | ** from sqlite3_malloc64(). |
| 28232 | ** + Use p->cColSep as the column separator. The default is ",". |
| 28233 | ** + Use p->cRowSep as the row separator. The default is "\n". |
| 28234 | ** + Keep track of the line number in p->nLine. |
| 28235 | ** + Store the character that terminates the field in p->cTerm. Store |
| 28236 | ** EOF on end-of-file. |
| 28237 | ** + Report syntax errors on stderr |
| 28238 | */ |
| @@ -27949,23 +28239,30 @@ | |
| 28239 | static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ |
| 28240 | int c; |
| 28241 | int cSep = (u8)p->cColSep; |
| 28242 | int rSep = (u8)p->cRowSep; |
| 28243 | p->n = 0; |
| 28244 | c = import_getc(p); |
| 28245 | if( c==EOF || seenInterrupt ){ |
| 28246 | p->cTerm = EOF; |
| 28247 | return 0; |
| 28248 | } |
| 28249 | if( c=='"' ){ |
| 28250 | int pc, ppc; |
| 28251 | int startLine = p->nLine; |
| 28252 | int cQuote = c; |
| 28253 | int cEsc = (u8)p->cQEscape; |
| 28254 | pc = ppc = 0; |
| 28255 | while( 1 ){ |
| 28256 | c = import_getc(p); |
| 28257 | if( c==rSep ) p->nLine++; |
| 28258 | if( c==cEsc && cEsc!=0 ){ |
| 28259 | c = import_getc(p); |
| 28260 | import_append_char(p, c); |
| 28261 | ppc = pc = 0; |
| 28262 | continue; |
| 28263 | } |
| 28264 | if( c==cQuote ){ |
| 28265 | if( pc==cQuote ){ |
| 28266 | pc = 0; |
| 28267 | continue; |
| 28268 | } |
| @@ -27979,11 +28276,11 @@ | |
| 28276 | p->cTerm = c; |
| 28277 | break; |
| 28278 | } |
| 28279 | if( pc==cQuote && c!='\r' ){ |
| 28280 | cli_printf(stderr,"%s:%d: unescaped %c character\n", |
| 28281 | p->zFile, p->nLine, cQuote); |
| 28282 | } |
| 28283 | if( c==EOF ){ |
| 28284 | cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n", |
| 28285 | p->zFile, startLine, cQuote); |
| 28286 | p->cTerm = c; |
| @@ -27994,26 +28291,28 @@ | |
| 28291 | pc = c; |
| 28292 | } |
| 28293 | }else{ |
| 28294 | /* If this is the first field being parsed and it begins with the |
| 28295 | ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ |
| 28296 | int cEsc = p->cUQEscape; |
| 28297 | if( (c&0xff)==0xef && p->bNotFirst==0 ){ |
| 28298 | import_append_char(p, c); |
| 28299 | c = import_getc(p); |
| 28300 | if( (c&0xff)==0xbb ){ |
| 28301 | import_append_char(p, c); |
| 28302 | c = import_getc(p); |
| 28303 | if( (c&0xff)==0xbf ){ |
| 28304 | p->bNotFirst = 1; |
| 28305 | p->n = 0; |
| 28306 | return csv_read_one_field(p); |
| 28307 | } |
| 28308 | } |
| 28309 | } |
| 28310 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 28311 | if( c==cEsc && cEsc!=0 ) c = import_getc(p); |
| 28312 | import_append_char(p, c); |
| 28313 | c = import_getc(p); |
| 28314 | } |
| 28315 | if( c==rSep ){ |
| 28316 | p->nLine++; |
| 28317 | if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
| 28318 | } |
| @@ -28027,12 +28326,12 @@ | |
| 28326 | /* Read a single field of ASCII delimited text. |
| 28327 | ** |
| 28328 | ** + Input comes from p->in. |
| 28329 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 28330 | ** from sqlite3_malloc64(). |
| 28331 | ** + Use p->cColSep as the column separator. The default is "\x1F". |
| 28332 | ** + Use p->cRowSep as the row separator. The default is "\x1E". |
| 28333 | ** + Keep track of the row number in p->nLine. |
| 28334 | ** + Store the character that terminates the field in p->cTerm. Store |
| 28335 | ** EOF on end-of-file. |
| 28336 | ** + Report syntax errors on stderr |
| 28337 | */ |
| @@ -28039,18 +28338,18 @@ | |
| 28338 | static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ |
| 28339 | int c; |
| 28340 | int cSep = (u8)p->cColSep; |
| 28341 | int rSep = (u8)p->cRowSep; |
| 28342 | p->n = 0; |
| 28343 | c = import_getc(p); |
| 28344 | if( c==EOF || seenInterrupt ){ |
| 28345 | p->cTerm = EOF; |
| 28346 | return 0; |
| 28347 | } |
| 28348 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 28349 | import_append_char(p, c); |
| 28350 | c = import_getc(p); |
| 28351 | } |
| 28352 | if( c==rSep ){ |
| 28353 | p->nLine++; |
| 28354 | } |
| 28355 | p->cTerm = c; |
| @@ -30151,11 +30450,11 @@ | |
| 30450 | ; |
| 30451 | static const char * const zCollectVar = "\ |
| 30452 | SELECT\ |
| 30453 | '('||x'0a'\ |
| 30454 | || group_concat(\ |
| 30455 | cname||' ANY',\ |
| 30456 | ','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\ |
| 30457 | ||')' AS ColsSpec \ |
| 30458 | FROM (\ |
| 30459 | SELECT cpos, printf('\"%w\"',printf('%!.*s%s', nlen-chop,name,suff)) AS cname \ |
| 30460 | FROM ColNames ORDER BY cpos\ |
| @@ -30351,19 +30650,33 @@ | |
| 30650 | ** USAGE: .import [OPTIONS] FILE TABLE |
| 30651 | ** |
| 30652 | ** Import CSV or similar text from FILE into TABLE. If TABLE does |
| 30653 | ** not exist, it is created using the first row of FILE as the column |
| 30654 | ** names. If FILE begins with "|" then it is a command that is run |
| 30655 | ** and the output from the command is used as the input data. If |
| 30656 | ** FILE begins with "<<" followed by a label, then content is read from |
| 30657 | ** the script until the first line that matches the label. |
| 30658 | ** |
| 30659 | ** The content of FILE is interpreted using RFC-4180 ("CSV") quoting |
| 30660 | ** rules unless the current mode is "ascii" or "tabs" or unless one |
| 30661 | ** the --ascii option is used. |
| 30662 | ** |
| 30663 | ** The column and row separators must be single ASCII characters. If |
| 30664 | ** multiple characters or a Unicode character are specified for the |
| 30665 | ** separators, then only the first byte of the separator is used. Except, |
| 30666 | ** if the row separator is \n and the mode is not --ascii, then \r\n is |
| 30667 | ** understood as a row separator too. |
| 30668 | ** |
| 30669 | ** Options: |
| 30670 | ** --ascii Do not use RFC-4180 quoting. Use \037 and \036 |
| 30671 | ** as column and row separators on input, unless other |
| 30672 | ** delimiters are specified using --colsep and/or --rowsep |
| 30673 | ** --colsep CHAR Use CHAR as the column separator. |
| 30674 | ** --csv Input is standard RFC-4180 CSV. |
| 30675 | ** --esc CHAR Use CHAR as an escape character in unquoted CSV inputs. |
| 30676 | ** --qesc CHAR Use CHAR as an escape character in quoted CSV inputs. |
| 30677 | ** --rowsep CHAR Use CHAR as the row separator. |
| 30678 | ** --schema S When creating TABLE, put it in schema S |
| 30679 | ** --skip N Ignore the first N rows of input |
| 30680 | ** -v Verbose mode |
| 30681 | */ |
| 30682 | static int dotCmdImport(ShellState *p){ |
| @@ -30375,17 +30688,16 @@ | |
| 30688 | sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 30689 | int nCol; /* Number of columns in the table */ |
| 30690 | i64 nByte; /* Number of bytes in an SQL string */ |
| 30691 | int i, j; /* Loop counters */ |
| 30692 | int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 30693 | char *zSql = 0; /* An SQL statement */ |
| 30694 | ImportCtx sCtx; /* Reader context */ |
| 30695 | char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ |
| 30696 | int eVerbose = 0; /* Larger for more console output */ |
| 30697 | i64 nSkip = 0; /* Initial lines to skip */ |
| 30698 | i64 iLineOffset = 0; /* Offset to the first line of input */ |
| 30699 | char *zCreate = 0; /* CREATE TABLE statement text */ |
| 30700 | int rc; /* Result code */ |
| 30701 | |
| 30702 | failIfSafeMode(p, "cannot run .import in safe mode"); |
| 30703 | memset(&sCtx, 0, sizeof(sCtx)); |
| @@ -30411,70 +30723,71 @@ | |
| 30723 | }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){ |
| 30724 | zSchema = azArg[++i]; |
| 30725 | }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){ |
| 30726 | nSkip = integerValue(azArg[++i]); |
| 30727 | }else if( cli_strcmp(z,"-ascii")==0 ){ |
| 30728 | if( sCtx.cColSep==0 ) sCtx.cColSep = SEP_Unit[0]; |
| 30729 | if( sCtx.cRowSep==0 ) sCtx.cRowSep = SEP_Record[0]; |
| 30730 | xRead = ascii_read_one_field; |
| 30731 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 30732 | if( sCtx.cColSep==0 ) sCtx.cColSep = ','; |
| 30733 | if( sCtx.cRowSep==0 ) sCtx.cRowSep = '\n'; |
| 30734 | xRead = csv_read_one_field; |
| 30735 | }else if( cli_strcmp(z,"-esc")==0 ){ |
| 30736 | sCtx.cUQEscape = azArg[++i][0]; |
| 30737 | }else if( cli_strcmp(z,"-qesc")==0 ){ |
| 30738 | sCtx.cQEscape = azArg[++i][0]; |
| 30739 | }else if( cli_strcmp(z,"-colsep")==0 ){ |
| 30740 | if( i==nArg-1 ){ |
| 30741 | dotCmdError(p, i, "missing argument", 0); |
| 30742 | return 1; |
| 30743 | } |
| 30744 | i++; |
| 30745 | sCtx.cColSep = azArg[i][0]; |
| 30746 | }else if( cli_strcmp(z,"-rowsep")==0 ){ |
| 30747 | if( i==nArg-1 ){ |
| 30748 | dotCmdError(p, i, "missing argument", 0); |
| 30749 | return 1; |
| 30750 | } |
| 30751 | i++; |
| 30752 | sCtx.cRowSep = azArg[i][0]; |
| 30753 | }else{ |
| 30754 | dotCmdError(p, i, "unknown option", 0); |
| 30755 | return 1; |
| 30756 | } |
| 30757 | } |
| 30758 | if( zTable==0 ){ |
| 30759 | dotCmdError(p, nArg, 0, "Missing %s argument\n", |
| 30760 | zFile==0 ? "FILE" : "TABLE"); |
| 30761 | return 1; |
| 30762 | } |
| 30763 | seenInterrupt = 0; |
| 30764 | open_db(p, 0); |
| 30765 | if( sCtx.cColSep==0 ){ |
| 30766 | if( p->mode.spec.zColumnSep && p->mode.spec.zColumnSep[0]!=0 ){ |
| 30767 | sCtx.cColSep = p->mode.spec.zColumnSep[0]; |
| 30768 | }else{ |
| 30769 | sCtx.cColSep = ','; |
| 30770 | } |
| 30771 | } |
| 30772 | if( (sCtx.cColSep & 0x80)!=0 ){ |
| 30773 | eputz("Error: .import column separator must be ASCII\n"); |
| 30774 | return 1; |
| 30775 | } |
| 30776 | if( sCtx.cRowSep==0 ){ |
| 30777 | if( p->mode.spec.zRowSep && p->mode.spec.zRowSep[0]!=0 ){ |
| 30778 | sCtx.cRowSep = p->mode.spec.zRowSep[0]; |
| 30779 | }else{ |
| 30780 | sCtx.cRowSep = '\n'; |
| 30781 | } |
| 30782 | } |
| 30783 | if( sCtx.cRowSep=='\r' && xRead!=ascii_read_one_field ){ |
| 30784 | sCtx.cRowSep = '\n'; |
| 30785 | } |
| 30786 | if( (sCtx.cRowSep & 0x80)!=0 ){ |
| 30787 | eputz("Error: .import row separator must be ASCII\n"); |
| 30788 | return 1; |
| 30789 | } |
| 30790 | sCtx.zFile = zFile; |
| 30791 | sCtx.nLine = 1; |
| 30792 | if( sCtx.zFile[0]=='|' ){ |
| 30793 | #ifdef SQLITE_OMIT_POPEN |
| @@ -30483,19 +30796,58 @@ | |
| 30796 | #else |
| 30797 | sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
| 30798 | sCtx.zFile = "<pipe>"; |
| 30799 | sCtx.xCloser = pclose; |
| 30800 | #endif |
| 30801 | }else if( sCtx.zFile[0]=='<' && sCtx.zFile[1]=='<' && sCtx.zFile[2]!=0 ){ |
| 30802 | /* Input text comes from subsequent lines of script until the zFile |
| 30803 | ** delimiter */ |
| 30804 | int nEndMark = strlen30(zFile)-2; |
| 30805 | char *zEndMark = &zFile[2]; |
| 30806 | sqlite3_str *pContent = sqlite3_str_new(p->db); |
| 30807 | int ckEnd = 1; |
| 30808 | i64 iStart = p->lineno; |
| 30809 | char zLine[2000]; |
| 30810 | sCtx.zFile = p->zInFile; |
| 30811 | sCtx.nLine = p->lineno+1; |
| 30812 | iLineOffset = p->lineno; |
| 30813 | while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
| 30814 | if( ckEnd && cli_strncmp(zLine,zEndMark,nEndMark)==0 ){ |
| 30815 | ckEnd = 2; |
| 30816 | if( strchr(zLine,'\n') ) p->lineno++; |
| 30817 | break; |
| 30818 | } |
| 30819 | if( strchr(zLine,'\n') ){ |
| 30820 | p->lineno++; |
| 30821 | ckEnd = 1; |
| 30822 | }else{ |
| 30823 | ckEnd = 0; |
| 30824 | } |
| 30825 | sqlite3_str_appendall(pContent, zLine); |
| 30826 | } |
| 30827 | sCtx.zIn = sqlite3_str_finish(pContent); |
| 30828 | if( sCtx.zIn==0 ){ |
| 30829 | sCtx.zIn = sqlite3_mprintf(""); |
| 30830 | } |
| 30831 | if( ckEnd<2 ){ |
| 30832 | i64 savedLn = p->lineno; |
| 30833 | p->lineno = iStart; |
| 30834 | dotCmdError(p, 0, 0,"Content terminator \"%s\" not found.",zEndMark); |
| 30835 | p->lineno = savedLn; |
| 30836 | import_cleanup(&sCtx); |
| 30837 | return 1; |
| 30838 | } |
| 30839 | }else{ |
| 30840 | sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
| 30841 | sCtx.xCloser = fclose; |
| 30842 | } |
| 30843 | if( sCtx.in==0 && sCtx.zIn==0 ){ |
| 30844 | dotCmdError(p, 0, 0, "cannot open \"%s\"", zFile); |
| 30845 | import_cleanup(&sCtx); |
| 30846 | return 1; |
| 30847 | } |
| 30848 | if( eVerbose>=1 ){ |
| 30849 | char zSep[2]; |
| 30850 | zSep[1] = 0; |
| 30851 | zSep[0] = sCtx.cColSep; |
| 30852 | cli_puts("Column separator ", p->out); |
| 30853 | output_c_string(p->out, zSep); |
| @@ -30648,10 +31000,14 @@ | |
| 31000 | if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 31001 | z = ""; |
| 31002 | } |
| 31003 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 31004 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 31005 | if( i==0 && (strcmp(z,"\n")==0 || strcmp(z,"\r\n")==0) ){ |
| 31006 | /* Ignore trailing \n or \r\n when some other row separator */ |
| 31007 | break; |
| 31008 | } |
| 31009 | cli_printf(stderr,"%s:%d: expected %d columns but found %d" |
| 31010 | " - filling the rest with NULL\n", |
| 31011 | sCtx.zFile, startLine, nCol, i+1); |
| 31012 | i += 2; |
| 31013 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| @@ -30671,10 +31027,11 @@ | |
| 31027 | rc = sqlite3_reset(pStmt); |
| 31028 | if( rc!=SQLITE_OK ){ |
| 31029 | cli_printf(stderr,"%s:%d: INSERT failed: %s\n", |
| 31030 | sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 31031 | sCtx.nErr++; |
| 31032 | if( bail_on_error ) break; |
| 31033 | }else{ |
| 31034 | sCtx.nRow++; |
| 31035 | } |
| 31036 | } |
| 31037 | }while( sCtx.cTerm!=EOF ); |
| @@ -30683,13 +31040,13 @@ | |
| 31040 | sqlite3_finalize(pStmt); |
| 31041 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 31042 | if( eVerbose>0 ){ |
| 31043 | cli_printf(p->out, |
| 31044 | "Added %d rows with %d errors using %d lines of input\n", |
| 31045 | sCtx.nRow, sCtx.nErr, sCtx.nLine-1-iLineOffset); |
| 31046 | } |
| 31047 | return sCtx.nErr ? 1 : 0; |
| 31048 | } |
| 31049 | |
| 31050 | |
| 31051 | /* |
| 31052 | ** This function computes what to show the user about the configured |
| @@ -31100,10 +31457,11 @@ | |
| 31457 | if( k<0 ){ |
| 31458 | dotCmdError(p, i, "bad --titles value","%z", zErr); |
| 31459 | return 1; |
| 31460 | } |
| 31461 | p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; |
| 31462 | p->mode.mFlags &= ~MFLG_HDR; |
| 31463 | p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; |
| 31464 | chng = 1; |
| 31465 | }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ |
| 31466 | int nWidth = 0; |
| 31467 | short int *aWidth; |
| @@ -31328,23 +31686,17 @@ | |
| 31686 | ** --bom Prepend a byte-order mark to the output |
| 31687 | ** -e Accumulate output in a temporary text file then |
| 31688 | ** launch a text editor when the redirection ends. |
| 31689 | ** --error-prefix X Use X as the left-margin prefix for error messages. |
| 31690 | ** Set to an empty string to restore the default. |
| 31691 | ** --keep Keep redirecting output to its current destination. |
| 31692 | ** Use this option in combination with --show or |
| 31693 | ** with --error-prefix when you do not want to stop |
| 31694 | ** a current redirection. |
| 31695 | ** --plain Use plain text rather than HTML tables with -w |
| 31696 | ** --show Show output text captured by .testcase or by |
| 31697 | ** redirecting to "memory". |
| 31698 | ** -w Show the output in a web browser. Output is |
| 31699 | ** written into a temporary HTML file until the |
| 31700 | ** redirect ends, then the web browser is launched. |
| 31701 | ** Query results are shown as HTML tables, unless |
| 31702 | ** the --plain is used too. |
| @@ -31382,13 +31734,11 @@ | |
| 31734 | char *zFile = 0; /* The FILE argument */ |
| 31735 | int i; /* Loop counter */ |
| 31736 | int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ |
| 31737 | int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
| 31738 | int bPlain = 0; /* --plain option */ |
| 31739 | int bKeep = 0; /* Keep redirecting */ |
| 31740 | static const char *zBomUtf8 = "\357\273\277"; |
| 31741 | const char *zBom = 0; |
| 31742 | char c = azArg[0][0]; |
| 31743 | int n = strlen30(azArg[0]); |
| 31744 | |
| @@ -31410,36 +31760,21 @@ | |
| 31760 | zBom = zBomUtf8; |
| 31761 | }else if( cli_strcmp(z,"-plain")==0 ){ |
| 31762 | bPlain = 1; |
| 31763 | }else if( c=='o' && z[0]=='1' && z[1]!=0 && z[2]==0 |
| 31764 | && (z[1]=='x' || z[1]=='e' || z[1]=='w') ){ |
| 31765 | if( bKeep || eMode ){ |
| 31766 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31767 | goto dotCmdOutput_error; |
| 31768 | } |
| 31769 | eMode = z[1]; |
| 31770 | }else if( cli_strcmp(z,"-show")==0 ){ |
| 31771 | if( cli_output_capture ){ |
| 31772 | sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
| 31773 | } |
| 31774 | }else if( cli_strcmp(z,"-keep")==0 ){ |
| 31775 | bKeep = 1; |
| 31776 | }else if( optionMatch(z,"error-prefix") ){ |
| 31777 | if( i+1>=nArg ){ |
| 31778 | dotCmdError(p, i, "missing argument", 0); |
| 31779 | return 1; |
| 31780 | } |
| @@ -31450,11 +31785,11 @@ | |
| 31785 | dotCmdError(p, i, "unknown option", 0); |
| 31786 | sqlite3_free(zFile); |
| 31787 | return 1; |
| 31788 | } |
| 31789 | }else if( zFile==0 && eMode==0 ){ |
| 31790 | if( bKeep ){ |
| 31791 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31792 | goto dotCmdOutput_error; |
| 31793 | } |
| 31794 | if( cli_strcmp(z, "memory")==0 && bOnce ){ |
| 31795 | dotCmdError(p, 0, "cannot redirect to \"memory\"", 0); |
| @@ -31486,53 +31821,10 @@ | |
| 31821 | if( bOnce ){ |
| 31822 | p->nPopOutput = 2; |
| 31823 | }else{ |
| 31824 | p->nPopOutput = 0; |
| 31825 | } |
| 31826 | if( !bKeep ) output_reset(p); |
| 31827 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 31828 | if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
| 31829 | p->doXdgOpen = 1; |
| 31830 | modePush(p); |
| @@ -31609,10 +31901,199 @@ | |
| 31901 | |
| 31902 | dotCmdOutput_error: |
| 31903 | sqlite3_free(zFile); |
| 31904 | return 1; |
| 31905 | } |
| 31906 | |
| 31907 | /* |
| 31908 | ** DOT-COMMAND: .check |
| 31909 | ** USAGE: .check [OPTIONS] PATTERN |
| 31910 | ** |
| 31911 | ** Verify results of commands since the most recent .testcase command. |
| 31912 | ** Restore output to the console, unless --keep is used. |
| 31913 | ** |
| 31914 | ** If PATTERN starts with "<<ENDMARK" then the actual pattern is taken from |
| 31915 | ** subsequent lines of text up to the first line that begins with ENDMARK. |
| 31916 | ** All pattern lines and the ENDMARK are discarded. |
| 31917 | ** |
| 31918 | ** Options: |
| 31919 | ** --exact Do an exact comparison including leading and |
| 31920 | ** trailing whitespace. |
| 31921 | ** --glob Treat PATTERN as a GLOB |
| 31922 | ** --keep Do not reset the testcase. More .check commands |
| 31923 | ** will follow. |
| 31924 | ** --notglob Output should not match PATTERN |
| 31925 | ** --show Write testcase output to the screen, for debugging. |
| 31926 | */ |
| 31927 | static int dotCmdCheck(ShellState *p){ |
| 31928 | int nArg = p->dot.nArg; /* Number of arguments */ |
| 31929 | char **azArg = p->dot.azArg; /* Text of the arguments */ |
| 31930 | int i; /* Loop counter */ |
| 31931 | int k; /* Result of pickStr() */ |
| 31932 | char *zTest; /* Textcase result */ |
| 31933 | int bKeep = 0; /* --keep option */ |
| 31934 | char *zCheck = 0; /* PATTERN argument */ |
| 31935 | char *zPattern = 0; /* Actual test pattern */ |
| 31936 | int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --exact */ |
| 31937 | int isOk; /* True if results are OK */ |
| 31938 | sqlite3_int64 iStart = p->lineno; /* Line number of .check statement */ |
| 31939 | |
| 31940 | if( p->zTestcase[0]==0 ){ |
| 31941 | dotCmdError(p, 0, "no .testcase is active", 0); |
| 31942 | return 1; |
| 31943 | } |
| 31944 | for(i=1; i<nArg; i++){ |
| 31945 | char *z = azArg[i]; |
| 31946 | if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; |
| 31947 | if( cli_strcmp(z,"-keep")==0 ){ |
| 31948 | bKeep = 1; |
| 31949 | }else if( cli_strcmp(z,"-show")==0 ){ |
| 31950 | if( cli_output_capture ){ |
| 31951 | sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
| 31952 | } |
| 31953 | bKeep = 1; |
| 31954 | }else if( z[0]=='-' |
| 31955 | && (k = pickStr(&z[1],0,"glob","notglob","exact",""))>=0 |
| 31956 | ){ |
| 31957 | if( eCheck && eCheck!=k+1 ){ |
| 31958 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31959 | return 1; |
| 31960 | } |
| 31961 | eCheck = k+1; |
| 31962 | }else if( zCheck ){ |
| 31963 | dotCmdError(p, i, "unknown option", 0); |
| 31964 | return 1; |
| 31965 | }else{ |
| 31966 | zCheck = azArg[i]; |
| 31967 | } |
| 31968 | } |
| 31969 | if( zCheck==0 ){ |
| 31970 | dotCmdError(p, 0, "no PATTERN specified", 0); |
| 31971 | return 1; |
| 31972 | } |
| 31973 | if( cli_output_capture && sqlite3_str_length(cli_output_capture) ){ |
| 31974 | zTest = sqlite3_str_value(cli_output_capture); |
| 31975 | shell_check_oom(zTest); |
| 31976 | }else{ |
| 31977 | zTest = ""; |
| 31978 | } |
| 31979 | p->nTestRun++; |
| 31980 | if( zCheck[0]=='<' && zCheck[1]=='<' && zCheck[2]!=0 ){ |
| 31981 | int nCheck = strlen30(zCheck); |
| 31982 | sqlite3_str *pPattern = sqlite3_str_new(p->db); |
| 31983 | char zLine[2000]; |
| 31984 | while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
| 31985 | if( strchr(zLine,'\n') ) p->lineno++; |
| 31986 | if( cli_strncmp(&zCheck[2],zLine,nCheck-2)==0 ) break; |
| 31987 | sqlite3_str_appendall(pPattern, zLine); |
| 31988 | } |
| 31989 | zPattern = sqlite3_str_finish(pPattern); |
| 31990 | if( zPattern==0 ){ |
| 31991 | zPattern = sqlite3_mprintf(""); |
| 31992 | } |
| 31993 | }else{ |
| 31994 | zPattern = zCheck; |
| 31995 | } |
| 31996 | shell_check_oom(zPattern); |
| 31997 | switch( eCheck ){ |
| 31998 | case 1: { |
| 31999 | char *zGlob = sqlite3_mprintf("*%s*", zPattern); |
| 32000 | isOk = testcase_glob(zGlob, zTest)!=0; |
| 32001 | sqlite3_free(zGlob); |
| 32002 | break; |
| 32003 | } |
| 32004 | case 2: { |
| 32005 | char *zGlob = sqlite3_mprintf("*%s*", zPattern); |
| 32006 | isOk = testcase_glob(zGlob, zTest)==0; |
| 32007 | sqlite3_free(zGlob); |
| 32008 | break; |
| 32009 | } |
| 32010 | case 3: { |
| 32011 | isOk = cli_strcmp(zTest,zPattern)==0; |
| 32012 | break; |
| 32013 | } |
| 32014 | default: { |
| 32015 | /* Skip leading and trailing \n and \r on both pattern and test output */ |
| 32016 | const char *z1 = zPattern; |
| 32017 | const char *z2 = zTest; |
| 32018 | size_t n1, n2; |
| 32019 | while( z1[0]=='\n' || z1[0]=='\r' ) z1++; |
| 32020 | n1 = strlen(z1); |
| 32021 | while( n1>0 && (z1[n1-1]=='\n' || z1[n1-1]=='\r') ) n1--; |
| 32022 | while( z2[0]=='\n' || z2[0]=='\r' ) z2++; |
| 32023 | n2 = strlen(z2); |
| 32024 | while( n2>0 && (z2[n2-1]=='\n' || z2[n2-1]=='\r') ) n2--; |
| 32025 | isOk = n1==n2 && memcmp(z1,z2,n1)==0; |
| 32026 | break; |
| 32027 | } |
| 32028 | } |
| 32029 | if( !isOk ){ |
| 32030 | sqlite3_fprintf(stderr, |
| 32031 | "%s:%lld: .check failed for testcase %s\n", |
| 32032 | p->zInFile, iStart, p->zTestcase); |
| 32033 | p->nTestErr++; |
| 32034 | sqlite3_fprintf(stderr, "Expected: [%s]\n", zPattern); |
| 32035 | sqlite3_fprintf(stderr, "Got: [%s]\n", zTest); |
| 32036 | } |
| 32037 | if( zPattern!=zCheck ){ |
| 32038 | sqlite3_free(zPattern); |
| 32039 | } |
| 32040 | if( !bKeep ){ |
| 32041 | output_reset(p); |
| 32042 | p->zTestcase[0] = 0; |
| 32043 | } |
| 32044 | return 0; |
| 32045 | } |
| 32046 | |
| 32047 | /* |
| 32048 | ** DOT-COMMAND: .testcase |
| 32049 | ** USAGE: .testcase [OPTIONS] NAME |
| 32050 | ** |
| 32051 | ** Start a new test case identified by NAME. All output |
| 32052 | ** through the next ".check" command is captured for comparison. See the |
| 32053 | ** ".check" commandn for additional informatioon. |
| 32054 | ** |
| 32055 | ** Options: |
| 32056 | ** --error-prefix TEXT Change error message prefix text to TEXT |
| 32057 | */ |
| 32058 | static int dotCmdTestcase(ShellState *p){ |
| 32059 | int nArg = p->dot.nArg; /* Number of arguments */ |
| 32060 | char **azArg = p->dot.azArg; /* Text of the arguments */ |
| 32061 | int i; /* Loop counter */ |
| 32062 | const char *zName = 0; /* Testcase name */ |
| 32063 | |
| 32064 | for(i=1; i<nArg; i++){ |
| 32065 | char *z = azArg[i]; |
| 32066 | if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; |
| 32067 | if( optionMatch(z,"error-prefix") ){ |
| 32068 | if( i+1>=nArg ){ |
| 32069 | dotCmdError(p, i, "missing argument", 0); |
| 32070 | return 1; |
| 32071 | } |
| 32072 | free(p->zErrPrefix); |
| 32073 | i++; |
| 32074 | p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]); |
| 32075 | }else if( zName ){ |
| 32076 | dotCmdError(p, i, "unknown option", 0); |
| 32077 | return 1; |
| 32078 | }else{ |
| 32079 | zName = azArg[i]; |
| 32080 | } |
| 32081 | } |
| 32082 | output_reset(p); |
| 32083 | if( cli_output_capture ){ |
| 32084 | sqlite3_str_free(cli_output_capture); |
| 32085 | } |
| 32086 | cli_output_capture = sqlite3_str_new(0); |
| 32087 | if( zName ){ |
| 32088 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", zName); |
| 32089 | }else{ |
| 32090 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s:%lld", |
| 32091 | p->zInFile, p->lineno); |
| 32092 | } |
| 32093 | return 0; |
| 32094 | } |
| 32095 | |
| 32096 | /* |
| 32097 | ** Enlarge the space allocated in p->dot so that it can hold more |
| 32098 | ** than nArg parsed command-line arguments. |
| 32099 | */ |
| @@ -31641,11 +32122,11 @@ | |
| 32122 | free(p->dot.zCopy); |
| 32123 | z = p->dot.zCopy = strdup(zLine); |
| 32124 | shell_check_oom(z); |
| 32125 | szLine = strlen(z); |
| 32126 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 32127 | if( szLine>0 && z[szLine-1]==';' ){ |
| 32128 | szLine--; |
| 32129 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 32130 | } |
| 32131 | z[szLine] = 0; |
| 32132 | parseDotRealloc(p, 2); |
| @@ -31861,35 +32342,17 @@ | |
| 32342 | eputz("Usage: .changes on|off\n"); |
| 32343 | rc = 1; |
| 32344 | } |
| 32345 | }else |
| 32346 | |
| 32347 | /* Cancel output redirection, if it is currently set (by .testcase) |
| 32348 | ** Then read the content of the testcase-out.txt file and compare against |
| 32349 | ** azArg[1]. If there are differences, report an error and exit. |
| 32350 | */ |
| 32351 | if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){ |
| 32352 | rc = dotCmdCheck(p); |
| 32353 | }else |
| 32354 | |
| 32355 | #ifndef SQLITE_SHELL_FIDDLE |
| 32356 | if( c=='c' && cli_strncmp(azArg[0], "clone", n)==0 ){ |
| 32357 | failIfSafeMode(p, "cannot run .clone in safe mode"); |
| 32358 | if( nArg==2 ){ |
| @@ -32468,10 +32931,11 @@ | |
| 32931 | }else |
| 32932 | |
| 32933 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 32934 | if( nArg==2 ){ |
| 32935 | p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
| 32936 | p->mode.mFlags |= MFLG_HDR; |
| 32937 | p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
| 32938 | }else{ |
| 32939 | eputz("Usage: .headers on|off\n"); |
| 32940 | rc = 1; |
| 32941 | } |
| @@ -32521,14 +32985,14 @@ | |
| 32985 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); |
| 32986 | goto meta_command_exit; |
| 32987 | } |
| 32988 | zSql = sqlite3_mprintf( |
| 32989 | "SELECT rootpage, 0 FROM sqlite_schema" |
| 32990 | " WHERE type='index' AND lower(name)=lower('%q')" |
| 32991 | "UNION ALL " |
| 32992 | "SELECT rootpage, 1 FROM sqlite_schema" |
| 32993 | " WHERE type='table' AND lower(name)=lower('%q')" |
| 32994 | " AND sql LIKE '%%without%%rowid%%'", |
| 32995 | azArg[1], azArg[1] |
| 32996 | ); |
| 32997 | sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 32998 | sqlite3_free(zSql); |
| @@ -32682,13 +33146,14 @@ | |
| 33146 | goto meta_command_exit; |
| 33147 | } |
| 33148 | if( nArg==3 ){ |
| 33149 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 33150 | (int)integerValue(azArg[2])); |
| 33151 | }else{ |
| 33152 | cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 33153 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 33154 | } |
| 33155 | } |
| 33156 | }else |
| 33157 | |
| 33158 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| 33159 | open_db(p, 0); |
| @@ -33027,10 +33492,23 @@ | |
| 33492 | continue; |
| 33493 | } |
| 33494 | if( cli_strcmp(z,"once")==0 ){ |
| 33495 | p->flgProgress |= SHELL_PROGRESS_ONCE; |
| 33496 | continue; |
| 33497 | } |
| 33498 | if( cli_strcmp(z,"timeout")==0 ){ |
| 33499 | if( i==nArg-1 ){ |
| 33500 | dotCmdError(p, i, "missing argument", 0); |
| 33501 | return 1; |
| 33502 | } |
| 33503 | i++; |
| 33504 | p->tmProgress = atof(azArg[i]); |
| 33505 | if( p->tmProgress>0.0 ){ |
| 33506 | p->flgProgress = SHELL_PROGRESS_QUIET|SHELL_PROGRESS_TMOUT; |
| 33507 | if( nn==0 ) nn = 100; |
| 33508 | } |
| 33509 | continue; |
| 33510 | } |
| 33511 | if( cli_strcmp(z,"limit")==0 ){ |
| 33512 | if( i+1>=nArg ){ |
| 33513 | eputz("Error: missing argument on --limit\n"); |
| 33514 | rc = 1; |
| @@ -33233,21 +33711,22 @@ | |
| 33711 | || sqlite3_strlike(zName, "sqlite_schema", '\\')==0 |
| 33712 | || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 |
| 33713 | || sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0; |
| 33714 | if( isSchema ){ |
| 33715 | cli_printf(p->out, |
| 33716 | "CREATE TABLE %ssqlite_schema (\n" |
| 33717 | " type text,\n" |
| 33718 | " name text,\n" |
| 33719 | " tbl_name text,\n" |
| 33720 | " rootpage integer,\n" |
| 33721 | " sql text\n" |
| 33722 | ");\n", |
| 33723 | sqlite3_strlike("sqlite_t%",zName,0)==0 ? "temp." : "" |
| 33724 | ); |
| 33725 | } |
| 33726 | } |
| 33727 | rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); |
| 33728 | if( rc ){ |
| 33729 | shellDatabaseError(p->db); |
| 33730 | sqlite3_finalize(pStmt); |
| 33731 | |
| 33732 | rc = 1; |
| @@ -33255,11 +33734,11 @@ | |
| 33734 | } |
| 33735 | pSql = sqlite3_str_new(p->db); |
| 33736 | sqlite3_str_appendf(pSql, "SELECT sql FROM", 0); |
| 33737 | iSchema = 0; |
| 33738 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 33739 | const char *zDb = (const char*)sqlite3_column_text(pStmt, 1); |
| 33740 | char zScNum[30]; |
| 33741 | sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); |
| 33742 | sqlite3_str_appendall(pSql, zDiv); |
| 33743 | zDiv = " UNION ALL "; |
| 33744 | if( sqlite3_stricmp(zDb, "main")==0 ){ |
| @@ -33275,11 +33754,12 @@ | |
| 33754 | " AS sql, type, tbl_name, name, rowid, %d AS snum, %Q as sname", |
| 33755 | ++iSchema, zDb); |
| 33756 | sqlite3_str_appendf(pSql," FROM \"%w\".sqlite_schema", zDb); |
| 33757 | } |
| 33758 | sqlite3_finalize(pStmt); |
| 33759 | #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) \ |
| 33760 | && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 33761 | if( zName ){ |
| 33762 | sqlite3_str_appendall(pSql, |
| 33763 | " UNION ALL SELECT shell_module_schema(name)," |
| 33764 | " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list"); |
| 33765 | } |
| @@ -33299,11 +33779,11 @@ | |
| 33779 | }else{ |
| 33780 | sqlite3_str_appendf(pSql, " LIKE %Q ESCAPE '\\' AND ", zName); |
| 33781 | } |
| 33782 | } |
| 33783 | if( bNoSystemTabs ){ |
| 33784 | sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCAPE '_' AND "); |
| 33785 | } |
| 33786 | sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid"); |
| 33787 | if( bDebug ){ |
| 33788 | cli_printf(p->out, "SQL: %s;\n", sqlite3_str_value(pSql)); |
| 33789 | }else{ |
| @@ -34017,25 +34497,15 @@ | |
| 34497 | sqlite3_str_free(pSql); |
| 34498 | modePop(p); |
| 34499 | if( rc ) return shellDatabaseError(p->db); |
| 34500 | }else |
| 34501 | |
| 34502 | /* Set the p->zTestcase name and begin redirecting output into |
| 34503 | ** the cli_output_capture sqlite3_str */ |
| 34504 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 34505 | rc = dotCmdTestcase(p); |
| 34506 | }else |
| 34507 | |
| 34508 | #ifndef SQLITE_UNTESTABLE |
| 34509 | if( c=='t' && n>=8 && cli_strncmp(azArg[0], "testctrl", n)==0 ){ |
| 34510 | static const struct { |
| 34511 | const char *zCtrlName; /* Name of a test-control option */ |
| @@ -34527,17 +34997,21 @@ | |
| 34997 | sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); |
| 34998 | }else |
| 34999 | |
| 35000 | if( c=='t' && n>=5 && cli_strncmp(azArg[0], "timer", n)==0 ){ |
| 35001 | if( nArg==2 ){ |
| 35002 | if( cli_strcmp(azArg[1],"once")==0 ){ |
| 35003 | p->enableTimer = 1; |
| 35004 | }else{ |
| 35005 | p->enableTimer = 2*booleanValue(azArg[1]); |
| 35006 | } |
| 35007 | if( p->enableTimer && !HAS_TIMER ){ |
| 35008 | eputz("Error: timer not available on this system.\n"); |
| 35009 | p->enableTimer = 0; |
| 35010 | } |
| 35011 | }else{ |
| 35012 | eputz("Usage: .timer on|off|once\n"); |
| 35013 | rc = 1; |
| 35014 | } |
| 35015 | }else |
| 35016 | |
| 35017 | #ifndef SQLITE_OMIT_TRACE |
| @@ -34708,10 +35182,11 @@ | |
| 35182 | if( p->nPopOutput ){ |
| 35183 | p->nPopOutput--; |
| 35184 | if( p->nPopOutput==0 ) output_reset(p); |
| 35185 | } |
| 35186 | p->bSafeMode = p->bSafeModePersist; |
| 35187 | p->dot.nArg = 0; |
| 35188 | return rc; |
| 35189 | } |
| 35190 | |
| 35191 | /* Line scan result and intermediate states (supporting scan resumption) |
| 35192 | */ |
| @@ -34944,13 +35419,13 @@ | |
| 35419 | char *zErrMsg = 0; |
| 35420 | |
| 35421 | open_db(p, 0); |
| 35422 | if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); |
| 35423 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 35424 | BEGIN_TIMER(p); |
| 35425 | rc = shell_exec(p, zSql, &zErrMsg); |
| 35426 | END_TIMER(p); |
| 35427 | if( rc || zErrMsg ){ |
| 35428 | char zPrefix[100]; |
| 35429 | const char *zErrorTail; |
| 35430 | const char *zErrorType; |
| 35431 | if( zErrMsg==0 ){ |
| @@ -35351,11 +35826,10 @@ | |
| 35826 | " -bail stop after hitting an error\n" |
| 35827 | " -batch force batch I/O\n" |
| 35828 | " -box set output mode to 'box'\n" |
| 35829 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 35830 | " -column set output mode to 'column'\n" |
| 35831 | " -csv set output mode to 'csv'\n" |
| 35832 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 35833 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 35834 | #endif |
| 35835 | " -echo print inputs before execution\n" |
| @@ -35435,15 +35909,10 @@ | |
| 35909 | /* |
| 35910 | ** Initialize the state information in data |
| 35911 | */ |
| 35912 | static void main_init(ShellState *p) { |
| 35913 | memset(p, 0, sizeof(*p)); |
| 35914 | p->pAuxDb = &p->aAuxDb[0]; |
| 35915 | p->shellFlgs = SHFLG_Lookaside; |
| 35916 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p); |
| 35917 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 35918 | verify_uninitialized(); |
| @@ -35498,29 +35967,65 @@ | |
| 35967 | cli_puts(z, p->out); |
| 35968 | fflush(p->out); |
| 35969 | return 1; |
| 35970 | } |
| 35971 | |
| 35972 | /* Alternative name to the entry point for Fiddle */ |
| 35973 | #ifdef SQLITE_SHELL_FIDDLE |
| 35974 | # define main fiddle_main |
| 35975 | #endif |
| 35976 | |
| 35977 | /* Use the wmain() entry point on Windows. Translate arguments to |
| 35978 | ** UTF8, then invoke the traditional main() entry point which is |
| 35979 | ** renamed using a #define to utf8_main() . |
| 35980 | */ |
| 35981 | #if defined(_WIN32) && !defined(main) |
| 35982 | # define main utf8_main /* Rename entry point to utf_main() */ |
| 35983 | int SQLITE_CDECL utf8_main(int,char**); /* Forward declaration */ |
| 35984 | int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ |
| 35985 | int rc, i; |
| 35986 | char **argv = malloc( sizeof(char*) * (argc+1) ); |
| 35987 | char **orig = argv; |
| 35988 | if( argv==0 ){ |
| 35989 | fprintf(stderr, "malloc failed\n"); |
| 35990 | exit(1); |
| 35991 | } |
| 35992 | for(i=0; i<argc; i++){ |
| 35993 | int nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, 0, 0, 0, 0); |
| 35994 | if( nByte==0 ){ |
| 35995 | argv[i] = 0; |
| 35996 | }else{ |
| 35997 | argv[i] = malloc( nByte ); |
| 35998 | if( argv[i]==0 ){ |
| 35999 | fprintf(stderr, "malloc failed\n"); |
| 36000 | exit(1); |
| 36001 | } |
| 36002 | nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i],nByte,0,0); |
| 36003 | if( nByte==0 ){ |
| 36004 | free(argv[i]); |
| 36005 | argv[i] = 0; |
| 36006 | } |
| 36007 | } |
| 36008 | } |
| 36009 | argv[argc] = 0; |
| 36010 | rc = utf8_main(argc, argv); |
| 36011 | for(i=0; i<argc; i++) free(orig[i]); |
| 36012 | free(argv); |
| 36013 | return rc; |
| 36014 | } |
| 36015 | #endif /* WIN32 */ |
| 36016 | |
| 36017 | /* |
| 36018 | ** This is the main entry point for the process. Everything starts here. |
| 36019 | ** |
| 36020 | ** The "main" identifier may have been #defined to something else: |
| 36021 | ** |
| 36022 | ** utf8_main On Windows |
| 36023 | ** fiddle_main In Fiddle |
| 36024 | ** sqlite3_shell Other projects that use shell.c as a subroutine |
| 36025 | */ |
| 36026 | int SQLITE_CDECL main(int argc, char **argv){ |
| 36027 | #ifdef SQLITE_DEBUG |
| 36028 | sqlite3_int64 mem_main_enter = 0; |
| 36029 | #endif |
| 36030 | char *zErrMsg = 0; |
| 36031 | #ifdef SQLITE_SHELL_FIDDLE |
| @@ -35538,14 +36043,10 @@ | |
| 36043 | int nOptsEnd = argc; |
| 36044 | int bEnableVfstrace = 0; |
| 36045 | char **azCmd = 0; |
| 36046 | int *aiCmd = 0; |
| 36047 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 36048 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 36049 | |
| 36050 | #ifdef SQLITE_SHELL_FIDDLE |
| 36051 | stdin_is_interactive = 0; |
| 36052 | stdout_is_console = 1; |
| @@ -35596,36 +36097,10 @@ | |
| 36097 | exit(1); |
| 36098 | } |
| 36099 | #endif |
| 36100 | main_init(&data); |
| 36101 | |
| 36102 | assert( argc>=1 && argv && argv[0] ); |
| 36103 | Argv0 = argv[0]; |
| 36104 | |
| 36105 | #ifdef SQLITE_SHELL_DBNAME_PROC |
| 36106 | { |
| @@ -35637,11 +36112,11 @@ | |
| 36112 | SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename); |
| 36113 | warnInmemoryDb = 0; |
| 36114 | } |
| 36115 | #endif |
| 36116 | |
| 36117 | /* Do an initial pass through the command-line arguments to locate |
| 36118 | ** the name of the database file, the name of the initialization file, |
| 36119 | ** the size of the alternative malloc heap, options affecting commands |
| 36120 | ** or SQL run from the command line, and the first command to execute. |
| 36121 | */ |
| 36122 | #ifndef SQLITE_SHELL_FIDDLE |
| @@ -35649,11 +36124,11 @@ | |
| 36124 | #endif |
| 36125 | for(i=1; i<argc; i++){ |
| 36126 | char *z; |
| 36127 | z = argv[i]; |
| 36128 | if( z[0]!='-' || i>nOptsEnd ){ |
| 36129 | if( data.aAuxDb->zDbFilename==0 && !isScriptFile(z,1) ){ |
| 36130 | data.aAuxDb->zDbFilename = z; |
| 36131 | }else{ |
| 36132 | /* Excess arguments are interpreted as SQL (or dot-commands) and |
| 36133 | ** mean that nothing is read from stdin */ |
| 36134 | readStdin = 0; |
| @@ -35694,15 +36169,10 @@ | |
| 36169 | if( n<2 ){ |
| 36170 | sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n"); |
| 36171 | exit(1); |
| 36172 | } |
| 36173 | stdout_tty_width = n; |
| 36174 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 36175 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 36176 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| 36177 | int val = 0; |
| 36178 | sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val); |
| @@ -35822,10 +36292,20 @@ | |
| 36292 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 36293 | /* no-op - catch this on the second pass */ |
| 36294 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| 36295 | /* skip over the argument */ |
| 36296 | i++; |
| 36297 | }else if( cli_strcmp(z,"-test-argv")==0 ){ |
| 36298 | /* Undocumented test option. Print the values in argv[] and exit. |
| 36299 | ** Use this to verify that any translation of the argv[], for example |
| 36300 | ** on Windows that receives wargv[] from the OS and must convert |
| 36301 | ** to UTF8 prior to calling this routine. */ |
| 36302 | int kk; |
| 36303 | for(kk=0; kk<argc; kk++){ |
| 36304 | sqlite3_fprintf(stdout,"argv[%d] = \"%s\"\n", kk, argv[kk]); |
| 36305 | } |
| 36306 | return 0; |
| 36307 | } |
| 36308 | } |
| 36309 | #ifndef SQLITE_SHELL_FIDDLE |
| 36310 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 36311 | #endif |
| @@ -35848,11 +36328,30 @@ | |
| 36328 | |
| 36329 | if( zVfs ){ |
| 36330 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 36331 | if( pVfs ){ |
| 36332 | sqlite3_vfs_register(pVfs, 1); |
| 36333 | } |
| 36334 | #if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 36335 | else if( access(zVfs,0)==0 ){ |
| 36336 | /* If the VFS name is not the name of an existing VFS, but it is |
| 36337 | ** the name of a file, then try to load that file as an extension. |
| 36338 | ** Presumably the extension implements the desired VFS. */ |
| 36339 | sqlite3 *db = 0; |
| 36340 | char *zErr = 0; |
| 36341 | sqlite3_open(":memory:", &db); |
| 36342 | sqlite3_enable_load_extension(db, 1); |
| 36343 | rc = sqlite3_load_extension(db, zVfs, 0, &zErr); |
| 36344 | sqlite3_close(db); |
| 36345 | if( (rc&0xff)!=SQLITE_OK ){ |
| 36346 | cli_printf(stderr, "could not load extension VFS \"%s\": %s\n", |
| 36347 | zVfs, zErr); |
| 36348 | exit(1); |
| 36349 | } |
| 36350 | } |
| 36351 | #endif |
| 36352 | else{ |
| 36353 | cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs); |
| 36354 | exit(1); |
| 36355 | } |
| 36356 | } |
| 36357 | |
| @@ -35889,11 +36388,11 @@ | |
| 36388 | ** is given on the command line, look for a file named ~/.sqliterc and |
| 36389 | ** try to process it. |
| 36390 | */ |
| 36391 | if( !noInit ) process_sqliterc(&data,zInitFile); |
| 36392 | |
| 36393 | /* Make a second pass through the command-line arguments and set |
| 36394 | ** options. This second pass is delayed until after the initialization |
| 36395 | ** file is processed so that the command-line arguments will override |
| 36396 | ** settings in the initialization file. |
| 36397 | */ |
| 36398 | for(i=1; i<argc; i++){ |
| @@ -36015,12 +36514,10 @@ | |
| 36514 | stdin_is_interactive = 1; |
| 36515 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 36516 | /* already handled */ |
| 36517 | }else if( cli_strcmp(z,"-screenwidth")==0 ){ |
| 36518 | i++; |
| 36519 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 36520 | /* already handled */ |
| 36521 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 36522 | /* already handled */ |
| 36523 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| @@ -36116,11 +36613,22 @@ | |
| 36613 | ** command-line inputs, except for the argToSkip argument which contains |
| 36614 | ** the database filename. |
| 36615 | */ |
| 36616 | for(i=0; i<nCmd; i++){ |
| 36617 | echo_group_input(&data, azCmd[i]); |
| 36618 | if( isScriptFile(azCmd[i],0) ){ |
| 36619 | FILE *inSaved = data.in; |
| 36620 | i64 savedLineno = data.lineno; |
| 36621 | int res = 1; |
| 36622 | if( (data.in = openChrSource(azCmd[i]))!=0 ){ |
| 36623 | res = process_input(&data, azCmd[i]); |
| 36624 | fclose(data.in); |
| 36625 | } |
| 36626 | data.in = inSaved; |
| 36627 | data.lineno = savedLineno; |
| 36628 | if( res ) i = nCmd; |
| 36629 | }else if( azCmd[i][0]=='.' ){ |
| 36630 | char *zErrCtx = malloc( 64 ); |
| 36631 | shell_check_oom(zErrCtx); |
| 36632 | sqlite3_snprintf(64,zErrCtx,"argv[%i]:",aiCmd[i]); |
| 36633 | data.zInFile = "<cmdline>"; |
| 36634 | data.zErrPrefix = zErrCtx; |
| @@ -36231,14 +36739,10 @@ | |
| 36739 | } |
| 36740 | find_home_dir(1); |
| 36741 | output_reset(&data); |
| 36742 | data.doXdgOpen = 0; |
| 36743 | clearTempFile(&data); |
| 36744 | modeFree(&data.mode); |
| 36745 | if( data.nSavedModes ){ |
| 36746 | int ii; |
| 36747 | for(ii=0; ii<data.nSavedModes; ii++){ |
| 36748 | modeFree(&data.aSavedModes[ii].mode); |
| 36749 |
+1112
-736
| --- extsrc/sqlite3.c | ||
| +++ extsrc/sqlite3.c | ||
| @@ -16,11 +16,11 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** 01409738afc2c0d5bdaa248ffb508aa5f36a with changes in files: | |
| 21 | +** c476d956d0bd3065cf894de6f9d393b999ff with changes in files: | |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| @@ -467,14 +467,14 @@ | ||
| 467 | 467 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 468 | 468 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 469 | 469 | */ |
| 470 | 470 | #define SQLITE_VERSION "3.52.0" |
| 471 | 471 | #define SQLITE_VERSION_NUMBER 3052000 |
| 472 | -#define SQLITE_SOURCE_ID "2025-12-11 23:24:05 01409738afc2c0d5bdaa248ffb508aa5f36a66390f6b8e4834734529ee8ed2fa" | |
| 472 | +#define SQLITE_SOURCE_ID "2026-02-04 20:51:27 c476d956d0bd3065cf894de6f9d393b999ff7d2268a35f01a6d88804789ab58f" | |
| 473 | 473 | #define SQLITE_SCM_BRANCH "trunk" |
| 474 | 474 | #define SQLITE_SCM_TAGS "" |
| 475 | -#define SQLITE_SCM_DATETIME "2025-12-11T23:24:05.667Z" | |
| 475 | +#define SQLITE_SCM_DATETIME "2026-02-04T20:51:27.822Z" | |
| 476 | 476 | |
| 477 | 477 | /* |
| 478 | 478 | ** CAPI3REF: Run-Time Library Version Numbers |
| 479 | 479 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 480 | 480 | ** |
| @@ -4752,16 +4752,32 @@ | ||
| 4752 | 4752 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4753 | 4753 | ** messages on the global error log when it is not. If the test compile |
| 4754 | 4754 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4755 | 4755 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4756 | 4756 | ** logs the error. |
| 4757 | +** | |
| 4758 | +** [[SQLITE_PREPARE_FROM_DDL]] <dt>SQLITE_PREPARE_FROM_DDL</dt> | |
| 4759 | +** <dd>The SQLITE_PREPARE_FROM_DDL flag causes the SQL compiler to behave as if | |
| 4760 | +** the SQL statement is part of a database schema. This makes a difference | |
| 4761 | +** when the [SQLITE_DBCONFIG_TRUSTED_SCHEMA] option is set to off. | |
| 4762 | +** When this option is used and SQLITE_DBCONFIG_TRUSTED_SCHEMA is off, | |
| 4763 | +** SQL functions may not be called unless they are tagged with | |
| 4764 | +** [SQLITE_INNOCUOUS] and virtual tables may not be used unless tagged | |
| 4765 | +** with [SQLITE_VTAB_INNOCUOUS]. Use the SQLITE_PREPARE_FROM_DDL option | |
| 4766 | +** when preparing SQL that is derived from parts of the database | |
| 4767 | +** schema. In particular, virtual table implementations that | |
| 4768 | +** run SQL statements based on the arguments to their CREATE VIRTUAL | |
| 4769 | +** TABLE statement should use [sqlite3_prepare_v3()] and set the | |
| 4770 | +** SQLITE_PREPARE_FROM_DLL flag to prevent bypass of the | |
| 4771 | +** [SQLITE_DBCONFIG_TRUSTED_SCHEMA] security checks. | |
| 4757 | 4772 | ** </dl> |
| 4758 | 4773 | */ |
| 4759 | 4774 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4760 | 4775 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4761 | 4776 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4762 | 4777 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4778 | +#define SQLITE_PREPARE_FROM_DDL 0x20 | |
| 4763 | 4779 | |
| 4764 | 4780 | /* |
| 4765 | 4781 | ** CAPI3REF: Compiling An SQL Statement |
| 4766 | 4782 | ** KEYWORDS: {SQL statement compiler} |
| 4767 | 4783 | ** METHOD: sqlite3 |
| @@ -4771,12 +4787,13 @@ | ||
| 4771 | 4787 | ** program using one of these routines. Or, in other words, these routines |
| 4772 | 4788 | ** are constructors for the [prepared statement] object. |
| 4773 | 4789 | ** |
| 4774 | 4790 | ** The preferred routine to use is [sqlite3_prepare_v2()]. The |
| 4775 | 4791 | ** [sqlite3_prepare()] interface is legacy and should be avoided. |
| 4776 | -** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used | |
| 4777 | -** for special purposes. | |
| 4792 | +** [sqlite3_prepare_v3()] has an extra | |
| 4793 | +** [SQLITE_PREPARE_FROM_DDL|"prepFlags" option] that is some times | |
| 4794 | +** needed for special purpose or to pass along security restrictions. | |
| 4778 | 4795 | ** |
| 4779 | 4796 | ** The use of the UTF-8 interfaces is preferred, as SQLite currently |
| 4780 | 4797 | ** does all parsing using UTF-8. The UTF-16 interfaces are provided |
| 4781 | 4798 | ** as a convenience. The UTF-16 interfaces work by converting the |
| 4782 | 4799 | ** input text into UTF-8, then invoking the corresponding UTF-8 interface. |
| @@ -5177,12 +5194,12 @@ | ||
| 5177 | 5194 | ** it should be a pointer to well-formed UTF8 text. |
| 5178 | 5195 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 5179 | 5196 | ** it should be a pointer to well-formed UTF16 text. |
| 5180 | 5197 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 5181 | 5198 | ** it should be a pointer to a well-formed unicode string that is |
| 5182 | -** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 | |
| 5183 | -** otherwise. | |
| 5199 | +** either UTF8 if the sixth parameter is SQLITE_UTF8 or SQLITE_UTF8_ZT, | |
| 5200 | +** or UTF16 otherwise. | |
| 5184 | 5201 | ** |
| 5185 | 5202 | ** [[byte-order determination rules]] ^The byte-order of |
| 5186 | 5203 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 5187 | 5204 | ** found in the first character, which is removed, or in the absence of a BOM |
| 5188 | 5205 | ** the byte order is the native byte order of the host |
| @@ -5224,14 +5241,19 @@ | ||
| 5224 | 5241 | ** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the |
| 5225 | 5242 | ** object is to be copied prior to the return from sqlite3_bind_*(). ^The |
| 5226 | 5243 | ** object and pointer to it must remain valid until then. ^SQLite will then |
| 5227 | 5244 | ** manage the lifetime of its private copy. |
| 5228 | 5245 | ** |
| 5229 | -** ^The sixth argument to sqlite3_bind_text64() must be one of | |
| 5230 | -** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] | |
| 5231 | -** to specify the encoding of the text in the third parameter. If | |
| 5232 | -** the sixth argument to sqlite3_bind_text64() is not one of the | |
| 5246 | +** ^The sixth argument (the E argument) | |
| 5247 | +** to sqlite3_bind_text64(S,K,Z,N,D,E) must be one of | |
| 5248 | +** [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], | |
| 5249 | +** or [SQLITE_UTF16LE] to specify the encoding of the text in the | |
| 5250 | +** third parameter, Z. The special value [SQLITE_UTF8_ZT] means that the | |
| 5251 | +** string argument is both UTF-8 encoded and is zero-terminated. In other | |
| 5252 | +** words, SQLITE_UTF8_ZT means that the Z array is allocated to hold at | |
| 5253 | +** least N+1 bytes and that the Z[N] byte is zero. If | |
| 5254 | +** the E argument to sqlite3_bind_text64(S,K,Z,N,D,E) is not one of the | |
| 5233 | 5255 | ** allowed values shown above, or if the text encoding is different |
| 5234 | 5256 | ** from the encoding specified by the sixth parameter, then the behavior |
| 5235 | 5257 | ** is undefined. |
| 5236 | 5258 | ** |
| 5237 | 5259 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
| @@ -6094,17 +6116,63 @@ | ||
| 6094 | 6116 | /* |
| 6095 | 6117 | ** CAPI3REF: Text Encodings |
| 6096 | 6118 | ** |
| 6097 | 6119 | ** These constants define integer codes that represent the various |
| 6098 | 6120 | ** text encodings supported by SQLite. |
| 6121 | +** | |
| 6122 | +** <dl> | |
| 6123 | +** [[SQLITE_UTF8]] <dt>SQLITE_UTF8</dt><dd>Text is encoding as UTF-8</dd> | |
| 6124 | +** | |
| 6125 | +** [[SQLITE_UTF16LE]] <dt>SQLITE_UTF16LE</dt><dd>Text is encoding as UTF-16 | |
| 6126 | +** with each code point being expressed "little endian" - the least significant | |
| 6127 | +** byte first. This is the usual encoding, for example on Windows.</dd> | |
| 6128 | +** | |
| 6129 | +** [[SQLITE_UTF16BE]] <dt>SQLITE_UTF16BE</dt><dd>Text is encoding as UTF-16 | |
| 6130 | +** with each code point being expressed "big endian" - the most significant | |
| 6131 | +** byte first. This encoding is less common, but is still sometimes seen, | |
| 6132 | +** specially on older systems. | |
| 6133 | +** | |
| 6134 | +** [[SQLITE_UTF16]] <dt>SQLITE_UTF16</dt><dd>Text is encoding as UTF-16 | |
| 6135 | +** with each code point being expressed either little endian or as big | |
| 6136 | +** endian, according to the native endianness of the host computer. | |
| 6137 | +** | |
| 6138 | +** [[SQLITE_ANY]] <dt>SQLITE_ANY</dt><dd>This encoding value may only be used | |
| 6139 | +** to declare the preferred text for [application-defined SQL functions] | |
| 6140 | +** created using [sqlite3_create_function()] and similar. If the preferred | |
| 6141 | +** encoding (the 4th parameter to sqlite3_create_function() - the eTextRep | |
| 6142 | +** parameter) is SQLITE_ANY, that indicates that the function does not have | |
| 6143 | +** a preference regarding the text encoding of its parameters and can take | |
| 6144 | +** any text encoding that the SQLite core find convenient to supply. This | |
| 6145 | +** option is deprecated. Please do not use it in new applications. | |
| 6146 | +** | |
| 6147 | +** [[SQLITE_UTF16_ALIGNED]] <dt>SQLITE_UTF16_ALIGNED</dt><dd>This encoding | |
| 6148 | +** value may be used as the 3rd parameter (the eTextRep parameter) to | |
| 6149 | +** [sqlite3_create_collation()] and similar. This encoding value means | |
| 6150 | +** that the application-defined collating sequence created expects its | |
| 6151 | +** input strings to be in UTF16 in native byte order, and that the start | |
| 6152 | +** of the strings must be aligned to a 2-byte boundary. | |
| 6153 | +** | |
| 6154 | +** [[SQLITE_UTF8_ZT]] <dt>SQLITE_UTF8_ZT</dt><dd>This option can only be | |
| 6155 | +** used to specify the text encoding to strings input to [sqlite3_result_text64()] | |
| 6156 | +** and [sqlite3_bind_text64()]. It means that the input string (call it "z") | |
| 6157 | +** is UTF-8 encoded and that it is zero-terminated. If the length parameter | |
| 6158 | +** (call it "n") is non-negative, this encoding option means that the caller | |
| 6159 | +** guarantees that z array contains at least n+1 bytes and that the z[n] | |
| 6160 | +** byte has a value of zero. | |
| 6161 | +** This option gives the same output as SQLITE_UTF8, but can be more efficient | |
| 6162 | +** by avoiding the need to make a copy of the input string, in some cases. | |
| 6163 | +** However, if z is allocated to hold fewer than n+1 bytes or if the | |
| 6164 | +** z[n] byte is not zero, undefined behavior may result. | |
| 6165 | +** </dl> | |
| 6099 | 6166 | */ |
| 6100 | 6167 | #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ |
| 6101 | 6168 | #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ |
| 6102 | 6169 | #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ |
| 6103 | 6170 | #define SQLITE_UTF16 4 /* Use native byte order */ |
| 6104 | 6171 | #define SQLITE_ANY 5 /* Deprecated */ |
| 6105 | 6172 | #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ |
| 6173 | +#define SQLITE_UTF8_ZT 16 /* Zero-terminated UTF8 */ | |
| 6106 | 6174 | |
| 6107 | 6175 | /* |
| 6108 | 6176 | ** CAPI3REF: Function Flags |
| 6109 | 6177 | ** |
| 6110 | 6178 | ** These constants may be ORed together with the |
| @@ -6606,14 +6674,18 @@ | ||
| 6606 | 6674 | ** use for client data is to provide a mechanism for wrapper libraries |
| 6607 | 6675 | ** to store additional information about an SQLite database connection. |
| 6608 | 6676 | ** |
| 6609 | 6677 | ** There is no limit (other than available memory) on the number of different |
| 6610 | 6678 | ** client data pointers (with different names) that can be attached to a |
| 6611 | -** single database connection. However, the implementation is optimized | |
| 6612 | -** for the case of having only one or two different client data names. | |
| 6613 | -** Applications and wrapper libraries are discouraged from using more than | |
| 6614 | -** one client data name each. | |
| 6679 | +** single database connection. However, the current implementation stores | |
| 6680 | +** the content on a linked list. Insert and retrieval performance will | |
| 6681 | +** be proportional to the number of entries. The design use case, and | |
| 6682 | +** the use case for which the implementation is optimized, is | |
| 6683 | +** that an application will store only small number of client data names, | |
| 6684 | +** typically just one or two. This interface is not intended to be a | |
| 6685 | +** generalized key/value store for thousands or millions of keys. It | |
| 6686 | +** will work for that, but performance might be disappointing. | |
| 6615 | 6687 | ** |
| 6616 | 6688 | ** There is no way to enumerate the client data pointers |
| 6617 | 6689 | ** associated with a database connection. The N parameter can be thought |
| 6618 | 6690 | ** of as a secret key such that only code that knows the secret key is able |
| 6619 | 6691 | ** to access the associated data. |
| @@ -6717,14 +6789,18 @@ | ||
| 6717 | 6789 | ** ^The sqlite3_result_text(), sqlite3_result_text16(), |
| 6718 | 6790 | ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces |
| 6719 | 6791 | ** set the return value of the application-defined function to be |
| 6720 | 6792 | ** a text string which is represented as UTF-8, UTF-16 native byte order, |
| 6721 | 6793 | ** UTF-16 little endian, or UTF-16 big endian, respectively. |
| 6722 | -** ^The sqlite3_result_text64() interface sets the return value of an | |
| 6794 | +** ^The sqlite3_result_text64(C,Z,N,D,E) interface sets the return value of an | |
| 6723 | 6795 | ** application-defined function to be a text string in an encoding |
| 6724 | -** specified by the fifth (and last) parameter, which must be one | |
| 6725 | -** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. | |
| 6796 | +** specified the E parameter, which must be one | |
| 6797 | +** of [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], | |
| 6798 | +** or [SQLITE_UTF16LE]. ^The special value [SQLITE_UTF8_ZT] means that | |
| 6799 | +** the result text is both UTF-8 and zero-terminated. In other words, | |
| 6800 | +** SQLITE_UTF8_ZT means that the Z array holds at least N+1 byes and that | |
| 6801 | +** the Z[N] is zero. | |
| 6726 | 6802 | ** ^SQLite takes the text result from the application from |
| 6727 | 6803 | ** the 2nd parameter of the sqlite3_result_text* interfaces. |
| 6728 | 6804 | ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces |
| 6729 | 6805 | ** other than sqlite3_result_text64() is negative, then SQLite computes |
| 6730 | 6806 | ** the string length itself by searching the 2nd parameter for the first |
| @@ -6807,11 +6883,11 @@ | ||
| 6807 | 6883 | SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); |
| 6808 | 6884 | SQLITE_API void sqlite3_result_int(sqlite3_context*, int); |
| 6809 | 6885 | SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); |
| 6810 | 6886 | SQLITE_API void sqlite3_result_null(sqlite3_context*); |
| 6811 | 6887 | SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); |
| 6812 | -SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, | |
| 6888 | +SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char *z, sqlite3_uint64 n, | |
| 6813 | 6889 | void(*)(void*), unsigned char encoding); |
| 6814 | 6890 | SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); |
| 6815 | 6891 | SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6816 | 6892 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6817 | 6893 | SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); |
| @@ -7746,22 +7822,22 @@ | ||
| 7746 | 7822 | ** ^This interface loads an SQLite extension library from the named file. |
| 7747 | 7823 | ** |
| 7748 | 7824 | ** ^The sqlite3_load_extension() interface attempts to load an |
| 7749 | 7825 | ** [SQLite extension] library contained in the file zFile. If |
| 7750 | 7826 | ** the file cannot be loaded directly, attempts are made to load |
| 7751 | -** with various operating-system specific extensions added. | |
| 7827 | +** with various operating-system specific filename extensions added. | |
| 7752 | 7828 | ** So for example, if "samplelib" cannot be loaded, then names like |
| 7753 | 7829 | ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might |
| 7754 | 7830 | ** be tried also. |
| 7755 | 7831 | ** |
| 7756 | 7832 | ** ^The entry point is zProc. |
| 7757 | 7833 | ** ^(zProc may be 0, in which case SQLite will try to come up with an |
| 7758 | 7834 | ** entry point name on its own. It first tries "sqlite3_extension_init". |
| 7759 | -** If that does not work, it constructs a name "sqlite3_X_init" where | |
| 7760 | -** X consists of the lower-case equivalent of all ASCII alphabetic | |
| 7761 | -** characters in the filename from the last "/" to the first following | |
| 7762 | -** "." and omitting any initial "lib".)^ | |
| 7835 | +** If that does not work, it tries names of the form "sqlite3_X_init" | |
| 7836 | +** where X consists of the lower-case equivalent of all ASCII alphabetic | |
| 7837 | +** characters or all ASCII alphanumeric characters in the filename from | |
| 7838 | +** the last "/" to the first following "." and omitting any initial "lib".)^ | |
| 7763 | 7839 | ** ^The sqlite3_load_extension() interface returns |
| 7764 | 7840 | ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. |
| 7765 | 7841 | ** ^If an error occurs and pzErrMsg is not 0, then the |
| 7766 | 7842 | ** [sqlite3_load_extension()] interface shall attempt to |
| 7767 | 7843 | ** fill *pzErrMsg with error message text stored in memory |
| @@ -11495,23 +11571,45 @@ | ||
| 11495 | 11571 | #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ |
| 11496 | 11572 | |
| 11497 | 11573 | /* |
| 11498 | 11574 | ** CAPI3REF: Bind array values to the CARRAY table-valued function |
| 11499 | 11575 | ** |
| 11500 | -** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to | |
| 11501 | -** one of the first argument of the [carray() table-valued function]. The | |
| 11502 | -** S parameter is a pointer to the [prepared statement] that uses the carray() | |
| 11503 | -** functions. I is the parameter index to be bound. P is a pointer to the | |
| 11504 | -** array to be bound, and N is the number of eements in the array. The | |
| 11505 | -** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], | |
| 11506 | -** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to | |
| 11507 | -** indicate the datatype of the array being bound. The X argument is not a | |
| 11508 | -** NULL pointer, then SQLite will invoke the function X on the P parameter | |
| 11509 | -** after it has finished using P, even if the call to | |
| 11510 | -** sqlite3_carray_bind() fails. The special-case finalizer | |
| 11511 | -** SQLITE_TRANSIENT has no effect here. | |
| 11576 | +** The sqlite3_carray_bind_v2(S,I,P,N,F,X,D) interface binds an array value to | |
| 11577 | +** parameter that is the first argument of the [carray() table-valued function]. | |
| 11578 | +** The S parameter is a pointer to the [prepared statement] that uses the carray() | |
| 11579 | +** functions. I is the parameter index to be bound. I must be the index of the | |
| 11580 | +** parameter that is the first argument to the carray() table-valued function. | |
| 11581 | +** P is a pointer to the array to be bound, and N is the number of elements in | |
| 11582 | +** the array. The F argument is one of constants [SQLITE_CARRAY_INT32], | |
| 11583 | +** [SQLITE_CARRAY_INT64], [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], | |
| 11584 | +** or [SQLITE_CARRAY_BLOB] to indicate the datatype of the array P. | |
| 11585 | +** | |
| 11586 | +** If the X argument is not a NULL pointer or one of the special | |
| 11587 | +** values [SQLITE_STATIC] or [SQLITE_TRANSIENT], then SQLite will invoke | |
| 11588 | +** the function X with argument D when it is finished using the data in P. | |
| 11589 | +** The call to X(D) is a destructor for the array P. The destructor X(D) | |
| 11590 | +** is invoked even if the call to sqlite3_carray_bind() fails. If the X | |
| 11591 | +** parameter is the special-case value [SQLITE_STATIC], then SQLite assumes | |
| 11592 | +** that the data static and the destructor is never invoked. If the X | |
| 11593 | +** parameter is the special-case value [SQLITE_TRANSIENT], then | |
| 11594 | +** sqlite3_carray_bind_v2() makes its own private copy of the data prior | |
| 11595 | +** to returning and never invokes the destructor X. | |
| 11596 | +** | |
| 11597 | +** The sqlite3_carray_bind() function works the same as sqlite_carray_bind_v2() | |
| 11598 | +** with a D parameter set to P. In other words, | |
| 11599 | +** sqlite3_carray_bind(S,I,P,N,F,X) is same as | |
| 11600 | +** sqlite3_carray_bind(S,I,P,N,F,X,P). | |
| 11512 | 11601 | */ |
| 11602 | +SQLITE_API int sqlite3_carray_bind_v2( | |
| 11603 | + sqlite3_stmt *pStmt, /* Statement to be bound */ | |
| 11604 | + int i, /* Parameter index */ | |
| 11605 | + void *aData, /* Pointer to array data */ | |
| 11606 | + int nData, /* Number of data elements */ | |
| 11607 | + int mFlags, /* CARRAY flags */ | |
| 11608 | + void (*xDel)(void*), /* Destructor for aData */ | |
| 11609 | + void *pDel /* Optional argument to xDel() */ | |
| 11610 | +); | |
| 11513 | 11611 | SQLITE_API int sqlite3_carray_bind( |
| 11514 | 11612 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11515 | 11613 | int i, /* Parameter index */ |
| 11516 | 11614 | void *aData, /* Pointer to array data */ |
| 11517 | 11615 | int nData, /* Number of data elements */ |
| @@ -14352,10 +14450,31 @@ | ||
| 14352 | 14450 | #ifndef SQLITE_MAX_LENGTH |
| 14353 | 14451 | # define SQLITE_MAX_LENGTH 1000000000 |
| 14354 | 14452 | #endif |
| 14355 | 14453 | #define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ |
| 14356 | 14454 | |
| 14455 | +/* | |
| 14456 | +** Maximum size of any single memory allocation. | |
| 14457 | +** | |
| 14458 | +** This is not a limit on the total amount of memory used. This is | |
| 14459 | +** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc(). | |
| 14460 | +** | |
| 14461 | +** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 | |
| 14462 | +** This provides a 256-byte safety margin for defense against 32-bit | |
| 14463 | +** signed integer overflow bugs when computing memory allocation sizes. | |
| 14464 | +** Paranoid applications might want to reduce the maximum allocation size | |
| 14465 | +** further for an even larger safety margin. 0x3fffffff or 0x0fffffff | |
| 14466 | +** or even smaller would be reasonable upper bounds on the size of a memory | |
| 14467 | +** allocations for most applications. | |
| 14468 | +*/ | |
| 14469 | +#ifndef SQLITE_MAX_ALLOCATION_SIZE | |
| 14470 | +# define SQLITE_MAX_ALLOCATION_SIZE 2147483391 | |
| 14471 | +#endif | |
| 14472 | +#if SQLITE_MAX_ALLOCATION_SIZE>2147483391 | |
| 14473 | +# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391 | |
| 14474 | +#endif | |
| 14475 | + | |
| 14357 | 14476 | /* |
| 14358 | 14477 | ** This is the maximum number of |
| 14359 | 14478 | ** |
| 14360 | 14479 | ** * Columns in a table |
| 14361 | 14480 | ** * Columns in an index |
| @@ -17529,11 +17648,11 @@ | ||
| 17529 | 17648 | |
| 17530 | 17649 | /* |
| 17531 | 17650 | ** Additional non-public SQLITE_PREPARE_* flags |
| 17532 | 17651 | */ |
| 17533 | 17652 | #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ |
| 17534 | -#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ | |
| 17653 | +#define SQLITE_PREPARE_MASK 0x3f /* Mask of public flags */ | |
| 17535 | 17654 | |
| 17536 | 17655 | /* |
| 17537 | 17656 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 17538 | 17657 | ** for a description of what each of these routines does. |
| 17539 | 17658 | */ |
| @@ -20202,31 +20321,17 @@ | ||
| 20202 | 20321 | }; |
| 20203 | 20322 | |
| 20204 | 20323 | /* |
| 20205 | 20324 | ** An instance of the following structure contains all information |
| 20206 | 20325 | ** needed to generate code for a single SELECT statement. |
| 20207 | -** | |
| 20208 | -** See the header comment on the computeLimitRegisters() routine for a | |
| 20209 | -** detailed description of the meaning of the iLimit and iOffset fields. | |
| 20210 | -** | |
| 20211 | -** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes. | |
| 20212 | -** These addresses must be stored so that we can go back and fill in | |
| 20213 | -** the P4_KEYINFO and P2 parameters later. Neither the KeyInfo nor | |
| 20214 | -** the number of columns in P2 can be computed at the same time | |
| 20215 | -** as the OP_OpenEphm instruction is coded because not | |
| 20216 | -** enough information about the compound query is known at that point. | |
| 20217 | -** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences | |
| 20218 | -** for the result set. The KeyInfo for addrOpenEphm[2] contains collating | |
| 20219 | -** sequences for the ORDER BY clause. | |
| 20220 | 20326 | */ |
| 20221 | 20327 | struct Select { |
| 20222 | 20328 | u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ |
| 20223 | 20329 | LogEst nSelectRow; /* Estimated number of result rows */ |
| 20224 | 20330 | u32 selFlags; /* Various SF_* values */ |
| 20225 | 20331 | int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ |
| 20226 | 20332 | u32 selId; /* Unique identifier number for this SELECT */ |
| 20227 | - int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */ | |
| 20228 | 20333 | ExprList *pEList; /* The fields of the result */ |
| 20229 | 20334 | SrcList *pSrc; /* The FROM clause */ |
| 20230 | 20335 | Expr *pWhere; /* The WHERE clause */ |
| 20231 | 20336 | ExprList *pGroupBy; /* The GROUP BY clause */ |
| 20232 | 20337 | Expr *pHaving; /* The HAVING clause */ |
| @@ -20254,28 +20359,28 @@ | ||
| 20254 | 20359 | #define SF_Distinct 0x0000001 /* Output should be DISTINCT */ |
| 20255 | 20360 | #define SF_All 0x0000002 /* Includes the ALL keyword */ |
| 20256 | 20361 | #define SF_Resolved 0x0000004 /* Identifiers have been resolved */ |
| 20257 | 20362 | #define SF_Aggregate 0x0000008 /* Contains agg functions or a GROUP BY */ |
| 20258 | 20363 | #define SF_HasAgg 0x0000010 /* Contains aggregate functions */ |
| 20259 | -#define SF_UsesEphemeral 0x0000020 /* Uses the OpenEphemeral opcode */ | |
| 20364 | +/* 0x0000020 // available for reuse */ | |
| 20260 | 20365 | #define SF_Expanded 0x0000040 /* sqlite3SelectExpand() called on this */ |
| 20261 | 20366 | #define SF_HasTypeInfo 0x0000080 /* FROM subqueries have Table metadata */ |
| 20262 | 20367 | #define SF_Compound 0x0000100 /* Part of a compound query */ |
| 20263 | 20368 | #define SF_Values 0x0000200 /* Synthesized from VALUES clause */ |
| 20264 | 20369 | #define SF_MultiValue 0x0000400 /* Single VALUES term with multiple rows */ |
| 20265 | 20370 | #define SF_NestedFrom 0x0000800 /* Part of a parenthesized FROM clause */ |
| 20266 | 20371 | #define SF_MinMaxAgg 0x0001000 /* Aggregate containing min() or max() */ |
| 20267 | 20372 | #define SF_Recursive 0x0002000 /* The recursive part of a recursive CTE */ |
| 20268 | 20373 | #define SF_FixedLimit 0x0004000 /* nSelectRow set by a constant LIMIT */ |
| 20269 | -#define SF_MaybeConvert 0x0008000 /* Need convertCompoundSelectToSubquery() */ | |
| 20374 | +/* 0x0008000 // available for reuse */ | |
| 20270 | 20375 | #define SF_Converted 0x0010000 /* By convertCompoundSelectToSubquery() */ |
| 20271 | 20376 | #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */ |
| 20272 | 20377 | #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */ |
| 20273 | 20378 | #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ |
| 20274 | 20379 | #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ |
| 20275 | 20380 | #define SF_View 0x0200000 /* SELECT statement is a view */ |
| 20276 | -#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ | |
| 20381 | +/* 0x0400000 // available for reuse */ | |
| 20277 | 20382 | #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ |
| 20278 | 20383 | #define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ |
| 20279 | 20384 | #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ |
| 20280 | 20385 | #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ |
| 20281 | 20386 | #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ |
| @@ -20291,15 +20396,10 @@ | ||
| 20291 | 20396 | /* |
| 20292 | 20397 | ** The results of a SELECT can be distributed in several ways, as defined |
| 20293 | 20398 | ** by one of the following macros. The "SRT" prefix means "SELECT Result |
| 20294 | 20399 | ** Type". |
| 20295 | 20400 | ** |
| 20296 | -** SRT_Union Store results as a key in a temporary index | |
| 20297 | -** identified by pDest->iSDParm. | |
| 20298 | -** | |
| 20299 | -** SRT_Except Remove results from the temporary index pDest->iSDParm. | |
| 20300 | -** | |
| 20301 | 20401 | ** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result |
| 20302 | 20402 | ** set is not empty. |
| 20303 | 20403 | ** |
| 20304 | 20404 | ** SRT_Discard Throw the results away. This is used by SELECT |
| 20305 | 20405 | ** statements within triggers whose only purpose is |
| @@ -20359,34 +20459,32 @@ | ||
| 20359 | 20459 | ** column returned by the SELECT is used as the integer |
| 20360 | 20460 | ** key. If (pDest->iSDParm>0), then the table is an index |
| 20361 | 20461 | ** table. (pDest->iSDParm) is the number of key columns in |
| 20362 | 20462 | ** each index record in this case. |
| 20363 | 20463 | */ |
| 20364 | -#define SRT_Union 1 /* Store result as keys in an index */ | |
| 20365 | -#define SRT_Except 2 /* Remove result from a UNION index */ | |
| 20366 | -#define SRT_Exists 3 /* Store 1 if the result is not empty */ | |
| 20367 | -#define SRT_Discard 4 /* Do not save the results anywhere */ | |
| 20368 | -#define SRT_DistFifo 5 /* Like SRT_Fifo, but unique results only */ | |
| 20369 | -#define SRT_DistQueue 6 /* Like SRT_Queue, but unique results only */ | |
| 20464 | +#define SRT_Exists 1 /* Store 1 if the result is not empty */ | |
| 20465 | +#define SRT_Discard 2 /* Do not save the results anywhere */ | |
| 20466 | +#define SRT_DistFifo 3 /* Like SRT_Fifo, but unique results only */ | |
| 20467 | +#define SRT_DistQueue 4 /* Like SRT_Queue, but unique results only */ | |
| 20370 | 20468 | |
| 20371 | 20469 | /* The DISTINCT clause is ignored for all of the above. Not that |
| 20372 | 20470 | ** IgnorableDistinct() implies IgnorableOrderby() */ |
| 20373 | 20471 | #define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue) |
| 20374 | 20472 | |
| 20375 | -#define SRT_Queue 7 /* Store result in an queue */ | |
| 20376 | -#define SRT_Fifo 8 /* Store result as data with an automatic rowid */ | |
| 20473 | +#define SRT_Queue 5 /* Store result in an queue */ | |
| 20474 | +#define SRT_Fifo 6 /* Store result as data with an automatic rowid */ | |
| 20377 | 20475 | |
| 20378 | 20476 | /* The ORDER BY clause is ignored for all of the above */ |
| 20379 | 20477 | #define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo) |
| 20380 | 20478 | |
| 20381 | -#define SRT_Output 9 /* Output each row of result */ | |
| 20382 | -#define SRT_Mem 10 /* Store result in a memory cell */ | |
| 20383 | -#define SRT_Set 11 /* Store results as keys in an index */ | |
| 20384 | -#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */ | |
| 20385 | -#define SRT_Coroutine 13 /* Generate a single row of result */ | |
| 20386 | -#define SRT_Table 14 /* Store result as data with an automatic rowid */ | |
| 20387 | -#define SRT_Upfrom 15 /* Store result as data with rowid */ | |
| 20479 | +#define SRT_Output 7 /* Output each row of result */ | |
| 20480 | +#define SRT_Mem 8 /* Store result in a memory cell */ | |
| 20481 | +#define SRT_Set 9 /* Store results as keys in an index */ | |
| 20482 | +#define SRT_EphemTab 10 /* Create transient tab and store like SRT_Table */ | |
| 20483 | +#define SRT_Coroutine 11 /* Generate a single row of result */ | |
| 20484 | +#define SRT_Table 12 /* Store result as data with an automatic rowid */ | |
| 20485 | +#define SRT_Upfrom 13 /* Store result as data with rowid */ | |
| 20388 | 20486 | |
| 20389 | 20487 | /* |
| 20390 | 20488 | ** An instance of this object describes where to put of the results of |
| 20391 | 20489 | ** a SELECT statement. |
| 20392 | 20490 | */ |
| @@ -21018,10 +21116,11 @@ | ||
| 21018 | 21116 | u16 mWFlags; /* Use-dependent flags */ |
| 21019 | 21117 | union { /* Extra data for callback */ |
| 21020 | 21118 | NameContext *pNC; /* Naming context */ |
| 21021 | 21119 | int n; /* A counter */ |
| 21022 | 21120 | int iCur; /* A cursor number */ |
| 21121 | + int sz; /* String literal length */ | |
| 21023 | 21122 | SrcList *pSrcList; /* FROM clause */ |
| 21024 | 21123 | struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ |
| 21025 | 21124 | struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */ |
| 21026 | 21125 | int *aiCol; /* array of column indexes */ |
| 21027 | 21126 | struct IdxCover *pIdxCover; /* Check for index coverage */ |
| @@ -21801,10 +21900,11 @@ | ||
| 21801 | 21900 | SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); |
| 21802 | 21901 | #endif |
| 21803 | 21902 | SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*, Parse*); |
| 21804 | 21903 | SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); |
| 21805 | 21904 | SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); |
| 21905 | +SQLITE_PRIVATE int sqlite3ExprIsLikeOperator(const Expr*); | |
| 21806 | 21906 | SQLITE_PRIVATE int sqlite3IsRowid(const char*); |
| 21807 | 21907 | SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); |
| 21808 | 21908 | SQLITE_PRIVATE void sqlite3GenerateRowDelete( |
| 21809 | 21909 | Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); |
| 21810 | 21910 | SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); |
| @@ -24489,10 +24589,11 @@ | ||
| 24489 | 24589 | SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); |
| 24490 | 24590 | SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); |
| 24491 | 24591 | SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); |
| 24492 | 24592 | SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); |
| 24493 | 24593 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); |
| 24594 | +SQLITE_PRIVATE int sqlite3VdbeMemSetText(Mem*, const char*, i64, void(*)(void*)); | |
| 24494 | 24595 | SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); |
| 24495 | 24596 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 24496 | 24597 | # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 |
| 24497 | 24598 | #else |
| 24498 | 24599 | SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); |
| @@ -31335,31 +31436,10 @@ | ||
| 31335 | 31436 | sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); |
| 31336 | 31437 | } |
| 31337 | 31438 | *pp = p; |
| 31338 | 31439 | } |
| 31339 | 31440 | |
| 31340 | -/* | |
| 31341 | -** Maximum size of any single memory allocation. | |
| 31342 | -** | |
| 31343 | -** This is not a limit on the total amount of memory used. This is | |
| 31344 | -** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc(). | |
| 31345 | -** | |
| 31346 | -** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 | |
| 31347 | -** This provides a 256-byte safety margin for defense against 32-bit | |
| 31348 | -** signed integer overflow bugs when computing memory allocation sizes. | |
| 31349 | -** Paranoid applications might want to reduce the maximum allocation size | |
| 31350 | -** further for an even larger safety margin. 0x3fffffff or 0x0fffffff | |
| 31351 | -** or even smaller would be reasonable upper bounds on the size of a memory | |
| 31352 | -** allocations for most applications. | |
| 31353 | -*/ | |
| 31354 | -#ifndef SQLITE_MAX_ALLOCATION_SIZE | |
| 31355 | -# define SQLITE_MAX_ALLOCATION_SIZE 2147483391 | |
| 31356 | -#endif | |
| 31357 | -#if SQLITE_MAX_ALLOCATION_SIZE>2147483391 | |
| 31358 | -# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391 | |
| 31359 | -#endif | |
| 31360 | - | |
| 31361 | 31441 | /* |
| 31362 | 31442 | ** Allocate memory. This routine is like sqlite3_malloc() except that it |
| 31363 | 31443 | ** assumes the memory subsystem has already been initialized. |
| 31364 | 31444 | */ |
| 31365 | 31445 | SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ |
| @@ -31579,12 +31659,11 @@ | ||
| 31579 | 31659 | } |
| 31580 | 31660 | if( nBytes==0 ){ |
| 31581 | 31661 | sqlite3_free(pOld); /* IMP: R-26507-47431 */ |
| 31582 | 31662 | return 0; |
| 31583 | 31663 | } |
| 31584 | - if( nBytes>=0x7fffff00 ){ | |
| 31585 | - /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ | |
| 31664 | + if( nBytes>SQLITE_MAX_ALLOCATION_SIZE ){ | |
| 31586 | 31665 | return 0; |
| 31587 | 31666 | } |
| 31588 | 31667 | nOld = sqlite3MallocSize(pOld); |
| 31589 | 31668 | /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second |
| 31590 | 31669 | ** argument to xRealloc is always a value returned by a prior call to |
| @@ -48881,78 +48960,54 @@ | ||
| 48881 | 48960 | { "WriteFile", (SYSCALL)WriteFile, 0 }, |
| 48882 | 48961 | |
| 48883 | 48962 | #define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ |
| 48884 | 48963 | LPOVERLAPPED))aSyscall[61].pCurrent) |
| 48885 | 48964 | |
| 48886 | - { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, | |
| 48887 | - | |
| 48888 | -#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ | |
| 48889 | - DWORD,DWORD))aSyscall[62].pCurrent) | |
| 48890 | - | |
| 48891 | 48965 | /* |
| 48892 | 48966 | ** For WaitForSingleObject(), MSDN says: |
| 48893 | 48967 | ** |
| 48894 | 48968 | ** Minimum supported client: Windows XP [desktop apps | UWP apps] |
| 48895 | 48969 | ** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] |
| 48896 | 48970 | */ |
| 48897 | 48971 | { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, |
| 48898 | 48972 | |
| 48899 | 48973 | #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ |
| 48900 | - DWORD))aSyscall[63].pCurrent) | |
| 48974 | + DWORD))aSyscall[62].pCurrent) | |
| 48901 | 48975 | |
| 48902 | 48976 | #if !SQLITE_OS_WINCE |
| 48903 | 48977 | { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, |
| 48904 | 48978 | #else |
| 48905 | 48979 | { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, |
| 48906 | 48980 | #endif |
| 48907 | 48981 | |
| 48908 | 48982 | #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ |
| 48909 | - BOOL))aSyscall[64].pCurrent) | |
| 48910 | - | |
| 48911 | - { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, | |
| 48912 | - | |
| 48913 | -#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ | |
| 48914 | - PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent) | |
| 48915 | - | |
| 48916 | - { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, | |
| 48917 | - | |
| 48918 | -#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ | |
| 48919 | - FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent) | |
| 48920 | - | |
| 48921 | - { "CreateFile2", (SYSCALL)CreateFile2, 0 }, | |
| 48922 | - | |
| 48923 | -#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ | |
| 48924 | - LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent) | |
| 48925 | - | |
| 48926 | - { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, | |
| 48927 | - | |
| 48928 | -#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent) | |
| 48983 | + BOOL))aSyscall[63].pCurrent) | |
| 48929 | 48984 | |
| 48930 | 48985 | { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, |
| 48931 | 48986 | |
| 48932 | 48987 | #define osGetNativeSystemInfo ((VOID(WINAPI*)( \ |
| 48933 | - LPSYSTEM_INFO))aSyscall[71].pCurrent) | |
| 48988 | + LPSYSTEM_INFO))aSyscall[64].pCurrent) | |
| 48934 | 48989 | |
| 48935 | 48990 | #if defined(SQLITE_WIN32_HAS_ANSI) |
| 48936 | 48991 | { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, |
| 48937 | 48992 | #else |
| 48938 | 48993 | { "OutputDebugStringA", (SYSCALL)0, 0 }, |
| 48939 | 48994 | #endif |
| 48940 | 48995 | |
| 48941 | -#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent) | |
| 48996 | +#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[65].pCurrent) | |
| 48942 | 48997 | |
| 48943 | 48998 | #if defined(SQLITE_WIN32_HAS_WIDE) |
| 48944 | 48999 | { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, |
| 48945 | 49000 | #else |
| 48946 | 49001 | { "OutputDebugStringW", (SYSCALL)0, 0 }, |
| 48947 | 49002 | #endif |
| 48948 | 49003 | |
| 48949 | -#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent) | |
| 49004 | +#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[66].pCurrent) | |
| 48950 | 49005 | |
| 48951 | 49006 | { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, |
| 48952 | 49007 | |
| 48953 | -#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent) | |
| 49008 | +#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[67].pCurrent) | |
| 48954 | 49009 | |
| 48955 | 49010 | /* |
| 48956 | 49011 | ** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" |
| 48957 | 49012 | ** is really just a macro that uses a compiler intrinsic (e.g. x64). |
| 48958 | 49013 | ** So do not try to make this is into a redefinable interface. |
| @@ -48963,38 +49018,38 @@ | ||
| 48963 | 49018 | #define osInterlockedCompareExchange InterlockedCompareExchange |
| 48964 | 49019 | #else |
| 48965 | 49020 | { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, |
| 48966 | 49021 | |
| 48967 | 49022 | #define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ |
| 48968 | - SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) | |
| 49023 | + SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[68].pCurrent) | |
| 48969 | 49024 | #endif /* defined(InterlockedCompareExchange) */ |
| 48970 | 49025 | |
| 48971 | 49026 | #if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID |
| 48972 | 49027 | { "UuidCreate", (SYSCALL)UuidCreate, 0 }, |
| 48973 | 49028 | #else |
| 48974 | 49029 | { "UuidCreate", (SYSCALL)0, 0 }, |
| 48975 | 49030 | #endif |
| 48976 | 49031 | |
| 48977 | -#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) | |
| 49032 | +#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[69].pCurrent) | |
| 48978 | 49033 | |
| 48979 | 49034 | #if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID |
| 48980 | 49035 | { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, |
| 48981 | 49036 | #else |
| 48982 | 49037 | { "UuidCreateSequential", (SYSCALL)0, 0 }, |
| 48983 | 49038 | #endif |
| 48984 | 49039 | |
| 48985 | 49040 | #define osUuidCreateSequential \ |
| 48986 | - ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) | |
| 49041 | + ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[70].pCurrent) | |
| 48987 | 49042 | |
| 48988 | 49043 | #if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 |
| 48989 | 49044 | { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, |
| 48990 | 49045 | #else |
| 48991 | 49046 | { "FlushViewOfFile", (SYSCALL)0, 0 }, |
| 48992 | 49047 | #endif |
| 48993 | 49048 | |
| 48994 | 49049 | #define osFlushViewOfFile \ |
| 48995 | - ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) | |
| 49050 | + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[71].pCurrent) | |
| 48996 | 49051 | |
| 48997 | 49052 | /* |
| 48998 | 49053 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() |
| 48999 | 49054 | ** to implement blocking locks with timeouts. MSDN says: |
| 49000 | 49055 | ** |
| @@ -49007,11 +49062,11 @@ | ||
| 49007 | 49062 | { "CreateEvent", (SYSCALL)0, 0 }, |
| 49008 | 49063 | #endif |
| 49009 | 49064 | |
| 49010 | 49065 | #define osCreateEvent ( \ |
| 49011 | 49066 | (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ |
| 49012 | - aSyscall[80].pCurrent \ | |
| 49067 | + aSyscall[72].pCurrent \ | |
| 49013 | 49068 | ) |
| 49014 | 49069 | |
| 49015 | 49070 | /* |
| 49016 | 49071 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() |
| 49017 | 49072 | ** for the case where a timeout expires and a lock request must be |
| @@ -49024,68 +49079,68 @@ | ||
| 49024 | 49079 | { "CancelIo", (SYSCALL)CancelIo, 0 }, |
| 49025 | 49080 | #else |
| 49026 | 49081 | { "CancelIo", (SYSCALL)0, 0 }, |
| 49027 | 49082 | #endif |
| 49028 | 49083 | |
| 49029 | -#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent) | |
| 49084 | +#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[73].pCurrent) | |
| 49030 | 49085 | |
| 49031 | 49086 | #if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32) |
| 49032 | 49087 | { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 }, |
| 49033 | 49088 | #else |
| 49034 | 49089 | { "GetModuleHandleW", (SYSCALL)0, 0 }, |
| 49035 | 49090 | #endif |
| 49036 | 49091 | |
| 49037 | -#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent) | |
| 49092 | +#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[74].pCurrent) | |
| 49038 | 49093 | |
| 49039 | 49094 | #ifndef _WIN32 |
| 49040 | 49095 | { "getenv", (SYSCALL)getenv, 0 }, |
| 49041 | 49096 | #else |
| 49042 | 49097 | { "getenv", (SYSCALL)0, 0 }, |
| 49043 | 49098 | #endif |
| 49044 | 49099 | |
| 49045 | -#define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent) | |
| 49100 | +#define osGetenv ((const char *(*)(const char *))aSyscall[75].pCurrent) | |
| 49046 | 49101 | |
| 49047 | 49102 | #ifndef _WIN32 |
| 49048 | 49103 | { "getcwd", (SYSCALL)getcwd, 0 }, |
| 49049 | 49104 | #else |
| 49050 | 49105 | { "getcwd", (SYSCALL)0, 0 }, |
| 49051 | 49106 | #endif |
| 49052 | 49107 | |
| 49053 | -#define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent) | |
| 49108 | +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[76].pCurrent) | |
| 49054 | 49109 | |
| 49055 | 49110 | #ifndef _WIN32 |
| 49056 | 49111 | { "readlink", (SYSCALL)readlink, 0 }, |
| 49057 | 49112 | #else |
| 49058 | 49113 | { "readlink", (SYSCALL)0, 0 }, |
| 49059 | 49114 | #endif |
| 49060 | 49115 | |
| 49061 | -#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent) | |
| 49116 | +#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[77].pCurrent) | |
| 49062 | 49117 | |
| 49063 | 49118 | #ifndef _WIN32 |
| 49064 | 49119 | { "lstat", (SYSCALL)lstat, 0 }, |
| 49065 | 49120 | #else |
| 49066 | 49121 | { "lstat", (SYSCALL)0, 0 }, |
| 49067 | 49122 | #endif |
| 49068 | 49123 | |
| 49069 | -#define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent) | |
| 49124 | +#define osLstat ((int(*)(const char*,struct stat*))aSyscall[78].pCurrent) | |
| 49070 | 49125 | |
| 49071 | 49126 | #ifndef _WIN32 |
| 49072 | 49127 | { "__errno", (SYSCALL)__errno, 0 }, |
| 49073 | 49128 | #else |
| 49074 | 49129 | { "__errno", (SYSCALL)0, 0 }, |
| 49075 | 49130 | #endif |
| 49076 | 49131 | |
| 49077 | -#define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)()) | |
| 49132 | +#define osErrno (*((int*(*)(void))aSyscall[79].pCurrent)()) | |
| 49078 | 49133 | |
| 49079 | 49134 | #ifndef _WIN32 |
| 49080 | 49135 | { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 }, |
| 49081 | 49136 | #else |
| 49082 | 49137 | { "cygwin_conv_path", (SYSCALL)0, 0 }, |
| 49083 | 49138 | #endif |
| 49084 | 49139 | |
| 49085 | 49140 | #define osCygwin_conv_path ((size_t(*)(unsigned int, \ |
| 49086 | - const void *, void *, size_t))aSyscall[88].pCurrent) | |
| 49141 | + const void *, void *, size_t))aSyscall[80].pCurrent) | |
| 49087 | 49142 | |
| 49088 | 49143 | }; /* End of the overrideable system calls */ |
| 49089 | 49144 | |
| 49090 | 49145 | /* |
| 49091 | 49146 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| @@ -53659,10 +53714,11 @@ | ||
| 53659 | 53714 | attr = INVALID_FILE_ATTRIBUTES; |
| 53660 | 53715 | }else{ |
| 53661 | 53716 | attr = sAttrData.dwFileAttributes; |
| 53662 | 53717 | } |
| 53663 | 53718 | }else{ |
| 53719 | + if( noRetry ) lastErrno = osGetLastError(); | |
| 53664 | 53720 | winLogIoerr(cnt, __LINE__); |
| 53665 | 53721 | if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ |
| 53666 | 53722 | sqlite3_free(zConverted); |
| 53667 | 53723 | return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", |
| 53668 | 53724 | zFilename); |
| @@ -54399,11 +54455,16 @@ | ||
| 54399 | 54455 | }; |
| 54400 | 54456 | #endif |
| 54401 | 54457 | |
| 54402 | 54458 | /* Double-check that the aSyscall[] array has been constructed |
| 54403 | 54459 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 54404 | - assert( ArraySize(aSyscall)==86 ); | |
| 54460 | + assert( ArraySize(aSyscall)==81 ); | |
| 54461 | + assert( strcmp(aSyscall[0].zName,"AreFileApisANSI")==0 ); | |
| 54462 | + assert( strcmp(aSyscall[20].zName,"GetFileAttributesA")==0 ); | |
| 54463 | + assert( strcmp(aSyscall[40].zName,"HeapReAlloc")==0 ); | |
| 54464 | + assert( strcmp(aSyscall[60].zName,"WideCharToMultiByte")==0 ); | |
| 54465 | + assert( strcmp(aSyscall[80].zName,"cygwin_conv_path")==0 ); | |
| 54405 | 54466 | |
| 54406 | 54467 | /* get memory map allocation granularity */ |
| 54407 | 54468 | memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); |
| 54408 | 54469 | osGetSystemInfo(&winSysInfo); |
| 54409 | 54470 | assert( winSysInfo.dwAllocationGranularity>0 ); |
| @@ -77426,11 +77487,11 @@ | ||
| 77426 | 77487 | } |
| 77427 | 77488 | assert( cursorHoldsMutex(pCur) ); |
| 77428 | 77489 | |
| 77429 | 77490 | getCellInfo(pCur); |
| 77430 | 77491 | aPayload = pCur->info.pPayload; |
| 77431 | - assert( offset+amt <= pCur->info.nPayload ); | |
| 77492 | + assert( (u64)offset+(u64)amt <= (u64)pCur->info.nPayload ); | |
| 77432 | 77493 | |
| 77433 | 77494 | assert( aPayload > pPage->aData ); |
| 77434 | 77495 | if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){ |
| 77435 | 77496 | /* Trying to read or write past the end of the data is an error. The |
| 77436 | 77497 | ** conditional above is really: |
| @@ -77983,11 +78044,11 @@ | ||
| 77983 | 78044 | SQLITE_PRIVATE int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes){ |
| 77984 | 78045 | int rc; |
| 77985 | 78046 | |
| 77986 | 78047 | assert( cursorOwnsBtShared(pCur) ); |
| 77987 | 78048 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 77988 | - if( pCur->eState==CURSOR_VALID ){ | |
| 78049 | + if( NEVER(pCur->eState==CURSOR_VALID) ){ | |
| 77989 | 78050 | *pRes = 0; |
| 77990 | 78051 | return SQLITE_OK; |
| 77991 | 78052 | } |
| 77992 | 78053 | rc = moveToRoot(pCur); |
| 77993 | 78054 | if( rc==SQLITE_EMPTY ){ |
| @@ -85876,10 +85937,88 @@ | ||
| 85876 | 85937 | #endif |
| 85877 | 85938 | |
| 85878 | 85939 | |
| 85879 | 85940 | return SQLITE_OK; |
| 85880 | 85941 | } |
| 85942 | + | |
| 85943 | +/* Like sqlite3VdbeMemSetStr() except: | |
| 85944 | +** | |
| 85945 | +** enc is always SQLITE_UTF8 | |
| 85946 | +** pMem->db is always non-NULL | |
| 85947 | +*/ | |
| 85948 | +SQLITE_PRIVATE int sqlite3VdbeMemSetText( | |
| 85949 | + Mem *pMem, /* Memory cell to set to string value */ | |
| 85950 | + const char *z, /* String pointer */ | |
| 85951 | + i64 n, /* Bytes in string, or negative */ | |
| 85952 | + void (*xDel)(void*) /* Destructor function */ | |
| 85953 | +){ | |
| 85954 | + i64 nByte = n; /* New value for pMem->n */ | |
| 85955 | + u16 flags; | |
| 85956 | + | |
| 85957 | + assert( pMem!=0 ); | |
| 85958 | + assert( pMem->db!=0 ); | |
| 85959 | + assert( sqlite3_mutex_held(pMem->db->mutex) ); | |
| 85960 | + assert( !sqlite3VdbeMemIsRowSet(pMem) ); | |
| 85961 | + | |
| 85962 | + /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ | |
| 85963 | + if( !z ){ | |
| 85964 | + sqlite3VdbeMemSetNull(pMem); | |
| 85965 | + return SQLITE_OK; | |
| 85966 | + } | |
| 85967 | + | |
| 85968 | + if( nByte<0 ){ | |
| 85969 | + nByte = strlen(z); | |
| 85970 | + flags = MEM_Str|MEM_Term; | |
| 85971 | + }else{ | |
| 85972 | + flags = MEM_Str; | |
| 85973 | + } | |
| 85974 | + if( nByte>(i64)pMem->db->aLimit[SQLITE_LIMIT_LENGTH] ){ | |
| 85975 | + if( xDel && xDel!=SQLITE_TRANSIENT ){ | |
| 85976 | + if( xDel==SQLITE_DYNAMIC ){ | |
| 85977 | + sqlite3DbFree(pMem->db, (void*)z); | |
| 85978 | + }else{ | |
| 85979 | + xDel((void*)z); | |
| 85980 | + } | |
| 85981 | + } | |
| 85982 | + sqlite3VdbeMemSetNull(pMem); | |
| 85983 | + return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); | |
| 85984 | + } | |
| 85985 | + | |
| 85986 | + /* The following block sets the new values of Mem.z and Mem.xDel. It | |
| 85987 | + ** also sets a flag in local variable "flags" to indicate the memory | |
| 85988 | + ** management (one of MEM_Dyn or MEM_Static). | |
| 85989 | + */ | |
| 85990 | + if( xDel==SQLITE_TRANSIENT ){ | |
| 85991 | + i64 nAlloc = nByte + 1; | |
| 85992 | + testcase( nAlloc==31 ); | |
| 85993 | + testcase( nAlloc==32 ); | |
| 85994 | + if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ | |
| 85995 | + return SQLITE_NOMEM_BKPT; | |
| 85996 | + } | |
| 85997 | + assert( pMem->z!=0 ); | |
| 85998 | + memcpy(pMem->z, z, nByte); | |
| 85999 | + pMem->z[nByte] = 0; | |
| 86000 | + }else{ | |
| 86001 | + sqlite3VdbeMemRelease(pMem); | |
| 86002 | + pMem->z = (char *)z; | |
| 86003 | + if( xDel==SQLITE_DYNAMIC ){ | |
| 86004 | + pMem->zMalloc = pMem->z; | |
| 86005 | + pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); | |
| 86006 | + pMem->xDel = 0; | |
| 86007 | + }else if( xDel==SQLITE_STATIC ){ | |
| 86008 | + pMem->xDel = xDel; | |
| 86009 | + flags |= MEM_Static; | |
| 86010 | + }else{ | |
| 86011 | + pMem->xDel = xDel; | |
| 86012 | + flags |= MEM_Dyn; | |
| 86013 | + } | |
| 86014 | + } | |
| 86015 | + pMem->flags = flags; | |
| 86016 | + pMem->n = (int)(nByte & 0x7fffffff); | |
| 86017 | + pMem->enc = SQLITE_UTF8; | |
| 86018 | + return SQLITE_OK; | |
| 86019 | +} | |
| 85881 | 86020 | |
| 85882 | 86021 | /* |
| 85883 | 86022 | ** Move data out of a btree key or data field and into a Mem structure. |
| 85884 | 86023 | ** The data is payload from the entry that pCur is currently pointing |
| 85885 | 86024 | ** to. offset and amt determine what portion of the data or key to retrieve. |
| @@ -85900,11 +86039,16 @@ | ||
| 85900 | 86039 | u32 amt, /* Number of bytes to return. */ |
| 85901 | 86040 | Mem *pMem /* OUT: Return data in this Mem structure. */ |
| 85902 | 86041 | ){ |
| 85903 | 86042 | int rc; |
| 85904 | 86043 | pMem->flags = MEM_Null; |
| 85905 | - if( sqlite3BtreeMaxRecordSize(pCur)<offset+amt ){ | |
| 86044 | + testcase( amt==SQLITE_MAX_ALLOCATION_SIZE-1 ); | |
| 86045 | + testcase( amt==SQLITE_MAX_ALLOCATION_SIZE ); | |
| 86046 | + if( amt>=SQLITE_MAX_ALLOCATION_SIZE ){ | |
| 86047 | + return SQLITE_NOMEM_BKPT; | |
| 86048 | + } | |
| 86049 | + if( (u64)amt + (u64)offset > (u64)sqlite3BtreeMaxRecordSize(pCur) ){ | |
| 85906 | 86050 | return SQLITE_CORRUPT_BKPT; |
| 85907 | 86051 | } |
| 85908 | 86052 | if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){ |
| 85909 | 86053 | rc = sqlite3BtreePayload(pCur, offset, amt, pMem->z); |
| 85910 | 86054 | if( rc==SQLITE_OK ){ |
| @@ -86568,10 +86712,15 @@ | ||
| 86568 | 86712 | ** the column value into *ppVal. If *ppVal is initially NULL then a new |
| 86569 | 86713 | ** sqlite3_value object is allocated. |
| 86570 | 86714 | ** |
| 86571 | 86715 | ** If *ppVal is initially NULL then the caller is responsible for |
| 86572 | 86716 | ** ensuring that the value written into *ppVal is eventually freed. |
| 86717 | +** | |
| 86718 | +** If the buffer does not contain a well-formed record, this routine may | |
| 86719 | +** read several bytes past the end of the buffer. Callers must therefore | |
| 86720 | +** ensure that any buffer which may contain a corrupt record is padded | |
| 86721 | +** with at least 8 bytes of addressable memory. | |
| 86573 | 86722 | */ |
| 86574 | 86723 | SQLITE_PRIVATE int sqlite3Stat4Column( |
| 86575 | 86724 | sqlite3 *db, /* Database handle */ |
| 86576 | 86725 | const void *pRec, /* Pointer to buffer containing record */ |
| 86577 | 86726 | int nRec, /* Size of buffer pRec in bytes */ |
| @@ -89584,11 +89733,11 @@ | ||
| 89584 | 89733 | assert( !zName || xDel!=SQLITE_DYNAMIC ); |
| 89585 | 89734 | return SQLITE_NOMEM_BKPT; |
| 89586 | 89735 | } |
| 89587 | 89736 | assert( p->aColName!=0 ); |
| 89588 | 89737 | pColName = &(p->aColName[idx+var*p->nResAlloc]); |
| 89589 | - rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); | |
| 89738 | + rc = sqlite3VdbeMemSetText(pColName, zName, -1, xDel); | |
| 89590 | 89739 | assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); |
| 89591 | 89740 | return rc; |
| 89592 | 89741 | } |
| 89593 | 89742 | |
| 89594 | 89743 | /* |
| @@ -92662,11 +92811,27 @@ | ||
| 92662 | 92811 | int n, /* Bytes in string, or negative */ |
| 92663 | 92812 | u8 enc, /* Encoding of z. 0 for BLOBs */ |
| 92664 | 92813 | void (*xDel)(void*) /* Destructor function */ |
| 92665 | 92814 | ){ |
| 92666 | 92815 | Mem *pOut = pCtx->pOut; |
| 92667 | - int rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); | |
| 92816 | + int rc; | |
| 92817 | + if( enc==SQLITE_UTF8 ){ | |
| 92818 | + rc = sqlite3VdbeMemSetText(pOut, z, n, xDel); | |
| 92819 | + }else if( enc==SQLITE_UTF8_ZT ){ | |
| 92820 | + /* It is usually considered improper to assert() on an input. However, | |
| 92821 | + ** the following assert() is checking for inputs that are documented | |
| 92822 | + ** to result in undefined behavior. */ | |
| 92823 | + assert( z==0 | |
| 92824 | + || n<0 | |
| 92825 | + || n>pOut->db->aLimit[SQLITE_LIMIT_LENGTH] | |
| 92826 | + || z[n]==0 | |
| 92827 | + ); | |
| 92828 | + rc = sqlite3VdbeMemSetText(pOut, z, n, xDel); | |
| 92829 | + pOut->flags |= MEM_Term; | |
| 92830 | + }else{ | |
| 92831 | + rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); | |
| 92832 | + } | |
| 92668 | 92833 | if( rc ){ |
| 92669 | 92834 | if( rc==SQLITE_TOOBIG ){ |
| 92670 | 92835 | sqlite3_result_error_toobig(pCtx); |
| 92671 | 92836 | }else{ |
| 92672 | 92837 | /* The only errors possible from sqlite3VdbeMemSetStr are |
| @@ -92855,11 +93020,11 @@ | ||
| 92855 | 93020 | return; |
| 92856 | 93021 | } |
| 92857 | 93022 | #endif |
| 92858 | 93023 | assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); |
| 92859 | 93024 | assert( xDel!=SQLITE_DYNAMIC ); |
| 92860 | - if( enc!=SQLITE_UTF8 ){ | |
| 93025 | + if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){ | |
| 92861 | 93026 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 92862 | 93027 | n &= ~(u64)1; |
| 92863 | 93028 | } |
| 92864 | 93029 | if( n>0x7fffffff ){ |
| 92865 | 93030 | (void)invokeValueDestructor(z, xDel, pCtx); |
| @@ -93315,11 +93480,11 @@ | ||
| 93315 | 93480 | if( rc==SQLITE_OK ){ |
| 93316 | 93481 | u32 sz; /* Size of current row in bytes */ |
| 93317 | 93482 | Mem sMem; /* Raw content of current row */ |
| 93318 | 93483 | memset(&sMem, 0, sizeof(sMem)); |
| 93319 | 93484 | sz = sqlite3BtreePayloadSize(pRhs->pCsr); |
| 93320 | - rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem); | |
| 93485 | + rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,sz,&sMem); | |
| 93321 | 93486 | if( rc==SQLITE_OK ){ |
| 93322 | 93487 | u8 *zBuf = (u8*)sMem.z; |
| 93323 | 93488 | u32 iSerial; |
| 93324 | 93489 | sqlite3_value *pOut = pRhs->pOut; |
| 93325 | 93490 | int iOff = 1 + getVarint32(&zBuf[1], iSerial); |
| @@ -93964,17 +94129,29 @@ | ||
| 93964 | 94129 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 93965 | 94130 | if( rc==SQLITE_OK ){ |
| 93966 | 94131 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 93967 | 94132 | if( zData!=0 ){ |
| 93968 | 94133 | pVar = &p->aVar[i-1]; |
| 93969 | - rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); | |
| 93970 | - if( rc==SQLITE_OK ){ | |
| 93971 | - if( encoding==0 ){ | |
| 93972 | - pVar->enc = ENC(p->db); | |
| 93973 | - }else{ | |
| 93974 | - rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); | |
| 93975 | - } | |
| 94134 | + if( encoding==SQLITE_UTF8 ){ | |
| 94135 | + rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel); | |
| 94136 | + }else if( encoding==SQLITE_UTF8_ZT ){ | |
| 94137 | + /* It is usually consider improper to assert() on an input. | |
| 94138 | + ** However, the following assert() is checking for inputs | |
| 94139 | + ** that are documented to result in undefined behavior. */ | |
| 94140 | + assert( zData==0 | |
| 94141 | + || nData<0 | |
| 94142 | + || nData>pVar->db->aLimit[SQLITE_LIMIT_LENGTH] | |
| 94143 | + || ((u8*)zData)[nData]==0 | |
| 94144 | + ); | |
| 94145 | + rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel); | |
| 94146 | + pVar->flags |= MEM_Term; | |
| 94147 | + }else{ | |
| 94148 | + rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); | |
| 94149 | + if( encoding==0 ) pVar->enc = ENC(p->db); | |
| 94150 | + } | |
| 94151 | + if( rc==SQLITE_OK && encoding!=0 ){ | |
| 94152 | + rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); | |
| 93976 | 94153 | } |
| 93977 | 94154 | if( rc ){ |
| 93978 | 94155 | sqlite3Error(p->db, rc); |
| 93979 | 94156 | rc = sqlite3ApiExit(p->db, rc); |
| 93980 | 94157 | } |
| @@ -94082,11 +94259,11 @@ | ||
| 94082 | 94259 | sqlite3_uint64 nData, |
| 94083 | 94260 | void (*xDel)(void*), |
| 94084 | 94261 | unsigned char enc |
| 94085 | 94262 | ){ |
| 94086 | 94263 | assert( xDel!=SQLITE_DYNAMIC ); |
| 94087 | - if( enc!=SQLITE_UTF8 ){ | |
| 94264 | + if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){ | |
| 94088 | 94265 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 94089 | 94266 | nData &= ~(u64)1; |
| 94090 | 94267 | } |
| 94091 | 94268 | return bindText(pStmt, i, zData, nData, xDel, enc); |
| 94092 | 94269 | } |
| @@ -101780,24 +101957,19 @@ | ||
| 101780 | 101957 | rc = sqlite3VdbeSorterWrite(pC, pIn2); |
| 101781 | 101958 | if( rc) goto abort_due_to_error; |
| 101782 | 101959 | break; |
| 101783 | 101960 | } |
| 101784 | 101961 | |
| 101785 | -/* Opcode: IdxDelete P1 P2 P3 * P5 | |
| 101962 | +/* Opcode: IdxDelete P1 P2 P3 * * | |
| 101786 | 101963 | ** Synopsis: key=r[P2@P3] |
| 101787 | 101964 | ** |
| 101788 | 101965 | ** The content of P3 registers starting at register P2 form |
| 101789 | 101966 | ** an unpacked index key. This opcode removes that entry from the |
| 101790 | 101967 | ** index opened by cursor P1. |
| 101791 | 101968 | ** |
| 101792 | -** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error | |
| 101793 | -** if no matching index entry is found. This happens when running | |
| 101794 | -** an UPDATE or DELETE statement and the index entry to be updated | |
| 101795 | -** or deleted is not found. For some uses of IdxDelete | |
| 101796 | -** (example: the EXCEPT operator) it does not matter that no matching | |
| 101797 | -** entry is found. For those cases, P5 is zero. Also, do not raise | |
| 101798 | -** this (self-correcting and non-critical) error if in writable_schema mode. | |
| 101969 | +** Raise an SQLITE_CORRUPT_INDEX error if no matching index entry is found | |
| 101970 | +** and not in writable_schema mode. | |
| 101799 | 101971 | */ |
| 101800 | 101972 | case OP_IdxDelete: { |
| 101801 | 101973 | VdbeCursor *pC; |
| 101802 | 101974 | BtCursor *pCrsr; |
| 101803 | 101975 | int res; |
| @@ -101819,11 +101991,11 @@ | ||
| 101819 | 101991 | rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res); |
| 101820 | 101992 | if( rc ) goto abort_due_to_error; |
| 101821 | 101993 | if( res==0 ){ |
| 101822 | 101994 | rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); |
| 101823 | 101995 | if( rc ) goto abort_due_to_error; |
| 101824 | - }else if( pOp->p5 && !sqlite3WritableSchema(db) ){ | |
| 101996 | + }else if( !sqlite3WritableSchema(db) ){ | |
| 101825 | 101997 | rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); |
| 101826 | 101998 | goto abort_due_to_error; |
| 101827 | 101999 | } |
| 101828 | 102000 | assert( pC->deferredMoveto==0 ); |
| 101829 | 102001 | pC->cacheStatus = CACHE_STALE; |
| @@ -110988,14 +111160,10 @@ | ||
| 110988 | 111160 | } |
| 110989 | 111161 | } |
| 110990 | 111162 | } |
| 110991 | 111163 | #endif |
| 110992 | 111164 | |
| 110993 | - /* The ORDER BY and GROUP BY clauses may not refer to terms in | |
| 110994 | - ** outer queries | |
| 110995 | - */ | |
| 110996 | - sNC.pNext = 0; | |
| 110997 | 111165 | sNC.ncFlags |= NC_AllowAgg|NC_AllowWin; |
| 110998 | 111166 | |
| 110999 | 111167 | /* If this is a converted compound query, move the ORDER BY clause from |
| 111000 | 111168 | ** the sub-query back to the parent query. At this point each term |
| 111001 | 111169 | ** within the ORDER BY clause has been transformed to an integer value. |
| @@ -112575,11 +112743,13 @@ | ||
| 112575 | 112743 | const Expr *pExpr, /* The function invocation */ |
| 112576 | 112744 | const FuncDef *pDef /* The function being invoked */ |
| 112577 | 112745 | ){ |
| 112578 | 112746 | assert( !IN_RENAME_OBJECT ); |
| 112579 | 112747 | assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); |
| 112580 | - if( ExprHasProperty(pExpr, EP_FromDDL) ){ | |
| 112748 | + if( ExprHasProperty(pExpr, EP_FromDDL) | |
| 112749 | + || pParse->prepFlags & SQLITE_PREPARE_FROM_DDL | |
| 112750 | + ){ | |
| 112581 | 112751 | if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0 |
| 112582 | 112752 | || (pParse->db->flags & SQLITE_TrustedSchema)==0 |
| 112583 | 112753 | ){ |
| 112584 | 112754 | /* Functions prohibited in triggers and views if: |
| 112585 | 112755 | ** (1) tagged with SQLITE_DIRECTONLY |
| @@ -113271,13 +113441,11 @@ | ||
| 113271 | 113441 | pNew->pNext = pNext; |
| 113272 | 113442 | pNew->pPrior = 0; |
| 113273 | 113443 | pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); |
| 113274 | 113444 | pNew->iLimit = 0; |
| 113275 | 113445 | pNew->iOffset = 0; |
| 113276 | - pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral; | |
| 113277 | - pNew->addrOpenEphm[0] = -1; | |
| 113278 | - pNew->addrOpenEphm[1] = -1; | |
| 113446 | + pNew->selFlags = p->selFlags; | |
| 113279 | 113447 | pNew->nSelectRow = p->nSelectRow; |
| 113280 | 113448 | pNew->pWith = sqlite3WithDup(db, p->pWith); |
| 113281 | 113449 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 113282 | 113450 | pNew->pWin = 0; |
| 113283 | 113451 | pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); |
| @@ -115495,12 +115663,13 @@ | ||
| 115495 | 115663 | if( destIfFalse==destIfNull ){ |
| 115496 | 115664 | /* Combine Step 3 and Step 5 into a single opcode */ |
| 115497 | 115665 | if( ExprHasProperty(pExpr, EP_Subrtn) ){ |
| 115498 | 115666 | const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); |
| 115499 | 115667 | assert( pOp->opcode==OP_Once || pParse->nErr ); |
| 115500 | - if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */ | |
| 115501 | - assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ); | |
| 115668 | + if( pOp->p3>0 ){ /* tag-202407032019 */ | |
| 115669 | + assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) | |
| 115670 | + || pParse->nErr ); | |
| 115502 | 115671 | sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, |
| 115503 | 115672 | rLhs, nVector); VdbeCoverage(v); |
| 115504 | 115673 | } |
| 115505 | 115674 | } |
| 115506 | 115675 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, |
| @@ -118687,11 +118856,14 @@ | ||
| 118687 | 118856 | if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break; |
| 118688 | 118857 | } |
| 118689 | 118858 | if( pIEpr==0 ) break; |
| 118690 | 118859 | if( NEVER(!ExprUseYTab(pExpr)) ) break; |
| 118691 | 118860 | for(i=0; i<pSrcList->nSrc; i++){ |
| 118692 | - if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break; | |
| 118861 | + if( pSrcList->a[i].iCursor==pIEpr->iDataCur ){ | |
| 118862 | + testcase( i>0 ); | |
| 118863 | + break; | |
| 118864 | + } | |
| 118693 | 118865 | } |
| 118694 | 118866 | if( i>=pSrcList->nSrc ) break; |
| 118695 | 118867 | if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */ |
| 118696 | 118868 | if( pParse->nErr ){ return WRC_Abort; } |
| 118697 | 118869 | |
| @@ -131401,11 +131573,11 @@ | ||
| 131401 | 131573 | ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY |
| 131402 | 131574 | ** virtual tables |
| 131403 | 131575 | ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS |
| 131404 | 131576 | ** virtual tables if PRAGMA trusted_schema=ON. |
| 131405 | 131577 | */ |
| 131406 | - if( pParse->pToplevel!=0 | |
| 131578 | + if( (pParse->pToplevel!=0 || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL)) | |
| 131407 | 131579 | && pTab->u.vtab.p->eVtabRisk > |
| 131408 | 131580 | ((pParse->db->flags & SQLITE_TrustedSchema)!=0) |
| 131409 | 131581 | ){ |
| 131410 | 131582 | sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", |
| 131411 | 131583 | pTab->zName); |
| @@ -132239,11 +132411,10 @@ | ||
| 132239 | 132411 | VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); |
| 132240 | 132412 | r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, |
| 132241 | 132413 | &iPartIdxLabel, pPrior, r1); |
| 132242 | 132414 | sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, |
| 132243 | 132415 | pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
| 132244 | - sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */ | |
| 132245 | 132416 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 132246 | 132417 | pPrior = pIdx; |
| 132247 | 132418 | } |
| 132248 | 132419 | } |
| 132249 | 132420 | |
| @@ -133586,11 +133757,11 @@ | ||
| 133586 | 133757 | }else{ |
| 133587 | 133758 | goto unistr_error; |
| 133588 | 133759 | } |
| 133589 | 133760 | } |
| 133590 | 133761 | zOut[j] = 0; |
| 133591 | - sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8); | |
| 133762 | + sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8_ZT); | |
| 133592 | 133763 | return; |
| 133593 | 133764 | |
| 133594 | 133765 | unistr_error: |
| 133595 | 133766 | sqlite3_free(zOut); |
| 133596 | 133767 | sqlite3_result_error(context, "invalid Unicode escape", -1); |
| @@ -133679,11 +133850,11 @@ | ||
| 133679 | 133850 | *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); |
| 133680 | 133851 | *zOut++ = 0x80 + (u8)(c & 0x3F); |
| 133681 | 133852 | } \ |
| 133682 | 133853 | } |
| 133683 | 133854 | *zOut = 0; |
| 133684 | - sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8); | |
| 133855 | + sqlite3_result_text64(context, (char*)z, zOut-z,sqlite3_free,SQLITE_UTF8_ZT); | |
| 133685 | 133856 | } |
| 133686 | 133857 | |
| 133687 | 133858 | /* |
| 133688 | 133859 | ** The hex() function. Interpret the argument as a blob. Return |
| 133689 | 133860 | ** a hexadecimal rendering as text. |
| @@ -133708,11 +133879,11 @@ | ||
| 133708 | 133879 | *(z++) = hexdigits[(c>>4)&0xf]; |
| 133709 | 133880 | *(z++) = hexdigits[c&0xf]; |
| 133710 | 133881 | } |
| 133711 | 133882 | *z = 0; |
| 133712 | 133883 | sqlite3_result_text64(context, zHex, (u64)(z-zHex), |
| 133713 | - sqlite3_free, SQLITE_UTF8); | |
| 133884 | + sqlite3_free, SQLITE_UTF8_ZT); | |
| 133714 | 133885 | } |
| 133715 | 133886 | } |
| 133716 | 133887 | |
| 133717 | 133888 | /* |
| 133718 | 133889 | ** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr |
| @@ -134046,11 +134217,11 @@ | ||
| 134046 | 134217 | } |
| 134047 | 134218 | } |
| 134048 | 134219 | } |
| 134049 | 134220 | z[j] = 0; |
| 134050 | 134221 | assert( j<=n ); |
| 134051 | - sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8); | |
| 134222 | + sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8_ZT); | |
| 134052 | 134223 | } |
| 134053 | 134224 | |
| 134054 | 134225 | /* |
| 134055 | 134226 | ** The CONCAT(...) function. Generate a string result that is the |
| 134056 | 134227 | ** concatentation of all non-null arguments. |
| @@ -141234,10 +141405,12 @@ | ||
| 141234 | 141405 | int (*set_errmsg)(sqlite3*,int,const char*); |
| 141235 | 141406 | int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int); |
| 141236 | 141407 | /* Version 3.52.0 and later */ |
| 141237 | 141408 | void (*str_truncate)(sqlite3_str*,int); |
| 141238 | 141409 | void (*str_free)(sqlite3_str*); |
| 141410 | + int (*carray_bind)(sqlite3_stmt*,int,void*,int,int,void(*)(void*)); | |
| 141411 | + int (*carray_bind_v2)(sqlite3_stmt*,int,void*,int,int,void(*)(void*),void*); | |
| 141239 | 141412 | }; |
| 141240 | 141413 | |
| 141241 | 141414 | /* |
| 141242 | 141415 | ** This is the function signature used for all extension entry points. It |
| 141243 | 141416 | ** is also defined in the file "loadext.c". |
| @@ -141575,10 +141748,12 @@ | ||
| 141575 | 141748 | #define sqlite3_set_errmsg sqlite3_api->set_errmsg |
| 141576 | 141749 | #define sqlite3_db_status64 sqlite3_api->db_status64 |
| 141577 | 141750 | /* Version 3.52.0 and later */ |
| 141578 | 141751 | #define sqlite3_str_truncate sqlite3_api->str_truncate |
| 141579 | 141752 | #define sqlite3_str_free sqlite3_api->str_free |
| 141753 | +#define sqlite3_carray_bind sqlite3_api->carray_bind | |
| 141754 | +#define sqlite3_carray_bind_v2 sqlite3_api->carray_bind_v2 | |
| 141580 | 141755 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 141581 | 141756 | |
| 141582 | 141757 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 141583 | 141758 | /* This case when the file really is being compiled as a loadable |
| 141584 | 141759 | ** extension */ |
| @@ -142104,11 +142279,18 @@ | ||
| 142104 | 142279 | /* Version 3.51.0 and later */ |
| 142105 | 142280 | sqlite3_set_errmsg, |
| 142106 | 142281 | sqlite3_db_status64, |
| 142107 | 142282 | /* Version 3.52.0 and later */ |
| 142108 | 142283 | sqlite3_str_truncate, |
| 142109 | - sqlite3_str_free | |
| 142284 | + sqlite3_str_free, | |
| 142285 | +#ifdef SQLITE_ENABLE_CARRAY | |
| 142286 | + sqlite3_carray_bind, | |
| 142287 | + sqlite3_carray_bind_v2 | |
| 142288 | +#else | |
| 142289 | + 0, | |
| 142290 | + 0 | |
| 142291 | +#endif | |
| 142110 | 142292 | }; |
| 142111 | 142293 | |
| 142112 | 142294 | /* True if x is the directory separator character |
| 142113 | 142295 | */ |
| 142114 | 142296 | #if SQLITE_OS_WIN |
| @@ -142206,37 +142388,46 @@ | ||
| 142206 | 142388 | |
| 142207 | 142389 | /* If no entry point was specified and the default legacy |
| 142208 | 142390 | ** entry point name "sqlite3_extension_init" was not found, then |
| 142209 | 142391 | ** construct an entry point name "sqlite3_X_init" where the X is |
| 142210 | 142392 | ** replaced by the lowercase value of every ASCII alphabetic |
| 142211 | - ** character in the filename after the last "/" upto the first ".", | |
| 142212 | - ** and eliding the first three characters if they are "lib". | |
| 142393 | + ** character in the filename after the last "/" up to the first ".", | |
| 142394 | + ** and skipping the first three characters if they are "lib". | |
| 142213 | 142395 | ** Examples: |
| 142214 | 142396 | ** |
| 142215 | 142397 | ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example_init |
| 142216 | 142398 | ** C:/lib/mathfuncs.dll ==> sqlite3_mathfuncs_init |
| 142399 | + ** | |
| 142400 | + ** If that still finds no entry point, repeat a second time but this | |
| 142401 | + ** time include both alphabetic and numeric characters up to the first | |
| 142402 | + ** ".". Example: | |
| 142403 | + ** | |
| 142404 | + ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example5_init | |
| 142217 | 142405 | */ |
| 142218 | 142406 | if( xInit==0 && zProc==0 ){ |
| 142219 | 142407 | int iFile, iEntry, c; |
| 142220 | 142408 | int ncFile = sqlite3Strlen30(zFile); |
| 142409 | + int cnt = 0; | |
| 142221 | 142410 | zAltEntry = sqlite3_malloc64(ncFile+30); |
| 142222 | 142411 | if( zAltEntry==0 ){ |
| 142223 | 142412 | sqlite3OsDlClose(pVfs, handle); |
| 142224 | 142413 | return SQLITE_NOMEM_BKPT; |
| 142225 | 142414 | } |
| 142226 | - memcpy(zAltEntry, "sqlite3_", 8); | |
| 142227 | - for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} | |
| 142228 | - iFile++; | |
| 142229 | - if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; | |
| 142230 | - for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ | |
| 142231 | - if( sqlite3Isalpha(c) ){ | |
| 142232 | - zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; | |
| 142233 | - } | |
| 142234 | - } | |
| 142235 | - memcpy(zAltEntry+iEntry, "_init", 6); | |
| 142236 | - zEntry = zAltEntry; | |
| 142237 | - xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); | |
| 142415 | + do{ | |
| 142416 | + memcpy(zAltEntry, "sqlite3_", 8); | |
| 142417 | + for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} | |
| 142418 | + iFile++; | |
| 142419 | + if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; | |
| 142420 | + for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ | |
| 142421 | + if( sqlite3Isalpha(c) || (cnt && sqlite3Isdigit(c)) ){ | |
| 142422 | + zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; | |
| 142423 | + } | |
| 142424 | + } | |
| 142425 | + memcpy(zAltEntry+iEntry, "_init", 6); | |
| 142426 | + zEntry = zAltEntry; | |
| 142427 | + xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); | |
| 142428 | + }while( xInit==0 && (++cnt)<2 ); | |
| 142238 | 142429 | } |
| 142239 | 142430 | if( xInit==0 ){ |
| 142240 | 142431 | if( pzErrMsg ){ |
| 142241 | 142432 | nMsg += strlen(zEntry) + 300; |
| 142242 | 142433 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| @@ -147518,12 +147709,10 @@ | ||
| 147518 | 147709 | pNew->op = TK_SELECT; |
| 147519 | 147710 | pNew->selFlags = selFlags; |
| 147520 | 147711 | pNew->iLimit = 0; |
| 147521 | 147712 | pNew->iOffset = 0; |
| 147522 | 147713 | pNew->selId = ++pParse->nSelect; |
| 147523 | - pNew->addrOpenEphm[0] = -1; | |
| 147524 | - pNew->addrOpenEphm[1] = -1; | |
| 147525 | 147714 | pNew->nSelectRow = 0; |
| 147526 | 147715 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); |
| 147527 | 147716 | pNew->pSrc = pSrc; |
| 147528 | 147717 | pNew->pWhere = pWhere; |
| 147529 | 147718 | pNew->pGroupBy = pGroupBy; |
| @@ -148667,33 +148856,10 @@ | ||
| 148667 | 148856 | codeOffset(v, p->iOffset, iContinue); |
| 148668 | 148857 | } |
| 148669 | 148858 | } |
| 148670 | 148859 | |
| 148671 | 148860 | switch( eDest ){ |
| 148672 | - /* In this mode, write each query result to the key of the temporary | |
| 148673 | - ** table iParm. | |
| 148674 | - */ | |
| 148675 | -#ifndef SQLITE_OMIT_COMPOUND_SELECT | |
| 148676 | - case SRT_Union: { | |
| 148677 | - int r1; | |
| 148678 | - r1 = sqlite3GetTempReg(pParse); | |
| 148679 | - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); | |
| 148680 | - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); | |
| 148681 | - sqlite3ReleaseTempReg(pParse, r1); | |
| 148682 | - break; | |
| 148683 | - } | |
| 148684 | - | |
| 148685 | - /* Construct a record from the query result, but instead of | |
| 148686 | - ** saving that record, use it as a key to delete elements from | |
| 148687 | - ** the temporary table iParm. | |
| 148688 | - */ | |
| 148689 | - case SRT_Except: { | |
| 148690 | - sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol); | |
| 148691 | - break; | |
| 148692 | - } | |
| 148693 | -#endif /* SQLITE_OMIT_COMPOUND_SELECT */ | |
| 148694 | - | |
| 148695 | 148861 | /* Store the result as data using a unique key. |
| 148696 | 148862 | */ |
| 148697 | 148863 | case SRT_Fifo: |
| 148698 | 148864 | case SRT_DistFifo: |
| 148699 | 148865 | case SRT_Table: |
| @@ -149976,13 +150142,13 @@ | ||
| 149976 | 150142 | ** |
| 149977 | 150143 | ** Space to hold the KeyInfo structure is obtained from malloc. The calling |
| 149978 | 150144 | ** function is responsible for ensuring that this structure is eventually |
| 149979 | 150145 | ** freed. |
| 149980 | 150146 | */ |
| 149981 | -static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ | |
| 150147 | +static KeyInfo *multiSelectByMergeKeyInfo(Parse *pParse, Select *p, int nExtra){ | |
| 149982 | 150148 | ExprList *pOrderBy = p->pOrderBy; |
| 149983 | - int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0; | |
| 150149 | + int nOrderBy = (pOrderBy!=0) ? pOrderBy->nExpr : 0; | |
| 149984 | 150150 | sqlite3 *db = pParse->db; |
| 149985 | 150151 | KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); |
| 149986 | 150152 | if( pRet ){ |
| 149987 | 150153 | int i; |
| 149988 | 150154 | for(i=0; i<nOrderBy; i++){ |
| @@ -150111,21 +150277,41 @@ | ||
| 150111 | 150277 | |
| 150112 | 150278 | /* Allocate cursors for Current, Queue, and Distinct. */ |
| 150113 | 150279 | regCurrent = ++pParse->nMem; |
| 150114 | 150280 | sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol); |
| 150115 | 150281 | if( pOrderBy ){ |
| 150116 | - KeyInfo *pKeyInfo = multiSelectOrderByKeyInfo(pParse, p, 1); | |
| 150282 | + KeyInfo *pKeyInfo = multiSelectByMergeKeyInfo(pParse, p, 1); | |
| 150117 | 150283 | sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0, |
| 150118 | 150284 | (char*)pKeyInfo, P4_KEYINFO); |
| 150119 | 150285 | destQueue.pOrderBy = pOrderBy; |
| 150120 | 150286 | }else{ |
| 150121 | 150287 | sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol); |
| 150122 | 150288 | } |
| 150123 | 150289 | VdbeComment((v, "Queue table")); |
| 150124 | 150290 | if( iDistinct ){ |
| 150125 | - p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0); | |
| 150126 | - p->selFlags |= SF_UsesEphemeral; | |
| 150291 | + /* Generate an ephemeral table used to enforce distinctness on the | |
| 150292 | + ** output of the recursive part of the CTE. | |
| 150293 | + */ | |
| 150294 | + KeyInfo *pKeyInfo; /* Collating sequence for the result set */ | |
| 150295 | + CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ | |
| 150296 | + | |
| 150297 | + assert( p->pNext==0 ); | |
| 150298 | + assert( p->pEList!=0 ); | |
| 150299 | + nCol = p->pEList->nExpr; | |
| 150300 | + pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nCol, 1); | |
| 150301 | + if( pKeyInfo ){ | |
| 150302 | + for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){ | |
| 150303 | + *apColl = multiSelectCollSeq(pParse, p, i); | |
| 150304 | + if( 0==*apColl ){ | |
| 150305 | + *apColl = pParse->db->pDfltColl; | |
| 150306 | + } | |
| 150307 | + } | |
| 150308 | + sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iDistinct, nCol, 0, | |
| 150309 | + (void*)pKeyInfo, P4_KEYINFO); | |
| 150310 | + }else{ | |
| 150311 | + assert( pParse->nErr>0 ); | |
| 150312 | + } | |
| 150127 | 150313 | } |
| 150128 | 150314 | |
| 150129 | 150315 | /* Detach the ORDER BY clause from the compound SELECT */ |
| 150130 | 150316 | p->pOrderBy = 0; |
| 150131 | 150317 | |
| @@ -150196,11 +150382,11 @@ | ||
| 150196 | 150382 | return; |
| 150197 | 150383 | } |
| 150198 | 150384 | #endif /* SQLITE_OMIT_CTE */ |
| 150199 | 150385 | |
| 150200 | 150386 | /* Forward references */ |
| 150201 | -static int multiSelectOrderBy( | |
| 150387 | +static int multiSelectByMerge( | |
| 150202 | 150388 | Parse *pParse, /* Parsing context */ |
| 150203 | 150389 | Select *p, /* The right-most of SELECTs to be coded */ |
| 150204 | 150390 | SelectDest *pDest /* What to do with query results */ |
| 150205 | 150391 | ); |
| 150206 | 150392 | |
| @@ -150345,317 +150531,80 @@ | ||
| 150345 | 150531 | #ifndef SQLITE_OMIT_CTE |
| 150346 | 150532 | if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){ |
| 150347 | 150533 | generateWithRecursiveQuery(pParse, p, &dest); |
| 150348 | 150534 | }else |
| 150349 | 150535 | #endif |
| 150350 | - | |
| 150351 | - /* Compound SELECTs that have an ORDER BY clause are handled separately. | |
| 150352 | - */ | |
| 150353 | 150536 | if( p->pOrderBy ){ |
| 150354 | - return multiSelectOrderBy(pParse, p, pDest); | |
| 150537 | + /* If the compound has an ORDER BY clause, then always use the merge | |
| 150538 | + ** algorithm. */ | |
| 150539 | + return multiSelectByMerge(pParse, p, pDest); | |
| 150540 | + }else if( p->op!=TK_ALL ){ | |
| 150541 | + /* If the compound is EXCEPT, INTERSECT, or UNION (anything other than | |
| 150542 | + ** UNION ALL) then also always use the merge algorithm. However, the | |
| 150543 | + ** multiSelectByMerge() routine requires that the compound have an | |
| 150544 | + ** ORDER BY clause, and it doesn't right now. So invent one first. */ | |
| 150545 | + Expr *pOne = sqlite3ExprInt32(db, 1); | |
| 150546 | + p->pOrderBy = sqlite3ExprListAppend(pParse, 0, pOne); | |
| 150547 | + if( pParse->nErr ) goto multi_select_end; | |
| 150548 | + assert( p->pOrderBy!=0 ); | |
| 150549 | + p->pOrderBy->a[0].u.x.iOrderByCol = 1; | |
| 150550 | + return multiSelectByMerge(pParse, p, pDest); | |
| 150355 | 150551 | }else{ |
| 150552 | + /* For a UNION ALL compound without ORDER BY, simply run the left | |
| 150553 | + ** query, then run the right query */ | |
| 150554 | + int addr = 0; | |
| 150555 | + int nLimit = 0; /* Initialize to suppress harmless compiler warning */ | |
| 150356 | 150556 | |
| 150357 | 150557 | #ifndef SQLITE_OMIT_EXPLAIN |
| 150358 | 150558 | if( pPrior->pPrior==0 ){ |
| 150359 | 150559 | ExplainQueryPlan((pParse, 1, "COMPOUND QUERY")); |
| 150360 | 150560 | ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY")); |
| 150361 | 150561 | } |
| 150362 | 150562 | #endif |
| 150363 | - | |
| 150364 | - /* Generate code for the left and right SELECT statements. | |
| 150365 | - */ | |
| 150366 | - switch( p->op ){ | |
| 150367 | - case TK_ALL: { | |
| 150368 | - int addr = 0; | |
| 150369 | - int nLimit = 0; /* Initialize to suppress harmless compiler warning */ | |
| 150370 | - assert( !pPrior->pLimit ); | |
| 150371 | - pPrior->iLimit = p->iLimit; | |
| 150372 | - pPrior->iOffset = p->iOffset; | |
| 150373 | - pPrior->pLimit = p->pLimit; | |
| 150374 | - TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); | |
| 150375 | - rc = sqlite3Select(pParse, pPrior, &dest); | |
| 150376 | - pPrior->pLimit = 0; | |
| 150377 | - if( rc ){ | |
| 150378 | - goto multi_select_end; | |
| 150379 | - } | |
| 150380 | - p->pPrior = 0; | |
| 150381 | - p->iLimit = pPrior->iLimit; | |
| 150382 | - p->iOffset = pPrior->iOffset; | |
| 150383 | - if( p->iLimit ){ | |
| 150384 | - addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); | |
| 150385 | - VdbeComment((v, "Jump ahead if LIMIT reached")); | |
| 150386 | - if( p->iOffset ){ | |
| 150387 | - sqlite3VdbeAddOp3(v, OP_OffsetLimit, | |
| 150388 | - p->iLimit, p->iOffset+1, p->iOffset); | |
| 150389 | - } | |
| 150390 | - } | |
| 150391 | - ExplainQueryPlan((pParse, 1, "UNION ALL")); | |
| 150392 | - TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); | |
| 150393 | - rc = sqlite3Select(pParse, p, &dest); | |
| 150394 | - testcase( rc!=SQLITE_OK ); | |
| 150395 | - pDelete = p->pPrior; | |
| 150396 | - p->pPrior = pPrior; | |
| 150397 | - p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); | |
| 150398 | - if( p->pLimit | |
| 150399 | - && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) | |
| 150400 | - && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) | |
| 150401 | - ){ | |
| 150402 | - p->nSelectRow = sqlite3LogEst((u64)nLimit); | |
| 150403 | - } | |
| 150404 | - if( addr ){ | |
| 150405 | - sqlite3VdbeJumpHere(v, addr); | |
| 150406 | - } | |
| 150407 | - break; | |
| 150408 | - } | |
| 150409 | - case TK_EXCEPT: | |
| 150410 | - case TK_UNION: { | |
| 150411 | - int unionTab; /* Cursor number of the temp table holding result */ | |
| 150412 | - u8 op = 0; /* One of the SRT_ operations to apply to self */ | |
| 150413 | - int priorOp; /* The SRT_ operation to apply to prior selects */ | |
| 150414 | - Expr *pLimit; /* Saved values of p->nLimit */ | |
| 150415 | - int addr; | |
| 150416 | - int emptyBypass = 0; /* IfEmpty opcode to bypass RHS */ | |
| 150417 | - SelectDest uniondest; | |
| 150418 | - | |
| 150419 | - | |
| 150420 | - testcase( p->op==TK_EXCEPT ); | |
| 150421 | - testcase( p->op==TK_UNION ); | |
| 150422 | - priorOp = SRT_Union; | |
| 150423 | - if( dest.eDest==priorOp ){ | |
| 150424 | - /* We can reuse a temporary table generated by a SELECT to our | |
| 150425 | - ** right. | |
| 150426 | - */ | |
| 150427 | - assert( p->pLimit==0 ); /* Not allowed on leftward elements */ | |
| 150428 | - unionTab = dest.iSDParm; | |
| 150429 | - }else{ | |
| 150430 | - /* We will need to create our own temporary table to hold the | |
| 150431 | - ** intermediate results. | |
| 150432 | - */ | |
| 150433 | - unionTab = pParse->nTab++; | |
| 150434 | - assert( p->pOrderBy==0 ); | |
| 150435 | - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0); | |
| 150436 | - assert( p->addrOpenEphm[0] == -1 ); | |
| 150437 | - p->addrOpenEphm[0] = addr; | |
| 150438 | - findRightmost(p)->selFlags |= SF_UsesEphemeral; | |
| 150439 | - assert( p->pEList ); | |
| 150440 | - } | |
| 150441 | - | |
| 150442 | - | |
| 150443 | - /* Code the SELECT statements to our left | |
| 150444 | - */ | |
| 150445 | - assert( !pPrior->pOrderBy ); | |
| 150446 | - sqlite3SelectDestInit(&uniondest, priorOp, unionTab); | |
| 150447 | - TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); | |
| 150448 | - rc = sqlite3Select(pParse, pPrior, &uniondest); | |
| 150449 | - if( rc ){ | |
| 150450 | - goto multi_select_end; | |
| 150451 | - } | |
| 150452 | - | |
| 150453 | - /* Code the current SELECT statement | |
| 150454 | - */ | |
| 150455 | - if( p->op==TK_EXCEPT ){ | |
| 150456 | - op = SRT_Except; | |
| 150457 | - emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab); | |
| 150458 | - VdbeCoverage(v); | |
| 150459 | - }else{ | |
| 150460 | - assert( p->op==TK_UNION ); | |
| 150461 | - op = SRT_Union; | |
| 150462 | - } | |
| 150463 | - p->pPrior = 0; | |
| 150464 | - pLimit = p->pLimit; | |
| 150465 | - p->pLimit = 0; | |
| 150466 | - uniondest.eDest = op; | |
| 150467 | - ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", | |
| 150468 | - sqlite3SelectOpName(p->op))); | |
| 150469 | - TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); | |
| 150470 | - rc = sqlite3Select(pParse, p, &uniondest); | |
| 150471 | - testcase( rc!=SQLITE_OK ); | |
| 150472 | - assert( p->pOrderBy==0 ); | |
| 150473 | - pDelete = p->pPrior; | |
| 150474 | - p->pPrior = pPrior; | |
| 150475 | - p->pOrderBy = 0; | |
| 150476 | - if( p->op==TK_UNION ){ | |
| 150477 | - p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); | |
| 150478 | - } | |
| 150479 | - if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass); | |
| 150480 | - sqlite3ExprDelete(db, p->pLimit); | |
| 150481 | - p->pLimit = pLimit; | |
| 150482 | - p->iLimit = 0; | |
| 150483 | - p->iOffset = 0; | |
| 150484 | - | |
| 150485 | - /* Convert the data in the temporary table into whatever form | |
| 150486 | - ** it is that we currently need. | |
| 150487 | - */ | |
| 150488 | - assert( unionTab==dest.iSDParm || dest.eDest!=priorOp ); | |
| 150489 | - assert( p->pEList || db->mallocFailed ); | |
| 150490 | - if( dest.eDest!=priorOp && db->mallocFailed==0 ){ | |
| 150491 | - int iCont, iBreak, iStart; | |
| 150492 | - iBreak = sqlite3VdbeMakeLabel(pParse); | |
| 150493 | - iCont = sqlite3VdbeMakeLabel(pParse); | |
| 150494 | - computeLimitRegisters(pParse, p, iBreak); | |
| 150495 | - sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v); | |
| 150496 | - iStart = sqlite3VdbeCurrentAddr(v); | |
| 150497 | - selectInnerLoop(pParse, p, unionTab, | |
| 150498 | - 0, 0, &dest, iCont, iBreak); | |
| 150499 | - sqlite3VdbeResolveLabel(v, iCont); | |
| 150500 | - sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v); | |
| 150501 | - sqlite3VdbeResolveLabel(v, iBreak); | |
| 150502 | - sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0); | |
| 150503 | - } | |
| 150504 | - break; | |
| 150505 | - } | |
| 150506 | - default: assert( p->op==TK_INTERSECT ); { | |
| 150507 | - int tab1, tab2; | |
| 150508 | - int iCont, iBreak, iStart; | |
| 150509 | - Expr *pLimit; | |
| 150510 | - int addr, iLimit, iOffset; | |
| 150511 | - SelectDest intersectdest; | |
| 150512 | - int r1; | |
| 150513 | - int emptyBypass; | |
| 150514 | - | |
| 150515 | - /* INTERSECT is different from the others since it requires | |
| 150516 | - ** two temporary tables. Hence it has its own case. Begin | |
| 150517 | - ** by allocating the tables we will need. | |
| 150518 | - */ | |
| 150519 | - tab1 = pParse->nTab++; | |
| 150520 | - tab2 = pParse->nTab++; | |
| 150521 | - assert( p->pOrderBy==0 ); | |
| 150522 | - | |
| 150523 | - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0); | |
| 150524 | - assert( p->addrOpenEphm[0] == -1 ); | |
| 150525 | - p->addrOpenEphm[0] = addr; | |
| 150526 | - findRightmost(p)->selFlags |= SF_UsesEphemeral; | |
| 150527 | - assert( p->pEList ); | |
| 150528 | - | |
| 150529 | - /* Code the SELECTs to our left into temporary table "tab1". | |
| 150530 | - */ | |
| 150531 | - sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); | |
| 150532 | - TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n")); | |
| 150533 | - rc = sqlite3Select(pParse, pPrior, &intersectdest); | |
| 150534 | - if( rc ){ | |
| 150535 | - goto multi_select_end; | |
| 150536 | - } | |
| 150537 | - | |
| 150538 | - /* Initialize LIMIT counters before checking to see if the LHS | |
| 150539 | - ** is empty, in case the jump is taken */ | |
| 150540 | - iBreak = sqlite3VdbeMakeLabel(pParse); | |
| 150541 | - computeLimitRegisters(pParse, p, iBreak); | |
| 150542 | - emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v); | |
| 150543 | - | |
| 150544 | - /* Code the current SELECT into temporary table "tab2" | |
| 150545 | - */ | |
| 150546 | - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0); | |
| 150547 | - assert( p->addrOpenEphm[1] == -1 ); | |
| 150548 | - p->addrOpenEphm[1] = addr; | |
| 150549 | - | |
| 150550 | - /* Disable prior SELECTs and the LIMIT counters during the computation | |
| 150551 | - ** of the RHS select */ | |
| 150552 | - pLimit = p->pLimit; | |
| 150553 | - iLimit = p->iLimit; | |
| 150554 | - iOffset = p->iOffset; | |
| 150555 | - p->pPrior = 0; | |
| 150556 | - p->pLimit = 0; | |
| 150557 | - p->iLimit = 0; | |
| 150558 | - p->iOffset = 0; | |
| 150559 | - | |
| 150560 | - intersectdest.iSDParm = tab2; | |
| 150561 | - ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", | |
| 150562 | - sqlite3SelectOpName(p->op))); | |
| 150563 | - TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n")); | |
| 150564 | - rc = sqlite3Select(pParse, p, &intersectdest); | |
| 150565 | - testcase( rc!=SQLITE_OK ); | |
| 150566 | - pDelete = p->pPrior; | |
| 150567 | - p->pPrior = pPrior; | |
| 150568 | - if( p->nSelectRow>pPrior->nSelectRow ){ | |
| 150569 | - p->nSelectRow = pPrior->nSelectRow; | |
| 150570 | - } | |
| 150571 | - sqlite3ExprDelete(db, p->pLimit); | |
| 150572 | - | |
| 150573 | - /* Reinstate the LIMIT counters prior to running the final intersect */ | |
| 150574 | - p->pLimit = pLimit; | |
| 150575 | - p->iLimit = iLimit; | |
| 150576 | - p->iOffset = iOffset; | |
| 150577 | - | |
| 150578 | - /* Generate code to take the intersection of the two temporary | |
| 150579 | - ** tables. | |
| 150580 | - */ | |
| 150581 | - if( rc ) break; | |
| 150582 | - assert( p->pEList ); | |
| 150583 | - sqlite3VdbeAddOp1(v, OP_Rewind, tab1); | |
| 150584 | - r1 = sqlite3GetTempReg(pParse); | |
| 150585 | - iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1); | |
| 150586 | - iCont = sqlite3VdbeMakeLabel(pParse); | |
| 150587 | - sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); | |
| 150588 | - VdbeCoverage(v); | |
| 150589 | - sqlite3ReleaseTempReg(pParse, r1); | |
| 150590 | - selectInnerLoop(pParse, p, tab1, | |
| 150591 | - 0, 0, &dest, iCont, iBreak); | |
| 150592 | - sqlite3VdbeResolveLabel(v, iCont); | |
| 150593 | - sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v); | |
| 150594 | - sqlite3VdbeResolveLabel(v, iBreak); | |
| 150595 | - sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); | |
| 150596 | - sqlite3VdbeJumpHere(v, emptyBypass); | |
| 150597 | - sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); | |
| 150598 | - break; | |
| 150599 | - } | |
| 150600 | - } | |
| 150601 | - | |
| 150602 | - #ifndef SQLITE_OMIT_EXPLAIN | |
| 150563 | + assert( !pPrior->pLimit ); | |
| 150564 | + pPrior->iLimit = p->iLimit; | |
| 150565 | + pPrior->iOffset = p->iOffset; | |
| 150566 | + pPrior->pLimit = sqlite3ExprDup(db, p->pLimit, 0); | |
| 150567 | + TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); | |
| 150568 | + rc = sqlite3Select(pParse, pPrior, &dest); | |
| 150569 | + sqlite3ExprDelete(db, pPrior->pLimit); | |
| 150570 | + pPrior->pLimit = 0; | |
| 150571 | + if( rc ){ | |
| 150572 | + goto multi_select_end; | |
| 150573 | + } | |
| 150574 | + p->pPrior = 0; | |
| 150575 | + p->iLimit = pPrior->iLimit; | |
| 150576 | + p->iOffset = pPrior->iOffset; | |
| 150577 | + if( p->iLimit ){ | |
| 150578 | + addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); | |
| 150579 | + VdbeComment((v, "Jump ahead if LIMIT reached")); | |
| 150580 | + if( p->iOffset ){ | |
| 150581 | + sqlite3VdbeAddOp3(v, OP_OffsetLimit, | |
| 150582 | + p->iLimit, p->iOffset+1, p->iOffset); | |
| 150583 | + } | |
| 150584 | + } | |
| 150585 | + ExplainQueryPlan((pParse, 1, "UNION ALL")); | |
| 150586 | + TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); | |
| 150587 | + rc = sqlite3Select(pParse, p, &dest); | |
| 150588 | + testcase( rc!=SQLITE_OK ); | |
| 150589 | + pDelete = p->pPrior; | |
| 150590 | + p->pPrior = pPrior; | |
| 150591 | + p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); | |
| 150592 | + if( p->pLimit | |
| 150593 | + && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) | |
| 150594 | + && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) | |
| 150595 | + ){ | |
| 150596 | + p->nSelectRow = sqlite3LogEst((u64)nLimit); | |
| 150597 | + } | |
| 150598 | + if( addr ){ | |
| 150599 | + sqlite3VdbeJumpHere(v, addr); | |
| 150600 | + } | |
| 150601 | +#ifndef SQLITE_OMIT_EXPLAIN | |
| 150603 | 150602 | if( p->pNext==0 ){ |
| 150604 | 150603 | ExplainQueryPlanPop(pParse); |
| 150605 | 150604 | } |
| 150606 | - #endif | |
| 150607 | - } | |
| 150608 | - if( pParse->nErr ) goto multi_select_end; | |
| 150609 | - | |
| 150610 | - /* Compute collating sequences used by | |
| 150611 | - ** temporary tables needed to implement the compound select. | |
| 150612 | - ** Attach the KeyInfo structure to all temporary tables. | |
| 150613 | - ** | |
| 150614 | - ** This section is run by the right-most SELECT statement only. | |
| 150615 | - ** SELECT statements to the left always skip this part. The right-most | |
| 150616 | - ** SELECT might also skip this part if it has no ORDER BY clause and | |
| 150617 | - ** no temp tables are required. | |
| 150618 | - */ | |
| 150619 | - if( p->selFlags & SF_UsesEphemeral ){ | |
| 150620 | - int i; /* Loop counter */ | |
| 150621 | - KeyInfo *pKeyInfo; /* Collating sequence for the result set */ | |
| 150622 | - Select *pLoop; /* For looping through SELECT statements */ | |
| 150623 | - CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ | |
| 150624 | - int nCol; /* Number of columns in result set */ | |
| 150625 | - | |
| 150626 | - assert( p->pNext==0 ); | |
| 150627 | - assert( p->pEList!=0 ); | |
| 150628 | - nCol = p->pEList->nExpr; | |
| 150629 | - pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); | |
| 150630 | - if( !pKeyInfo ){ | |
| 150631 | - rc = SQLITE_NOMEM_BKPT; | |
| 150632 | - goto multi_select_end; | |
| 150633 | - } | |
| 150634 | - for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){ | |
| 150635 | - *apColl = multiSelectCollSeq(pParse, p, i); | |
| 150636 | - if( 0==*apColl ){ | |
| 150637 | - *apColl = db->pDfltColl; | |
| 150638 | - } | |
| 150639 | - } | |
| 150640 | - | |
| 150641 | - for(pLoop=p; pLoop; pLoop=pLoop->pPrior){ | |
| 150642 | - for(i=0; i<2; i++){ | |
| 150643 | - int addr = pLoop->addrOpenEphm[i]; | |
| 150644 | - if( addr<0 ){ | |
| 150645 | - /* If [0] is unused then [1] is also unused. So we can | |
| 150646 | - ** always safely abort as soon as the first unused slot is found */ | |
| 150647 | - assert( pLoop->addrOpenEphm[1]<0 ); | |
| 150648 | - break; | |
| 150649 | - } | |
| 150650 | - sqlite3VdbeChangeP2(v, addr, nCol); | |
| 150651 | - sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo), | |
| 150652 | - P4_KEYINFO); | |
| 150653 | - pLoop->addrOpenEphm[i] = -1; | |
| 150654 | - } | |
| 150655 | - } | |
| 150656 | - sqlite3KeyInfoUnref(pKeyInfo); | |
| 150605 | +#endif | |
| 150657 | 150606 | } |
| 150658 | 150607 | |
| 150659 | 150608 | multi_select_end: |
| 150660 | 150609 | pDest->iSdst = dest.iSdst; |
| 150661 | 150610 | pDest->nSdst = dest.nSdst; |
| @@ -150683,12 +150632,12 @@ | ||
| 150683 | 150632 | |
| 150684 | 150633 | /* |
| 150685 | 150634 | ** Code an output subroutine for a coroutine implementation of a |
| 150686 | 150635 | ** SELECT statement. |
| 150687 | 150636 | ** |
| 150688 | -** The data to be output is contained in pIn->iSdst. There are | |
| 150689 | -** pIn->nSdst columns to be output. pDest is where the output should | |
| 150637 | +** The data to be output is contained in an array of pIn->nSdst registers | |
| 150638 | +** starting at register pIn->iSdst. pDest is where the output should | |
| 150690 | 150639 | ** be sent. |
| 150691 | 150640 | ** |
| 150692 | 150641 | ** regReturn is the number of the register holding the subroutine |
| 150693 | 150642 | ** return address. |
| 150694 | 150643 | ** |
| @@ -150713,10 +150662,12 @@ | ||
| 150713 | 150662 | ){ |
| 150714 | 150663 | Vdbe *v = pParse->pVdbe; |
| 150715 | 150664 | int iContinue; |
| 150716 | 150665 | int addr; |
| 150717 | 150666 | |
| 150667 | + assert( pIn->eDest==SRT_Coroutine ); | |
| 150668 | + | |
| 150718 | 150669 | addr = sqlite3VdbeCurrentAddr(v); |
| 150719 | 150670 | iContinue = sqlite3VdbeMakeLabel(pParse); |
| 150720 | 150671 | |
| 150721 | 150672 | /* Suppress duplicates for UNION, EXCEPT, and INTERSECT |
| 150722 | 150673 | */ |
| @@ -150734,26 +150685,63 @@ | ||
| 150734 | 150685 | |
| 150735 | 150686 | /* Suppress the first OFFSET entries if there is an OFFSET clause |
| 150736 | 150687 | */ |
| 150737 | 150688 | codeOffset(v, p->iOffset, iContinue); |
| 150738 | 150689 | |
| 150739 | - assert( pDest->eDest!=SRT_Exists ); | |
| 150740 | - assert( pDest->eDest!=SRT_Table ); | |
| 150741 | 150690 | switch( pDest->eDest ){ |
| 150742 | 150691 | /* Store the result as data using a unique key. |
| 150743 | 150692 | */ |
| 150693 | + case SRT_Fifo: | |
| 150694 | + case SRT_DistFifo: | |
| 150695 | + case SRT_Table: | |
| 150744 | 150696 | case SRT_EphemTab: { |
| 150745 | 150697 | int r1 = sqlite3GetTempReg(pParse); |
| 150746 | 150698 | int r2 = sqlite3GetTempReg(pParse); |
| 150699 | + int iParm = pDest->iSDParm; | |
| 150700 | + testcase( pDest->eDest==SRT_Table ); | |
| 150701 | + testcase( pDest->eDest==SRT_EphemTab ); | |
| 150702 | + testcase( pDest->eDest==SRT_Fifo ); | |
| 150703 | + testcase( pDest->eDest==SRT_DistFifo ); | |
| 150747 | 150704 | sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1); |
| 150748 | - sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2); | |
| 150749 | - sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2); | |
| 150705 | +#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG) | |
| 150706 | + /* A destination of SRT_Table and a non-zero iSDParm2 parameter means | |
| 150707 | + ** that this is an "UPDATE ... FROM" on a virtual table or view. In this | |
| 150708 | + ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC. | |
| 150709 | + ** This does not affect operation in any way - it just allows MakeRecord | |
| 150710 | + ** to process OPFLAG_NOCHANGE values without an assert() failing. */ | |
| 150711 | + if( pDest->eDest==SRT_Table && pDest->iSDParm2 ){ | |
| 150712 | + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); | |
| 150713 | + } | |
| 150714 | +#endif | |
| 150715 | +#ifndef SQLITE_OMIT_CTE | |
| 150716 | + if( pDest->eDest==SRT_DistFifo ){ | |
| 150717 | + /* If the destination is DistFifo, then cursor (iParm+1) is open | |
| 150718 | + ** on an ephemeral index that is used to enforce uniqueness on the | |
| 150719 | + ** total result. At this point, we are processing the setup portion | |
| 150720 | + ** of the recursive CTE using the merge algorithm, so the results are | |
| 150721 | + ** guaranteed to be unique anyhow. But we still need to populate the | |
| 150722 | + ** (iParm+1) cursor for use by the subsequent recursive phase. | |
| 150723 | + */ | |
| 150724 | + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm+1, r1, | |
| 150725 | + pIn->iSdst, pIn->nSdst); | |
| 150726 | + } | |
| 150727 | +#endif | |
| 150728 | + sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2); | |
| 150729 | + sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2); | |
| 150750 | 150730 | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| 150751 | 150731 | sqlite3ReleaseTempReg(pParse, r2); |
| 150752 | 150732 | sqlite3ReleaseTempReg(pParse, r1); |
| 150753 | 150733 | break; |
| 150754 | 150734 | } |
| 150735 | + | |
| 150736 | + /* If any row exist in the result set, record that fact and abort. | |
| 150737 | + */ | |
| 150738 | + case SRT_Exists: { | |
| 150739 | + sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm); | |
| 150740 | + /* The LIMIT clause will terminate the loop for us */ | |
| 150741 | + break; | |
| 150742 | + } | |
| 150755 | 150743 | |
| 150756 | 150744 | #ifndef SQLITE_OMIT_SUBQUERY |
| 150757 | 150745 | /* If we are creating a set for an "expr IN (SELECT ...)". |
| 150758 | 150746 | */ |
| 150759 | 150747 | case SRT_Set: { |
| @@ -150796,14 +150784,74 @@ | ||
| 150796 | 150784 | } |
| 150797 | 150785 | sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pIn->nSdst); |
| 150798 | 150786 | sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); |
| 150799 | 150787 | break; |
| 150800 | 150788 | } |
| 150789 | + | |
| 150790 | +#ifndef SQLITE_OMIT_CTE | |
| 150791 | + /* Write the results into a priority queue that is order according to | |
| 150792 | + ** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an | |
| 150793 | + ** index with pSO->nExpr+2 columns. Build a key using pSO for the first | |
| 150794 | + ** pSO->nExpr columns, then make sure all keys are unique by adding a | |
| 150795 | + ** final OP_Sequence column. The last column is the record as a blob. | |
| 150796 | + */ | |
| 150797 | + case SRT_DistQueue: | |
| 150798 | + case SRT_Queue: { | |
| 150799 | + int nKey; | |
| 150800 | + int r1, r2, r3, ii; | |
| 150801 | + ExprList *pSO; | |
| 150802 | + int iParm = pDest->iSDParm; | |
| 150803 | + pSO = pDest->pOrderBy; | |
| 150804 | + assert( pSO ); | |
| 150805 | + nKey = pSO->nExpr; | |
| 150806 | + r1 = sqlite3GetTempReg(pParse); | |
| 150807 | + r2 = sqlite3GetTempRange(pParse, nKey+2); | |
| 150808 | + r3 = r2+nKey+1; | |
| 150809 | + | |
| 150810 | +#if 0 /* <-- Why the next block of code is commented out: (tag-20260125-a) | |
| 150811 | + ** | |
| 150812 | + ** If the destination is DistQueue, then cursor (iParm+1) is open | |
| 150813 | + ** on a second ephemeral index that holds all values previously | |
| 150814 | + ** added to the queue. This code only runs during the setup phase | |
| 150815 | + ** using the merge algorithm, and so the values here are already | |
| 150816 | + ** guaranteed to be unique. | |
| 150817 | + */ | |
| 150818 | + if( pDest->eDest==SRT_DistQueue ){ | |
| 150819 | + addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0, | |
| 150820 | + pIn->iSdst, pIn->nSdst); | |
| 150821 | + VdbeCoverage(v); | |
| 150822 | + } | |
| 150823 | +#endif | |
| 150824 | + sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r3); | |
| 150825 | + if( pDest->eDest==SRT_DistQueue ){ | |
| 150826 | + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3); | |
| 150827 | + sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); | |
| 150828 | + } | |
| 150829 | + for(ii=0; ii<nKey; ii++){ | |
| 150830 | + sqlite3VdbeAddOp2(v, OP_SCopy, | |
| 150831 | + pIn->iSdst + pSO->a[ii].u.x.iOrderByCol - 1, | |
| 150832 | + r2+ii); | |
| 150833 | + } | |
| 150834 | + sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey); | |
| 150835 | + sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1); | |
| 150836 | + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, r2, nKey+2); | |
| 150837 | +#if 0 /* tag-20260125-a */ | |
| 150838 | + if( addrTest ) sqlite3VdbeJumpHere(v, addrTest); | |
| 150839 | +#endif | |
| 150840 | + sqlite3ReleaseTempReg(pParse, r1); | |
| 150841 | + sqlite3ReleaseTempRange(pParse, r2, nKey+2); | |
| 150842 | + break; | |
| 150843 | + } | |
| 150844 | +#endif /* SQLITE_OMIT_CTE */ | |
| 150845 | + | |
| 150846 | + /* Ignore the output */ | |
| 150847 | + case SRT_Discard: { | |
| 150848 | + break; | |
| 150849 | + } | |
| 150801 | 150850 | |
| 150802 | 150851 | /* If none of the above, then the result destination must be |
| 150803 | - ** SRT_Output. This routine is never called with any other | |
| 150804 | - ** destination other than the ones handled above or SRT_Output. | |
| 150852 | + ** SRT_Output. | |
| 150805 | 150853 | ** |
| 150806 | 150854 | ** For SRT_Output, results are stored in a sequence of registers. |
| 150807 | 150855 | ** Then the OP_ResultRow opcode is used to cause sqlite3_step() to |
| 150808 | 150856 | ** return the next row of result. |
| 150809 | 150857 | */ |
| @@ -150827,12 +150875,13 @@ | ||
| 150827 | 150875 | |
| 150828 | 150876 | return addr; |
| 150829 | 150877 | } |
| 150830 | 150878 | |
| 150831 | 150879 | /* |
| 150832 | -** Alternative compound select code generator for cases when there | |
| 150833 | -** is an ORDER BY clause. | |
| 150880 | +** Generate code for a compound SELECT statement using a merge | |
| 150881 | +** algorithm. The compound must have an ORDER BY clause for this | |
| 150882 | +** to work. | |
| 150834 | 150883 | ** |
| 150835 | 150884 | ** We assume a query of the following form: |
| 150836 | 150885 | ** |
| 150837 | 150886 | ** <selectA> <operator> <selectB> ORDER BY <orderbylist> |
| 150838 | 150887 | ** |
| @@ -150845,11 +150894,11 @@ | ||
| 150845 | 150894 | ** outA: Move the output of the selectA coroutine into the output |
| 150846 | 150895 | ** of the compound query. |
| 150847 | 150896 | ** |
| 150848 | 150897 | ** outB: Move the output of the selectB coroutine into the output |
| 150849 | 150898 | ** of the compound query. (Only generated for UNION and |
| 150850 | -** UNION ALL. EXCEPT and INSERTSECT never output a row that | |
| 150899 | +** UNION ALL. EXCEPT and INTERSECT never output a row that | |
| 150851 | 150900 | ** appears only in B.) |
| 150852 | 150901 | ** |
| 150853 | 150902 | ** AltB: Called when there is data from both coroutines and A<B. |
| 150854 | 150903 | ** |
| 150855 | 150904 | ** AeqB: Called when there is data from both coroutines and A==B. |
| @@ -150898,25 +150947,23 @@ | ||
| 150898 | 150947 | ** EofB: ... |
| 150899 | 150948 | ** AltB: ... |
| 150900 | 150949 | ** AeqB: ... |
| 150901 | 150950 | ** AgtB: ... |
| 150902 | 150951 | ** Init: initialize coroutine registers |
| 150903 | -** yield coA | |
| 150904 | -** if eof(A) goto EofA | |
| 150905 | -** yield coB | |
| 150906 | -** if eof(B) goto EofB | |
| 150952 | +** yield coA, on eof goto EofA | |
| 150953 | +** yield coB, on eof goto EofB | |
| 150907 | 150954 | ** Cmpr: Compare A, B |
| 150908 | 150955 | ** Jump AltB, AeqB, AgtB |
| 150909 | 150956 | ** End: ... |
| 150910 | 150957 | ** |
| 150911 | 150958 | ** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not |
| 150912 | 150959 | ** actually called using Gosub and they do not Return. EofA and EofB loop |
| 150913 | 150960 | ** until all data is exhausted then jump to the "end" label. AltB, AeqB, |
| 150914 | -** and AgtB jump to either L2 or to one of EofA or EofB. | |
| 150961 | +** and AgtB jump to either Cmpr or to one of EofA or EofB. | |
| 150915 | 150962 | */ |
| 150916 | 150963 | #ifndef SQLITE_OMIT_COMPOUND_SELECT |
| 150917 | -static int multiSelectOrderBy( | |
| 150964 | +static int multiSelectByMerge( | |
| 150918 | 150965 | Parse *pParse, /* Parsing context */ |
| 150919 | 150966 | Select *p, /* The right-most of SELECTs to be coded */ |
| 150920 | 150967 | SelectDest *pDest /* What to do with query results */ |
| 150921 | 150968 | ){ |
| 150922 | 150969 | int i, j; /* Loop counters */ |
| @@ -150993,30 +151040,33 @@ | ||
| 150993 | 151040 | } |
| 150994 | 151041 | } |
| 150995 | 151042 | } |
| 150996 | 151043 | |
| 150997 | 151044 | /* Compute the comparison permutation and keyinfo that is used with |
| 150998 | - ** the permutation used to determine if the next | |
| 150999 | - ** row of results comes from selectA or selectB. Also add explicit | |
| 151000 | - ** collations to the ORDER BY clause terms so that when the subqueries | |
| 151001 | - ** to the right and the left are evaluated, they use the correct | |
| 151002 | - ** collation. | |
| 151045 | + ** the permutation to determine if the next row of results comes | |
| 151046 | + ** from selectA or selectB. Also add literal collations to the | |
| 151047 | + ** ORDER BY clause terms so that when selectA and selectB are | |
| 151048 | + ** evaluated, they use the correct collation. | |
| 151003 | 151049 | */ |
| 151004 | 151050 | aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); |
| 151005 | 151051 | if( aPermute ){ |
| 151006 | 151052 | struct ExprList_item *pItem; |
| 151053 | + int bKeep = 0; | |
| 151007 | 151054 | aPermute[0] = nOrderBy; |
| 151008 | 151055 | for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ |
| 151009 | 151056 | assert( pItem!=0 ); |
| 151010 | 151057 | assert( pItem->u.x.iOrderByCol>0 ); |
| 151011 | 151058 | assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); |
| 151012 | 151059 | aPermute[i] = pItem->u.x.iOrderByCol - 1; |
| 151060 | + if( aPermute[i]!=(u32)i-1 ) bKeep = 1; | |
| 151013 | 151061 | } |
| 151014 | - pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1); | |
| 151015 | - }else{ | |
| 151016 | - pKeyMerge = 0; | |
| 151062 | + if( bKeep==0 ){ | |
| 151063 | + sqlite3DbFreeNN(db, aPermute); | |
| 151064 | + aPermute = 0; | |
| 151065 | + } | |
| 151017 | 151066 | } |
| 151067 | + pKeyMerge = multiSelectByMergeKeyInfo(pParse, p, 1); | |
| 151018 | 151068 | |
| 151019 | 151069 | /* Allocate a range of temporary registers and the KeyInfo needed |
| 151020 | 151070 | ** for the logic that removes duplicate result rows when the |
| 151021 | 151071 | ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). |
| 151022 | 151072 | */ |
| @@ -151091,11 +151141,11 @@ | ||
| 151091 | 151141 | /* Generate a coroutine to evaluate the SELECT statement to the |
| 151092 | 151142 | ** left of the compound operator - the "A" select. |
| 151093 | 151143 | */ |
| 151094 | 151144 | addrSelectA = sqlite3VdbeCurrentAddr(v) + 1; |
| 151095 | 151145 | addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA); |
| 151096 | - VdbeComment((v, "left SELECT")); | |
| 151146 | + VdbeComment((v, "SUBR: next-A")); | |
| 151097 | 151147 | pPrior->iLimit = regLimitA; |
| 151098 | 151148 | ExplainQueryPlan((pParse, 1, "LEFT")); |
| 151099 | 151149 | sqlite3Select(pParse, pPrior, &destA); |
| 151100 | 151150 | sqlite3VdbeEndCoroutine(v, regAddrA); |
| 151101 | 151151 | sqlite3VdbeJumpHere(v, addr1); |
| @@ -151103,11 +151153,11 @@ | ||
| 151103 | 151153 | /* Generate a coroutine to evaluate the SELECT statement on |
| 151104 | 151154 | ** the right - the "B" select |
| 151105 | 151155 | */ |
| 151106 | 151156 | addrSelectB = sqlite3VdbeCurrentAddr(v) + 1; |
| 151107 | 151157 | addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB); |
| 151108 | - VdbeComment((v, "right SELECT")); | |
| 151158 | + VdbeComment((v, "SUBR: next-B")); | |
| 151109 | 151159 | savedLimit = p->iLimit; |
| 151110 | 151160 | savedOffset = p->iOffset; |
| 151111 | 151161 | p->iLimit = regLimitB; |
| 151112 | 151162 | p->iOffset = 0; |
| 151113 | 151163 | ExplainQueryPlan((pParse, 1, "RIGHT")); |
| @@ -151117,20 +151167,20 @@ | ||
| 151117 | 151167 | sqlite3VdbeEndCoroutine(v, regAddrB); |
| 151118 | 151168 | |
| 151119 | 151169 | /* Generate a subroutine that outputs the current row of the A |
| 151120 | 151170 | ** select as the next output row of the compound select. |
| 151121 | 151171 | */ |
| 151122 | - VdbeNoopComment((v, "Output routine for A")); | |
| 151172 | + VdbeNoopComment((v, "SUBR: out-A")); | |
| 151123 | 151173 | addrOutA = generateOutputSubroutine(pParse, |
| 151124 | 151174 | p, &destA, pDest, regOutA, |
| 151125 | 151175 | regPrev, pKeyDup, labelEnd); |
| 151126 | 151176 | |
| 151127 | 151177 | /* Generate a subroutine that outputs the current row of the B |
| 151128 | 151178 | ** select as the next output row of the compound select. |
| 151129 | 151179 | */ |
| 151130 | 151180 | if( op==TK_ALL || op==TK_UNION ){ |
| 151131 | - VdbeNoopComment((v, "Output routine for B")); | |
| 151181 | + VdbeNoopComment((v, "SUBR: out-B")); | |
| 151132 | 151182 | addrOutB = generateOutputSubroutine(pParse, |
| 151133 | 151183 | p, &destB, pDest, regOutB, |
| 151134 | 151184 | regPrev, pKeyDup, labelEnd); |
| 151135 | 151185 | } |
| 151136 | 151186 | sqlite3KeyInfoUnref(pKeyDup); |
| @@ -151139,14 +151189,16 @@ | ||
| 151139 | 151189 | ** are exhausted and only data in select B remains. |
| 151140 | 151190 | */ |
| 151141 | 151191 | if( op==TK_EXCEPT || op==TK_INTERSECT ){ |
| 151142 | 151192 | addrEofA_noB = addrEofA = labelEnd; |
| 151143 | 151193 | }else{ |
| 151144 | - VdbeNoopComment((v, "eof-A subroutine")); | |
| 151194 | + VdbeNoopComment((v, "SUBR: eof-A")); | |
| 151145 | 151195 | addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); |
| 151196 | + VdbeComment((v, "out-B")); | |
| 151146 | 151197 | addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd); |
| 151147 | 151198 | VdbeCoverage(v); |
| 151199 | + VdbeComment((v, "next-B")); | |
| 151148 | 151200 | sqlite3VdbeGoto(v, addrEofA); |
| 151149 | 151201 | p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); |
| 151150 | 151202 | } |
| 151151 | 151203 | |
| 151152 | 151204 | /* Generate a subroutine to run when the results from select B |
| @@ -151154,21 +151206,24 @@ | ||
| 151154 | 151206 | */ |
| 151155 | 151207 | if( op==TK_INTERSECT ){ |
| 151156 | 151208 | addrEofB = addrEofA; |
| 151157 | 151209 | if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; |
| 151158 | 151210 | }else{ |
| 151159 | - VdbeNoopComment((v, "eof-B subroutine")); | |
| 151211 | + VdbeNoopComment((v, "SUBR: eof-B")); | |
| 151160 | 151212 | addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); |
| 151213 | + VdbeComment((v, "out-A")); | |
| 151161 | 151214 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v); |
| 151215 | + VdbeComment((v, "next-A")); | |
| 151162 | 151216 | sqlite3VdbeGoto(v, addrEofB); |
| 151163 | 151217 | } |
| 151164 | 151218 | |
| 151165 | 151219 | /* Generate code to handle the case of A<B |
| 151166 | 151220 | */ |
| 151167 | - VdbeNoopComment((v, "A-lt-B subroutine")); | |
| 151168 | 151221 | addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); |
| 151222 | + VdbeComment((v, "out-A")); | |
| 151169 | 151223 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v); |
| 151224 | + VdbeComment((v, "next-A")); | |
| 151170 | 151225 | sqlite3VdbeGoto(v, labelCmpr); |
| 151171 | 151226 | |
| 151172 | 151227 | /* Generate code to handle the case of A==B |
| 151173 | 151228 | */ |
| 151174 | 151229 | if( op==TK_ALL ){ |
| @@ -151175,40 +151230,52 @@ | ||
| 151175 | 151230 | addrAeqB = addrAltB; |
| 151176 | 151231 | }else if( op==TK_INTERSECT ){ |
| 151177 | 151232 | addrAeqB = addrAltB; |
| 151178 | 151233 | addrAltB++; |
| 151179 | 151234 | }else{ |
| 151180 | - VdbeNoopComment((v, "A-eq-B subroutine")); | |
| 151181 | - addrAeqB = | |
| 151182 | - sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v); | |
| 151183 | - sqlite3VdbeGoto(v, labelCmpr); | |
| 151235 | + addrAeqB = addrAltB + 1; | |
| 151184 | 151236 | } |
| 151185 | 151237 | |
| 151186 | 151238 | /* Generate code to handle the case of A>B |
| 151187 | 151239 | */ |
| 151188 | - VdbeNoopComment((v, "A-gt-B subroutine")); | |
| 151189 | 151240 | addrAgtB = sqlite3VdbeCurrentAddr(v); |
| 151190 | 151241 | if( op==TK_ALL || op==TK_UNION ){ |
| 151191 | 151242 | sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); |
| 151243 | + VdbeComment((v, "out-B")); | |
| 151244 | + sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); | |
| 151245 | + VdbeComment((v, "next-B")); | |
| 151246 | + sqlite3VdbeGoto(v, labelCmpr); | |
| 151247 | + }else{ | |
| 151248 | + addrAgtB++; /* Just do next-B. Might as well use the next-B call | |
| 151249 | + ** in the next code block */ | |
| 151192 | 151250 | } |
| 151193 | - sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); | |
| 151194 | - sqlite3VdbeGoto(v, labelCmpr); | |
| 151195 | 151251 | |
| 151196 | 151252 | /* This code runs once to initialize everything. |
| 151197 | 151253 | */ |
| 151198 | 151254 | sqlite3VdbeJumpHere(v, addr1); |
| 151199 | 151255 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v); |
| 151256 | + VdbeComment((v, "next-A")); | |
| 151257 | + /* v--- Also the A>B case for EXCEPT and INTERSECT */ | |
| 151200 | 151258 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); |
| 151259 | + VdbeComment((v, "next-B")); | |
| 151201 | 151260 | |
| 151202 | 151261 | /* Implement the main merge loop |
| 151203 | 151262 | */ |
| 151263 | + if( aPermute!=0 ){ | |
| 151264 | + sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); | |
| 151265 | + } | |
| 151204 | 151266 | sqlite3VdbeResolveLabel(v, labelCmpr); |
| 151205 | - sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); | |
| 151206 | 151267 | sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, |
| 151207 | 151268 | (char*)pKeyMerge, P4_KEYINFO); |
| 151208 | - sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); | |
| 151209 | - sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v); | |
| 151269 | + if( aPermute!=0 ){ | |
| 151270 | + sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); | |
| 151271 | + } | |
| 151272 | + sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); | |
| 151273 | + VdbeCoverageIf(v, op==TK_ALL); | |
| 151274 | + VdbeCoverageIf(v, op==TK_UNION); | |
| 151275 | + VdbeCoverageIf(v, op==TK_EXCEPT); | |
| 151276 | + VdbeCoverageIf(v, op==TK_INTERSECT); | |
| 151210 | 151277 | |
| 151211 | 151278 | /* Jump to the this point in order to terminate the query. |
| 151212 | 151279 | */ |
| 151213 | 151280 | sqlite3VdbeResolveLabel(v, labelEnd); |
| 151214 | 151281 | |
| @@ -152121,11 +152188,11 @@ | ||
| 152121 | 152188 | pItem->fg.jointype |= (jointype & JT_LTORJ); |
| 152122 | 152189 | memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); |
| 152123 | 152190 | } |
| 152124 | 152191 | pSubitem->fg.jointype |= jointype; |
| 152125 | 152192 | |
| 152126 | - /* Now begin substituting subquery result set expressions for | |
| 152193 | + /* Begin substituting subquery result set expressions for | |
| 152127 | 152194 | ** references to the iParent in the outer query. |
| 152128 | 152195 | ** |
| 152129 | 152196 | ** Example: |
| 152130 | 152197 | ** |
| 152131 | 152198 | ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; |
| @@ -152133,21 +152200,21 @@ | ||
| 152133 | 152200 | ** \_____________________ outer query ______________________________/ |
| 152134 | 152201 | ** |
| 152135 | 152202 | ** We look at every expression in the outer query and every place we see |
| 152136 | 152203 | ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". |
| 152137 | 152204 | */ |
| 152138 | - if( pSub->pOrderBy && (pParent->selFlags & SF_NoopOrderBy)==0 ){ | |
| 152205 | + if( pSub->pOrderBy ){ | |
| 152139 | 152206 | /* At this point, any non-zero iOrderByCol values indicate that the |
| 152140 | 152207 | ** ORDER BY column expression is identical to the iOrderByCol'th |
| 152141 | 152208 | ** expression returned by SELECT statement pSub. Since these values |
| 152142 | 152209 | ** do not necessarily correspond to columns in SELECT statement pParent, |
| 152143 | 152210 | ** zero them before transferring the ORDER BY clause. |
| 152144 | 152211 | ** |
| 152145 | 152212 | ** Not doing this may cause an error if a subsequent call to this |
| 152146 | - ** function attempts to flatten a compound sub-query into pParent | |
| 152147 | - ** (the only way this can happen is if the compound sub-query is | |
| 152148 | - ** currently part of pSub->pSrc). See ticket [d11a6e908f]. */ | |
| 152213 | + ** function attempts to flatten a compound sub-query into pParent. | |
| 152214 | + ** See ticket [d11a6e908f]. | |
| 152215 | + */ | |
| 152149 | 152216 | ExprList *pOrderBy = pSub->pOrderBy; |
| 152150 | 152217 | for(i=0; i<pOrderBy->nExpr; i++){ |
| 152151 | 152218 | pOrderBy->a[i].u.x.iOrderByCol = 0; |
| 152152 | 152219 | } |
| 152153 | 152220 | assert( pParent->pOrderBy==0 ); |
| @@ -152995,18 +153062,18 @@ | ||
| 152995 | 153062 | ** These are rewritten as a subquery: |
| 152996 | 153063 | ** |
| 152997 | 153064 | ** SELECT * FROM (SELECT ... FROM t1 EXCEPT SELECT ... FROM t2) |
| 152998 | 153065 | ** ORDER BY ... COLLATE ... |
| 152999 | 153066 | ** |
| 153000 | -** This transformation is necessary because the multiSelectOrderBy() routine | |
| 153067 | +** This transformation is necessary because the multiSelectByMerge() routine | |
| 153001 | 153068 | ** above that generates the code for a compound SELECT with an ORDER BY clause |
| 153002 | 153069 | ** uses a merge algorithm that requires the same collating sequence on the |
| 153003 | 153070 | ** result columns as on the ORDER BY clause. See ticket |
| 153004 | 153071 | ** http://sqlite.org/src/info/6709574d2a |
| 153005 | 153072 | ** |
| 153006 | 153073 | ** This transformation is only needed for EXCEPT, INTERSECT, and UNION. |
| 153007 | -** The UNION ALL operator works fine with multiSelectOrderBy() even when | |
| 153074 | +** The UNION ALL operator works fine with multiSelectByMerge() even when | |
| 153008 | 153075 | ** there are COLLATE terms in the ORDER BY. |
| 153009 | 153076 | */ |
| 153010 | 153077 | static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ |
| 153011 | 153078 | int i; |
| 153012 | 153079 | Select *pNew; |
| @@ -153548,11 +153615,11 @@ | ||
| 153548 | 153615 | } |
| 153549 | 153616 | sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1); |
| 153550 | 153617 | } |
| 153551 | 153618 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 153552 | 153619 | else if( ALWAYS(IsVirtual(pTab)) |
| 153553 | - && pFrom->fg.fromDDL | |
| 153620 | + && (pFrom->fg.fromDDL || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL)) | |
| 153554 | 153621 | && ALWAYS(pTab->u.vtab.p!=0) |
| 153555 | 153622 | && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) |
| 153556 | 153623 | ){ |
| 153557 | 153624 | sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", |
| 153558 | 153625 | pTab->zName); |
| @@ -154832,19 +154899,30 @@ | ||
| 154832 | 154899 | Expr *pSubWhere = pSub->pWhere; |
| 154833 | 154900 | if( pSub->pSrc->nSrc==1 |
| 154834 | 154901 | && (pSub->selFlags & SF_Aggregate)==0 |
| 154835 | 154902 | && !pSub->pSrc->a[0].fg.isSubquery |
| 154836 | 154903 | && pSub->pLimit==0 |
| 154904 | + && pSub->pPrior==0 | |
| 154837 | 154905 | ){ |
| 154906 | + /* Before combining the sub-select with the parent, renumber the | |
| 154907 | + ** cursor used by the subselect. This is because the EXISTS expression | |
| 154908 | + ** might be a copy of another EXISTS expression from somewhere | |
| 154909 | + ** else in the tree, and in this case it is important that it use | |
| 154910 | + ** a unique cursor number. */ | |
| 154911 | + sqlite3 *db = pParse->db; | |
| 154912 | + int *aCsrMap = sqlite3DbMallocZero(db, (pParse->nTab+2)*sizeof(int)); | |
| 154913 | + if( aCsrMap==0 ) return; | |
| 154914 | + aCsrMap[0] = (pParse->nTab+1); | |
| 154915 | + renumberCursors(pParse, pSub, -1, aCsrMap); | |
| 154916 | + sqlite3DbFree(db, aCsrMap); | |
| 154917 | + | |
| 154838 | 154918 | memset(pWhere, 0, sizeof(*pWhere)); |
| 154839 | 154919 | pWhere->op = TK_INTEGER; |
| 154840 | 154920 | pWhere->u.iValue = 1; |
| 154841 | 154921 | ExprSetProperty(pWhere, EP_IntValue); |
| 154842 | - | |
| 154843 | 154922 | assert( p->pWhere!=0 ); |
| 154844 | 154923 | pSub->pSrc->a[0].fg.fromExists = 1; |
| 154845 | - pSub->pSrc->a[0].fg.jointype |= JT_CROSS; | |
| 154846 | 154924 | p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); |
| 154847 | 154925 | if( pSubWhere ){ |
| 154848 | 154926 | p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere); |
| 154849 | 154927 | pSub->pWhere = 0; |
| 154850 | 154928 | } |
| @@ -154969,10 +155047,34 @@ | ||
| 154969 | 155047 | memset(&sCtx, 0, sizeof(sCtx)); |
| 154970 | 155048 | sCtx.pSrc = pSelect->pSrc; |
| 154971 | 155049 | sqlite3WalkExprNN(&w, pSelect->pWhere); |
| 154972 | 155050 | pSelect->selFlags &= ~SF_OnToWhere; |
| 154973 | 155051 | } |
| 155052 | + | |
| 155053 | +/* | |
| 155054 | +** If p2 exists and p1 and p2 have the same number of terms, then change | |
| 155055 | +** every term of p1 to have the same sort order as p2 and return true. | |
| 155056 | +** | |
| 155057 | +** If p2 is NULL or p1 and p2 are different lengths, then make no changes | |
| 155058 | +** and return false. | |
| 155059 | +** | |
| 155060 | +** p1 must be non-NULL. | |
| 155061 | +*/ | |
| 155062 | +static int sqlite3CopySortOrder(ExprList *p1, ExprList *p2){ | |
| 155063 | + assert( p1 ); | |
| 155064 | + if( p2 && p1->nExpr==p2->nExpr ){ | |
| 155065 | + int ii; | |
| 155066 | + for(ii=0; ii<p1->nExpr; ii++){ | |
| 155067 | + u8 sortFlags; | |
| 155068 | + sortFlags = p2->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; | |
| 155069 | + p1->a[ii].fg.sortFlags = sortFlags; | |
| 155070 | + } | |
| 155071 | + return 1; | |
| 155072 | + }else{ | |
| 155073 | + return 0; | |
| 155074 | + } | |
| 155075 | +} | |
| 154974 | 155076 | |
| 154975 | 155077 | /* |
| 154976 | 155078 | ** Generate byte-code for the SELECT statement given in the p argument. |
| 154977 | 155079 | ** |
| 154978 | 155080 | ** The results are returned according to the SelectDest structure. |
| @@ -155065,12 +155167,11 @@ | ||
| 155065 | 155167 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); |
| 155066 | 155168 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); |
| 155067 | 155169 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); |
| 155068 | 155170 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue ); |
| 155069 | 155171 | if( IgnorableDistinct(pDest) ){ |
| 155070 | - assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || | |
| 155071 | - pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || | |
| 155172 | + assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Discard || | |
| 155072 | 155173 | pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); |
| 155073 | 155174 | /* All of these destinations are also able to ignore the ORDER BY clause */ |
| 155074 | 155175 | if( p->pOrderBy ){ |
| 155075 | 155176 | #if TREETRACE_ENABLED |
| 155076 | 155177 | TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); |
| @@ -155082,11 +155183,10 @@ | ||
| 155082 | 155183 | p->pOrderBy); |
| 155083 | 155184 | testcase( pParse->earlyCleanup ); |
| 155084 | 155185 | p->pOrderBy = 0; |
| 155085 | 155186 | } |
| 155086 | 155187 | p->selFlags &= ~(u32)SF_Distinct; |
| 155087 | - p->selFlags |= SF_NoopOrderBy; | |
| 155088 | 155188 | } |
| 155089 | 155189 | sqlite3SelectPrep(pParse, p, 0); |
| 155090 | 155190 | if( pParse->nErr ){ |
| 155091 | 155191 | goto select_end; |
| 155092 | 155192 | } |
| @@ -155622,11 +155722,12 @@ | ||
| 155622 | 155722 | ** used for both the ORDER BY and DISTINCT processing. As originally |
| 155623 | 155723 | ** written the query must use a temp-table for at least one of the ORDER |
| 155624 | 155724 | ** BY and DISTINCT, and an index or separate temp-table for the other. |
| 155625 | 155725 | */ |
| 155626 | 155726 | if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct |
| 155627 | - && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 | |
| 155727 | + && sqlite3CopySortOrder(pEList, sSort.pOrderBy) | |
| 155728 | + && sqlite3ExprListCompare(pEList, sSort.pOrderBy, -1)==0 | |
| 155628 | 155729 | && OptimizationEnabled(db, SQLITE_GroupByOrder) |
| 155629 | 155730 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 155630 | 155731 | && p->pWin==0 |
| 155631 | 155732 | #endif |
| 155632 | 155733 | ){ |
| @@ -155836,25 +155937,14 @@ | ||
| 155836 | 155937 | ** in the correct order. It also may not - the GROUP BY might use a |
| 155837 | 155938 | ** database index that causes rows to be grouped together as required |
| 155838 | 155939 | ** but not actually sorted. Either way, record the fact that the |
| 155839 | 155940 | ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp |
| 155840 | 155941 | ** variable. */ |
| 155841 | - if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){ | |
| 155842 | - int ii; | |
| 155843 | - /* The GROUP BY processing doesn't care whether rows are delivered in | |
| 155844 | - ** ASC or DESC order - only that each group is returned contiguously. | |
| 155845 | - ** So set the ASC/DESC flags in the GROUP BY to match those in the | |
| 155846 | - ** ORDER BY to maximize the chances of rows being delivered in an | |
| 155847 | - ** order that makes the ORDER BY redundant. */ | |
| 155848 | - for(ii=0; ii<pGroupBy->nExpr; ii++){ | |
| 155849 | - u8 sortFlags; | |
| 155850 | - sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; | |
| 155851 | - pGroupBy->a[ii].fg.sortFlags = sortFlags; | |
| 155852 | - } | |
| 155853 | - if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ | |
| 155854 | - orderByGrp = 1; | |
| 155855 | - } | |
| 155942 | + if( sqlite3CopySortOrder(pGroupBy, sSort.pOrderBy) | |
| 155943 | + && sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 | |
| 155944 | + ){ | |
| 155945 | + orderByGrp = 1; | |
| 155856 | 155946 | } |
| 155857 | 155947 | }else{ |
| 155858 | 155948 | assert( 0==sqlite3LogEst(1) ); |
| 155859 | 155949 | p->nSelectRow = 0; |
| 155860 | 155950 | } |
| @@ -156849,15 +156939,20 @@ | ||
| 156849 | 156939 | } |
| 156850 | 156940 | goto trigger_cleanup; |
| 156851 | 156941 | } |
| 156852 | 156942 | } |
| 156853 | 156943 | |
| 156944 | + /* NB: The SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES compile-time option is | |
| 156945 | + ** experimental and unsupported. Do not use it unless understand the | |
| 156946 | + ** implications and you cannot get by without this capability. */ | |
| 156947 | +#if !defined(SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES) /* Experimental */ | |
| 156854 | 156948 | /* Do not create a trigger on a system table */ |
| 156855 | 156949 | if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ |
| 156856 | 156950 | sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); |
| 156857 | 156951 | goto trigger_cleanup; |
| 156858 | 156952 | } |
| 156953 | +#endif | |
| 156859 | 156954 | |
| 156860 | 156955 | /* INSTEAD of triggers are only for views and views only support INSTEAD |
| 156861 | 156956 | ** of triggers. |
| 156862 | 156957 | */ |
| 156863 | 156958 | if( IsView(pTab) && tr_tm!=TK_INSTEAD ){ |
| @@ -160118,13 +160213,15 @@ | ||
| 160118 | 160213 | if( rc!=SQLITE_OK ) goto end_of_vacuum; |
| 160119 | 160214 | assert( (db->nDb-1)==nDb ); |
| 160120 | 160215 | pDb = &db->aDb[nDb]; |
| 160121 | 160216 | assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); |
| 160122 | 160217 | pTemp = pDb->pBt; |
| 160218 | + nRes = sqlite3BtreeGetRequestedReserve(pMain); | |
| 160123 | 160219 | if( pOut ){ |
| 160124 | 160220 | sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); |
| 160125 | 160221 | i64 sz = 0; |
| 160222 | + const char *zFilename; | |
| 160126 | 160223 | if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ |
| 160127 | 160224 | rc = SQLITE_ERROR; |
| 160128 | 160225 | sqlite3SetString(pzErrMsg, db, "output file already exists"); |
| 160129 | 160226 | goto end_of_vacuum; |
| 160130 | 160227 | } |
| @@ -160132,12 +160229,20 @@ | ||
| 160132 | 160229 | |
| 160133 | 160230 | /* For a VACUUM INTO, the pager-flags are set to the same values as |
| 160134 | 160231 | ** they are for the database being vacuumed, except that PAGER_CACHESPILL |
| 160135 | 160232 | ** is always set. */ |
| 160136 | 160233 | pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); |
| 160234 | + | |
| 160235 | + /* If the VACUUM INTO target file is a URI filename and if the | |
| 160236 | + ** "reserve=N" query parameter is present, reset the reserve to the | |
| 160237 | + ** amount specified, if the amount is within range */ | |
| 160238 | + zFilename = sqlite3BtreeGetFilename(pTemp); | |
| 160239 | + if( ALWAYS(zFilename) ){ | |
| 160240 | + int nNew = (int)sqlite3_uri_int64(zFilename, "reserve", nRes); | |
| 160241 | + if( nNew>=0 && nNew<=255 ) nRes = nNew; | |
| 160242 | + } | |
| 160137 | 160243 | } |
| 160138 | - nRes = sqlite3BtreeGetRequestedReserve(pMain); | |
| 160139 | 160244 | |
| 160140 | 160245 | sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); |
| 160141 | 160246 | sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); |
| 160142 | 160247 | sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); |
| 160143 | 160248 | |
| @@ -165646,10 +165751,38 @@ | ||
| 165646 | 165751 | sqlite3ValueFree(pVal); |
| 165647 | 165752 | return rc; |
| 165648 | 165753 | } |
| 165649 | 165754 | #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ |
| 165650 | 165755 | |
| 165756 | +/* | |
| 165757 | +** If pExpr is one of "like", "glob", "match", or "regexp", then | |
| 165758 | +** return the corresponding SQLITE_INDEX_CONSTRAINT_xxxx value. | |
| 165759 | +** If not, return 0. | |
| 165760 | +** | |
| 165761 | +** pExpr is guaranteed to be a TK_FUNCTION. | |
| 165762 | +*/ | |
| 165763 | +SQLITE_PRIVATE int sqlite3ExprIsLikeOperator(const Expr *pExpr){ | |
| 165764 | + static const struct { | |
| 165765 | + const char *zOp; | |
| 165766 | + unsigned char eOp; | |
| 165767 | + } aOp[] = { | |
| 165768 | + { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, | |
| 165769 | + { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, | |
| 165770 | + { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, | |
| 165771 | + { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } | |
| 165772 | + }; | |
| 165773 | + int i; | |
| 165774 | + assert( pExpr->op==TK_FUNCTION ); | |
| 165775 | + assert( !ExprHasProperty(pExpr, EP_IntValue) ); | |
| 165776 | + for(i=0; i<ArraySize(aOp); i++){ | |
| 165777 | + if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){ | |
| 165778 | + return aOp[i].eOp; | |
| 165779 | + } | |
| 165780 | + } | |
| 165781 | + return 0; | |
| 165782 | +} | |
| 165783 | + | |
| 165651 | 165784 | |
| 165652 | 165785 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 165653 | 165786 | /* |
| 165654 | 165787 | ** Check to see if the pExpr expression is a form that needs to be passed |
| 165655 | 165788 | ** to the xBestIndex method of virtual tables. Forms of interest include: |
| @@ -165682,19 +165815,10 @@ | ||
| 165682 | 165815 | unsigned char *peOp2, /* OUT: 0 for MATCH, or else an op2 value */ |
| 165683 | 165816 | Expr **ppLeft, /* Column expression to left of MATCH/op2 */ |
| 165684 | 165817 | Expr **ppRight /* Expression to left of MATCH/op2 */ |
| 165685 | 165818 | ){ |
| 165686 | 165819 | if( pExpr->op==TK_FUNCTION ){ |
| 165687 | - static const struct Op2 { | |
| 165688 | - const char *zOp; | |
| 165689 | - unsigned char eOp2; | |
| 165690 | - } aOp[] = { | |
| 165691 | - { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, | |
| 165692 | - { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, | |
| 165693 | - { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, | |
| 165694 | - { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } | |
| 165695 | - }; | |
| 165696 | 165820 | ExprList *pList; |
| 165697 | 165821 | Expr *pCol; /* Column reference */ |
| 165698 | 165822 | int i; |
| 165699 | 165823 | |
| 165700 | 165824 | assert( ExprUseXList(pExpr) ); |
| @@ -165710,20 +165834,15 @@ | ||
| 165710 | 165834 | ** vtab_column MATCH expression |
| 165711 | 165835 | ** MATCH(expression,vtab_column) |
| 165712 | 165836 | */ |
| 165713 | 165837 | pCol = pList->a[1].pExpr; |
| 165714 | 165838 | assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); |
| 165715 | - if( ExprIsVtab(pCol) ){ | |
| 165716 | - for(i=0; i<ArraySize(aOp); i++){ | |
| 165717 | - assert( !ExprHasProperty(pExpr, EP_IntValue) ); | |
| 165718 | - if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){ | |
| 165719 | - *peOp2 = aOp[i].eOp2; | |
| 165720 | - *ppRight = pList->a[0].pExpr; | |
| 165721 | - *ppLeft = pCol; | |
| 165722 | - return 1; | |
| 165723 | - } | |
| 165724 | - } | |
| 165839 | + if( ExprIsVtab(pCol) && (i = sqlite3ExprIsLikeOperator(pExpr))!=0 ){ | |
| 165840 | + *peOp2 = i; | |
| 165841 | + *ppRight = pList->a[0].pExpr; | |
| 165842 | + *ppLeft = pCol; | |
| 165843 | + return 1; | |
| 165725 | 165844 | } |
| 165726 | 165845 | |
| 165727 | 165846 | /* We can also match against the first column of overloaded |
| 165728 | 165847 | ** functions where xFindFunction returns a value of at least |
| 165729 | 165848 | ** SQLITE_INDEX_CONSTRAINT_FUNCTION. |
| @@ -165853,20 +165972,26 @@ | ||
| 165853 | 165972 | u16 eOp = pOne->eOperator | pTwo->eOperator; |
| 165854 | 165973 | sqlite3 *db; /* Database connection (for malloc) */ |
| 165855 | 165974 | Expr *pNew; /* New virtual expression */ |
| 165856 | 165975 | int op; /* Operator for the combined expression */ |
| 165857 | 165976 | int idxNew; /* Index in pWC of the next virtual term */ |
| 165977 | + Expr *pA, *pB; /* Expressions associated with pOne and pTwo */ | |
| 165858 | 165978 | |
| 165859 | 165979 | if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; |
| 165860 | 165980 | if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; |
| 165861 | 165981 | if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; |
| 165862 | 165982 | if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp |
| 165863 | 165983 | && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; |
| 165864 | - assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 ); | |
| 165865 | - assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 ); | |
| 165866 | - if( sqlite3ExprCompare(0,pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return; | |
| 165867 | - if( sqlite3ExprCompare(0,pOne->pExpr->pRight, pTwo->pExpr->pRight,-1) )return; | |
| 165984 | + pA = pOne->pExpr; | |
| 165985 | + pB = pTwo->pExpr; | |
| 165986 | + assert( pA->pLeft!=0 && pA->pRight!=0 ); | |
| 165987 | + assert( pB->pLeft!=0 && pB->pRight!=0 ); | |
| 165988 | + if( sqlite3ExprCompare(0,pA->pLeft, pB->pLeft, -1) ) return; | |
| 165989 | + if( sqlite3ExprCompare(0,pA->pRight, pB->pRight,-1) ) return; | |
| 165990 | + if( ExprHasProperty(pA,EP_Commuted)!=ExprHasProperty(pB,EP_Commuted) ){ | |
| 165991 | + return; | |
| 165992 | + } | |
| 165868 | 165993 | /* If we reach this point, it means the two subterms can be combined */ |
| 165869 | 165994 | if( (eOp & (eOp-1))!=0 ){ |
| 165870 | 165995 | if( eOp & (WO_LT|WO_LE) ){ |
| 165871 | 165996 | eOp = WO_LE; |
| 165872 | 165997 | }else{ |
| @@ -165873,11 +165998,11 @@ | ||
| 165873 | 165998 | assert( eOp & (WO_GT|WO_GE) ); |
| 165874 | 165999 | eOp = WO_GE; |
| 165875 | 166000 | } |
| 165876 | 166001 | } |
| 165877 | 166002 | db = pWC->pWInfo->pParse->db; |
| 165878 | - pNew = sqlite3ExprDup(db, pOne->pExpr, 0); | |
| 166003 | + pNew = sqlite3ExprDup(db, pA, 0); | |
| 165879 | 166004 | if( pNew==0 ) return; |
| 165880 | 166005 | for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( op<TK_GE ); } |
| 165881 | 166006 | pNew->op = op; |
| 165882 | 166007 | idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); |
| 165883 | 166008 | exprAnalyze(pSrc, pWC, idxNew); |
| @@ -168744,15 +168869,18 @@ | ||
| 168744 | 168869 | |
| 168745 | 168870 | /* No matches cause a break out of the loop */ |
| 168746 | 168871 | break; |
| 168747 | 168872 | } |
| 168748 | 168873 | if( i==n ){ |
| 168874 | + int bSortByGroup = (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0; | |
| 168749 | 168875 | nOrderBy = n; |
| 168750 | 168876 | if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ |
| 168751 | - eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); | |
| 168877 | + eDistinct = 2 + bSortByGroup; | |
| 168752 | 168878 | }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ |
| 168753 | - eDistinct = 1; | |
| 168879 | + eDistinct = 1 - bSortByGroup; | |
| 168880 | + }else if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ | |
| 168881 | + eDistinct = 3; | |
| 168754 | 168882 | } |
| 168755 | 168883 | } |
| 168756 | 168884 | } |
| 168757 | 168885 | |
| 168758 | 168886 | /* Allocate the sqlite3_index_info structure |
| @@ -170158,10 +170286,71 @@ | ||
| 170158 | 170286 | p->u.btree.pIndex = 0; |
| 170159 | 170287 | } |
| 170160 | 170288 | } |
| 170161 | 170289 | return rc; |
| 170162 | 170290 | } |
| 170291 | + | |
| 170292 | +/* | |
| 170293 | +** Callback for estLikePatternLength(). | |
| 170294 | +** | |
| 170295 | +** If this node is a string literal that is longer pWalker->sz, then set | |
| 170296 | +** pWalker->sz to the byte length of that string literal. | |
| 170297 | +** | |
| 170298 | +** pWalker->eCode indicates how to count characters: | |
| 170299 | +** | |
| 170300 | +** eCode==0 Count as a GLOB pattern | |
| 170301 | +** eCode==1 Count as a LIKE pattern | |
| 170302 | +*/ | |
| 170303 | +static int exprNodePatternLengthEst(Walker *pWalker, Expr *pExpr){ | |
| 170304 | + if( pExpr->op==TK_STRING ){ | |
| 170305 | + int sz = 0; /* Pattern size in bytes */ | |
| 170306 | + u8 *z = (u8*)pExpr->u.zToken; /* The pattern */ | |
| 170307 | + u8 c; /* Next character of the pattern */ | |
| 170308 | + u8 c1, c2, c3; /* Wildcards */ | |
| 170309 | + if( pWalker->eCode ){ | |
| 170310 | + c1 = '%'; | |
| 170311 | + c2 = '_'; | |
| 170312 | + c3 = 0; | |
| 170313 | + }else{ | |
| 170314 | + c1 = '*'; | |
| 170315 | + c2 = '?'; | |
| 170316 | + c3 = '['; | |
| 170317 | + } | |
| 170318 | + while( (c = *(z++))!=0 ){ | |
| 170319 | + if( c==c3 ){ | |
| 170320 | + if( *z ) z++; | |
| 170321 | + while( *z && *z!=']' ) z++; | |
| 170322 | + }else if( c!=c1 && c!=c2 ){ | |
| 170323 | + sz++; | |
| 170324 | + } | |
| 170325 | + } | |
| 170326 | + if( sz>pWalker->u.sz ) pWalker->u.sz = sz; | |
| 170327 | + } | |
| 170328 | + return WRC_Continue; | |
| 170329 | +} | |
| 170330 | + | |
| 170331 | +/* | |
| 170332 | +** Return the length of the longest string literal in the given | |
| 170333 | +** expression. | |
| 170334 | +** | |
| 170335 | +** eCode indicates how to count characters: | |
| 170336 | +** | |
| 170337 | +** eCode==0 Count as a GLOB pattern | |
| 170338 | +** eCode==1 Count as a LIKE pattern | |
| 170339 | +*/ | |
| 170340 | +static int estLikePatternLength(Expr *p, u16 eCode){ | |
| 170341 | + Walker w; | |
| 170342 | + w.u.sz = 0; | |
| 170343 | + w.eCode = eCode; | |
| 170344 | + w.xExprCallback = exprNodePatternLengthEst; | |
| 170345 | + w.xSelectCallback = sqlite3SelectWalkFail; | |
| 170346 | +#ifdef SQLITE_DEBUG | |
| 170347 | + w.xSelectCallback2 = sqlite3SelectWalkAssert2; | |
| 170348 | +#endif | |
| 170349 | + sqlite3WalkExpr(&w, p); | |
| 170350 | + return w.u.sz; | |
| 170351 | +} | |
| 170163 | 170352 | |
| 170164 | 170353 | /* |
| 170165 | 170354 | ** Adjust the WhereLoop.nOut value downward to account for terms of the |
| 170166 | 170355 | ** WHERE clause that reference the loop but which are not used by an |
| 170167 | 170356 | ** index. |
| @@ -170187,10 +170376,17 @@ | ||
| 170187 | 170376 | ** of rows in the table. In other words, assume that x==EXPR will filter |
| 170188 | 170377 | ** out at least 3 out of 4 rows. If EXPR is -1 or 0 or 1, then maybe the |
| 170189 | 170378 | ** "x" column is boolean or else -1 or 0 or 1 is a common default value |
| 170190 | 170379 | ** on the "x" column and so in that case only cap the output row estimate |
| 170191 | 170380 | ** at 1/2 instead of 1/4. |
| 170381 | +** | |
| 170382 | +** Heuristic 3: If there is a LIKE or GLOB (or REGEXP or MATCH) operator | |
| 170383 | +** with a large constant pattern, then reduce the size of the search | |
| 170384 | +** space according to the length of the pattern, under the theory that | |
| 170385 | +** longer patterns are less likely to match. This heuristic was added | |
| 170386 | +** to give better output-row count estimates when preparing queries for | |
| 170387 | +** the Join-Order Benchmarks. See forum thread 2026-01-30T09:57:54z | |
| 170192 | 170388 | */ |
| 170193 | 170389 | static void whereLoopOutputAdjust( |
| 170194 | 170390 | WhereClause *pWC, /* The WHERE clause */ |
| 170195 | 170391 | WhereLoop *pLoop, /* The loop to adjust downward */ |
| 170196 | 170392 | LogEst nRow /* Number of rows in the entire table */ |
| @@ -170236,25 +170432,43 @@ | ||
| 170236 | 170432 | ** then use the probability provided by the application. */ |
| 170237 | 170433 | pLoop->nOut += pTerm->truthProb; |
| 170238 | 170434 | }else{ |
| 170239 | 170435 | /* In the absence of explicit truth probabilities, use heuristics to |
| 170240 | 170436 | ** guess a reasonable truth probability. */ |
| 170437 | + Expr *pOpExpr = pTerm->pExpr; | |
| 170241 | 170438 | pLoop->nOut--; |
| 170242 | 170439 | if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 |
| 170243 | 170440 | && (pTerm->wtFlags & TERM_HIGHTRUTH)==0 /* tag-20200224-1 */ |
| 170244 | 170441 | ){ |
| 170245 | - Expr *pRight = pTerm->pExpr->pRight; | |
| 170442 | + Expr *pRight = pOpExpr->pRight; | |
| 170246 | 170443 | int k = 0; |
| 170247 | - testcase( pTerm->pExpr->op==TK_IS ); | |
| 170444 | + testcase( pOpExpr->op==TK_IS ); | |
| 170248 | 170445 | if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){ |
| 170249 | 170446 | k = 10; |
| 170250 | 170447 | }else{ |
| 170251 | 170448 | k = 20; |
| 170252 | 170449 | } |
| 170253 | 170450 | if( iReduce<k ){ |
| 170254 | 170451 | pTerm->wtFlags |= TERM_HEURTRUTH; |
| 170255 | 170452 | iReduce = k; |
| 170453 | + } | |
| 170454 | + }else | |
| 170455 | + if( ExprHasProperty(pOpExpr, EP_InfixFunc) | |
| 170456 | + && pOpExpr->op==TK_FUNCTION | |
| 170457 | + ){ | |
| 170458 | + int eOp; | |
| 170459 | + assert( ExprUseXList(pOpExpr) ); | |
| 170460 | + assert( pOpExpr->x.pList->nExpr>=2 ); | |
| 170461 | + eOp = sqlite3ExprIsLikeOperator(pOpExpr); | |
| 170462 | + if( ALWAYS(eOp>0) ){ | |
| 170463 | + int szPattern; | |
| 170464 | + Expr *pRHS = pOpExpr->x.pList->a[0].pExpr; | |
| 170465 | + eOp = eOp==SQLITE_INDEX_CONSTRAINT_LIKE; | |
| 170466 | + szPattern = estLikePatternLength(pRHS, eOp); | |
| 170467 | + if( szPattern>0 ){ | |
| 170468 | + pLoop->nOut -= szPattern*2; | |
| 170469 | + } | |
| 170256 | 170470 | } |
| 170257 | 170471 | } |
| 170258 | 170472 | } |
| 170259 | 170473 | } |
| 170260 | 170474 | } |
| @@ -170712,10 +170926,11 @@ | ||
| 170712 | 170926 | |
| 170713 | 170927 | nOutUnadjusted = pNew->nOut; |
| 170714 | 170928 | pNew->rRun += nInMul + nIn; |
| 170715 | 170929 | pNew->nOut += nInMul + nIn; |
| 170716 | 170930 | whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize); |
| 170931 | + if( pSrc->fg.fromExists ) pNew->nOut = 0; | |
| 170717 | 170932 | rc = whereLoopInsert(pBuilder, pNew); |
| 170718 | 170933 | |
| 170719 | 170934 | if( pNew->wsFlags & WHERE_COLUMN_RANGE ){ |
| 170720 | 170935 | pNew->nOut = saved_nOut; |
| 170721 | 170936 | }else{ |
| @@ -171308,10 +171523,12 @@ | ||
| 171308 | 171523 | ApplyCostMultiplier(pNew->rRun, pTab->costMult); |
| 171309 | 171524 | whereLoopOutputAdjust(pWC, pNew, rSize); |
| 171310 | 171525 | if( pSrc->fg.isSubquery ){ |
| 171311 | 171526 | if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE; |
| 171312 | 171527 | pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; |
| 171528 | + }else if( pSrc->fg.fromExists ){ | |
| 171529 | + pNew->nOut = 0; | |
| 171313 | 171530 | } |
| 171314 | 171531 | rc = whereLoopInsert(pBuilder, pNew); |
| 171315 | 171532 | pNew->nOut = rSize; |
| 171316 | 171533 | if( rc ) break; |
| 171317 | 171534 | }else{ |
| @@ -171410,10 +171627,11 @@ | ||
| 171410 | 171627 | /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN |
| 171411 | 171628 | ** because the cursor used to access the index might not be |
| 171412 | 171629 | ** positioned to the correct row during the right-join no-match |
| 171413 | 171630 | ** loop. */ |
| 171414 | 171631 | }else{ |
| 171632 | + if( pSrc->fg.fromExists ) pNew->nOut = 0; | |
| 171415 | 171633 | rc = whereLoopInsert(pBuilder, pNew); |
| 171416 | 171634 | } |
| 171417 | 171635 | pNew->nOut = rSize; |
| 171418 | 171636 | if( rc ) break; |
| 171419 | 171637 | } |
| @@ -172072,11 +172290,11 @@ | ||
| 172072 | 172290 | SrcItem *pItem; |
| 172073 | 172291 | SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; |
| 172074 | 172292 | sqlite3 *db = pWInfo->pParse->db; |
| 172075 | 172293 | int rc = SQLITE_OK; |
| 172076 | 172294 | int bFirstPastRJ = 0; |
| 172077 | - int hasRightJoin = 0; | |
| 172295 | + int hasRightCrossJoin = 0; | |
| 172078 | 172296 | WhereLoop *pNew; |
| 172079 | 172297 | |
| 172080 | 172298 | |
| 172081 | 172299 | /* Loop over the tables in the join, from left to right */ |
| 172082 | 172300 | pNew = pBuilder->pNew; |
| @@ -172099,19 +172317,38 @@ | ||
| 172099 | 172317 | /* Add prerequisites to prevent reordering of FROM clause terms |
| 172100 | 172318 | ** across CROSS joins and outer joins. The bFirstPastRJ boolean |
| 172101 | 172319 | ** prevents the right operand of a RIGHT JOIN from being swapped with |
| 172102 | 172320 | ** other elements even further to the right. |
| 172103 | 172321 | ** |
| 172104 | - ** The JT_LTORJ case and the hasRightJoin flag work together to | |
| 172105 | - ** prevent FROM-clause terms from moving from the right side of | |
| 172106 | - ** a LEFT JOIN over to the left side of that join if the LEFT JOIN | |
| 172107 | - ** is itself on the left side of a RIGHT JOIN. | |
| 172322 | + ** The hasRightCrossJoin flag prevent FROM-clause terms from moving | |
| 172323 | + ** from the right side of a LEFT JOIN or CROSS JOIN over to the | |
| 172324 | + ** left side of that same join. This is a required restriction in | |
| 172325 | + ** the case of LEFT JOIN - an incorrect answer may results if it is | |
| 172326 | + ** not enforced. This restriction is not required for CROSS JOIN. | |
| 172327 | + ** It is provided merely as a means of controlling join order, under | |
| 172328 | + ** the theory that no real-world queries that care about performance | |
| 172329 | + ** actually use the CROSS JOIN syntax. | |
| 172108 | 172330 | */ |
| 172109 | - if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; | |
| 172331 | + if( pItem->fg.jointype & (JT_LTORJ|JT_CROSS) ){ | |
| 172332 | + testcase( pItem->fg.jointype & JT_LTORJ ); | |
| 172333 | + testcase( pItem->fg.jointype & JT_CROSS ); | |
| 172334 | + hasRightCrossJoin = 1; | |
| 172335 | + } | |
| 172110 | 172336 | mPrereq |= mPrior; |
| 172111 | 172337 | bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; |
| 172112 | - }else if( !hasRightJoin ){ | |
| 172338 | + }else if( pItem->fg.fromExists ){ | |
| 172339 | + /* joins that result from the EXISTS-to-JOIN optimization should not | |
| 172340 | + ** be moved to the left of any of their dependencies */ | |
| 172341 | + WhereClause *pWC = &pWInfo->sWC; | |
| 172342 | + WhereTerm *pTerm; | |
| 172343 | + int i; | |
| 172344 | + for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){ | |
| 172345 | + if( (pNew->maskSelf & pTerm->prereqAll)!=0 ){ | |
| 172346 | + mPrereq |= (pTerm->prereqAll & (pNew->maskSelf-1)); | |
| 172347 | + } | |
| 172348 | + } | |
| 172349 | + }else if( !hasRightCrossJoin ){ | |
| 172113 | 172350 | mPrereq = 0; |
| 172114 | 172351 | } |
| 172115 | 172352 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 172116 | 172353 | if( IsVirtual(pItem->pSTab) ){ |
| 172117 | 172354 | SrcItem *p; |
| @@ -172330,13 +172567,11 @@ | ||
| 172330 | 172567 | if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue; |
| 172331 | 172568 | }else{ |
| 172332 | 172569 | pLoop = pLast; |
| 172333 | 172570 | } |
| 172334 | 172571 | if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ |
| 172335 | - if( pLoop->u.vtab.isOrdered | |
| 172336 | - && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) | |
| 172337 | - ){ | |
| 172572 | + if( pLoop->u.vtab.isOrdered && pWInfo->pOrderBy==pOrderBy ){ | |
| 172338 | 172573 | obSat = obDone; |
| 172339 | 172574 | }else{ |
| 172340 | 172575 | /* No further ORDER BY terms may be matched. So this call should |
| 172341 | 172576 | ** return >=0, not -1. Clear isOrderDistinct to ensure it does so. */ |
| 172342 | 172577 | isOrderDistinct = 0; |
| @@ -173673,10 +173908,11 @@ | ||
| 173673 | 173908 | pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); |
| 173674 | 173909 | notReady &= ~pLoop->maskSelf; |
| 173675 | 173910 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| 173676 | 173911 | if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ |
| 173677 | 173912 | pTerm->wtFlags |= TERM_CODED; |
| 173913 | + pTerm->prereqAll = 0; | |
| 173678 | 173914 | } |
| 173679 | 173915 | } |
| 173680 | 173916 | if( i!=pWInfo->nLevel-1 ){ |
| 173681 | 173917 | int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); |
| 173682 | 173918 | memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); |
| @@ -174660,26 +174896,31 @@ | ||
| 174660 | 174896 | VdbeCoverageIf(v, op==OP_SeekGT); |
| 174661 | 174897 | sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); |
| 174662 | 174898 | } |
| 174663 | 174899 | #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ |
| 174664 | 174900 | } |
| 174665 | - if( pTabList->a[pLevel->iFrom].fg.fromExists && i==pWInfo->nLevel-1 ){ | |
| 174666 | - /* If the EXISTS-to-JOIN optimization was applied, then the EXISTS | |
| 174667 | - ** loop(s) will be the inner-most loops of the join. There might be | |
| 174668 | - ** multiple EXISTS loops, but they will all be nested, and the join | |
| 174669 | - ** order will not have been changed by the query planner. If the | |
| 174670 | - ** inner-most EXISTS loop sees a single successful row, it should | |
| 174671 | - ** break out of *all* EXISTS loops. But only the inner-most of the | |
| 174672 | - ** nested EXISTS loops should do this breakout. */ | |
| 174901 | + if( pTabList->a[pLevel->iFrom].fg.fromExists | |
| 174902 | + && (i==pWInfo->nLevel-1 | |
| 174903 | + || pTabList->a[pWInfo->a[i+1].iFrom].fg.fromExists==0) | |
| 174904 | + ){ | |
| 174905 | + /* This is an EXISTS-to-JOIN optimization which is either the | |
| 174906 | + ** inner-most loop, or the inner-most of a group of nested | |
| 174907 | + ** EXISTS-to-JOIN optimization loops. If this loop sees a successful | |
| 174908 | + ** row, it should break out of itself as well as other EXISTS-to-JOIN | |
| 174909 | + ** loops in which is is directly nested. */ | |
| 174673 | 174910 | int nOuter = 0; /* Nr of outer EXISTS that this one is nested within */ |
| 174674 | 174911 | while( nOuter<i ){ |
| 174675 | 174912 | if( !pTabList->a[pLevel[-nOuter-1].iFrom].fg.fromExists ) break; |
| 174676 | 174913 | nOuter++; |
| 174677 | 174914 | } |
| 174678 | 174915 | testcase( nOuter>0 ); |
| 174679 | 174916 | sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel[-nOuter].addrBrk); |
| 174680 | - VdbeComment((v, "EXISTS break")); | |
| 174917 | + if( nOuter ){ | |
| 174918 | + VdbeComment((v, "EXISTS break %d..%d", i-nOuter, i)); | |
| 174919 | + }else{ | |
| 174920 | + VdbeComment((v, "EXISTS break %d", i)); | |
| 174921 | + } | |
| 174681 | 174922 | } |
| 174682 | 174923 | sqlite3VdbeResolveLabel(v, pLevel->addrCont); |
| 174683 | 174924 | if( pLevel->op!=OP_Noop ){ |
| 174684 | 174925 | sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); |
| 174685 | 174926 | sqlite3VdbeChangeP5(v, pLevel->p5); |
| @@ -184823,11 +185064,11 @@ | ||
| 184823 | 185064 | for(i=1; sqlite3Isdigit(z[i]); i++){} |
| 184824 | 185065 | return i; |
| 184825 | 185066 | } |
| 184826 | 185067 | case CC_DOLLAR: |
| 184827 | 185068 | case CC_VARALPHA: { |
| 184828 | - int n = 0; | |
| 185069 | + i64 n = 0; | |
| 184829 | 185070 | testcase( z[0]=='$' ); testcase( z[0]=='@' ); |
| 184830 | 185071 | testcase( z[0]==':' ); testcase( z[0]=='#' ); |
| 184831 | 185072 | *tokenType = TK_VARIABLE; |
| 184832 | 185073 | for(i=1; (c=z[i])!=0; i++){ |
| 184833 | 185074 | if( IdChar(c) ){ |
| @@ -189448,10 +189689,16 @@ | ||
| 189448 | 189689 | /* |
| 189449 | 189690 | ** Find existing client data. |
| 189450 | 189691 | */ |
| 189451 | 189692 | SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){ |
| 189452 | 189693 | DbClientData *p; |
| 189694 | +#ifdef SQLITE_ENABLE_API_ARMOR | |
| 189695 | + if( !zName || !sqlite3SafetyCheckOk(db) ){ | |
| 189696 | + (void)SQLITE_MISUSE_BKPT; | |
| 189697 | + return 0; | |
| 189698 | + } | |
| 189699 | +#endif | |
| 189453 | 189700 | sqlite3_mutex_enter(db->mutex); |
| 189454 | 189701 | for(p=db->pDbData; p; p=p->pNext){ |
| 189455 | 189702 | if( strcmp(p->zName, zName)==0 ){ |
| 189456 | 189703 | void *pResult = p->pData; |
| 189457 | 189704 | sqlite3_mutex_leave(db->mutex); |
| @@ -192292,10 +192539,19 @@ | ||
| 192292 | 192539 | SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); |
| 192293 | 192540 | |
| 192294 | 192541 | #define fts3GetVarint32(p, piVal) ( \ |
| 192295 | 192542 | (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \ |
| 192296 | 192543 | ) |
| 192544 | + | |
| 192545 | +SQLITE_PRIVATE int sqlite3Fts3PrepareStmt( | |
| 192546 | + Fts3Table *p, /* Prepare for this connection */ | |
| 192547 | + const char *zSql, /* SQL to prepare */ | |
| 192548 | + int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */ | |
| 192549 | + int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */ | |
| 192550 | + sqlite3_stmt **pp /* OUT: Prepared statement */ | |
| 192551 | +); | |
| 192552 | + | |
| 192297 | 192553 | |
| 192298 | 192554 | /* fts3.c */ |
| 192299 | 192555 | SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...); |
| 192300 | 192556 | SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); |
| 192301 | 192557 | SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); |
| @@ -193900,13 +194156,11 @@ | ||
| 193900 | 194156 | p->pSeekStmt = 0; |
| 193901 | 194157 | }else{ |
| 193902 | 194158 | zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); |
| 193903 | 194159 | if( !zSql ) return SQLITE_NOMEM; |
| 193904 | 194160 | p->bLock++; |
| 193905 | - rc = sqlite3_prepare_v3( | |
| 193906 | - p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 | |
| 193907 | - ); | |
| 194161 | + rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt); | |
| 193908 | 194162 | p->bLock--; |
| 193909 | 194163 | sqlite3_free(zSql); |
| 193910 | 194164 | } |
| 193911 | 194165 | if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1; |
| 193912 | 194166 | } |
| @@ -195477,13 +195731,11 @@ | ||
| 195477 | 195731 | p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") |
| 195478 | 195732 | ); |
| 195479 | 195733 | } |
| 195480 | 195734 | if( zSql ){ |
| 195481 | 195735 | p->bLock++; |
| 195482 | - rc = sqlite3_prepare_v3( | |
| 195483 | - p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 | |
| 195484 | - ); | |
| 195736 | + rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt); | |
| 195485 | 195737 | p->bLock--; |
| 195486 | 195738 | sqlite3_free(zSql); |
| 195487 | 195739 | }else{ |
| 195488 | 195740 | rc = SQLITE_NOMEM; |
| 195489 | 195741 | } |
| @@ -196102,10 +196354,11 @@ | ||
| 196102 | 196354 | int rc = SQLITE_OK; |
| 196103 | 196355 | int bOk = 0; |
| 196104 | 196356 | |
| 196105 | 196357 | UNUSED_PARAMETER(isQuick); |
| 196106 | 196358 | rc = sqlite3Fts3IntegrityCheck(p, &bOk); |
| 196359 | + assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK ); | |
| 196107 | 196360 | assert( rc!=SQLITE_CORRUPT_VTAB ); |
| 196108 | 196361 | if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ |
| 196109 | 196362 | *pzErr = sqlite3_mprintf("unable to validate the inverted index for" |
| 196110 | 196363 | " FTS%d table %s.%s: %s", |
| 196111 | 196364 | p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); |
| @@ -202713,10 +202966,28 @@ | ||
| 202713 | 202966 | #define SQL_SELECT_MXLEVEL 36 |
| 202714 | 202967 | |
| 202715 | 202968 | #define SQL_SELECT_LEVEL_RANGE2 37 |
| 202716 | 202969 | #define SQL_UPDATE_LEVEL_IDX 38 |
| 202717 | 202970 | #define SQL_UPDATE_LEVEL 39 |
| 202971 | + | |
| 202972 | +/* | |
| 202973 | +** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL | |
| 202974 | +** is always set. | |
| 202975 | +*/ | |
| 202976 | +SQLITE_PRIVATE int sqlite3Fts3PrepareStmt( | |
| 202977 | + Fts3Table *p, /* Prepare for this connection */ | |
| 202978 | + const char *zSql, /* SQL to prepare */ | |
| 202979 | + int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */ | |
| 202980 | + int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */ | |
| 202981 | + sqlite3_stmt **pp /* OUT: Prepared statement */ | |
| 202982 | +){ | |
| 202983 | + int f = SQLITE_PREPARE_FROM_DDL | |
| 202984 | + |((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0) | |
| 202985 | + |(bPersist ? SQLITE_PREPARE_PERSISTENT : 0); | |
| 202986 | + | |
| 202987 | + return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL); | |
| 202988 | +} | |
| 202718 | 202989 | |
| 202719 | 202990 | /* |
| 202720 | 202991 | ** This function is used to obtain an SQLite prepared statement handle |
| 202721 | 202992 | ** for the statement identified by the second argument. If successful, |
| 202722 | 202993 | ** *pp is set to the requested statement handle and SQLITE_OK returned. |
| @@ -202839,24 +203110,24 @@ | ||
| 202839 | 203110 | assert( SizeofArray(azSql)==SizeofArray(p->aStmt) ); |
| 202840 | 203111 | assert( eStmt<SizeofArray(azSql) && eStmt>=0 ); |
| 202841 | 203112 | |
| 202842 | 203113 | pStmt = p->aStmt[eStmt]; |
| 202843 | 203114 | if( !pStmt ){ |
| 202844 | - int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; | |
| 203115 | + int bAllowVtab = 0; | |
| 202845 | 203116 | char *zSql; |
| 202846 | 203117 | if( eStmt==SQL_CONTENT_INSERT ){ |
| 202847 | 203118 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); |
| 202848 | 203119 | }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ |
| 202849 | - f &= ~SQLITE_PREPARE_NO_VTAB; | |
| 203120 | + bAllowVtab = 1; | |
| 202850 | 203121 | zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); |
| 202851 | 203122 | }else{ |
| 202852 | 203123 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); |
| 202853 | 203124 | } |
| 202854 | 203125 | if( !zSql ){ |
| 202855 | 203126 | rc = SQLITE_NOMEM; |
| 202856 | 203127 | }else{ |
| 202857 | - rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL); | |
| 203128 | + rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt); | |
| 202858 | 203129 | sqlite3_free(zSql); |
| 202859 | 203130 | assert( rc==SQLITE_OK || pStmt==0 ); |
| 202860 | 203131 | p->aStmt[eStmt] = pStmt; |
| 202861 | 203132 | } |
| 202862 | 203133 | } |
| @@ -206019,11 +206290,11 @@ | ||
| 206019 | 206290 | /* Compose and prepare an SQL statement to loop through the content table */ |
| 206020 | 206291 | char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); |
| 206021 | 206292 | if( !zSql ){ |
| 206022 | 206293 | rc = SQLITE_NOMEM; |
| 206023 | 206294 | }else{ |
| 206024 | - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); | |
| 206295 | + rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt); | |
| 206025 | 206296 | sqlite3_free(zSql); |
| 206026 | 206297 | } |
| 206027 | 206298 | |
| 206028 | 206299 | if( rc==SQLITE_OK ){ |
| 206029 | 206300 | sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3; |
| @@ -207772,11 +208043,11 @@ | ||
| 207772 | 208043 | |
| 207773 | 208044 | zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); |
| 207774 | 208045 | if( !zSql ){ |
| 207775 | 208046 | rc = SQLITE_NOMEM; |
| 207776 | 208047 | }else{ |
| 207777 | - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); | |
| 208048 | + rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt); | |
| 207778 | 208049 | sqlite3_free(zSql); |
| 207779 | 208050 | } |
| 207780 | 208051 | |
| 207781 | 208052 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 207782 | 208053 | i64 iDocid = sqlite3_column_int64(pStmt, 0); |
| @@ -211177,11 +211448,14 @@ | ||
| 211177 | 211448 | */ |
| 211178 | 211449 | #define JSON_JSON 0x01 /* Result is always JSON */ |
| 211179 | 211450 | #define JSON_SQL 0x02 /* Result is always SQL */ |
| 211180 | 211451 | #define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ |
| 211181 | 211452 | #define JSON_ISSET 0x04 /* json_set(), not json_insert() */ |
| 211182 | -#define JSON_BLOB 0x08 /* Use the BLOB output format */ | |
| 211453 | +#define JSON_AINS 0x08 /* json_array_insert(), not json_insert() */ | |
| 211454 | +#define JSON_BLOB 0x10 /* Use the BLOB output format */ | |
| 211455 | + | |
| 211456 | +#define JSON_INSERT_TYPE(X) (((X)&0xC)>>2) | |
| 211183 | 211457 | |
| 211184 | 211458 | |
| 211185 | 211459 | /* A parsed JSON value. Lifecycle: |
| 211186 | 211460 | ** |
| 211187 | 211461 | ** 1. JSON comes in and is parsed into a JSONB value in aBlob. The |
| @@ -211223,10 +211497,11 @@ | ||
| 211223 | 211497 | /* Allowed values for JsonParse.eEdit */ |
| 211224 | 211498 | #define JEDIT_DEL 1 /* Delete if exists */ |
| 211225 | 211499 | #define JEDIT_REPL 2 /* Overwrite if exists */ |
| 211226 | 211500 | #define JEDIT_INS 3 /* Insert if not exists */ |
| 211227 | 211501 | #define JEDIT_SET 4 /* Insert or overwrite */ |
| 211502 | +#define JEDIT_AINS 5 /* array_insert() */ | |
| 211228 | 211503 | |
| 211229 | 211504 | /* |
| 211230 | 211505 | ** Maximum nesting depth of JSON for this implementation. |
| 211231 | 211506 | ** |
| 211232 | 211507 | ** This limit is needed to avoid a stack overflow in the recursive |
| @@ -213719,11 +213994,12 @@ | ||
| 213719 | 213994 | /* |
| 213720 | 213995 | ** Error returns from jsonLookupStep() |
| 213721 | 213996 | */ |
| 213722 | 213997 | #define JSON_LOOKUP_ERROR 0xffffffff |
| 213723 | 213998 | #define JSON_LOOKUP_NOTFOUND 0xfffffffe |
| 213724 | -#define JSON_LOOKUP_PATHERROR 0xfffffffd | |
| 213999 | +#define JSON_LOOKUP_NOTARRAY 0xfffffffd | |
| 214000 | +#define JSON_LOOKUP_PATHERROR 0xfffffffc | |
| 213725 | 214001 | #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) |
| 213726 | 214002 | |
| 213727 | 214003 | /* Forward declaration */ |
| 213728 | 214004 | static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); |
| 213729 | 214005 | |
| @@ -213748,11 +214024,11 @@ | ||
| 213748 | 214024 | ** using the substructure. |
| 213749 | 214025 | */ |
| 213750 | 214026 | static u32 jsonCreateEditSubstructure( |
| 213751 | 214027 | JsonParse *pParse, /* The original JSONB that is being edited */ |
| 213752 | 214028 | JsonParse *pIns, /* Populate this with the blob data to insert */ |
| 213753 | - const char *zTail /* Tail of the path that determins substructure */ | |
| 214029 | + const char *zTail /* Tail of the path that determines substructure */ | |
| 213754 | 214030 | ){ |
| 213755 | 214031 | static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; |
| 213756 | 214032 | int rc; |
| 213757 | 214033 | memset(pIns, 0, sizeof(*pIns)); |
| 213758 | 214034 | pIns->db = pParse->db; |
| @@ -213783,13 +214059,13 @@ | ||
| 213783 | 214059 | ** label, before returning. |
| 213784 | 214060 | ** |
| 213785 | 214061 | ** Return one of the JSON_LOOKUP error codes if problems are seen. |
| 213786 | 214062 | ** |
| 213787 | 214063 | ** This routine will also modify the blob. If pParse->eEdit is one of |
| 213788 | -** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be | |
| 213789 | -** made to the selected value. If an edit is performed, then the return | |
| 213790 | -** value does not necessarily point to the select element. If an edit | |
| 214064 | +** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, JEDIT_SET, or JEDIT_AINS, then changes | |
| 214065 | +** might be made to the selected value. If an edit is performed, then the | |
| 214066 | +** return value does not necessarily point to the select element. If an edit | |
| 213791 | 214067 | ** is performed, the return value is only useful for detecting error |
| 213792 | 214068 | ** conditions. |
| 213793 | 214069 | */ |
| 213794 | 214070 | static u32 jsonLookupStep( |
| 213795 | 214071 | JsonParse *pParse, /* The JSON to search */ |
| @@ -213811,10 +214087,17 @@ | ||
| 213811 | 214087 | iRoot = iLabel; |
| 213812 | 214088 | } |
| 213813 | 214089 | jsonBlobEdit(pParse, iRoot, sz, 0, 0); |
| 213814 | 214090 | }else if( pParse->eEdit==JEDIT_INS ){ |
| 213815 | 214091 | /* Already exists, so json_insert() is a no-op */ |
| 214092 | + }else if( pParse->eEdit==JEDIT_AINS ){ | |
| 214093 | + /* json_array_insert() */ | |
| 214094 | + if( zPath[-1]!=']' ){ | |
| 214095 | + return JSON_LOOKUP_NOTARRAY; | |
| 214096 | + }else{ | |
| 214097 | + jsonBlobEdit(pParse, iRoot, 0, pParse->aIns, pParse->nIns); | |
| 214098 | + } | |
| 213816 | 214099 | }else{ |
| 213817 | 214100 | /* json_set() or json_replace() */ |
| 213818 | 214101 | jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); |
| 213819 | 214102 | } |
| 213820 | 214103 | } |
| @@ -213882,10 +214165,14 @@ | ||
| 213882 | 214165 | u32 nIns; /* Total bytes to insert (label+value) */ |
| 213883 | 214166 | JsonParse v; /* BLOB encoding of the value to be inserted */ |
| 213884 | 214167 | JsonParse ix; /* Header of the label to be inserted */ |
| 213885 | 214168 | testcase( pParse->eEdit==JEDIT_INS ); |
| 213886 | 214169 | testcase( pParse->eEdit==JEDIT_SET ); |
| 214170 | + testcase( pParse->eEdit==JEDIT_AINS ); | |
| 214171 | + if( pParse->eEdit==JEDIT_AINS && sqlite3_strglob("*]",&zPath[i])!=0 ){ | |
| 214172 | + return JSON_LOOKUP_NOTARRAY; | |
| 214173 | + } | |
| 213887 | 214174 | memset(&ix, 0, sizeof(ix)); |
| 213888 | 214175 | ix.db = pParse->db; |
| 213889 | 214176 | jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); |
| 213890 | 214177 | pParse->oom |= ix.oom; |
| 213891 | 214178 | rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); |
| @@ -213909,32 +214196,36 @@ | ||
| 213909 | 214196 | jsonParseReset(&v); |
| 213910 | 214197 | jsonParseReset(&ix); |
| 213911 | 214198 | return rc; |
| 213912 | 214199 | } |
| 213913 | 214200 | }else if( zPath[0]=='[' ){ |
| 214201 | + u64 kk = 0; | |
| 213914 | 214202 | x = pParse->aBlob[iRoot] & 0x0f; |
| 213915 | 214203 | if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; |
| 213916 | 214204 | n = jsonbPayloadSize(pParse, iRoot, &sz); |
| 213917 | - k = 0; | |
| 213918 | 214205 | i = 1; |
| 213919 | 214206 | while( sqlite3Isdigit(zPath[i]) ){ |
| 213920 | - k = k*10 + zPath[i] - '0'; | |
| 214207 | + if( kk<0xffffffff ) kk = kk*10 + zPath[i] - '0'; | |
| 214208 | + /* ^^^^^^^^^^--- Allow kk to be bigger than any JSON array so that | |
| 214209 | + ** we get NOTFOUND instead of PATHERROR, without overflowing kk. */ | |
| 213921 | 214210 | i++; |
| 213922 | 214211 | } |
| 213923 | 214212 | if( i<2 || zPath[i]!=']' ){ |
| 213924 | 214213 | if( zPath[1]=='#' ){ |
| 213925 | - k = jsonbArrayCount(pParse, iRoot); | |
| 214214 | + kk = jsonbArrayCount(pParse, iRoot); | |
| 213926 | 214215 | i = 2; |
| 213927 | 214216 | if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ |
| 213928 | - unsigned int nn = 0; | |
| 214217 | + u64 nn = 0; | |
| 213929 | 214218 | i = 3; |
| 213930 | 214219 | do{ |
| 213931 | - nn = nn*10 + zPath[i] - '0'; | |
| 214220 | + if( nn<0xffffffff ) nn = nn*10 + zPath[i] - '0'; | |
| 214221 | + /* ^^^^^^^^^^--- Allow nn to be bigger than any JSON array to | |
| 214222 | + ** get NOTFOUND instead of PATHERROR, without overflowing nn. */ | |
| 213932 | 214223 | i++; |
| 213933 | 214224 | }while( sqlite3Isdigit(zPath[i]) ); |
| 213934 | - if( nn>k ) return JSON_LOOKUP_NOTFOUND; | |
| 213935 | - k -= nn; | |
| 214225 | + if( nn>kk ) return JSON_LOOKUP_NOTFOUND; | |
| 214226 | + kk -= nn; | |
| 213936 | 214227 | } |
| 213937 | 214228 | if( zPath[i]!=']' ){ |
| 213938 | 214229 | return JSON_LOOKUP_PATHERROR; |
| 213939 | 214230 | } |
| 213940 | 214231 | }else{ |
| @@ -213942,25 +214233,26 @@ | ||
| 213942 | 214233 | } |
| 213943 | 214234 | } |
| 213944 | 214235 | j = iRoot+n; |
| 213945 | 214236 | iEnd = j+sz; |
| 213946 | 214237 | while( j<iEnd ){ |
| 213947 | - if( k==0 ){ | |
| 214238 | + if( kk==0 ){ | |
| 213948 | 214239 | rc = jsonLookupStep(pParse, j, &zPath[i+1], 0); |
| 213949 | 214240 | if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); |
| 213950 | 214241 | return rc; |
| 213951 | 214242 | } |
| 213952 | - k--; | |
| 214243 | + kk--; | |
| 213953 | 214244 | n = jsonbPayloadSize(pParse, j, &sz); |
| 213954 | 214245 | if( n==0 ) return JSON_LOOKUP_ERROR; |
| 213955 | 214246 | j += n+sz; |
| 213956 | 214247 | } |
| 213957 | 214248 | if( j>iEnd ) return JSON_LOOKUP_ERROR; |
| 213958 | - if( k>0 ) return JSON_LOOKUP_NOTFOUND; | |
| 214249 | + if( kk>0 ) return JSON_LOOKUP_NOTFOUND; | |
| 213959 | 214250 | if( pParse->eEdit>=JEDIT_INS ){ |
| 213960 | 214251 | JsonParse v; |
| 213961 | 214252 | testcase( pParse->eEdit==JEDIT_INS ); |
| 214253 | + testcase( pParse->eEdit==JEDIT_AINS ); | |
| 213962 | 214254 | testcase( pParse->eEdit==JEDIT_SET ); |
| 213963 | 214255 | rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); |
| 213964 | 214256 | if( !JSON_LOOKUP_ISERROR(rc) |
| 213965 | 214257 | && jsonBlobMakeEditable(pParse, v.nBlob) |
| 213966 | 214258 | ){ |
| @@ -214281,13 +214573,19 @@ | ||
| 214281 | 214573 | ** If ctx is not NULL then push the error message into ctx and return NULL. |
| 214282 | 214574 | ** If ctx is NULL, then return the text of the error message. |
| 214283 | 214575 | */ |
| 214284 | 214576 | static char *jsonBadPathError( |
| 214285 | 214577 | sqlite3_context *ctx, /* The function call containing the error */ |
| 214286 | - const char *zPath /* The path with the problem */ | |
| 214578 | + const char *zPath, /* The path with the problem */ | |
| 214579 | + int rc /* Maybe JSON_LOOKUP_NOTARRAY */ | |
| 214287 | 214580 | ){ |
| 214288 | - char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); | |
| 214581 | + char *zMsg; | |
| 214582 | + if( rc==(int)JSON_LOOKUP_NOTARRAY ){ | |
| 214583 | + zMsg = sqlite3_mprintf("not an array element: %Q", zPath); | |
| 214584 | + }else{ | |
| 214585 | + zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); | |
| 214586 | + } | |
| 214289 | 214587 | if( ctx==0 ) return zMsg; |
| 214290 | 214588 | if( zMsg ){ |
| 214291 | 214589 | sqlite3_result_error(ctx, zMsg, -1); |
| 214292 | 214590 | sqlite3_free(zMsg); |
| 214293 | 214591 | }else{ |
| @@ -214300,17 +214598,17 @@ | ||
| 214300 | 214598 | ** arguments come in pairs where each pair contains a JSON path and |
| 214301 | 214599 | ** content to insert or set at that patch. Do the updates |
| 214302 | 214600 | ** and return the result. |
| 214303 | 214601 | ** |
| 214304 | 214602 | ** The specific operation is determined by eEdit, which can be one |
| 214305 | -** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. | |
| 214603 | +** of JEDIT_INS, JEDIT_REPL, JEDIT_SET, or JEDIT_AINS. | |
| 214306 | 214604 | */ |
| 214307 | 214605 | static void jsonInsertIntoBlob( |
| 214308 | 214606 | sqlite3_context *ctx, |
| 214309 | 214607 | int argc, |
| 214310 | 214608 | sqlite3_value **argv, |
| 214311 | - int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ | |
| 214609 | + int eEdit /* JEDIT_INS, JEDIT_REPL, JEDIT_SET, JEDIT_AINS */ | |
| 214312 | 214610 | ){ |
| 214313 | 214611 | int i; |
| 214314 | 214612 | u32 rc = 0; |
| 214315 | 214613 | const char *zPath = 0; |
| 214316 | 214614 | int flgs; |
| @@ -214358,11 +214656,11 @@ | ||
| 214358 | 214656 | jsonInsertIntoBlob_patherror: |
| 214359 | 214657 | jsonParseFree(p); |
| 214360 | 214658 | if( rc==JSON_LOOKUP_ERROR ){ |
| 214361 | 214659 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 214362 | 214660 | }else{ |
| 214363 | - jsonBadPathError(ctx, zPath); | |
| 214661 | + jsonBadPathError(ctx, zPath, rc); | |
| 214364 | 214662 | } |
| 214365 | 214663 | return; |
| 214366 | 214664 | } |
| 214367 | 214665 | |
| 214368 | 214666 | /* |
| @@ -214800,11 +215098,11 @@ | ||
| 214800 | 215098 | i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); |
| 214801 | 215099 | if( JSON_LOOKUP_ISERROR(i) ){ |
| 214802 | 215100 | if( i==JSON_LOOKUP_NOTFOUND ){ |
| 214803 | 215101 | /* no-op */ |
| 214804 | 215102 | }else if( i==JSON_LOOKUP_PATHERROR ){ |
| 214805 | - jsonBadPathError(ctx, zPath); | |
| 215103 | + jsonBadPathError(ctx, zPath, 0); | |
| 214806 | 215104 | }else{ |
| 214807 | 215105 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 214808 | 215106 | } |
| 214809 | 215107 | eErr = 1; |
| 214810 | 215108 | i = 0; |
| @@ -214905,11 +215203,11 @@ | ||
| 214905 | 215203 | } |
| 214906 | 215204 | jsonStringTerminate(&jx); |
| 214907 | 215205 | j = jsonLookupStep(p, 0, jx.zBuf, 0); |
| 214908 | 215206 | jsonStringReset(&jx); |
| 214909 | 215207 | }else{ |
| 214910 | - jsonBadPathError(ctx, zPath); | |
| 215208 | + jsonBadPathError(ctx, zPath, 0); | |
| 214911 | 215209 | goto json_extract_error; |
| 214912 | 215210 | } |
| 214913 | 215211 | if( j<p->nBlob ){ |
| 214914 | 215212 | if( argc==2 ){ |
| 214915 | 215213 | if( flags & JSON_JSON ){ |
| @@ -214940,11 +215238,11 @@ | ||
| 214940 | 215238 | } |
| 214941 | 215239 | }else if( j==JSON_LOOKUP_ERROR ){ |
| 214942 | 215240 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 214943 | 215241 | goto json_extract_error; |
| 214944 | 215242 | }else{ |
| 214945 | - jsonBadPathError(ctx, zPath); | |
| 215243 | + jsonBadPathError(ctx, zPath, 0); | |
| 214946 | 215244 | goto json_extract_error; |
| 214947 | 215245 | } |
| 214948 | 215246 | } |
| 214949 | 215247 | if( argc>2 ){ |
| 214950 | 215248 | jsonAppendChar(&jx, ']'); |
| @@ -215269,11 +215567,11 @@ | ||
| 215269 | 215567 | rc = jsonLookupStep(p, 0, zPath+1, 0); |
| 215270 | 215568 | if( JSON_LOOKUP_ISERROR(rc) ){ |
| 215271 | 215569 | if( rc==JSON_LOOKUP_NOTFOUND ){ |
| 215272 | 215570 | continue; /* No-op */ |
| 215273 | 215571 | }else if( rc==JSON_LOOKUP_PATHERROR ){ |
| 215274 | - jsonBadPathError(ctx, zPath); | |
| 215572 | + jsonBadPathError(ctx, zPath, rc); | |
| 215275 | 215573 | }else{ |
| 215276 | 215574 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215277 | 215575 | } |
| 215278 | 215576 | goto json_remove_done; |
| 215279 | 215577 | } |
| @@ -215281,11 +215579,11 @@ | ||
| 215281 | 215579 | jsonReturnParse(ctx, p); |
| 215282 | 215580 | jsonParseFree(p); |
| 215283 | 215581 | return; |
| 215284 | 215582 | |
| 215285 | 215583 | json_remove_patherror: |
| 215286 | - jsonBadPathError(ctx, zPath); | |
| 215584 | + jsonBadPathError(ctx, zPath, 0); | |
| 215287 | 215585 | |
| 215288 | 215586 | json_remove_done: |
| 215289 | 215587 | jsonParseFree(p); |
| 215290 | 215588 | return; |
| 215291 | 215589 | } |
| @@ -215325,20 +215623,22 @@ | ||
| 215325 | 215623 | static void jsonSetFunc( |
| 215326 | 215624 | sqlite3_context *ctx, |
| 215327 | 215625 | int argc, |
| 215328 | 215626 | sqlite3_value **argv |
| 215329 | 215627 | ){ |
| 215330 | - | |
| 215331 | 215628 | int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); |
| 215332 | - int bIsSet = (flags&JSON_ISSET)!=0; | |
| 215629 | + int eInsType = JSON_INSERT_TYPE(flags); | |
| 215630 | + static const char *azInsType[] = { "insert", "set", "array_insert" }; | |
| 215631 | + static const u8 aEditType[] = { JEDIT_INS, JEDIT_SET, JEDIT_AINS }; | |
| 215333 | 215632 | |
| 215334 | 215633 | if( argc<1 ) return; |
| 215634 | + assert( eInsType>=0 && eInsType<=2 ); | |
| 215335 | 215635 | if( (argc&1)==0 ) { |
| 215336 | - jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); | |
| 215636 | + jsonWrongNumArgs(ctx, azInsType[eInsType]); | |
| 215337 | 215637 | return; |
| 215338 | 215638 | } |
| 215339 | - jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); | |
| 215639 | + jsonInsertIntoBlob(ctx, argc, argv, aEditType[eInsType]); | |
| 215340 | 215640 | } |
| 215341 | 215641 | |
| 215342 | 215642 | /* |
| 215343 | 215643 | ** json_type(JSON) |
| 215344 | 215644 | ** json_type(JSON, PATH) |
| @@ -215359,19 +215659,19 @@ | ||
| 215359 | 215659 | if( p==0 ) return; |
| 215360 | 215660 | if( argc==2 ){ |
| 215361 | 215661 | zPath = (const char*)sqlite3_value_text(argv[1]); |
| 215362 | 215662 | if( zPath==0 ) goto json_type_done; |
| 215363 | 215663 | if( zPath[0]!='$' ){ |
| 215364 | - jsonBadPathError(ctx, zPath); | |
| 215664 | + jsonBadPathError(ctx, zPath, 0); | |
| 215365 | 215665 | goto json_type_done; |
| 215366 | 215666 | } |
| 215367 | 215667 | i = jsonLookupStep(p, 0, zPath+1, 0); |
| 215368 | 215668 | if( JSON_LOOKUP_ISERROR(i) ){ |
| 215369 | 215669 | if( i==JSON_LOOKUP_NOTFOUND ){ |
| 215370 | 215670 | /* no-op */ |
| 215371 | 215671 | }else if( i==JSON_LOOKUP_PATHERROR ){ |
| 215372 | - jsonBadPathError(ctx, zPath); | |
| 215672 | + jsonBadPathError(ctx, zPath, 0); | |
| 215373 | 215673 | }else{ |
| 215374 | 215674 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215375 | 215675 | } |
| 215376 | 215676 | goto json_type_done; |
| 215377 | 215677 | } |
| @@ -215623,16 +215923,15 @@ | ||
| 215623 | 215923 | jsonAppendSqlValue(pStr, argv[0]); |
| 215624 | 215924 | } |
| 215625 | 215925 | } |
| 215626 | 215926 | static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ |
| 215627 | 215927 | JsonString *pStr; |
| 215928 | + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); | |
| 215628 | 215929 | pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); |
| 215629 | 215930 | if( pStr ){ |
| 215630 | - int flags; | |
| 215631 | 215931 | pStr->pCtx = ctx; |
| 215632 | 215932 | jsonAppendChar(pStr, ']'); |
| 215633 | - flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); | |
| 215634 | 215933 | if( pStr->eErr ){ |
| 215635 | 215934 | jsonReturnString(pStr, 0, 0); |
| 215636 | 215935 | return; |
| 215637 | 215936 | }else if( flags & JSON_BLOB ){ |
| 215638 | 215937 | jsonReturnStringAsBlob(pStr); |
| @@ -215649,10 +215948,13 @@ | ||
| 215649 | 215948 | pStr->bStatic = 1; |
| 215650 | 215949 | }else{ |
| 215651 | 215950 | sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); |
| 215652 | 215951 | jsonStringTrimOneChar(pStr); |
| 215653 | 215952 | } |
| 215953 | + }else if( flags & JSON_BLOB ){ | |
| 215954 | + static const u8 emptyArray = 0x0b; | |
| 215955 | + sqlite3_result_blob(ctx, &emptyArray, 1, SQLITE_STATIC); | |
| 215654 | 215956 | }else{ |
| 215655 | 215957 | sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); |
| 215656 | 215958 | } |
| 215657 | 215959 | sqlite3_result_subtype(ctx, JSON_SUBTYPE); |
| 215658 | 215960 | } |
| @@ -215745,16 +216047,15 @@ | ||
| 215745 | 216047 | } |
| 215746 | 216048 | } |
| 215747 | 216049 | } |
| 215748 | 216050 | static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ |
| 215749 | 216051 | JsonString *pStr; |
| 216052 | + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); | |
| 215750 | 216053 | pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); |
| 215751 | 216054 | if( pStr ){ |
| 215752 | - int flags; | |
| 215753 | 216055 | jsonAppendChar(pStr, '}'); |
| 215754 | 216056 | pStr->pCtx = ctx; |
| 215755 | - flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); | |
| 215756 | 216057 | if( pStr->eErr ){ |
| 215757 | 216058 | jsonReturnString(pStr, 0, 0); |
| 215758 | 216059 | return; |
| 215759 | 216060 | }else if( flags & JSON_BLOB ){ |
| 215760 | 216061 | jsonReturnStringAsBlob(pStr); |
| @@ -215771,10 +216072,13 @@ | ||
| 215771 | 216072 | pStr->bStatic = 1; |
| 215772 | 216073 | }else{ |
| 215773 | 216074 | sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); |
| 215774 | 216075 | jsonStringTrimOneChar(pStr); |
| 215775 | 216076 | } |
| 216077 | + }else if( flags & JSON_BLOB ){ | |
| 216078 | + static const unsigned char emptyObject = 0x0c; | |
| 216079 | + sqlite3_result_blob(ctx, &emptyObject, 1, SQLITE_STATIC); | |
| 215776 | 216080 | }else{ |
| 215777 | 216081 | sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); |
| 215778 | 216082 | } |
| 215779 | 216083 | sqlite3_result_subtype(ctx, JSON_SUBTYPE); |
| 215780 | 216084 | } |
| @@ -216271,11 +216575,11 @@ | ||
| 216271 | 216575 | if( idxNum==3 ){ |
| 216272 | 216576 | zRoot = (const char*)sqlite3_value_text(argv[1]); |
| 216273 | 216577 | if( zRoot==0 ) return SQLITE_OK; |
| 216274 | 216578 | if( zRoot[0]!='$' ){ |
| 216275 | 216579 | sqlite3_free(cur->pVtab->zErrMsg); |
| 216276 | - cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); | |
| 216580 | + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); | |
| 216277 | 216581 | jsonEachCursorReset(p); |
| 216278 | 216582 | return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 216279 | 216583 | } |
| 216280 | 216584 | p->nRoot = sqlite3Strlen30(zRoot); |
| 216281 | 216585 | if( zRoot[1]==0 ){ |
| @@ -216289,11 +216593,11 @@ | ||
| 216289 | 216593 | p->eType = 0; |
| 216290 | 216594 | p->iEnd = 0; |
| 216291 | 216595 | return SQLITE_OK; |
| 216292 | 216596 | } |
| 216293 | 216597 | sqlite3_free(cur->pVtab->zErrMsg); |
| 216294 | - cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); | |
| 216598 | + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); | |
| 216295 | 216599 | jsonEachCursorReset(p); |
| 216296 | 216600 | return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 216297 | 216601 | } |
| 216298 | 216602 | if( p->sParse.iLabel ){ |
| 216299 | 216603 | p->i = p->sParse.iLabel; |
| @@ -216379,10 +216683,12 @@ | ||
| 216379 | 216683 | /* | | | | | | */ |
| 216380 | 216684 | JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), |
| 216381 | 216685 | JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), |
| 216382 | 216686 | JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), |
| 216383 | 216687 | JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), |
| 216688 | + JFUNCTION(json_array_insert, -1,1,1, 1,0,JSON_AINS, jsonSetFunc), | |
| 216689 | + JFUNCTION(jsonb_array_insert,-1,1,0, 1,1,JSON_AINS, jsonSetFunc), | |
| 216384 | 216690 | JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), |
| 216385 | 216691 | JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), |
| 216386 | 216692 | JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), |
| 216387 | 216693 | JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc), |
| 216388 | 216694 | JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc), |
| @@ -220226,11 +220532,11 @@ | ||
| 220226 | 220532 | tree.nBytesPerCell = 8 + 8 * tree.nDim; |
| 220227 | 220533 | node.zData = (u8 *)sqlite3_value_blob(apArg[1]); |
| 220228 | 220534 | if( node.zData==0 ) return; |
| 220229 | 220535 | nData = sqlite3_value_bytes(apArg[1]); |
| 220230 | 220536 | if( nData<4 ) return; |
| 220231 | - if( nData<NCELL(&node)*tree.nBytesPerCell ) return; | |
| 220537 | + if( nData<4+NCELL(&node)*tree.nBytesPerCell ) return; | |
| 220232 | 220538 | |
| 220233 | 220539 | pOut = sqlite3_str_new(0); |
| 220234 | 220540 | for(ii=0; ii<NCELL(&node); ii++){ |
| 220235 | 220541 | RtreeCell cell; |
| 220236 | 220542 | int jj; |
| @@ -231218,10 +231524,11 @@ | ||
| 231218 | 231524 | struct carray_bind { |
| 231219 | 231525 | void *aData; /* The data */ |
| 231220 | 231526 | int nData; /* Number of elements */ |
| 231221 | 231527 | int mFlags; /* Control flags */ |
| 231222 | 231528 | void (*xDel)(void*); /* Destructor for aData */ |
| 231529 | + void *pDel; /* Alternative argument to xDel() */ | |
| 231223 | 231530 | }; |
| 231224 | 231531 | |
| 231225 | 231532 | |
| 231226 | 231533 | /* carray_cursor is a subclass of sqlite3_vtab_cursor which will |
| 231227 | 231534 | ** serve as the underlying representation of a cursor that scans |
| @@ -231550,26 +231857,38 @@ | ||
| 231550 | 231857 | ** Destructor for the carray_bind object |
| 231551 | 231858 | */ |
| 231552 | 231859 | static void carrayBindDel(void *pPtr){ |
| 231553 | 231860 | carray_bind *p = (carray_bind*)pPtr; |
| 231554 | 231861 | if( p->xDel!=SQLITE_STATIC ){ |
| 231555 | - p->xDel(p->aData); | |
| 231862 | + p->xDel(p->pDel); | |
| 231556 | 231863 | } |
| 231557 | 231864 | sqlite3_free(p); |
| 231558 | 231865 | } |
| 231559 | 231866 | |
| 231560 | 231867 | /* |
| 231561 | 231868 | ** Invoke this interface in order to bind to the single-argument |
| 231562 | 231869 | ** version of CARRAY(). |
| 231870 | +** | |
| 231871 | +** pStmt The prepared statement to which to bind | |
| 231872 | +** idx The index of the parameter of pStmt to which to bind | |
| 231873 | +** aData The data to be bound | |
| 231874 | +** nData The number of elements in aData | |
| 231875 | +** mFlags One of SQLITE_CARRAY_xxxx indicating datatype of aData | |
| 231876 | +** xDestroy Destructor for pDestroy or aData if pDestroy==NULL. | |
| 231877 | +** pDestroy Invoke xDestroy on this pointer if not NULL | |
| 231878 | +** | |
| 231879 | +** The destructor is called pDestroy if pDestroy!=NULL, or against | |
| 231880 | +** aData if pDestroy==NULL. | |
| 231563 | 231881 | */ |
| 231564 | -SQLITE_API int sqlite3_carray_bind( | |
| 231882 | +SQLITE_API int sqlite3_carray_bind_v2( | |
| 231565 | 231883 | sqlite3_stmt *pStmt, |
| 231566 | 231884 | int idx, |
| 231567 | 231885 | void *aData, |
| 231568 | 231886 | int nData, |
| 231569 | 231887 | int mFlags, |
| 231570 | - void (*xDestroy)(void*) | |
| 231888 | + void (*xDestroy)(void*), | |
| 231889 | + void *pDestroy | |
| 231571 | 231890 | ){ |
| 231572 | 231891 | carray_bind *pNew = 0; |
| 231573 | 231892 | int i; |
| 231574 | 231893 | int rc = SQLITE_OK; |
| 231575 | 231894 | |
| @@ -231642,23 +231961,41 @@ | ||
| 231642 | 231961 | } |
| 231643 | 231962 | }else{ |
| 231644 | 231963 | memcpy(pNew->aData, aData, sz); |
| 231645 | 231964 | } |
| 231646 | 231965 | pNew->xDel = sqlite3_free; |
| 231966 | + pNew->pDel = pNew->aData; | |
| 231647 | 231967 | }else{ |
| 231648 | 231968 | pNew->aData = aData; |
| 231649 | 231969 | pNew->xDel = xDestroy; |
| 231970 | + pNew->pDel = pDestroy; | |
| 231650 | 231971 | } |
| 231651 | 231972 | return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel); |
| 231652 | 231973 | |
| 231653 | 231974 | carray_bind_error: |
| 231654 | 231975 | if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){ |
| 231655 | - xDestroy(aData); | |
| 231976 | + xDestroy(pDestroy); | |
| 231656 | 231977 | } |
| 231657 | 231978 | sqlite3_free(pNew); |
| 231658 | 231979 | return rc; |
| 231659 | 231980 | } |
| 231981 | + | |
| 231982 | +/* | |
| 231983 | +** Invoke this interface in order to bind to the single-argument | |
| 231984 | +** version of CARRAY(). Same as sqlite3_carray_bind_v2() with the | |
| 231985 | +** pDestroy parameter set to NULL. | |
| 231986 | +*/ | |
| 231987 | +SQLITE_API int sqlite3_carray_bind( | |
| 231988 | + sqlite3_stmt *pStmt, | |
| 231989 | + int idx, | |
| 231990 | + void *aData, | |
| 231991 | + int nData, | |
| 231992 | + int mFlags, | |
| 231993 | + void (*xDestroy)(void*) | |
| 231994 | +){ | |
| 231995 | + return sqlite3_carray_bind_v2(pStmt,idx,aData,nData,mFlags,xDestroy,aData); | |
| 231996 | +} | |
| 231660 | 231997 | |
| 231661 | 231998 | /* |
| 231662 | 231999 | ** Invoke this routine to register the carray() function. |
| 231663 | 232000 | */ |
| 231664 | 232001 | SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3 *db){ |
| @@ -232017,10 +232354,24 @@ | ||
| 232017 | 232354 | ** bytes read. |
| 232018 | 232355 | */ |
| 232019 | 232356 | static int sessionVarintGet(const u8 *aBuf, int *piVal){ |
| 232020 | 232357 | return getVarint32(aBuf, *piVal); |
| 232021 | 232358 | } |
| 232359 | + | |
| 232360 | +/* | |
| 232361 | +** Read a varint value from buffer aBuf[], size nBuf bytes, into *piVal. | |
| 232362 | +** Return the number of bytes read. | |
| 232363 | +*/ | |
| 232364 | +static int sessionVarintGetSafe(const u8 *aBuf, int nBuf, int *piVal){ | |
| 232365 | + u8 aCopy[5]; | |
| 232366 | + const u8 *aRead = aBuf; | |
| 232367 | + if( nBuf<5 ){ | |
| 232368 | + memcpy(aCopy, aBuf, nBuf); | |
| 232369 | + aRead = aCopy; | |
| 232370 | + } | |
| 232371 | + return getVarint32(aRead, *piVal); | |
| 232372 | +} | |
| 232022 | 232373 | |
| 232023 | 232374 | /* Load an unaligned and unsigned 32-bit integer */ |
| 232024 | 232375 | #define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) |
| 232025 | 232376 | |
| 232026 | 232377 | /* |
| @@ -232312,31 +232663,31 @@ | ||
| 232312 | 232663 | for(i=0; i<pTab->nCol; i++){ |
| 232313 | 232664 | int eType = *a; |
| 232314 | 232665 | int isPK = pTab->abPK[i]; |
| 232315 | 232666 | if( bPkOnly && isPK==0 ) continue; |
| 232316 | 232667 | |
| 232317 | - /* It is not possible for eType to be SQLITE_NULL here. The session | |
| 232318 | - ** module does not record changes for rows with NULL values stored in | |
| 232319 | - ** primary key columns. */ | |
| 232320 | 232668 | assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT |
| 232321 | 232669 | || eType==SQLITE_TEXT || eType==SQLITE_BLOB |
| 232322 | 232670 | || eType==SQLITE_NULL || eType==0 |
| 232323 | 232671 | ); |
| 232324 | - assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) ); | |
| 232325 | 232672 | |
| 232326 | 232673 | if( isPK ){ |
| 232327 | 232674 | a++; |
| 232328 | 232675 | h = sessionHashAppendType(h, eType); |
| 232329 | 232676 | if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ |
| 232330 | 232677 | h = sessionHashAppendI64(h, sessionGetI64(a)); |
| 232331 | 232678 | a += 8; |
| 232332 | - }else{ | |
| 232679 | + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ | |
| 232333 | 232680 | int n; |
| 232334 | 232681 | a += sessionVarintGet(a, &n); |
| 232335 | 232682 | h = sessionHashAppendBlob(h, n, a); |
| 232336 | 232683 | a += n; |
| 232337 | 232684 | } |
| 232685 | + /* It should not be possible for eType to be SQLITE_NULL or 0x00 here, | |
| 232686 | + ** as the session module does not record changes for rows with NULL | |
| 232687 | + ** values stored in primary key columns. But a corrupt changesets | |
| 232688 | + ** may contain such a value. */ | |
| 232338 | 232689 | }else{ |
| 232339 | 232690 | a += sessionSerialLen(a); |
| 232340 | 232691 | } |
| 232341 | 232692 | } |
| 232342 | 232693 | return (h % nBucket); |
| @@ -234741,14 +235092,17 @@ | ||
| 234741 | 235092 | *pnChangeset = 0; |
| 234742 | 235093 | *ppChangeset = 0; |
| 234743 | 235094 | } |
| 234744 | 235095 | |
| 234745 | 235096 | if( pSession->rc ) return pSession->rc; |
| 234746 | - rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0); | |
| 234747 | - if( rc!=SQLITE_OK ) return rc; | |
| 234748 | 235097 | |
| 234749 | 235098 | sqlite3_mutex_enter(sqlite3_db_mutex(db)); |
| 235099 | + rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0); | |
| 235100 | + if( rc!=SQLITE_OK ){ | |
| 235101 | + sqlite3_mutex_leave(sqlite3_db_mutex(db)); | |
| 235102 | + return rc; | |
| 235103 | + } | |
| 234750 | 235104 | |
| 234751 | 235105 | for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ |
| 234752 | 235106 | if( pTab->nEntry ){ |
| 234753 | 235107 | const char *zName = pTab->zName; |
| 234754 | 235108 | int i; /* Used to iterate through hash buckets */ |
| @@ -235227,11 +235581,12 @@ | ||
| 235227 | 235581 | |
| 235228 | 235582 | if( rc==SQLITE_OK ){ |
| 235229 | 235583 | u8 *aVal = &pIn->aData[pIn->iNext]; |
| 235230 | 235584 | if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ |
| 235231 | 235585 | int nByte; |
| 235232 | - pIn->iNext += sessionVarintGet(aVal, &nByte); | |
| 235586 | + int nRem = pIn->nData - pIn->iNext; | |
| 235587 | + pIn->iNext += sessionVarintGetSafe(aVal, nRem, &nByte); | |
| 235233 | 235588 | rc = sessionInputBuffer(pIn, nByte); |
| 235234 | 235589 | if( rc==SQLITE_OK ){ |
| 235235 | 235590 | if( nByte<0 || nByte>pIn->nData-pIn->iNext ){ |
| 235236 | 235591 | rc = SQLITE_CORRUPT_BKPT; |
| 235237 | 235592 | }else{ |
| @@ -235280,11 +235635,12 @@ | ||
| 235280 | 235635 | int nCol = 0; |
| 235281 | 235636 | int nRead = 0; |
| 235282 | 235637 | |
| 235283 | 235638 | rc = sessionInputBuffer(pIn, 9); |
| 235284 | 235639 | if( rc==SQLITE_OK ){ |
| 235285 | - nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol); | |
| 235640 | + int nBuf = pIn->nData - pIn->iNext; | |
| 235641 | + nRead += sessionVarintGetSafe(&pIn->aData[pIn->iNext], nBuf, &nCol); | |
| 235286 | 235642 | /* The hard upper limit for the number of columns in an SQLite |
| 235287 | 235643 | ** database table is, according to sqliteLimit.h, 32676. So |
| 235288 | 235644 | ** consider any table-header that purports to have more than 65536 |
| 235289 | 235645 | ** columns to be corrupt. This is convenient because otherwise, |
| 235290 | 235646 | ** if the (nCol>65536) condition below were omitted, a sufficiently |
| @@ -235300,12 +235656,19 @@ | ||
| 235300 | 235656 | |
| 235301 | 235657 | while( rc==SQLITE_OK ){ |
| 235302 | 235658 | while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){ |
| 235303 | 235659 | nRead++; |
| 235304 | 235660 | } |
| 235661 | + | |
| 235662 | + /* Break out of the loop if if the nul-terminator byte has been found. | |
| 235663 | + ** Otherwise, read some more input data and keep seeking. If there is | |
| 235664 | + ** no more input data, consider the changeset corrupt. */ | |
| 235305 | 235665 | if( (pIn->iNext + nRead)<pIn->nData ) break; |
| 235306 | 235666 | rc = sessionInputBuffer(pIn, nRead + 100); |
| 235667 | + if( rc==SQLITE_OK && (pIn->iNext + nRead)>=pIn->nData ){ | |
| 235668 | + rc = SQLITE_CORRUPT_BKPT; | |
| 235669 | + } | |
| 235307 | 235670 | } |
| 235308 | 235671 | *pnByte = nRead+1; |
| 235309 | 235672 | return rc; |
| 235310 | 235673 | } |
| 235311 | 235674 | |
| @@ -235433,14 +235796,14 @@ | ||
| 235433 | 235796 | sqlite3ValueFree(p->apValue[i]); |
| 235434 | 235797 | } |
| 235435 | 235798 | memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); |
| 235436 | 235799 | } |
| 235437 | 235800 | |
| 235438 | - /* Make sure the buffer contains at least 10 bytes of input data, or all | |
| 235439 | - ** remaining data if there are less than 10 bytes available. This is | |
| 235440 | - ** sufficient either for the 'T' or 'P' byte and the varint that follows | |
| 235441 | - ** it, or for the two single byte values otherwise. */ | |
| 235801 | + /* Make sure the buffer contains at least 2 bytes of input data, or all | |
| 235802 | + ** remaining data if there are less than 2 bytes available. This is | |
| 235803 | + ** sufficient either for the 'T' or 'P' byte that begins a new table, | |
| 235804 | + ** or for the "op" and "bIndirect" single bytes otherwise. */ | |
| 235442 | 235805 | p->rc = sessionInputBuffer(&p->in, 2); |
| 235443 | 235806 | if( p->rc!=SQLITE_OK ) return p->rc; |
| 235444 | 235807 | |
| 235445 | 235808 | p->in.iCurrent = p->in.iNext; |
| 235446 | 235809 | sessionDiscardData(&p->in); |
| @@ -235466,15 +235829,17 @@ | ||
| 235466 | 235829 | ** corrupt changeset. */ |
| 235467 | 235830 | assert( p->in.iNext==1 || p->zTab ); |
| 235468 | 235831 | return (p->rc = SQLITE_CORRUPT_BKPT); |
| 235469 | 235832 | } |
| 235470 | 235833 | |
| 235471 | - p->op = op; | |
| 235472 | - p->bIndirect = p->in.aData[p->in.iNext++]; | |
| 235473 | - if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ | |
| 235834 | + if( (op!=SQLITE_UPDATE && op!=SQLITE_DELETE && op!=SQLITE_INSERT) | |
| 235835 | + || (p->in.iNext>=p->in.nData) | |
| 235836 | + ){ | |
| 235474 | 235837 | return (p->rc = SQLITE_CORRUPT_BKPT); |
| 235475 | 235838 | } |
| 235839 | + p->op = op; | |
| 235840 | + p->bIndirect = p->in.aData[p->in.iNext++]; | |
| 235476 | 235841 | |
| 235477 | 235842 | if( paRec ){ |
| 235478 | 235843 | int nVal; /* Number of values to buffer */ |
| 235479 | 235844 | if( p->bPatchset==0 && op==SQLITE_UPDATE ){ |
| 235480 | 235845 | nVal = p->nCol * 2; |
| @@ -239307,11 +239672,17 @@ | ||
| 239307 | 239672 | # define FLEXARRAY |
| 239308 | 239673 | #else |
| 239309 | 239674 | # define FLEXARRAY 1 |
| 239310 | 239675 | #endif |
| 239311 | 239676 | |
| 239312 | -#endif | |
| 239677 | +#endif /* SQLITE_AMALGAMATION */ | |
| 239678 | + | |
| 239679 | +/* | |
| 239680 | +** Constants for the largest and smallest possible 32-bit signed integers. | |
| 239681 | +*/ | |
| 239682 | +# define LARGEST_INT32 ((int)(0x7fffffff)) | |
| 239683 | +# define SMALLEST_INT32 ((int)((-1) - LARGEST_INT32)) | |
| 239313 | 239684 | |
| 239314 | 239685 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 239315 | 239686 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 239316 | 239687 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| 239317 | 239688 | #define FTS5_MAX_TOKEN_SIZE 32768 |
| @@ -253198,11 +253569,11 @@ | ||
| 253198 | 253569 | ){ |
| 253199 | 253570 | const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); |
| 253200 | 253571 | int iSegid = pSeg->pSeg->iSegid; |
| 253201 | 253572 | u8 *aPg = pSeg->pLeaf->p; |
| 253202 | 253573 | int nPg = pSeg->pLeaf->nn; |
| 253203 | - int iPgIdx = pSeg->pLeaf->szLeaf; | |
| 253574 | + int iPgIdx = pSeg->pLeaf->szLeaf; /* Offset of page footer */ | |
| 253204 | 253575 | |
| 253205 | 253576 | u64 iDelta = 0; |
| 253206 | 253577 | int iNextOff = 0; |
| 253207 | 253578 | int iOff = 0; |
| 253208 | 253579 | int nIdx = 0; |
| @@ -253277,11 +253648,11 @@ | ||
| 253277 | 253648 | iStart = iSOP + (nPos/2); |
| 253278 | 253649 | iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); |
| 253279 | 253650 | iSOP += fts5GetVarint32(&aPg[iSOP], nPos); |
| 253280 | 253651 | } |
| 253281 | 253652 | assert_nc( iSOP==pSeg->iLeafOffset ); |
| 253282 | - iNextOff = pSeg->iLeafOffset + pSeg->nPos; | |
| 253653 | + iNextOff = iSOP + pSeg->nPos; | |
| 253283 | 253654 | } |
| 253284 | 253655 | } |
| 253285 | 253656 | |
| 253286 | 253657 | iOff = iStart; |
| 253287 | 253658 | |
| @@ -253357,35 +253728,35 @@ | ||
| 253357 | 253728 | if( iNextOff!=iPgIdx ){ |
| 253358 | 253729 | /* This is the only position-list associated with the term, and there |
| 253359 | 253730 | ** is another term following it on this page. So the subsequent term |
| 253360 | 253731 | ** needs to be moved to replace the term associated with the entry |
| 253361 | 253732 | ** being removed. */ |
| 253362 | - int nPrefix = 0; | |
| 253363 | - int nSuffix = 0; | |
| 253364 | - int nPrefix2 = 0; | |
| 253365 | - int nSuffix2 = 0; | |
| 253733 | + u64 nPrefix = 0; | |
| 253734 | + u64 nSuffix = 0; | |
| 253735 | + u64 nPrefix2 = 0; | |
| 253736 | + u64 nSuffix2 = 0; | |
| 253366 | 253737 | |
| 253367 | 253738 | iDelKeyOff = iNextOff; |
| 253368 | - iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); | |
| 253369 | - iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); | |
| 253739 | + iNextOff += fts5GetVarint(&aPg[iNextOff], &nPrefix2); | |
| 253740 | + iNextOff += fts5GetVarint(&aPg[iNextOff], &nSuffix2); | |
| 253370 | 253741 | |
| 253371 | 253742 | if( iKey!=1 ){ |
| 253372 | - iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); | |
| 253743 | + iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nPrefix); | |
| 253373 | 253744 | } |
| 253374 | - iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); | |
| 253745 | + iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nSuffix); | |
| 253375 | 253746 | |
| 253376 | 253747 | nPrefix = MIN(nPrefix, nPrefix2); |
| 253377 | 253748 | nSuffix = (nPrefix2 + nSuffix2) - nPrefix; |
| 253378 | 253749 | |
| 253379 | - if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ | |
| 253750 | + if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){ | |
| 253380 | 253751 | FTS5_CORRUPT_IDX(p); |
| 253381 | 253752 | }else{ |
| 253382 | 253753 | if( iKey!=1 ){ |
| 253383 | 253754 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); |
| 253384 | 253755 | } |
| 253385 | 253756 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); |
| 253386 | - if( nPrefix2>pSeg->term.n ){ | |
| 253757 | + if( nPrefix2>(u64)pSeg->term.n ){ | |
| 253387 | 253758 | FTS5_CORRUPT_IDX(p); |
| 253388 | 253759 | }else if( nPrefix2>nPrefix ){ |
| 253389 | 253760 | memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); |
| 253390 | 253761 | iOff += (nPrefix2-nPrefix); |
| 253391 | 253762 | } |
| @@ -253412,11 +253783,11 @@ | ||
| 253412 | 253783 | Fts5Data *pTerm = fts5DataRead(p, iId); |
| 253413 | 253784 | if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ |
| 253414 | 253785 | u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; |
| 253415 | 253786 | int nTermIdx = pTerm->nn - pTerm->szLeaf; |
| 253416 | 253787 | int iTermIdx = 0; |
| 253417 | - int iTermOff = 0; | |
| 253788 | + i64 iTermOff = 0; | |
| 253418 | 253789 | |
| 253419 | 253790 | while( 1 ){ |
| 253420 | 253791 | u32 iVal = 0; |
| 253421 | 253792 | int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); |
| 253422 | 253793 | iTermOff += iVal; |
| @@ -253423,16 +253794,19 @@ | ||
| 253423 | 253794 | if( (iTermIdx+nByte)>=nTermIdx ) break; |
| 253424 | 253795 | iTermIdx += nByte; |
| 253425 | 253796 | } |
| 253426 | 253797 | nTermIdx = iTermIdx; |
| 253427 | 253798 | |
| 253428 | - memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); | |
| 253429 | - fts5PutU16(&pTerm->p[2], iTermOff); | |
| 253430 | - | |
| 253431 | - fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); | |
| 253432 | - if( nTermIdx==0 ){ | |
| 253433 | - fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); | |
| 253799 | + if( iTermOff>pTerm->szLeaf ){ | |
| 253800 | + FTS5_CORRUPT_IDX(p); | |
| 253801 | + }else{ | |
| 253802 | + memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); | |
| 253803 | + fts5PutU16(&pTerm->p[2], iTermOff); | |
| 253804 | + fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); | |
| 253805 | + if( nTermIdx==0 ){ | |
| 253806 | + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); | |
| 253807 | + } | |
| 253434 | 253808 | } |
| 253435 | 253809 | } |
| 253436 | 253810 | fts5DataRelease(pTerm); |
| 253437 | 253811 | } |
| 253438 | 253812 | } |
| @@ -253451,11 +253825,13 @@ | ||
| 253451 | 253825 | int nShift = iNextOff - iOff; /* Distance to move them */ |
| 253452 | 253826 | |
| 253453 | 253827 | int iPrevKeyOut = 0; |
| 253454 | 253828 | int iKeyIn = 0; |
| 253455 | 253829 | |
| 253456 | - memmove(&aPg[iOff], &aPg[iNextOff], nMove); | |
| 253830 | + if( nMove>0 ){ | |
| 253831 | + memmove(&aPg[iOff], &aPg[iNextOff], nMove); | |
| 253832 | + } | |
| 253457 | 253833 | iPgIdx -= nShift; |
| 253458 | 253834 | nPg = iPgIdx; |
| 253459 | 253835 | fts5PutU16(&aPg[2], iPgIdx); |
| 253460 | 253836 | |
| 253461 | 253837 | for(iIdx=0; iIdx<nIdx; /* no-op */){ |
| @@ -253889,11 +254265,11 @@ | ||
| 253889 | 254265 | if( nMerge<0 ){ |
| 253890 | 254266 | Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct); |
| 253891 | 254267 | fts5StructureRelease(pStruct); |
| 253892 | 254268 | pStruct = pNew; |
| 253893 | 254269 | nMin = 1; |
| 253894 | - nMerge = nMerge*-1; | |
| 254270 | + nMerge = (nMerge==SMALLEST_INT32 ? LARGEST_INT32 : (nMerge*-1)); | |
| 253895 | 254271 | } |
| 253896 | 254272 | if( pStruct && pStruct->nLevel ){ |
| 253897 | 254273 | if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){ |
| 253898 | 254274 | fts5StructureWrite(p, pStruct); |
| 253899 | 254275 | } |
| @@ -261097,11 +261473,11 @@ | ||
| 261097 | 261473 | int nArg, /* Number of args */ |
| 261098 | 261474 | sqlite3_value **apUnused /* Function arguments */ |
| 261099 | 261475 | ){ |
| 261100 | 261476 | assert( nArg==0 ); |
| 261101 | 261477 | UNUSED_PARAM2(nArg, apUnused); |
| 261102 | - sqlite3_result_text(pCtx, "fts5: 2025-12-11 18:16:39 e785a80e4100c368dca8d73cb662cff4d0fd76734fa0f3fa9b5754a380f7c746", -1, SQLITE_TRANSIENT); | |
| 261478 | + sqlite3_result_text(pCtx, "fts5: 2026-02-04 18:10:49 e6902937ecdbeb449986469859b46631272fb0a9e7e1c31adea14cff072b6d67", -1, SQLITE_TRANSIENT); | |
| 261103 | 261479 | } |
| 261104 | 261480 | |
| 261105 | 261481 | /* |
| 261106 | 261482 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 261107 | 261483 | ** |
| 261108 | 261484 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** 01409738afc2c0d5bdaa248ffb508aa5f36a with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -467,14 +467,14 @@ | |
| 467 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 468 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 469 | */ |
| 470 | #define SQLITE_VERSION "3.52.0" |
| 471 | #define SQLITE_VERSION_NUMBER 3052000 |
| 472 | #define SQLITE_SOURCE_ID "2025-12-11 23:24:05 01409738afc2c0d5bdaa248ffb508aa5f36a66390f6b8e4834734529ee8ed2fa" |
| 473 | #define SQLITE_SCM_BRANCH "trunk" |
| 474 | #define SQLITE_SCM_TAGS "" |
| 475 | #define SQLITE_SCM_DATETIME "2025-12-11T23:24:05.667Z" |
| 476 | |
| 477 | /* |
| 478 | ** CAPI3REF: Run-Time Library Version Numbers |
| 479 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 480 | ** |
| @@ -4752,16 +4752,32 @@ | |
| 4752 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4753 | ** messages on the global error log when it is not. If the test compile |
| 4754 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4755 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4756 | ** logs the error. |
| 4757 | ** </dl> |
| 4758 | */ |
| 4759 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4760 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4761 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4762 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4763 | |
| 4764 | /* |
| 4765 | ** CAPI3REF: Compiling An SQL Statement |
| 4766 | ** KEYWORDS: {SQL statement compiler} |
| 4767 | ** METHOD: sqlite3 |
| @@ -4771,12 +4787,13 @@ | |
| 4771 | ** program using one of these routines. Or, in other words, these routines |
| 4772 | ** are constructors for the [prepared statement] object. |
| 4773 | ** |
| 4774 | ** The preferred routine to use is [sqlite3_prepare_v2()]. The |
| 4775 | ** [sqlite3_prepare()] interface is legacy and should be avoided. |
| 4776 | ** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used |
| 4777 | ** for special purposes. |
| 4778 | ** |
| 4779 | ** The use of the UTF-8 interfaces is preferred, as SQLite currently |
| 4780 | ** does all parsing using UTF-8. The UTF-16 interfaces are provided |
| 4781 | ** as a convenience. The UTF-16 interfaces work by converting the |
| 4782 | ** input text into UTF-8, then invoking the corresponding UTF-8 interface. |
| @@ -5177,12 +5194,12 @@ | |
| 5177 | ** it should be a pointer to well-formed UTF8 text. |
| 5178 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 5179 | ** it should be a pointer to well-formed UTF16 text. |
| 5180 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 5181 | ** it should be a pointer to a well-formed unicode string that is |
| 5182 | ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 |
| 5183 | ** otherwise. |
| 5184 | ** |
| 5185 | ** [[byte-order determination rules]] ^The byte-order of |
| 5186 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 5187 | ** found in the first character, which is removed, or in the absence of a BOM |
| 5188 | ** the byte order is the native byte order of the host |
| @@ -5224,14 +5241,19 @@ | |
| 5224 | ** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the |
| 5225 | ** object is to be copied prior to the return from sqlite3_bind_*(). ^The |
| 5226 | ** object and pointer to it must remain valid until then. ^SQLite will then |
| 5227 | ** manage the lifetime of its private copy. |
| 5228 | ** |
| 5229 | ** ^The sixth argument to sqlite3_bind_text64() must be one of |
| 5230 | ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] |
| 5231 | ** to specify the encoding of the text in the third parameter. If |
| 5232 | ** the sixth argument to sqlite3_bind_text64() is not one of the |
| 5233 | ** allowed values shown above, or if the text encoding is different |
| 5234 | ** from the encoding specified by the sixth parameter, then the behavior |
| 5235 | ** is undefined. |
| 5236 | ** |
| 5237 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
| @@ -6094,17 +6116,63 @@ | |
| 6094 | /* |
| 6095 | ** CAPI3REF: Text Encodings |
| 6096 | ** |
| 6097 | ** These constants define integer codes that represent the various |
| 6098 | ** text encodings supported by SQLite. |
| 6099 | */ |
| 6100 | #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ |
| 6101 | #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ |
| 6102 | #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ |
| 6103 | #define SQLITE_UTF16 4 /* Use native byte order */ |
| 6104 | #define SQLITE_ANY 5 /* Deprecated */ |
| 6105 | #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ |
| 6106 | |
| 6107 | /* |
| 6108 | ** CAPI3REF: Function Flags |
| 6109 | ** |
| 6110 | ** These constants may be ORed together with the |
| @@ -6606,14 +6674,18 @@ | |
| 6606 | ** use for client data is to provide a mechanism for wrapper libraries |
| 6607 | ** to store additional information about an SQLite database connection. |
| 6608 | ** |
| 6609 | ** There is no limit (other than available memory) on the number of different |
| 6610 | ** client data pointers (with different names) that can be attached to a |
| 6611 | ** single database connection. However, the implementation is optimized |
| 6612 | ** for the case of having only one or two different client data names. |
| 6613 | ** Applications and wrapper libraries are discouraged from using more than |
| 6614 | ** one client data name each. |
| 6615 | ** |
| 6616 | ** There is no way to enumerate the client data pointers |
| 6617 | ** associated with a database connection. The N parameter can be thought |
| 6618 | ** of as a secret key such that only code that knows the secret key is able |
| 6619 | ** to access the associated data. |
| @@ -6717,14 +6789,18 @@ | |
| 6717 | ** ^The sqlite3_result_text(), sqlite3_result_text16(), |
| 6718 | ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces |
| 6719 | ** set the return value of the application-defined function to be |
| 6720 | ** a text string which is represented as UTF-8, UTF-16 native byte order, |
| 6721 | ** UTF-16 little endian, or UTF-16 big endian, respectively. |
| 6722 | ** ^The sqlite3_result_text64() interface sets the return value of an |
| 6723 | ** application-defined function to be a text string in an encoding |
| 6724 | ** specified by the fifth (and last) parameter, which must be one |
| 6725 | ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. |
| 6726 | ** ^SQLite takes the text result from the application from |
| 6727 | ** the 2nd parameter of the sqlite3_result_text* interfaces. |
| 6728 | ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces |
| 6729 | ** other than sqlite3_result_text64() is negative, then SQLite computes |
| 6730 | ** the string length itself by searching the 2nd parameter for the first |
| @@ -6807,11 +6883,11 @@ | |
| 6807 | SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); |
| 6808 | SQLITE_API void sqlite3_result_int(sqlite3_context*, int); |
| 6809 | SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); |
| 6810 | SQLITE_API void sqlite3_result_null(sqlite3_context*); |
| 6811 | SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); |
| 6812 | SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, |
| 6813 | void(*)(void*), unsigned char encoding); |
| 6814 | SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); |
| 6815 | SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6816 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6817 | SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); |
| @@ -7746,22 +7822,22 @@ | |
| 7746 | ** ^This interface loads an SQLite extension library from the named file. |
| 7747 | ** |
| 7748 | ** ^The sqlite3_load_extension() interface attempts to load an |
| 7749 | ** [SQLite extension] library contained in the file zFile. If |
| 7750 | ** the file cannot be loaded directly, attempts are made to load |
| 7751 | ** with various operating-system specific extensions added. |
| 7752 | ** So for example, if "samplelib" cannot be loaded, then names like |
| 7753 | ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might |
| 7754 | ** be tried also. |
| 7755 | ** |
| 7756 | ** ^The entry point is zProc. |
| 7757 | ** ^(zProc may be 0, in which case SQLite will try to come up with an |
| 7758 | ** entry point name on its own. It first tries "sqlite3_extension_init". |
| 7759 | ** If that does not work, it constructs a name "sqlite3_X_init" where |
| 7760 | ** X consists of the lower-case equivalent of all ASCII alphabetic |
| 7761 | ** characters in the filename from the last "/" to the first following |
| 7762 | ** "." and omitting any initial "lib".)^ |
| 7763 | ** ^The sqlite3_load_extension() interface returns |
| 7764 | ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. |
| 7765 | ** ^If an error occurs and pzErrMsg is not 0, then the |
| 7766 | ** [sqlite3_load_extension()] interface shall attempt to |
| 7767 | ** fill *pzErrMsg with error message text stored in memory |
| @@ -11495,23 +11571,45 @@ | |
| 11495 | #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ |
| 11496 | |
| 11497 | /* |
| 11498 | ** CAPI3REF: Bind array values to the CARRAY table-valued function |
| 11499 | ** |
| 11500 | ** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to |
| 11501 | ** one of the first argument of the [carray() table-valued function]. The |
| 11502 | ** S parameter is a pointer to the [prepared statement] that uses the carray() |
| 11503 | ** functions. I is the parameter index to be bound. P is a pointer to the |
| 11504 | ** array to be bound, and N is the number of eements in the array. The |
| 11505 | ** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], |
| 11506 | ** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to |
| 11507 | ** indicate the datatype of the array being bound. The X argument is not a |
| 11508 | ** NULL pointer, then SQLite will invoke the function X on the P parameter |
| 11509 | ** after it has finished using P, even if the call to |
| 11510 | ** sqlite3_carray_bind() fails. The special-case finalizer |
| 11511 | ** SQLITE_TRANSIENT has no effect here. |
| 11512 | */ |
| 11513 | SQLITE_API int sqlite3_carray_bind( |
| 11514 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11515 | int i, /* Parameter index */ |
| 11516 | void *aData, /* Pointer to array data */ |
| 11517 | int nData, /* Number of data elements */ |
| @@ -14352,10 +14450,31 @@ | |
| 14352 | #ifndef SQLITE_MAX_LENGTH |
| 14353 | # define SQLITE_MAX_LENGTH 1000000000 |
| 14354 | #endif |
| 14355 | #define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ |
| 14356 | |
| 14357 | /* |
| 14358 | ** This is the maximum number of |
| 14359 | ** |
| 14360 | ** * Columns in a table |
| 14361 | ** * Columns in an index |
| @@ -17529,11 +17648,11 @@ | |
| 17529 | |
| 17530 | /* |
| 17531 | ** Additional non-public SQLITE_PREPARE_* flags |
| 17532 | */ |
| 17533 | #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ |
| 17534 | #define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ |
| 17535 | |
| 17536 | /* |
| 17537 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 17538 | ** for a description of what each of these routines does. |
| 17539 | */ |
| @@ -20202,31 +20321,17 @@ | |
| 20202 | }; |
| 20203 | |
| 20204 | /* |
| 20205 | ** An instance of the following structure contains all information |
| 20206 | ** needed to generate code for a single SELECT statement. |
| 20207 | ** |
| 20208 | ** See the header comment on the computeLimitRegisters() routine for a |
| 20209 | ** detailed description of the meaning of the iLimit and iOffset fields. |
| 20210 | ** |
| 20211 | ** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes. |
| 20212 | ** These addresses must be stored so that we can go back and fill in |
| 20213 | ** the P4_KEYINFO and P2 parameters later. Neither the KeyInfo nor |
| 20214 | ** the number of columns in P2 can be computed at the same time |
| 20215 | ** as the OP_OpenEphm instruction is coded because not |
| 20216 | ** enough information about the compound query is known at that point. |
| 20217 | ** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences |
| 20218 | ** for the result set. The KeyInfo for addrOpenEphm[2] contains collating |
| 20219 | ** sequences for the ORDER BY clause. |
| 20220 | */ |
| 20221 | struct Select { |
| 20222 | u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ |
| 20223 | LogEst nSelectRow; /* Estimated number of result rows */ |
| 20224 | u32 selFlags; /* Various SF_* values */ |
| 20225 | int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ |
| 20226 | u32 selId; /* Unique identifier number for this SELECT */ |
| 20227 | int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */ |
| 20228 | ExprList *pEList; /* The fields of the result */ |
| 20229 | SrcList *pSrc; /* The FROM clause */ |
| 20230 | Expr *pWhere; /* The WHERE clause */ |
| 20231 | ExprList *pGroupBy; /* The GROUP BY clause */ |
| 20232 | Expr *pHaving; /* The HAVING clause */ |
| @@ -20254,28 +20359,28 @@ | |
| 20254 | #define SF_Distinct 0x0000001 /* Output should be DISTINCT */ |
| 20255 | #define SF_All 0x0000002 /* Includes the ALL keyword */ |
| 20256 | #define SF_Resolved 0x0000004 /* Identifiers have been resolved */ |
| 20257 | #define SF_Aggregate 0x0000008 /* Contains agg functions or a GROUP BY */ |
| 20258 | #define SF_HasAgg 0x0000010 /* Contains aggregate functions */ |
| 20259 | #define SF_UsesEphemeral 0x0000020 /* Uses the OpenEphemeral opcode */ |
| 20260 | #define SF_Expanded 0x0000040 /* sqlite3SelectExpand() called on this */ |
| 20261 | #define SF_HasTypeInfo 0x0000080 /* FROM subqueries have Table metadata */ |
| 20262 | #define SF_Compound 0x0000100 /* Part of a compound query */ |
| 20263 | #define SF_Values 0x0000200 /* Synthesized from VALUES clause */ |
| 20264 | #define SF_MultiValue 0x0000400 /* Single VALUES term with multiple rows */ |
| 20265 | #define SF_NestedFrom 0x0000800 /* Part of a parenthesized FROM clause */ |
| 20266 | #define SF_MinMaxAgg 0x0001000 /* Aggregate containing min() or max() */ |
| 20267 | #define SF_Recursive 0x0002000 /* The recursive part of a recursive CTE */ |
| 20268 | #define SF_FixedLimit 0x0004000 /* nSelectRow set by a constant LIMIT */ |
| 20269 | #define SF_MaybeConvert 0x0008000 /* Need convertCompoundSelectToSubquery() */ |
| 20270 | #define SF_Converted 0x0010000 /* By convertCompoundSelectToSubquery() */ |
| 20271 | #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */ |
| 20272 | #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */ |
| 20273 | #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ |
| 20274 | #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ |
| 20275 | #define SF_View 0x0200000 /* SELECT statement is a view */ |
| 20276 | #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ |
| 20277 | #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ |
| 20278 | #define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ |
| 20279 | #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ |
| 20280 | #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ |
| 20281 | #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ |
| @@ -20291,15 +20396,10 @@ | |
| 20291 | /* |
| 20292 | ** The results of a SELECT can be distributed in several ways, as defined |
| 20293 | ** by one of the following macros. The "SRT" prefix means "SELECT Result |
| 20294 | ** Type". |
| 20295 | ** |
| 20296 | ** SRT_Union Store results as a key in a temporary index |
| 20297 | ** identified by pDest->iSDParm. |
| 20298 | ** |
| 20299 | ** SRT_Except Remove results from the temporary index pDest->iSDParm. |
| 20300 | ** |
| 20301 | ** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result |
| 20302 | ** set is not empty. |
| 20303 | ** |
| 20304 | ** SRT_Discard Throw the results away. This is used by SELECT |
| 20305 | ** statements within triggers whose only purpose is |
| @@ -20359,34 +20459,32 @@ | |
| 20359 | ** column returned by the SELECT is used as the integer |
| 20360 | ** key. If (pDest->iSDParm>0), then the table is an index |
| 20361 | ** table. (pDest->iSDParm) is the number of key columns in |
| 20362 | ** each index record in this case. |
| 20363 | */ |
| 20364 | #define SRT_Union 1 /* Store result as keys in an index */ |
| 20365 | #define SRT_Except 2 /* Remove result from a UNION index */ |
| 20366 | #define SRT_Exists 3 /* Store 1 if the result is not empty */ |
| 20367 | #define SRT_Discard 4 /* Do not save the results anywhere */ |
| 20368 | #define SRT_DistFifo 5 /* Like SRT_Fifo, but unique results only */ |
| 20369 | #define SRT_DistQueue 6 /* Like SRT_Queue, but unique results only */ |
| 20370 | |
| 20371 | /* The DISTINCT clause is ignored for all of the above. Not that |
| 20372 | ** IgnorableDistinct() implies IgnorableOrderby() */ |
| 20373 | #define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue) |
| 20374 | |
| 20375 | #define SRT_Queue 7 /* Store result in an queue */ |
| 20376 | #define SRT_Fifo 8 /* Store result as data with an automatic rowid */ |
| 20377 | |
| 20378 | /* The ORDER BY clause is ignored for all of the above */ |
| 20379 | #define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo) |
| 20380 | |
| 20381 | #define SRT_Output 9 /* Output each row of result */ |
| 20382 | #define SRT_Mem 10 /* Store result in a memory cell */ |
| 20383 | #define SRT_Set 11 /* Store results as keys in an index */ |
| 20384 | #define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */ |
| 20385 | #define SRT_Coroutine 13 /* Generate a single row of result */ |
| 20386 | #define SRT_Table 14 /* Store result as data with an automatic rowid */ |
| 20387 | #define SRT_Upfrom 15 /* Store result as data with rowid */ |
| 20388 | |
| 20389 | /* |
| 20390 | ** An instance of this object describes where to put of the results of |
| 20391 | ** a SELECT statement. |
| 20392 | */ |
| @@ -21018,10 +21116,11 @@ | |
| 21018 | u16 mWFlags; /* Use-dependent flags */ |
| 21019 | union { /* Extra data for callback */ |
| 21020 | NameContext *pNC; /* Naming context */ |
| 21021 | int n; /* A counter */ |
| 21022 | int iCur; /* A cursor number */ |
| 21023 | SrcList *pSrcList; /* FROM clause */ |
| 21024 | struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ |
| 21025 | struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */ |
| 21026 | int *aiCol; /* array of column indexes */ |
| 21027 | struct IdxCover *pIdxCover; /* Check for index coverage */ |
| @@ -21801,10 +21900,11 @@ | |
| 21801 | SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); |
| 21802 | #endif |
| 21803 | SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*, Parse*); |
| 21804 | SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); |
| 21805 | SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); |
| 21806 | SQLITE_PRIVATE int sqlite3IsRowid(const char*); |
| 21807 | SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); |
| 21808 | SQLITE_PRIVATE void sqlite3GenerateRowDelete( |
| 21809 | Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); |
| 21810 | SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); |
| @@ -24489,10 +24589,11 @@ | |
| 24489 | SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); |
| 24490 | SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); |
| 24491 | SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); |
| 24492 | SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); |
| 24493 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); |
| 24494 | SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); |
| 24495 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 24496 | # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 |
| 24497 | #else |
| 24498 | SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); |
| @@ -31335,31 +31436,10 @@ | |
| 31335 | sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); |
| 31336 | } |
| 31337 | *pp = p; |
| 31338 | } |
| 31339 | |
| 31340 | /* |
| 31341 | ** Maximum size of any single memory allocation. |
| 31342 | ** |
| 31343 | ** This is not a limit on the total amount of memory used. This is |
| 31344 | ** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc(). |
| 31345 | ** |
| 31346 | ** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 |
| 31347 | ** This provides a 256-byte safety margin for defense against 32-bit |
| 31348 | ** signed integer overflow bugs when computing memory allocation sizes. |
| 31349 | ** Paranoid applications might want to reduce the maximum allocation size |
| 31350 | ** further for an even larger safety margin. 0x3fffffff or 0x0fffffff |
| 31351 | ** or even smaller would be reasonable upper bounds on the size of a memory |
| 31352 | ** allocations for most applications. |
| 31353 | */ |
| 31354 | #ifndef SQLITE_MAX_ALLOCATION_SIZE |
| 31355 | # define SQLITE_MAX_ALLOCATION_SIZE 2147483391 |
| 31356 | #endif |
| 31357 | #if SQLITE_MAX_ALLOCATION_SIZE>2147483391 |
| 31358 | # error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391 |
| 31359 | #endif |
| 31360 | |
| 31361 | /* |
| 31362 | ** Allocate memory. This routine is like sqlite3_malloc() except that it |
| 31363 | ** assumes the memory subsystem has already been initialized. |
| 31364 | */ |
| 31365 | SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ |
| @@ -31579,12 +31659,11 @@ | |
| 31579 | } |
| 31580 | if( nBytes==0 ){ |
| 31581 | sqlite3_free(pOld); /* IMP: R-26507-47431 */ |
| 31582 | return 0; |
| 31583 | } |
| 31584 | if( nBytes>=0x7fffff00 ){ |
| 31585 | /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ |
| 31586 | return 0; |
| 31587 | } |
| 31588 | nOld = sqlite3MallocSize(pOld); |
| 31589 | /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second |
| 31590 | ** argument to xRealloc is always a value returned by a prior call to |
| @@ -48881,78 +48960,54 @@ | |
| 48881 | { "WriteFile", (SYSCALL)WriteFile, 0 }, |
| 48882 | |
| 48883 | #define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ |
| 48884 | LPOVERLAPPED))aSyscall[61].pCurrent) |
| 48885 | |
| 48886 | { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, |
| 48887 | |
| 48888 | #define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ |
| 48889 | DWORD,DWORD))aSyscall[62].pCurrent) |
| 48890 | |
| 48891 | /* |
| 48892 | ** For WaitForSingleObject(), MSDN says: |
| 48893 | ** |
| 48894 | ** Minimum supported client: Windows XP [desktop apps | UWP apps] |
| 48895 | ** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] |
| 48896 | */ |
| 48897 | { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, |
| 48898 | |
| 48899 | #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ |
| 48900 | DWORD))aSyscall[63].pCurrent) |
| 48901 | |
| 48902 | #if !SQLITE_OS_WINCE |
| 48903 | { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, |
| 48904 | #else |
| 48905 | { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, |
| 48906 | #endif |
| 48907 | |
| 48908 | #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ |
| 48909 | BOOL))aSyscall[64].pCurrent) |
| 48910 | |
| 48911 | { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, |
| 48912 | |
| 48913 | #define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ |
| 48914 | PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent) |
| 48915 | |
| 48916 | { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, |
| 48917 | |
| 48918 | #define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ |
| 48919 | FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent) |
| 48920 | |
| 48921 | { "CreateFile2", (SYSCALL)CreateFile2, 0 }, |
| 48922 | |
| 48923 | #define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ |
| 48924 | LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent) |
| 48925 | |
| 48926 | { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, |
| 48927 | |
| 48928 | #define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent) |
| 48929 | |
| 48930 | { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, |
| 48931 | |
| 48932 | #define osGetNativeSystemInfo ((VOID(WINAPI*)( \ |
| 48933 | LPSYSTEM_INFO))aSyscall[71].pCurrent) |
| 48934 | |
| 48935 | #if defined(SQLITE_WIN32_HAS_ANSI) |
| 48936 | { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, |
| 48937 | #else |
| 48938 | { "OutputDebugStringA", (SYSCALL)0, 0 }, |
| 48939 | #endif |
| 48940 | |
| 48941 | #define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent) |
| 48942 | |
| 48943 | #if defined(SQLITE_WIN32_HAS_WIDE) |
| 48944 | { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, |
| 48945 | #else |
| 48946 | { "OutputDebugStringW", (SYSCALL)0, 0 }, |
| 48947 | #endif |
| 48948 | |
| 48949 | #define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent) |
| 48950 | |
| 48951 | { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, |
| 48952 | |
| 48953 | #define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent) |
| 48954 | |
| 48955 | /* |
| 48956 | ** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" |
| 48957 | ** is really just a macro that uses a compiler intrinsic (e.g. x64). |
| 48958 | ** So do not try to make this is into a redefinable interface. |
| @@ -48963,38 +49018,38 @@ | |
| 48963 | #define osInterlockedCompareExchange InterlockedCompareExchange |
| 48964 | #else |
| 48965 | { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, |
| 48966 | |
| 48967 | #define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ |
| 48968 | SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) |
| 48969 | #endif /* defined(InterlockedCompareExchange) */ |
| 48970 | |
| 48971 | #if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID |
| 48972 | { "UuidCreate", (SYSCALL)UuidCreate, 0 }, |
| 48973 | #else |
| 48974 | { "UuidCreate", (SYSCALL)0, 0 }, |
| 48975 | #endif |
| 48976 | |
| 48977 | #define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) |
| 48978 | |
| 48979 | #if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID |
| 48980 | { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, |
| 48981 | #else |
| 48982 | { "UuidCreateSequential", (SYSCALL)0, 0 }, |
| 48983 | #endif |
| 48984 | |
| 48985 | #define osUuidCreateSequential \ |
| 48986 | ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) |
| 48987 | |
| 48988 | #if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 |
| 48989 | { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, |
| 48990 | #else |
| 48991 | { "FlushViewOfFile", (SYSCALL)0, 0 }, |
| 48992 | #endif |
| 48993 | |
| 48994 | #define osFlushViewOfFile \ |
| 48995 | ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) |
| 48996 | |
| 48997 | /* |
| 48998 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() |
| 48999 | ** to implement blocking locks with timeouts. MSDN says: |
| 49000 | ** |
| @@ -49007,11 +49062,11 @@ | |
| 49007 | { "CreateEvent", (SYSCALL)0, 0 }, |
| 49008 | #endif |
| 49009 | |
| 49010 | #define osCreateEvent ( \ |
| 49011 | (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ |
| 49012 | aSyscall[80].pCurrent \ |
| 49013 | ) |
| 49014 | |
| 49015 | /* |
| 49016 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() |
| 49017 | ** for the case where a timeout expires and a lock request must be |
| @@ -49024,68 +49079,68 @@ | |
| 49024 | { "CancelIo", (SYSCALL)CancelIo, 0 }, |
| 49025 | #else |
| 49026 | { "CancelIo", (SYSCALL)0, 0 }, |
| 49027 | #endif |
| 49028 | |
| 49029 | #define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent) |
| 49030 | |
| 49031 | #if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32) |
| 49032 | { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 }, |
| 49033 | #else |
| 49034 | { "GetModuleHandleW", (SYSCALL)0, 0 }, |
| 49035 | #endif |
| 49036 | |
| 49037 | #define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent) |
| 49038 | |
| 49039 | #ifndef _WIN32 |
| 49040 | { "getenv", (SYSCALL)getenv, 0 }, |
| 49041 | #else |
| 49042 | { "getenv", (SYSCALL)0, 0 }, |
| 49043 | #endif |
| 49044 | |
| 49045 | #define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent) |
| 49046 | |
| 49047 | #ifndef _WIN32 |
| 49048 | { "getcwd", (SYSCALL)getcwd, 0 }, |
| 49049 | #else |
| 49050 | { "getcwd", (SYSCALL)0, 0 }, |
| 49051 | #endif |
| 49052 | |
| 49053 | #define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent) |
| 49054 | |
| 49055 | #ifndef _WIN32 |
| 49056 | { "readlink", (SYSCALL)readlink, 0 }, |
| 49057 | #else |
| 49058 | { "readlink", (SYSCALL)0, 0 }, |
| 49059 | #endif |
| 49060 | |
| 49061 | #define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent) |
| 49062 | |
| 49063 | #ifndef _WIN32 |
| 49064 | { "lstat", (SYSCALL)lstat, 0 }, |
| 49065 | #else |
| 49066 | { "lstat", (SYSCALL)0, 0 }, |
| 49067 | #endif |
| 49068 | |
| 49069 | #define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent) |
| 49070 | |
| 49071 | #ifndef _WIN32 |
| 49072 | { "__errno", (SYSCALL)__errno, 0 }, |
| 49073 | #else |
| 49074 | { "__errno", (SYSCALL)0, 0 }, |
| 49075 | #endif |
| 49076 | |
| 49077 | #define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)()) |
| 49078 | |
| 49079 | #ifndef _WIN32 |
| 49080 | { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 }, |
| 49081 | #else |
| 49082 | { "cygwin_conv_path", (SYSCALL)0, 0 }, |
| 49083 | #endif |
| 49084 | |
| 49085 | #define osCygwin_conv_path ((size_t(*)(unsigned int, \ |
| 49086 | const void *, void *, size_t))aSyscall[88].pCurrent) |
| 49087 | |
| 49088 | }; /* End of the overrideable system calls */ |
| 49089 | |
| 49090 | /* |
| 49091 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| @@ -53659,10 +53714,11 @@ | |
| 53659 | attr = INVALID_FILE_ATTRIBUTES; |
| 53660 | }else{ |
| 53661 | attr = sAttrData.dwFileAttributes; |
| 53662 | } |
| 53663 | }else{ |
| 53664 | winLogIoerr(cnt, __LINE__); |
| 53665 | if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ |
| 53666 | sqlite3_free(zConverted); |
| 53667 | return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", |
| 53668 | zFilename); |
| @@ -54399,11 +54455,16 @@ | |
| 54399 | }; |
| 54400 | #endif |
| 54401 | |
| 54402 | /* Double-check that the aSyscall[] array has been constructed |
| 54403 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 54404 | assert( ArraySize(aSyscall)==86 ); |
| 54405 | |
| 54406 | /* get memory map allocation granularity */ |
| 54407 | memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); |
| 54408 | osGetSystemInfo(&winSysInfo); |
| 54409 | assert( winSysInfo.dwAllocationGranularity>0 ); |
| @@ -77426,11 +77487,11 @@ | |
| 77426 | } |
| 77427 | assert( cursorHoldsMutex(pCur) ); |
| 77428 | |
| 77429 | getCellInfo(pCur); |
| 77430 | aPayload = pCur->info.pPayload; |
| 77431 | assert( offset+amt <= pCur->info.nPayload ); |
| 77432 | |
| 77433 | assert( aPayload > pPage->aData ); |
| 77434 | if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){ |
| 77435 | /* Trying to read or write past the end of the data is an error. The |
| 77436 | ** conditional above is really: |
| @@ -77983,11 +78044,11 @@ | |
| 77983 | SQLITE_PRIVATE int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes){ |
| 77984 | int rc; |
| 77985 | |
| 77986 | assert( cursorOwnsBtShared(pCur) ); |
| 77987 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 77988 | if( pCur->eState==CURSOR_VALID ){ |
| 77989 | *pRes = 0; |
| 77990 | return SQLITE_OK; |
| 77991 | } |
| 77992 | rc = moveToRoot(pCur); |
| 77993 | if( rc==SQLITE_EMPTY ){ |
| @@ -85876,10 +85937,88 @@ | |
| 85876 | #endif |
| 85877 | |
| 85878 | |
| 85879 | return SQLITE_OK; |
| 85880 | } |
| 85881 | |
| 85882 | /* |
| 85883 | ** Move data out of a btree key or data field and into a Mem structure. |
| 85884 | ** The data is payload from the entry that pCur is currently pointing |
| 85885 | ** to. offset and amt determine what portion of the data or key to retrieve. |
| @@ -85900,11 +86039,16 @@ | |
| 85900 | u32 amt, /* Number of bytes to return. */ |
| 85901 | Mem *pMem /* OUT: Return data in this Mem structure. */ |
| 85902 | ){ |
| 85903 | int rc; |
| 85904 | pMem->flags = MEM_Null; |
| 85905 | if( sqlite3BtreeMaxRecordSize(pCur)<offset+amt ){ |
| 85906 | return SQLITE_CORRUPT_BKPT; |
| 85907 | } |
| 85908 | if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){ |
| 85909 | rc = sqlite3BtreePayload(pCur, offset, amt, pMem->z); |
| 85910 | if( rc==SQLITE_OK ){ |
| @@ -86568,10 +86712,15 @@ | |
| 86568 | ** the column value into *ppVal. If *ppVal is initially NULL then a new |
| 86569 | ** sqlite3_value object is allocated. |
| 86570 | ** |
| 86571 | ** If *ppVal is initially NULL then the caller is responsible for |
| 86572 | ** ensuring that the value written into *ppVal is eventually freed. |
| 86573 | */ |
| 86574 | SQLITE_PRIVATE int sqlite3Stat4Column( |
| 86575 | sqlite3 *db, /* Database handle */ |
| 86576 | const void *pRec, /* Pointer to buffer containing record */ |
| 86577 | int nRec, /* Size of buffer pRec in bytes */ |
| @@ -89584,11 +89733,11 @@ | |
| 89584 | assert( !zName || xDel!=SQLITE_DYNAMIC ); |
| 89585 | return SQLITE_NOMEM_BKPT; |
| 89586 | } |
| 89587 | assert( p->aColName!=0 ); |
| 89588 | pColName = &(p->aColName[idx+var*p->nResAlloc]); |
| 89589 | rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); |
| 89590 | assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); |
| 89591 | return rc; |
| 89592 | } |
| 89593 | |
| 89594 | /* |
| @@ -92662,11 +92811,27 @@ | |
| 92662 | int n, /* Bytes in string, or negative */ |
| 92663 | u8 enc, /* Encoding of z. 0 for BLOBs */ |
| 92664 | void (*xDel)(void*) /* Destructor function */ |
| 92665 | ){ |
| 92666 | Mem *pOut = pCtx->pOut; |
| 92667 | int rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); |
| 92668 | if( rc ){ |
| 92669 | if( rc==SQLITE_TOOBIG ){ |
| 92670 | sqlite3_result_error_toobig(pCtx); |
| 92671 | }else{ |
| 92672 | /* The only errors possible from sqlite3VdbeMemSetStr are |
| @@ -92855,11 +93020,11 @@ | |
| 92855 | return; |
| 92856 | } |
| 92857 | #endif |
| 92858 | assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); |
| 92859 | assert( xDel!=SQLITE_DYNAMIC ); |
| 92860 | if( enc!=SQLITE_UTF8 ){ |
| 92861 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 92862 | n &= ~(u64)1; |
| 92863 | } |
| 92864 | if( n>0x7fffffff ){ |
| 92865 | (void)invokeValueDestructor(z, xDel, pCtx); |
| @@ -93315,11 +93480,11 @@ | |
| 93315 | if( rc==SQLITE_OK ){ |
| 93316 | u32 sz; /* Size of current row in bytes */ |
| 93317 | Mem sMem; /* Raw content of current row */ |
| 93318 | memset(&sMem, 0, sizeof(sMem)); |
| 93319 | sz = sqlite3BtreePayloadSize(pRhs->pCsr); |
| 93320 | rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem); |
| 93321 | if( rc==SQLITE_OK ){ |
| 93322 | u8 *zBuf = (u8*)sMem.z; |
| 93323 | u32 iSerial; |
| 93324 | sqlite3_value *pOut = pRhs->pOut; |
| 93325 | int iOff = 1 + getVarint32(&zBuf[1], iSerial); |
| @@ -93964,17 +94129,29 @@ | |
| 93964 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 93965 | if( rc==SQLITE_OK ){ |
| 93966 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 93967 | if( zData!=0 ){ |
| 93968 | pVar = &p->aVar[i-1]; |
| 93969 | rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); |
| 93970 | if( rc==SQLITE_OK ){ |
| 93971 | if( encoding==0 ){ |
| 93972 | pVar->enc = ENC(p->db); |
| 93973 | }else{ |
| 93974 | rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); |
| 93975 | } |
| 93976 | } |
| 93977 | if( rc ){ |
| 93978 | sqlite3Error(p->db, rc); |
| 93979 | rc = sqlite3ApiExit(p->db, rc); |
| 93980 | } |
| @@ -94082,11 +94259,11 @@ | |
| 94082 | sqlite3_uint64 nData, |
| 94083 | void (*xDel)(void*), |
| 94084 | unsigned char enc |
| 94085 | ){ |
| 94086 | assert( xDel!=SQLITE_DYNAMIC ); |
| 94087 | if( enc!=SQLITE_UTF8 ){ |
| 94088 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 94089 | nData &= ~(u64)1; |
| 94090 | } |
| 94091 | return bindText(pStmt, i, zData, nData, xDel, enc); |
| 94092 | } |
| @@ -101780,24 +101957,19 @@ | |
| 101780 | rc = sqlite3VdbeSorterWrite(pC, pIn2); |
| 101781 | if( rc) goto abort_due_to_error; |
| 101782 | break; |
| 101783 | } |
| 101784 | |
| 101785 | /* Opcode: IdxDelete P1 P2 P3 * P5 |
| 101786 | ** Synopsis: key=r[P2@P3] |
| 101787 | ** |
| 101788 | ** The content of P3 registers starting at register P2 form |
| 101789 | ** an unpacked index key. This opcode removes that entry from the |
| 101790 | ** index opened by cursor P1. |
| 101791 | ** |
| 101792 | ** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error |
| 101793 | ** if no matching index entry is found. This happens when running |
| 101794 | ** an UPDATE or DELETE statement and the index entry to be updated |
| 101795 | ** or deleted is not found. For some uses of IdxDelete |
| 101796 | ** (example: the EXCEPT operator) it does not matter that no matching |
| 101797 | ** entry is found. For those cases, P5 is zero. Also, do not raise |
| 101798 | ** this (self-correcting and non-critical) error if in writable_schema mode. |
| 101799 | */ |
| 101800 | case OP_IdxDelete: { |
| 101801 | VdbeCursor *pC; |
| 101802 | BtCursor *pCrsr; |
| 101803 | int res; |
| @@ -101819,11 +101991,11 @@ | |
| 101819 | rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res); |
| 101820 | if( rc ) goto abort_due_to_error; |
| 101821 | if( res==0 ){ |
| 101822 | rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); |
| 101823 | if( rc ) goto abort_due_to_error; |
| 101824 | }else if( pOp->p5 && !sqlite3WritableSchema(db) ){ |
| 101825 | rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); |
| 101826 | goto abort_due_to_error; |
| 101827 | } |
| 101828 | assert( pC->deferredMoveto==0 ); |
| 101829 | pC->cacheStatus = CACHE_STALE; |
| @@ -110988,14 +111160,10 @@ | |
| 110988 | } |
| 110989 | } |
| 110990 | } |
| 110991 | #endif |
| 110992 | |
| 110993 | /* The ORDER BY and GROUP BY clauses may not refer to terms in |
| 110994 | ** outer queries |
| 110995 | */ |
| 110996 | sNC.pNext = 0; |
| 110997 | sNC.ncFlags |= NC_AllowAgg|NC_AllowWin; |
| 110998 | |
| 110999 | /* If this is a converted compound query, move the ORDER BY clause from |
| 111000 | ** the sub-query back to the parent query. At this point each term |
| 111001 | ** within the ORDER BY clause has been transformed to an integer value. |
| @@ -112575,11 +112743,13 @@ | |
| 112575 | const Expr *pExpr, /* The function invocation */ |
| 112576 | const FuncDef *pDef /* The function being invoked */ |
| 112577 | ){ |
| 112578 | assert( !IN_RENAME_OBJECT ); |
| 112579 | assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); |
| 112580 | if( ExprHasProperty(pExpr, EP_FromDDL) ){ |
| 112581 | if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0 |
| 112582 | || (pParse->db->flags & SQLITE_TrustedSchema)==0 |
| 112583 | ){ |
| 112584 | /* Functions prohibited in triggers and views if: |
| 112585 | ** (1) tagged with SQLITE_DIRECTONLY |
| @@ -113271,13 +113441,11 @@ | |
| 113271 | pNew->pNext = pNext; |
| 113272 | pNew->pPrior = 0; |
| 113273 | pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); |
| 113274 | pNew->iLimit = 0; |
| 113275 | pNew->iOffset = 0; |
| 113276 | pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral; |
| 113277 | pNew->addrOpenEphm[0] = -1; |
| 113278 | pNew->addrOpenEphm[1] = -1; |
| 113279 | pNew->nSelectRow = p->nSelectRow; |
| 113280 | pNew->pWith = sqlite3WithDup(db, p->pWith); |
| 113281 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 113282 | pNew->pWin = 0; |
| 113283 | pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); |
| @@ -115495,12 +115663,13 @@ | |
| 115495 | if( destIfFalse==destIfNull ){ |
| 115496 | /* Combine Step 3 and Step 5 into a single opcode */ |
| 115497 | if( ExprHasProperty(pExpr, EP_Subrtn) ){ |
| 115498 | const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); |
| 115499 | assert( pOp->opcode==OP_Once || pParse->nErr ); |
| 115500 | if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */ |
| 115501 | assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ); |
| 115502 | sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, |
| 115503 | rLhs, nVector); VdbeCoverage(v); |
| 115504 | } |
| 115505 | } |
| 115506 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, |
| @@ -118687,11 +118856,14 @@ | |
| 118687 | if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break; |
| 118688 | } |
| 118689 | if( pIEpr==0 ) break; |
| 118690 | if( NEVER(!ExprUseYTab(pExpr)) ) break; |
| 118691 | for(i=0; i<pSrcList->nSrc; i++){ |
| 118692 | if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break; |
| 118693 | } |
| 118694 | if( i>=pSrcList->nSrc ) break; |
| 118695 | if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */ |
| 118696 | if( pParse->nErr ){ return WRC_Abort; } |
| 118697 | |
| @@ -131401,11 +131573,11 @@ | |
| 131401 | ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY |
| 131402 | ** virtual tables |
| 131403 | ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS |
| 131404 | ** virtual tables if PRAGMA trusted_schema=ON. |
| 131405 | */ |
| 131406 | if( pParse->pToplevel!=0 |
| 131407 | && pTab->u.vtab.p->eVtabRisk > |
| 131408 | ((pParse->db->flags & SQLITE_TrustedSchema)!=0) |
| 131409 | ){ |
| 131410 | sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", |
| 131411 | pTab->zName); |
| @@ -132239,11 +132411,10 @@ | |
| 132239 | VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); |
| 132240 | r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, |
| 132241 | &iPartIdxLabel, pPrior, r1); |
| 132242 | sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, |
| 132243 | pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
| 132244 | sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */ |
| 132245 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 132246 | pPrior = pIdx; |
| 132247 | } |
| 132248 | } |
| 132249 | |
| @@ -133586,11 +133757,11 @@ | |
| 133586 | }else{ |
| 133587 | goto unistr_error; |
| 133588 | } |
| 133589 | } |
| 133590 | zOut[j] = 0; |
| 133591 | sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8); |
| 133592 | return; |
| 133593 | |
| 133594 | unistr_error: |
| 133595 | sqlite3_free(zOut); |
| 133596 | sqlite3_result_error(context, "invalid Unicode escape", -1); |
| @@ -133679,11 +133850,11 @@ | |
| 133679 | *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); |
| 133680 | *zOut++ = 0x80 + (u8)(c & 0x3F); |
| 133681 | } \ |
| 133682 | } |
| 133683 | *zOut = 0; |
| 133684 | sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8); |
| 133685 | } |
| 133686 | |
| 133687 | /* |
| 133688 | ** The hex() function. Interpret the argument as a blob. Return |
| 133689 | ** a hexadecimal rendering as text. |
| @@ -133708,11 +133879,11 @@ | |
| 133708 | *(z++) = hexdigits[(c>>4)&0xf]; |
| 133709 | *(z++) = hexdigits[c&0xf]; |
| 133710 | } |
| 133711 | *z = 0; |
| 133712 | sqlite3_result_text64(context, zHex, (u64)(z-zHex), |
| 133713 | sqlite3_free, SQLITE_UTF8); |
| 133714 | } |
| 133715 | } |
| 133716 | |
| 133717 | /* |
| 133718 | ** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr |
| @@ -134046,11 +134217,11 @@ | |
| 134046 | } |
| 134047 | } |
| 134048 | } |
| 134049 | z[j] = 0; |
| 134050 | assert( j<=n ); |
| 134051 | sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8); |
| 134052 | } |
| 134053 | |
| 134054 | /* |
| 134055 | ** The CONCAT(...) function. Generate a string result that is the |
| 134056 | ** concatentation of all non-null arguments. |
| @@ -141234,10 +141405,12 @@ | |
| 141234 | int (*set_errmsg)(sqlite3*,int,const char*); |
| 141235 | int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int); |
| 141236 | /* Version 3.52.0 and later */ |
| 141237 | void (*str_truncate)(sqlite3_str*,int); |
| 141238 | void (*str_free)(sqlite3_str*); |
| 141239 | }; |
| 141240 | |
| 141241 | /* |
| 141242 | ** This is the function signature used for all extension entry points. It |
| 141243 | ** is also defined in the file "loadext.c". |
| @@ -141575,10 +141748,12 @@ | |
| 141575 | #define sqlite3_set_errmsg sqlite3_api->set_errmsg |
| 141576 | #define sqlite3_db_status64 sqlite3_api->db_status64 |
| 141577 | /* Version 3.52.0 and later */ |
| 141578 | #define sqlite3_str_truncate sqlite3_api->str_truncate |
| 141579 | #define sqlite3_str_free sqlite3_api->str_free |
| 141580 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 141581 | |
| 141582 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 141583 | /* This case when the file really is being compiled as a loadable |
| 141584 | ** extension */ |
| @@ -142104,11 +142279,18 @@ | |
| 142104 | /* Version 3.51.0 and later */ |
| 142105 | sqlite3_set_errmsg, |
| 142106 | sqlite3_db_status64, |
| 142107 | /* Version 3.52.0 and later */ |
| 142108 | sqlite3_str_truncate, |
| 142109 | sqlite3_str_free |
| 142110 | }; |
| 142111 | |
| 142112 | /* True if x is the directory separator character |
| 142113 | */ |
| 142114 | #if SQLITE_OS_WIN |
| @@ -142206,37 +142388,46 @@ | |
| 142206 | |
| 142207 | /* If no entry point was specified and the default legacy |
| 142208 | ** entry point name "sqlite3_extension_init" was not found, then |
| 142209 | ** construct an entry point name "sqlite3_X_init" where the X is |
| 142210 | ** replaced by the lowercase value of every ASCII alphabetic |
| 142211 | ** character in the filename after the last "/" upto the first ".", |
| 142212 | ** and eliding the first three characters if they are "lib". |
| 142213 | ** Examples: |
| 142214 | ** |
| 142215 | ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example_init |
| 142216 | ** C:/lib/mathfuncs.dll ==> sqlite3_mathfuncs_init |
| 142217 | */ |
| 142218 | if( xInit==0 && zProc==0 ){ |
| 142219 | int iFile, iEntry, c; |
| 142220 | int ncFile = sqlite3Strlen30(zFile); |
| 142221 | zAltEntry = sqlite3_malloc64(ncFile+30); |
| 142222 | if( zAltEntry==0 ){ |
| 142223 | sqlite3OsDlClose(pVfs, handle); |
| 142224 | return SQLITE_NOMEM_BKPT; |
| 142225 | } |
| 142226 | memcpy(zAltEntry, "sqlite3_", 8); |
| 142227 | for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} |
| 142228 | iFile++; |
| 142229 | if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; |
| 142230 | for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ |
| 142231 | if( sqlite3Isalpha(c) ){ |
| 142232 | zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; |
| 142233 | } |
| 142234 | } |
| 142235 | memcpy(zAltEntry+iEntry, "_init", 6); |
| 142236 | zEntry = zAltEntry; |
| 142237 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 142238 | } |
| 142239 | if( xInit==0 ){ |
| 142240 | if( pzErrMsg ){ |
| 142241 | nMsg += strlen(zEntry) + 300; |
| 142242 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| @@ -147518,12 +147709,10 @@ | |
| 147518 | pNew->op = TK_SELECT; |
| 147519 | pNew->selFlags = selFlags; |
| 147520 | pNew->iLimit = 0; |
| 147521 | pNew->iOffset = 0; |
| 147522 | pNew->selId = ++pParse->nSelect; |
| 147523 | pNew->addrOpenEphm[0] = -1; |
| 147524 | pNew->addrOpenEphm[1] = -1; |
| 147525 | pNew->nSelectRow = 0; |
| 147526 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); |
| 147527 | pNew->pSrc = pSrc; |
| 147528 | pNew->pWhere = pWhere; |
| 147529 | pNew->pGroupBy = pGroupBy; |
| @@ -148667,33 +148856,10 @@ | |
| 148667 | codeOffset(v, p->iOffset, iContinue); |
| 148668 | } |
| 148669 | } |
| 148670 | |
| 148671 | switch( eDest ){ |
| 148672 | /* In this mode, write each query result to the key of the temporary |
| 148673 | ** table iParm. |
| 148674 | */ |
| 148675 | #ifndef SQLITE_OMIT_COMPOUND_SELECT |
| 148676 | case SRT_Union: { |
| 148677 | int r1; |
| 148678 | r1 = sqlite3GetTempReg(pParse); |
| 148679 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); |
| 148680 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); |
| 148681 | sqlite3ReleaseTempReg(pParse, r1); |
| 148682 | break; |
| 148683 | } |
| 148684 | |
| 148685 | /* Construct a record from the query result, but instead of |
| 148686 | ** saving that record, use it as a key to delete elements from |
| 148687 | ** the temporary table iParm. |
| 148688 | */ |
| 148689 | case SRT_Except: { |
| 148690 | sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol); |
| 148691 | break; |
| 148692 | } |
| 148693 | #endif /* SQLITE_OMIT_COMPOUND_SELECT */ |
| 148694 | |
| 148695 | /* Store the result as data using a unique key. |
| 148696 | */ |
| 148697 | case SRT_Fifo: |
| 148698 | case SRT_DistFifo: |
| 148699 | case SRT_Table: |
| @@ -149976,13 +150142,13 @@ | |
| 149976 | ** |
| 149977 | ** Space to hold the KeyInfo structure is obtained from malloc. The calling |
| 149978 | ** function is responsible for ensuring that this structure is eventually |
| 149979 | ** freed. |
| 149980 | */ |
| 149981 | static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ |
| 149982 | ExprList *pOrderBy = p->pOrderBy; |
| 149983 | int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0; |
| 149984 | sqlite3 *db = pParse->db; |
| 149985 | KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); |
| 149986 | if( pRet ){ |
| 149987 | int i; |
| 149988 | for(i=0; i<nOrderBy; i++){ |
| @@ -150111,21 +150277,41 @@ | |
| 150111 | |
| 150112 | /* Allocate cursors for Current, Queue, and Distinct. */ |
| 150113 | regCurrent = ++pParse->nMem; |
| 150114 | sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol); |
| 150115 | if( pOrderBy ){ |
| 150116 | KeyInfo *pKeyInfo = multiSelectOrderByKeyInfo(pParse, p, 1); |
| 150117 | sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0, |
| 150118 | (char*)pKeyInfo, P4_KEYINFO); |
| 150119 | destQueue.pOrderBy = pOrderBy; |
| 150120 | }else{ |
| 150121 | sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol); |
| 150122 | } |
| 150123 | VdbeComment((v, "Queue table")); |
| 150124 | if( iDistinct ){ |
| 150125 | p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0); |
| 150126 | p->selFlags |= SF_UsesEphemeral; |
| 150127 | } |
| 150128 | |
| 150129 | /* Detach the ORDER BY clause from the compound SELECT */ |
| 150130 | p->pOrderBy = 0; |
| 150131 | |
| @@ -150196,11 +150382,11 @@ | |
| 150196 | return; |
| 150197 | } |
| 150198 | #endif /* SQLITE_OMIT_CTE */ |
| 150199 | |
| 150200 | /* Forward references */ |
| 150201 | static int multiSelectOrderBy( |
| 150202 | Parse *pParse, /* Parsing context */ |
| 150203 | Select *p, /* The right-most of SELECTs to be coded */ |
| 150204 | SelectDest *pDest /* What to do with query results */ |
| 150205 | ); |
| 150206 | |
| @@ -150345,317 +150531,80 @@ | |
| 150345 | #ifndef SQLITE_OMIT_CTE |
| 150346 | if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){ |
| 150347 | generateWithRecursiveQuery(pParse, p, &dest); |
| 150348 | }else |
| 150349 | #endif |
| 150350 | |
| 150351 | /* Compound SELECTs that have an ORDER BY clause are handled separately. |
| 150352 | */ |
| 150353 | if( p->pOrderBy ){ |
| 150354 | return multiSelectOrderBy(pParse, p, pDest); |
| 150355 | }else{ |
| 150356 | |
| 150357 | #ifndef SQLITE_OMIT_EXPLAIN |
| 150358 | if( pPrior->pPrior==0 ){ |
| 150359 | ExplainQueryPlan((pParse, 1, "COMPOUND QUERY")); |
| 150360 | ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY")); |
| 150361 | } |
| 150362 | #endif |
| 150363 | |
| 150364 | /* Generate code for the left and right SELECT statements. |
| 150365 | */ |
| 150366 | switch( p->op ){ |
| 150367 | case TK_ALL: { |
| 150368 | int addr = 0; |
| 150369 | int nLimit = 0; /* Initialize to suppress harmless compiler warning */ |
| 150370 | assert( !pPrior->pLimit ); |
| 150371 | pPrior->iLimit = p->iLimit; |
| 150372 | pPrior->iOffset = p->iOffset; |
| 150373 | pPrior->pLimit = p->pLimit; |
| 150374 | TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); |
| 150375 | rc = sqlite3Select(pParse, pPrior, &dest); |
| 150376 | pPrior->pLimit = 0; |
| 150377 | if( rc ){ |
| 150378 | goto multi_select_end; |
| 150379 | } |
| 150380 | p->pPrior = 0; |
| 150381 | p->iLimit = pPrior->iLimit; |
| 150382 | p->iOffset = pPrior->iOffset; |
| 150383 | if( p->iLimit ){ |
| 150384 | addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); |
| 150385 | VdbeComment((v, "Jump ahead if LIMIT reached")); |
| 150386 | if( p->iOffset ){ |
| 150387 | sqlite3VdbeAddOp3(v, OP_OffsetLimit, |
| 150388 | p->iLimit, p->iOffset+1, p->iOffset); |
| 150389 | } |
| 150390 | } |
| 150391 | ExplainQueryPlan((pParse, 1, "UNION ALL")); |
| 150392 | TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); |
| 150393 | rc = sqlite3Select(pParse, p, &dest); |
| 150394 | testcase( rc!=SQLITE_OK ); |
| 150395 | pDelete = p->pPrior; |
| 150396 | p->pPrior = pPrior; |
| 150397 | p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); |
| 150398 | if( p->pLimit |
| 150399 | && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) |
| 150400 | && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) |
| 150401 | ){ |
| 150402 | p->nSelectRow = sqlite3LogEst((u64)nLimit); |
| 150403 | } |
| 150404 | if( addr ){ |
| 150405 | sqlite3VdbeJumpHere(v, addr); |
| 150406 | } |
| 150407 | break; |
| 150408 | } |
| 150409 | case TK_EXCEPT: |
| 150410 | case TK_UNION: { |
| 150411 | int unionTab; /* Cursor number of the temp table holding result */ |
| 150412 | u8 op = 0; /* One of the SRT_ operations to apply to self */ |
| 150413 | int priorOp; /* The SRT_ operation to apply to prior selects */ |
| 150414 | Expr *pLimit; /* Saved values of p->nLimit */ |
| 150415 | int addr; |
| 150416 | int emptyBypass = 0; /* IfEmpty opcode to bypass RHS */ |
| 150417 | SelectDest uniondest; |
| 150418 | |
| 150419 | |
| 150420 | testcase( p->op==TK_EXCEPT ); |
| 150421 | testcase( p->op==TK_UNION ); |
| 150422 | priorOp = SRT_Union; |
| 150423 | if( dest.eDest==priorOp ){ |
| 150424 | /* We can reuse a temporary table generated by a SELECT to our |
| 150425 | ** right. |
| 150426 | */ |
| 150427 | assert( p->pLimit==0 ); /* Not allowed on leftward elements */ |
| 150428 | unionTab = dest.iSDParm; |
| 150429 | }else{ |
| 150430 | /* We will need to create our own temporary table to hold the |
| 150431 | ** intermediate results. |
| 150432 | */ |
| 150433 | unionTab = pParse->nTab++; |
| 150434 | assert( p->pOrderBy==0 ); |
| 150435 | addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0); |
| 150436 | assert( p->addrOpenEphm[0] == -1 ); |
| 150437 | p->addrOpenEphm[0] = addr; |
| 150438 | findRightmost(p)->selFlags |= SF_UsesEphemeral; |
| 150439 | assert( p->pEList ); |
| 150440 | } |
| 150441 | |
| 150442 | |
| 150443 | /* Code the SELECT statements to our left |
| 150444 | */ |
| 150445 | assert( !pPrior->pOrderBy ); |
| 150446 | sqlite3SelectDestInit(&uniondest, priorOp, unionTab); |
| 150447 | TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); |
| 150448 | rc = sqlite3Select(pParse, pPrior, &uniondest); |
| 150449 | if( rc ){ |
| 150450 | goto multi_select_end; |
| 150451 | } |
| 150452 | |
| 150453 | /* Code the current SELECT statement |
| 150454 | */ |
| 150455 | if( p->op==TK_EXCEPT ){ |
| 150456 | op = SRT_Except; |
| 150457 | emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab); |
| 150458 | VdbeCoverage(v); |
| 150459 | }else{ |
| 150460 | assert( p->op==TK_UNION ); |
| 150461 | op = SRT_Union; |
| 150462 | } |
| 150463 | p->pPrior = 0; |
| 150464 | pLimit = p->pLimit; |
| 150465 | p->pLimit = 0; |
| 150466 | uniondest.eDest = op; |
| 150467 | ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", |
| 150468 | sqlite3SelectOpName(p->op))); |
| 150469 | TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); |
| 150470 | rc = sqlite3Select(pParse, p, &uniondest); |
| 150471 | testcase( rc!=SQLITE_OK ); |
| 150472 | assert( p->pOrderBy==0 ); |
| 150473 | pDelete = p->pPrior; |
| 150474 | p->pPrior = pPrior; |
| 150475 | p->pOrderBy = 0; |
| 150476 | if( p->op==TK_UNION ){ |
| 150477 | p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); |
| 150478 | } |
| 150479 | if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass); |
| 150480 | sqlite3ExprDelete(db, p->pLimit); |
| 150481 | p->pLimit = pLimit; |
| 150482 | p->iLimit = 0; |
| 150483 | p->iOffset = 0; |
| 150484 | |
| 150485 | /* Convert the data in the temporary table into whatever form |
| 150486 | ** it is that we currently need. |
| 150487 | */ |
| 150488 | assert( unionTab==dest.iSDParm || dest.eDest!=priorOp ); |
| 150489 | assert( p->pEList || db->mallocFailed ); |
| 150490 | if( dest.eDest!=priorOp && db->mallocFailed==0 ){ |
| 150491 | int iCont, iBreak, iStart; |
| 150492 | iBreak = sqlite3VdbeMakeLabel(pParse); |
| 150493 | iCont = sqlite3VdbeMakeLabel(pParse); |
| 150494 | computeLimitRegisters(pParse, p, iBreak); |
| 150495 | sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v); |
| 150496 | iStart = sqlite3VdbeCurrentAddr(v); |
| 150497 | selectInnerLoop(pParse, p, unionTab, |
| 150498 | 0, 0, &dest, iCont, iBreak); |
| 150499 | sqlite3VdbeResolveLabel(v, iCont); |
| 150500 | sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v); |
| 150501 | sqlite3VdbeResolveLabel(v, iBreak); |
| 150502 | sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0); |
| 150503 | } |
| 150504 | break; |
| 150505 | } |
| 150506 | default: assert( p->op==TK_INTERSECT ); { |
| 150507 | int tab1, tab2; |
| 150508 | int iCont, iBreak, iStart; |
| 150509 | Expr *pLimit; |
| 150510 | int addr, iLimit, iOffset; |
| 150511 | SelectDest intersectdest; |
| 150512 | int r1; |
| 150513 | int emptyBypass; |
| 150514 | |
| 150515 | /* INTERSECT is different from the others since it requires |
| 150516 | ** two temporary tables. Hence it has its own case. Begin |
| 150517 | ** by allocating the tables we will need. |
| 150518 | */ |
| 150519 | tab1 = pParse->nTab++; |
| 150520 | tab2 = pParse->nTab++; |
| 150521 | assert( p->pOrderBy==0 ); |
| 150522 | |
| 150523 | addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0); |
| 150524 | assert( p->addrOpenEphm[0] == -1 ); |
| 150525 | p->addrOpenEphm[0] = addr; |
| 150526 | findRightmost(p)->selFlags |= SF_UsesEphemeral; |
| 150527 | assert( p->pEList ); |
| 150528 | |
| 150529 | /* Code the SELECTs to our left into temporary table "tab1". |
| 150530 | */ |
| 150531 | sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); |
| 150532 | TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n")); |
| 150533 | rc = sqlite3Select(pParse, pPrior, &intersectdest); |
| 150534 | if( rc ){ |
| 150535 | goto multi_select_end; |
| 150536 | } |
| 150537 | |
| 150538 | /* Initialize LIMIT counters before checking to see if the LHS |
| 150539 | ** is empty, in case the jump is taken */ |
| 150540 | iBreak = sqlite3VdbeMakeLabel(pParse); |
| 150541 | computeLimitRegisters(pParse, p, iBreak); |
| 150542 | emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v); |
| 150543 | |
| 150544 | /* Code the current SELECT into temporary table "tab2" |
| 150545 | */ |
| 150546 | addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0); |
| 150547 | assert( p->addrOpenEphm[1] == -1 ); |
| 150548 | p->addrOpenEphm[1] = addr; |
| 150549 | |
| 150550 | /* Disable prior SELECTs and the LIMIT counters during the computation |
| 150551 | ** of the RHS select */ |
| 150552 | pLimit = p->pLimit; |
| 150553 | iLimit = p->iLimit; |
| 150554 | iOffset = p->iOffset; |
| 150555 | p->pPrior = 0; |
| 150556 | p->pLimit = 0; |
| 150557 | p->iLimit = 0; |
| 150558 | p->iOffset = 0; |
| 150559 | |
| 150560 | intersectdest.iSDParm = tab2; |
| 150561 | ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", |
| 150562 | sqlite3SelectOpName(p->op))); |
| 150563 | TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n")); |
| 150564 | rc = sqlite3Select(pParse, p, &intersectdest); |
| 150565 | testcase( rc!=SQLITE_OK ); |
| 150566 | pDelete = p->pPrior; |
| 150567 | p->pPrior = pPrior; |
| 150568 | if( p->nSelectRow>pPrior->nSelectRow ){ |
| 150569 | p->nSelectRow = pPrior->nSelectRow; |
| 150570 | } |
| 150571 | sqlite3ExprDelete(db, p->pLimit); |
| 150572 | |
| 150573 | /* Reinstate the LIMIT counters prior to running the final intersect */ |
| 150574 | p->pLimit = pLimit; |
| 150575 | p->iLimit = iLimit; |
| 150576 | p->iOffset = iOffset; |
| 150577 | |
| 150578 | /* Generate code to take the intersection of the two temporary |
| 150579 | ** tables. |
| 150580 | */ |
| 150581 | if( rc ) break; |
| 150582 | assert( p->pEList ); |
| 150583 | sqlite3VdbeAddOp1(v, OP_Rewind, tab1); |
| 150584 | r1 = sqlite3GetTempReg(pParse); |
| 150585 | iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1); |
| 150586 | iCont = sqlite3VdbeMakeLabel(pParse); |
| 150587 | sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); |
| 150588 | VdbeCoverage(v); |
| 150589 | sqlite3ReleaseTempReg(pParse, r1); |
| 150590 | selectInnerLoop(pParse, p, tab1, |
| 150591 | 0, 0, &dest, iCont, iBreak); |
| 150592 | sqlite3VdbeResolveLabel(v, iCont); |
| 150593 | sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v); |
| 150594 | sqlite3VdbeResolveLabel(v, iBreak); |
| 150595 | sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); |
| 150596 | sqlite3VdbeJumpHere(v, emptyBypass); |
| 150597 | sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); |
| 150598 | break; |
| 150599 | } |
| 150600 | } |
| 150601 | |
| 150602 | #ifndef SQLITE_OMIT_EXPLAIN |
| 150603 | if( p->pNext==0 ){ |
| 150604 | ExplainQueryPlanPop(pParse); |
| 150605 | } |
| 150606 | #endif |
| 150607 | } |
| 150608 | if( pParse->nErr ) goto multi_select_end; |
| 150609 | |
| 150610 | /* Compute collating sequences used by |
| 150611 | ** temporary tables needed to implement the compound select. |
| 150612 | ** Attach the KeyInfo structure to all temporary tables. |
| 150613 | ** |
| 150614 | ** This section is run by the right-most SELECT statement only. |
| 150615 | ** SELECT statements to the left always skip this part. The right-most |
| 150616 | ** SELECT might also skip this part if it has no ORDER BY clause and |
| 150617 | ** no temp tables are required. |
| 150618 | */ |
| 150619 | if( p->selFlags & SF_UsesEphemeral ){ |
| 150620 | int i; /* Loop counter */ |
| 150621 | KeyInfo *pKeyInfo; /* Collating sequence for the result set */ |
| 150622 | Select *pLoop; /* For looping through SELECT statements */ |
| 150623 | CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ |
| 150624 | int nCol; /* Number of columns in result set */ |
| 150625 | |
| 150626 | assert( p->pNext==0 ); |
| 150627 | assert( p->pEList!=0 ); |
| 150628 | nCol = p->pEList->nExpr; |
| 150629 | pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); |
| 150630 | if( !pKeyInfo ){ |
| 150631 | rc = SQLITE_NOMEM_BKPT; |
| 150632 | goto multi_select_end; |
| 150633 | } |
| 150634 | for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){ |
| 150635 | *apColl = multiSelectCollSeq(pParse, p, i); |
| 150636 | if( 0==*apColl ){ |
| 150637 | *apColl = db->pDfltColl; |
| 150638 | } |
| 150639 | } |
| 150640 | |
| 150641 | for(pLoop=p; pLoop; pLoop=pLoop->pPrior){ |
| 150642 | for(i=0; i<2; i++){ |
| 150643 | int addr = pLoop->addrOpenEphm[i]; |
| 150644 | if( addr<0 ){ |
| 150645 | /* If [0] is unused then [1] is also unused. So we can |
| 150646 | ** always safely abort as soon as the first unused slot is found */ |
| 150647 | assert( pLoop->addrOpenEphm[1]<0 ); |
| 150648 | break; |
| 150649 | } |
| 150650 | sqlite3VdbeChangeP2(v, addr, nCol); |
| 150651 | sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo), |
| 150652 | P4_KEYINFO); |
| 150653 | pLoop->addrOpenEphm[i] = -1; |
| 150654 | } |
| 150655 | } |
| 150656 | sqlite3KeyInfoUnref(pKeyInfo); |
| 150657 | } |
| 150658 | |
| 150659 | multi_select_end: |
| 150660 | pDest->iSdst = dest.iSdst; |
| 150661 | pDest->nSdst = dest.nSdst; |
| @@ -150683,12 +150632,12 @@ | |
| 150683 | |
| 150684 | /* |
| 150685 | ** Code an output subroutine for a coroutine implementation of a |
| 150686 | ** SELECT statement. |
| 150687 | ** |
| 150688 | ** The data to be output is contained in pIn->iSdst. There are |
| 150689 | ** pIn->nSdst columns to be output. pDest is where the output should |
| 150690 | ** be sent. |
| 150691 | ** |
| 150692 | ** regReturn is the number of the register holding the subroutine |
| 150693 | ** return address. |
| 150694 | ** |
| @@ -150713,10 +150662,12 @@ | |
| 150713 | ){ |
| 150714 | Vdbe *v = pParse->pVdbe; |
| 150715 | int iContinue; |
| 150716 | int addr; |
| 150717 | |
| 150718 | addr = sqlite3VdbeCurrentAddr(v); |
| 150719 | iContinue = sqlite3VdbeMakeLabel(pParse); |
| 150720 | |
| 150721 | /* Suppress duplicates for UNION, EXCEPT, and INTERSECT |
| 150722 | */ |
| @@ -150734,26 +150685,63 @@ | |
| 150734 | |
| 150735 | /* Suppress the first OFFSET entries if there is an OFFSET clause |
| 150736 | */ |
| 150737 | codeOffset(v, p->iOffset, iContinue); |
| 150738 | |
| 150739 | assert( pDest->eDest!=SRT_Exists ); |
| 150740 | assert( pDest->eDest!=SRT_Table ); |
| 150741 | switch( pDest->eDest ){ |
| 150742 | /* Store the result as data using a unique key. |
| 150743 | */ |
| 150744 | case SRT_EphemTab: { |
| 150745 | int r1 = sqlite3GetTempReg(pParse); |
| 150746 | int r2 = sqlite3GetTempReg(pParse); |
| 150747 | sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1); |
| 150748 | sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2); |
| 150749 | sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2); |
| 150750 | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| 150751 | sqlite3ReleaseTempReg(pParse, r2); |
| 150752 | sqlite3ReleaseTempReg(pParse, r1); |
| 150753 | break; |
| 150754 | } |
| 150755 | |
| 150756 | #ifndef SQLITE_OMIT_SUBQUERY |
| 150757 | /* If we are creating a set for an "expr IN (SELECT ...)". |
| 150758 | */ |
| 150759 | case SRT_Set: { |
| @@ -150796,14 +150784,74 @@ | |
| 150796 | } |
| 150797 | sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pIn->nSdst); |
| 150798 | sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); |
| 150799 | break; |
| 150800 | } |
| 150801 | |
| 150802 | /* If none of the above, then the result destination must be |
| 150803 | ** SRT_Output. This routine is never called with any other |
| 150804 | ** destination other than the ones handled above or SRT_Output. |
| 150805 | ** |
| 150806 | ** For SRT_Output, results are stored in a sequence of registers. |
| 150807 | ** Then the OP_ResultRow opcode is used to cause sqlite3_step() to |
| 150808 | ** return the next row of result. |
| 150809 | */ |
| @@ -150827,12 +150875,13 @@ | |
| 150827 | |
| 150828 | return addr; |
| 150829 | } |
| 150830 | |
| 150831 | /* |
| 150832 | ** Alternative compound select code generator for cases when there |
| 150833 | ** is an ORDER BY clause. |
| 150834 | ** |
| 150835 | ** We assume a query of the following form: |
| 150836 | ** |
| 150837 | ** <selectA> <operator> <selectB> ORDER BY <orderbylist> |
| 150838 | ** |
| @@ -150845,11 +150894,11 @@ | |
| 150845 | ** outA: Move the output of the selectA coroutine into the output |
| 150846 | ** of the compound query. |
| 150847 | ** |
| 150848 | ** outB: Move the output of the selectB coroutine into the output |
| 150849 | ** of the compound query. (Only generated for UNION and |
| 150850 | ** UNION ALL. EXCEPT and INSERTSECT never output a row that |
| 150851 | ** appears only in B.) |
| 150852 | ** |
| 150853 | ** AltB: Called when there is data from both coroutines and A<B. |
| 150854 | ** |
| 150855 | ** AeqB: Called when there is data from both coroutines and A==B. |
| @@ -150898,25 +150947,23 @@ | |
| 150898 | ** EofB: ... |
| 150899 | ** AltB: ... |
| 150900 | ** AeqB: ... |
| 150901 | ** AgtB: ... |
| 150902 | ** Init: initialize coroutine registers |
| 150903 | ** yield coA |
| 150904 | ** if eof(A) goto EofA |
| 150905 | ** yield coB |
| 150906 | ** if eof(B) goto EofB |
| 150907 | ** Cmpr: Compare A, B |
| 150908 | ** Jump AltB, AeqB, AgtB |
| 150909 | ** End: ... |
| 150910 | ** |
| 150911 | ** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not |
| 150912 | ** actually called using Gosub and they do not Return. EofA and EofB loop |
| 150913 | ** until all data is exhausted then jump to the "end" label. AltB, AeqB, |
| 150914 | ** and AgtB jump to either L2 or to one of EofA or EofB. |
| 150915 | */ |
| 150916 | #ifndef SQLITE_OMIT_COMPOUND_SELECT |
| 150917 | static int multiSelectOrderBy( |
| 150918 | Parse *pParse, /* Parsing context */ |
| 150919 | Select *p, /* The right-most of SELECTs to be coded */ |
| 150920 | SelectDest *pDest /* What to do with query results */ |
| 150921 | ){ |
| 150922 | int i, j; /* Loop counters */ |
| @@ -150993,30 +151040,33 @@ | |
| 150993 | } |
| 150994 | } |
| 150995 | } |
| 150996 | |
| 150997 | /* Compute the comparison permutation and keyinfo that is used with |
| 150998 | ** the permutation used to determine if the next |
| 150999 | ** row of results comes from selectA or selectB. Also add explicit |
| 151000 | ** collations to the ORDER BY clause terms so that when the subqueries |
| 151001 | ** to the right and the left are evaluated, they use the correct |
| 151002 | ** collation. |
| 151003 | */ |
| 151004 | aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); |
| 151005 | if( aPermute ){ |
| 151006 | struct ExprList_item *pItem; |
| 151007 | aPermute[0] = nOrderBy; |
| 151008 | for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ |
| 151009 | assert( pItem!=0 ); |
| 151010 | assert( pItem->u.x.iOrderByCol>0 ); |
| 151011 | assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); |
| 151012 | aPermute[i] = pItem->u.x.iOrderByCol - 1; |
| 151013 | } |
| 151014 | pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1); |
| 151015 | }else{ |
| 151016 | pKeyMerge = 0; |
| 151017 | } |
| 151018 | |
| 151019 | /* Allocate a range of temporary registers and the KeyInfo needed |
| 151020 | ** for the logic that removes duplicate result rows when the |
| 151021 | ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). |
| 151022 | */ |
| @@ -151091,11 +151141,11 @@ | |
| 151091 | /* Generate a coroutine to evaluate the SELECT statement to the |
| 151092 | ** left of the compound operator - the "A" select. |
| 151093 | */ |
| 151094 | addrSelectA = sqlite3VdbeCurrentAddr(v) + 1; |
| 151095 | addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA); |
| 151096 | VdbeComment((v, "left SELECT")); |
| 151097 | pPrior->iLimit = regLimitA; |
| 151098 | ExplainQueryPlan((pParse, 1, "LEFT")); |
| 151099 | sqlite3Select(pParse, pPrior, &destA); |
| 151100 | sqlite3VdbeEndCoroutine(v, regAddrA); |
| 151101 | sqlite3VdbeJumpHere(v, addr1); |
| @@ -151103,11 +151153,11 @@ | |
| 151103 | /* Generate a coroutine to evaluate the SELECT statement on |
| 151104 | ** the right - the "B" select |
| 151105 | */ |
| 151106 | addrSelectB = sqlite3VdbeCurrentAddr(v) + 1; |
| 151107 | addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB); |
| 151108 | VdbeComment((v, "right SELECT")); |
| 151109 | savedLimit = p->iLimit; |
| 151110 | savedOffset = p->iOffset; |
| 151111 | p->iLimit = regLimitB; |
| 151112 | p->iOffset = 0; |
| 151113 | ExplainQueryPlan((pParse, 1, "RIGHT")); |
| @@ -151117,20 +151167,20 @@ | |
| 151117 | sqlite3VdbeEndCoroutine(v, regAddrB); |
| 151118 | |
| 151119 | /* Generate a subroutine that outputs the current row of the A |
| 151120 | ** select as the next output row of the compound select. |
| 151121 | */ |
| 151122 | VdbeNoopComment((v, "Output routine for A")); |
| 151123 | addrOutA = generateOutputSubroutine(pParse, |
| 151124 | p, &destA, pDest, regOutA, |
| 151125 | regPrev, pKeyDup, labelEnd); |
| 151126 | |
| 151127 | /* Generate a subroutine that outputs the current row of the B |
| 151128 | ** select as the next output row of the compound select. |
| 151129 | */ |
| 151130 | if( op==TK_ALL || op==TK_UNION ){ |
| 151131 | VdbeNoopComment((v, "Output routine for B")); |
| 151132 | addrOutB = generateOutputSubroutine(pParse, |
| 151133 | p, &destB, pDest, regOutB, |
| 151134 | regPrev, pKeyDup, labelEnd); |
| 151135 | } |
| 151136 | sqlite3KeyInfoUnref(pKeyDup); |
| @@ -151139,14 +151189,16 @@ | |
| 151139 | ** are exhausted and only data in select B remains. |
| 151140 | */ |
| 151141 | if( op==TK_EXCEPT || op==TK_INTERSECT ){ |
| 151142 | addrEofA_noB = addrEofA = labelEnd; |
| 151143 | }else{ |
| 151144 | VdbeNoopComment((v, "eof-A subroutine")); |
| 151145 | addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); |
| 151146 | addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd); |
| 151147 | VdbeCoverage(v); |
| 151148 | sqlite3VdbeGoto(v, addrEofA); |
| 151149 | p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); |
| 151150 | } |
| 151151 | |
| 151152 | /* Generate a subroutine to run when the results from select B |
| @@ -151154,21 +151206,24 @@ | |
| 151154 | */ |
| 151155 | if( op==TK_INTERSECT ){ |
| 151156 | addrEofB = addrEofA; |
| 151157 | if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; |
| 151158 | }else{ |
| 151159 | VdbeNoopComment((v, "eof-B subroutine")); |
| 151160 | addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); |
| 151161 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v); |
| 151162 | sqlite3VdbeGoto(v, addrEofB); |
| 151163 | } |
| 151164 | |
| 151165 | /* Generate code to handle the case of A<B |
| 151166 | */ |
| 151167 | VdbeNoopComment((v, "A-lt-B subroutine")); |
| 151168 | addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); |
| 151169 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v); |
| 151170 | sqlite3VdbeGoto(v, labelCmpr); |
| 151171 | |
| 151172 | /* Generate code to handle the case of A==B |
| 151173 | */ |
| 151174 | if( op==TK_ALL ){ |
| @@ -151175,40 +151230,52 @@ | |
| 151175 | addrAeqB = addrAltB; |
| 151176 | }else if( op==TK_INTERSECT ){ |
| 151177 | addrAeqB = addrAltB; |
| 151178 | addrAltB++; |
| 151179 | }else{ |
| 151180 | VdbeNoopComment((v, "A-eq-B subroutine")); |
| 151181 | addrAeqB = |
| 151182 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v); |
| 151183 | sqlite3VdbeGoto(v, labelCmpr); |
| 151184 | } |
| 151185 | |
| 151186 | /* Generate code to handle the case of A>B |
| 151187 | */ |
| 151188 | VdbeNoopComment((v, "A-gt-B subroutine")); |
| 151189 | addrAgtB = sqlite3VdbeCurrentAddr(v); |
| 151190 | if( op==TK_ALL || op==TK_UNION ){ |
| 151191 | sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); |
| 151192 | } |
| 151193 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); |
| 151194 | sqlite3VdbeGoto(v, labelCmpr); |
| 151195 | |
| 151196 | /* This code runs once to initialize everything. |
| 151197 | */ |
| 151198 | sqlite3VdbeJumpHere(v, addr1); |
| 151199 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v); |
| 151200 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); |
| 151201 | |
| 151202 | /* Implement the main merge loop |
| 151203 | */ |
| 151204 | sqlite3VdbeResolveLabel(v, labelCmpr); |
| 151205 | sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); |
| 151206 | sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, |
| 151207 | (char*)pKeyMerge, P4_KEYINFO); |
| 151208 | sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); |
| 151209 | sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v); |
| 151210 | |
| 151211 | /* Jump to the this point in order to terminate the query. |
| 151212 | */ |
| 151213 | sqlite3VdbeResolveLabel(v, labelEnd); |
| 151214 | |
| @@ -152121,11 +152188,11 @@ | |
| 152121 | pItem->fg.jointype |= (jointype & JT_LTORJ); |
| 152122 | memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); |
| 152123 | } |
| 152124 | pSubitem->fg.jointype |= jointype; |
| 152125 | |
| 152126 | /* Now begin substituting subquery result set expressions for |
| 152127 | ** references to the iParent in the outer query. |
| 152128 | ** |
| 152129 | ** Example: |
| 152130 | ** |
| 152131 | ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; |
| @@ -152133,21 +152200,21 @@ | |
| 152133 | ** \_____________________ outer query ______________________________/ |
| 152134 | ** |
| 152135 | ** We look at every expression in the outer query and every place we see |
| 152136 | ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". |
| 152137 | */ |
| 152138 | if( pSub->pOrderBy && (pParent->selFlags & SF_NoopOrderBy)==0 ){ |
| 152139 | /* At this point, any non-zero iOrderByCol values indicate that the |
| 152140 | ** ORDER BY column expression is identical to the iOrderByCol'th |
| 152141 | ** expression returned by SELECT statement pSub. Since these values |
| 152142 | ** do not necessarily correspond to columns in SELECT statement pParent, |
| 152143 | ** zero them before transferring the ORDER BY clause. |
| 152144 | ** |
| 152145 | ** Not doing this may cause an error if a subsequent call to this |
| 152146 | ** function attempts to flatten a compound sub-query into pParent |
| 152147 | ** (the only way this can happen is if the compound sub-query is |
| 152148 | ** currently part of pSub->pSrc). See ticket [d11a6e908f]. */ |
| 152149 | ExprList *pOrderBy = pSub->pOrderBy; |
| 152150 | for(i=0; i<pOrderBy->nExpr; i++){ |
| 152151 | pOrderBy->a[i].u.x.iOrderByCol = 0; |
| 152152 | } |
| 152153 | assert( pParent->pOrderBy==0 ); |
| @@ -152995,18 +153062,18 @@ | |
| 152995 | ** These are rewritten as a subquery: |
| 152996 | ** |
| 152997 | ** SELECT * FROM (SELECT ... FROM t1 EXCEPT SELECT ... FROM t2) |
| 152998 | ** ORDER BY ... COLLATE ... |
| 152999 | ** |
| 153000 | ** This transformation is necessary because the multiSelectOrderBy() routine |
| 153001 | ** above that generates the code for a compound SELECT with an ORDER BY clause |
| 153002 | ** uses a merge algorithm that requires the same collating sequence on the |
| 153003 | ** result columns as on the ORDER BY clause. See ticket |
| 153004 | ** http://sqlite.org/src/info/6709574d2a |
| 153005 | ** |
| 153006 | ** This transformation is only needed for EXCEPT, INTERSECT, and UNION. |
| 153007 | ** The UNION ALL operator works fine with multiSelectOrderBy() even when |
| 153008 | ** there are COLLATE terms in the ORDER BY. |
| 153009 | */ |
| 153010 | static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ |
| 153011 | int i; |
| 153012 | Select *pNew; |
| @@ -153548,11 +153615,11 @@ | |
| 153548 | } |
| 153549 | sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1); |
| 153550 | } |
| 153551 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 153552 | else if( ALWAYS(IsVirtual(pTab)) |
| 153553 | && pFrom->fg.fromDDL |
| 153554 | && ALWAYS(pTab->u.vtab.p!=0) |
| 153555 | && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) |
| 153556 | ){ |
| 153557 | sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", |
| 153558 | pTab->zName); |
| @@ -154832,19 +154899,30 @@ | |
| 154832 | Expr *pSubWhere = pSub->pWhere; |
| 154833 | if( pSub->pSrc->nSrc==1 |
| 154834 | && (pSub->selFlags & SF_Aggregate)==0 |
| 154835 | && !pSub->pSrc->a[0].fg.isSubquery |
| 154836 | && pSub->pLimit==0 |
| 154837 | ){ |
| 154838 | memset(pWhere, 0, sizeof(*pWhere)); |
| 154839 | pWhere->op = TK_INTEGER; |
| 154840 | pWhere->u.iValue = 1; |
| 154841 | ExprSetProperty(pWhere, EP_IntValue); |
| 154842 | |
| 154843 | assert( p->pWhere!=0 ); |
| 154844 | pSub->pSrc->a[0].fg.fromExists = 1; |
| 154845 | pSub->pSrc->a[0].fg.jointype |= JT_CROSS; |
| 154846 | p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); |
| 154847 | if( pSubWhere ){ |
| 154848 | p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere); |
| 154849 | pSub->pWhere = 0; |
| 154850 | } |
| @@ -154969,10 +155047,34 @@ | |
| 154969 | memset(&sCtx, 0, sizeof(sCtx)); |
| 154970 | sCtx.pSrc = pSelect->pSrc; |
| 154971 | sqlite3WalkExprNN(&w, pSelect->pWhere); |
| 154972 | pSelect->selFlags &= ~SF_OnToWhere; |
| 154973 | } |
| 154974 | |
| 154975 | /* |
| 154976 | ** Generate byte-code for the SELECT statement given in the p argument. |
| 154977 | ** |
| 154978 | ** The results are returned according to the SelectDest structure. |
| @@ -155065,12 +155167,11 @@ | |
| 155065 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); |
| 155066 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); |
| 155067 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); |
| 155068 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue ); |
| 155069 | if( IgnorableDistinct(pDest) ){ |
| 155070 | assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || |
| 155071 | pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || |
| 155072 | pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); |
| 155073 | /* All of these destinations are also able to ignore the ORDER BY clause */ |
| 155074 | if( p->pOrderBy ){ |
| 155075 | #if TREETRACE_ENABLED |
| 155076 | TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); |
| @@ -155082,11 +155183,10 @@ | |
| 155082 | p->pOrderBy); |
| 155083 | testcase( pParse->earlyCleanup ); |
| 155084 | p->pOrderBy = 0; |
| 155085 | } |
| 155086 | p->selFlags &= ~(u32)SF_Distinct; |
| 155087 | p->selFlags |= SF_NoopOrderBy; |
| 155088 | } |
| 155089 | sqlite3SelectPrep(pParse, p, 0); |
| 155090 | if( pParse->nErr ){ |
| 155091 | goto select_end; |
| 155092 | } |
| @@ -155622,11 +155722,12 @@ | |
| 155622 | ** used for both the ORDER BY and DISTINCT processing. As originally |
| 155623 | ** written the query must use a temp-table for at least one of the ORDER |
| 155624 | ** BY and DISTINCT, and an index or separate temp-table for the other. |
| 155625 | */ |
| 155626 | if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct |
| 155627 | && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 |
| 155628 | && OptimizationEnabled(db, SQLITE_GroupByOrder) |
| 155629 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 155630 | && p->pWin==0 |
| 155631 | #endif |
| 155632 | ){ |
| @@ -155836,25 +155937,14 @@ | |
| 155836 | ** in the correct order. It also may not - the GROUP BY might use a |
| 155837 | ** database index that causes rows to be grouped together as required |
| 155838 | ** but not actually sorted. Either way, record the fact that the |
| 155839 | ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp |
| 155840 | ** variable. */ |
| 155841 | if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){ |
| 155842 | int ii; |
| 155843 | /* The GROUP BY processing doesn't care whether rows are delivered in |
| 155844 | ** ASC or DESC order - only that each group is returned contiguously. |
| 155845 | ** So set the ASC/DESC flags in the GROUP BY to match those in the |
| 155846 | ** ORDER BY to maximize the chances of rows being delivered in an |
| 155847 | ** order that makes the ORDER BY redundant. */ |
| 155848 | for(ii=0; ii<pGroupBy->nExpr; ii++){ |
| 155849 | u8 sortFlags; |
| 155850 | sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; |
| 155851 | pGroupBy->a[ii].fg.sortFlags = sortFlags; |
| 155852 | } |
| 155853 | if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ |
| 155854 | orderByGrp = 1; |
| 155855 | } |
| 155856 | } |
| 155857 | }else{ |
| 155858 | assert( 0==sqlite3LogEst(1) ); |
| 155859 | p->nSelectRow = 0; |
| 155860 | } |
| @@ -156849,15 +156939,20 @@ | |
| 156849 | } |
| 156850 | goto trigger_cleanup; |
| 156851 | } |
| 156852 | } |
| 156853 | |
| 156854 | /* Do not create a trigger on a system table */ |
| 156855 | if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ |
| 156856 | sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); |
| 156857 | goto trigger_cleanup; |
| 156858 | } |
| 156859 | |
| 156860 | /* INSTEAD of triggers are only for views and views only support INSTEAD |
| 156861 | ** of triggers. |
| 156862 | */ |
| 156863 | if( IsView(pTab) && tr_tm!=TK_INSTEAD ){ |
| @@ -160118,13 +160213,15 @@ | |
| 160118 | if( rc!=SQLITE_OK ) goto end_of_vacuum; |
| 160119 | assert( (db->nDb-1)==nDb ); |
| 160120 | pDb = &db->aDb[nDb]; |
| 160121 | assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); |
| 160122 | pTemp = pDb->pBt; |
| 160123 | if( pOut ){ |
| 160124 | sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); |
| 160125 | i64 sz = 0; |
| 160126 | if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ |
| 160127 | rc = SQLITE_ERROR; |
| 160128 | sqlite3SetString(pzErrMsg, db, "output file already exists"); |
| 160129 | goto end_of_vacuum; |
| 160130 | } |
| @@ -160132,12 +160229,20 @@ | |
| 160132 | |
| 160133 | /* For a VACUUM INTO, the pager-flags are set to the same values as |
| 160134 | ** they are for the database being vacuumed, except that PAGER_CACHESPILL |
| 160135 | ** is always set. */ |
| 160136 | pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); |
| 160137 | } |
| 160138 | nRes = sqlite3BtreeGetRequestedReserve(pMain); |
| 160139 | |
| 160140 | sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); |
| 160141 | sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); |
| 160142 | sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); |
| 160143 | |
| @@ -165646,10 +165751,38 @@ | |
| 165646 | sqlite3ValueFree(pVal); |
| 165647 | return rc; |
| 165648 | } |
| 165649 | #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ |
| 165650 | |
| 165651 | |
| 165652 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 165653 | /* |
| 165654 | ** Check to see if the pExpr expression is a form that needs to be passed |
| 165655 | ** to the xBestIndex method of virtual tables. Forms of interest include: |
| @@ -165682,19 +165815,10 @@ | |
| 165682 | unsigned char *peOp2, /* OUT: 0 for MATCH, or else an op2 value */ |
| 165683 | Expr **ppLeft, /* Column expression to left of MATCH/op2 */ |
| 165684 | Expr **ppRight /* Expression to left of MATCH/op2 */ |
| 165685 | ){ |
| 165686 | if( pExpr->op==TK_FUNCTION ){ |
| 165687 | static const struct Op2 { |
| 165688 | const char *zOp; |
| 165689 | unsigned char eOp2; |
| 165690 | } aOp[] = { |
| 165691 | { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, |
| 165692 | { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, |
| 165693 | { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, |
| 165694 | { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } |
| 165695 | }; |
| 165696 | ExprList *pList; |
| 165697 | Expr *pCol; /* Column reference */ |
| 165698 | int i; |
| 165699 | |
| 165700 | assert( ExprUseXList(pExpr) ); |
| @@ -165710,20 +165834,15 @@ | |
| 165710 | ** vtab_column MATCH expression |
| 165711 | ** MATCH(expression,vtab_column) |
| 165712 | */ |
| 165713 | pCol = pList->a[1].pExpr; |
| 165714 | assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); |
| 165715 | if( ExprIsVtab(pCol) ){ |
| 165716 | for(i=0; i<ArraySize(aOp); i++){ |
| 165717 | assert( !ExprHasProperty(pExpr, EP_IntValue) ); |
| 165718 | if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){ |
| 165719 | *peOp2 = aOp[i].eOp2; |
| 165720 | *ppRight = pList->a[0].pExpr; |
| 165721 | *ppLeft = pCol; |
| 165722 | return 1; |
| 165723 | } |
| 165724 | } |
| 165725 | } |
| 165726 | |
| 165727 | /* We can also match against the first column of overloaded |
| 165728 | ** functions where xFindFunction returns a value of at least |
| 165729 | ** SQLITE_INDEX_CONSTRAINT_FUNCTION. |
| @@ -165853,20 +165972,26 @@ | |
| 165853 | u16 eOp = pOne->eOperator | pTwo->eOperator; |
| 165854 | sqlite3 *db; /* Database connection (for malloc) */ |
| 165855 | Expr *pNew; /* New virtual expression */ |
| 165856 | int op; /* Operator for the combined expression */ |
| 165857 | int idxNew; /* Index in pWC of the next virtual term */ |
| 165858 | |
| 165859 | if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; |
| 165860 | if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; |
| 165861 | if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; |
| 165862 | if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp |
| 165863 | && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; |
| 165864 | assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 ); |
| 165865 | assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 ); |
| 165866 | if( sqlite3ExprCompare(0,pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return; |
| 165867 | if( sqlite3ExprCompare(0,pOne->pExpr->pRight, pTwo->pExpr->pRight,-1) )return; |
| 165868 | /* If we reach this point, it means the two subterms can be combined */ |
| 165869 | if( (eOp & (eOp-1))!=0 ){ |
| 165870 | if( eOp & (WO_LT|WO_LE) ){ |
| 165871 | eOp = WO_LE; |
| 165872 | }else{ |
| @@ -165873,11 +165998,11 @@ | |
| 165873 | assert( eOp & (WO_GT|WO_GE) ); |
| 165874 | eOp = WO_GE; |
| 165875 | } |
| 165876 | } |
| 165877 | db = pWC->pWInfo->pParse->db; |
| 165878 | pNew = sqlite3ExprDup(db, pOne->pExpr, 0); |
| 165879 | if( pNew==0 ) return; |
| 165880 | for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( op<TK_GE ); } |
| 165881 | pNew->op = op; |
| 165882 | idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); |
| 165883 | exprAnalyze(pSrc, pWC, idxNew); |
| @@ -168744,15 +168869,18 @@ | |
| 168744 | |
| 168745 | /* No matches cause a break out of the loop */ |
| 168746 | break; |
| 168747 | } |
| 168748 | if( i==n ){ |
| 168749 | nOrderBy = n; |
| 168750 | if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ |
| 168751 | eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); |
| 168752 | }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ |
| 168753 | eDistinct = 1; |
| 168754 | } |
| 168755 | } |
| 168756 | } |
| 168757 | |
| 168758 | /* Allocate the sqlite3_index_info structure |
| @@ -170158,10 +170286,71 @@ | |
| 170158 | p->u.btree.pIndex = 0; |
| 170159 | } |
| 170160 | } |
| 170161 | return rc; |
| 170162 | } |
| 170163 | |
| 170164 | /* |
| 170165 | ** Adjust the WhereLoop.nOut value downward to account for terms of the |
| 170166 | ** WHERE clause that reference the loop but which are not used by an |
| 170167 | ** index. |
| @@ -170187,10 +170376,17 @@ | |
| 170187 | ** of rows in the table. In other words, assume that x==EXPR will filter |
| 170188 | ** out at least 3 out of 4 rows. If EXPR is -1 or 0 or 1, then maybe the |
| 170189 | ** "x" column is boolean or else -1 or 0 or 1 is a common default value |
| 170190 | ** on the "x" column and so in that case only cap the output row estimate |
| 170191 | ** at 1/2 instead of 1/4. |
| 170192 | */ |
| 170193 | static void whereLoopOutputAdjust( |
| 170194 | WhereClause *pWC, /* The WHERE clause */ |
| 170195 | WhereLoop *pLoop, /* The loop to adjust downward */ |
| 170196 | LogEst nRow /* Number of rows in the entire table */ |
| @@ -170236,25 +170432,43 @@ | |
| 170236 | ** then use the probability provided by the application. */ |
| 170237 | pLoop->nOut += pTerm->truthProb; |
| 170238 | }else{ |
| 170239 | /* In the absence of explicit truth probabilities, use heuristics to |
| 170240 | ** guess a reasonable truth probability. */ |
| 170241 | pLoop->nOut--; |
| 170242 | if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 |
| 170243 | && (pTerm->wtFlags & TERM_HIGHTRUTH)==0 /* tag-20200224-1 */ |
| 170244 | ){ |
| 170245 | Expr *pRight = pTerm->pExpr->pRight; |
| 170246 | int k = 0; |
| 170247 | testcase( pTerm->pExpr->op==TK_IS ); |
| 170248 | if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){ |
| 170249 | k = 10; |
| 170250 | }else{ |
| 170251 | k = 20; |
| 170252 | } |
| 170253 | if( iReduce<k ){ |
| 170254 | pTerm->wtFlags |= TERM_HEURTRUTH; |
| 170255 | iReduce = k; |
| 170256 | } |
| 170257 | } |
| 170258 | } |
| 170259 | } |
| 170260 | } |
| @@ -170712,10 +170926,11 @@ | |
| 170712 | |
| 170713 | nOutUnadjusted = pNew->nOut; |
| 170714 | pNew->rRun += nInMul + nIn; |
| 170715 | pNew->nOut += nInMul + nIn; |
| 170716 | whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize); |
| 170717 | rc = whereLoopInsert(pBuilder, pNew); |
| 170718 | |
| 170719 | if( pNew->wsFlags & WHERE_COLUMN_RANGE ){ |
| 170720 | pNew->nOut = saved_nOut; |
| 170721 | }else{ |
| @@ -171308,10 +171523,12 @@ | |
| 171308 | ApplyCostMultiplier(pNew->rRun, pTab->costMult); |
| 171309 | whereLoopOutputAdjust(pWC, pNew, rSize); |
| 171310 | if( pSrc->fg.isSubquery ){ |
| 171311 | if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE; |
| 171312 | pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; |
| 171313 | } |
| 171314 | rc = whereLoopInsert(pBuilder, pNew); |
| 171315 | pNew->nOut = rSize; |
| 171316 | if( rc ) break; |
| 171317 | }else{ |
| @@ -171410,10 +171627,11 @@ | |
| 171410 | /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN |
| 171411 | ** because the cursor used to access the index might not be |
| 171412 | ** positioned to the correct row during the right-join no-match |
| 171413 | ** loop. */ |
| 171414 | }else{ |
| 171415 | rc = whereLoopInsert(pBuilder, pNew); |
| 171416 | } |
| 171417 | pNew->nOut = rSize; |
| 171418 | if( rc ) break; |
| 171419 | } |
| @@ -172072,11 +172290,11 @@ | |
| 172072 | SrcItem *pItem; |
| 172073 | SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; |
| 172074 | sqlite3 *db = pWInfo->pParse->db; |
| 172075 | int rc = SQLITE_OK; |
| 172076 | int bFirstPastRJ = 0; |
| 172077 | int hasRightJoin = 0; |
| 172078 | WhereLoop *pNew; |
| 172079 | |
| 172080 | |
| 172081 | /* Loop over the tables in the join, from left to right */ |
| 172082 | pNew = pBuilder->pNew; |
| @@ -172099,19 +172317,38 @@ | |
| 172099 | /* Add prerequisites to prevent reordering of FROM clause terms |
| 172100 | ** across CROSS joins and outer joins. The bFirstPastRJ boolean |
| 172101 | ** prevents the right operand of a RIGHT JOIN from being swapped with |
| 172102 | ** other elements even further to the right. |
| 172103 | ** |
| 172104 | ** The JT_LTORJ case and the hasRightJoin flag work together to |
| 172105 | ** prevent FROM-clause terms from moving from the right side of |
| 172106 | ** a LEFT JOIN over to the left side of that join if the LEFT JOIN |
| 172107 | ** is itself on the left side of a RIGHT JOIN. |
| 172108 | */ |
| 172109 | if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; |
| 172110 | mPrereq |= mPrior; |
| 172111 | bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; |
| 172112 | }else if( !hasRightJoin ){ |
| 172113 | mPrereq = 0; |
| 172114 | } |
| 172115 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 172116 | if( IsVirtual(pItem->pSTab) ){ |
| 172117 | SrcItem *p; |
| @@ -172330,13 +172567,11 @@ | |
| 172330 | if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue; |
| 172331 | }else{ |
| 172332 | pLoop = pLast; |
| 172333 | } |
| 172334 | if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ |
| 172335 | if( pLoop->u.vtab.isOrdered |
| 172336 | && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) |
| 172337 | ){ |
| 172338 | obSat = obDone; |
| 172339 | }else{ |
| 172340 | /* No further ORDER BY terms may be matched. So this call should |
| 172341 | ** return >=0, not -1. Clear isOrderDistinct to ensure it does so. */ |
| 172342 | isOrderDistinct = 0; |
| @@ -173673,10 +173908,11 @@ | |
| 173673 | pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); |
| 173674 | notReady &= ~pLoop->maskSelf; |
| 173675 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| 173676 | if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ |
| 173677 | pTerm->wtFlags |= TERM_CODED; |
| 173678 | } |
| 173679 | } |
| 173680 | if( i!=pWInfo->nLevel-1 ){ |
| 173681 | int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); |
| 173682 | memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); |
| @@ -174660,26 +174896,31 @@ | |
| 174660 | VdbeCoverageIf(v, op==OP_SeekGT); |
| 174661 | sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); |
| 174662 | } |
| 174663 | #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ |
| 174664 | } |
| 174665 | if( pTabList->a[pLevel->iFrom].fg.fromExists && i==pWInfo->nLevel-1 ){ |
| 174666 | /* If the EXISTS-to-JOIN optimization was applied, then the EXISTS |
| 174667 | ** loop(s) will be the inner-most loops of the join. There might be |
| 174668 | ** multiple EXISTS loops, but they will all be nested, and the join |
| 174669 | ** order will not have been changed by the query planner. If the |
| 174670 | ** inner-most EXISTS loop sees a single successful row, it should |
| 174671 | ** break out of *all* EXISTS loops. But only the inner-most of the |
| 174672 | ** nested EXISTS loops should do this breakout. */ |
| 174673 | int nOuter = 0; /* Nr of outer EXISTS that this one is nested within */ |
| 174674 | while( nOuter<i ){ |
| 174675 | if( !pTabList->a[pLevel[-nOuter-1].iFrom].fg.fromExists ) break; |
| 174676 | nOuter++; |
| 174677 | } |
| 174678 | testcase( nOuter>0 ); |
| 174679 | sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel[-nOuter].addrBrk); |
| 174680 | VdbeComment((v, "EXISTS break")); |
| 174681 | } |
| 174682 | sqlite3VdbeResolveLabel(v, pLevel->addrCont); |
| 174683 | if( pLevel->op!=OP_Noop ){ |
| 174684 | sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); |
| 174685 | sqlite3VdbeChangeP5(v, pLevel->p5); |
| @@ -184823,11 +185064,11 @@ | |
| 184823 | for(i=1; sqlite3Isdigit(z[i]); i++){} |
| 184824 | return i; |
| 184825 | } |
| 184826 | case CC_DOLLAR: |
| 184827 | case CC_VARALPHA: { |
| 184828 | int n = 0; |
| 184829 | testcase( z[0]=='$' ); testcase( z[0]=='@' ); |
| 184830 | testcase( z[0]==':' ); testcase( z[0]=='#' ); |
| 184831 | *tokenType = TK_VARIABLE; |
| 184832 | for(i=1; (c=z[i])!=0; i++){ |
| 184833 | if( IdChar(c) ){ |
| @@ -189448,10 +189689,16 @@ | |
| 189448 | /* |
| 189449 | ** Find existing client data. |
| 189450 | */ |
| 189451 | SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){ |
| 189452 | DbClientData *p; |
| 189453 | sqlite3_mutex_enter(db->mutex); |
| 189454 | for(p=db->pDbData; p; p=p->pNext){ |
| 189455 | if( strcmp(p->zName, zName)==0 ){ |
| 189456 | void *pResult = p->pData; |
| 189457 | sqlite3_mutex_leave(db->mutex); |
| @@ -192292,10 +192539,19 @@ | |
| 192292 | SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); |
| 192293 | |
| 192294 | #define fts3GetVarint32(p, piVal) ( \ |
| 192295 | (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \ |
| 192296 | ) |
| 192297 | |
| 192298 | /* fts3.c */ |
| 192299 | SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...); |
| 192300 | SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); |
| 192301 | SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); |
| @@ -193900,13 +194156,11 @@ | |
| 193900 | p->pSeekStmt = 0; |
| 193901 | }else{ |
| 193902 | zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); |
| 193903 | if( !zSql ) return SQLITE_NOMEM; |
| 193904 | p->bLock++; |
| 193905 | rc = sqlite3_prepare_v3( |
| 193906 | p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 |
| 193907 | ); |
| 193908 | p->bLock--; |
| 193909 | sqlite3_free(zSql); |
| 193910 | } |
| 193911 | if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1; |
| 193912 | } |
| @@ -195477,13 +195731,11 @@ | |
| 195477 | p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") |
| 195478 | ); |
| 195479 | } |
| 195480 | if( zSql ){ |
| 195481 | p->bLock++; |
| 195482 | rc = sqlite3_prepare_v3( |
| 195483 | p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 |
| 195484 | ); |
| 195485 | p->bLock--; |
| 195486 | sqlite3_free(zSql); |
| 195487 | }else{ |
| 195488 | rc = SQLITE_NOMEM; |
| 195489 | } |
| @@ -196102,10 +196354,11 @@ | |
| 196102 | int rc = SQLITE_OK; |
| 196103 | int bOk = 0; |
| 196104 | |
| 196105 | UNUSED_PARAMETER(isQuick); |
| 196106 | rc = sqlite3Fts3IntegrityCheck(p, &bOk); |
| 196107 | assert( rc!=SQLITE_CORRUPT_VTAB ); |
| 196108 | if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ |
| 196109 | *pzErr = sqlite3_mprintf("unable to validate the inverted index for" |
| 196110 | " FTS%d table %s.%s: %s", |
| 196111 | p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); |
| @@ -202713,10 +202966,28 @@ | |
| 202713 | #define SQL_SELECT_MXLEVEL 36 |
| 202714 | |
| 202715 | #define SQL_SELECT_LEVEL_RANGE2 37 |
| 202716 | #define SQL_UPDATE_LEVEL_IDX 38 |
| 202717 | #define SQL_UPDATE_LEVEL 39 |
| 202718 | |
| 202719 | /* |
| 202720 | ** This function is used to obtain an SQLite prepared statement handle |
| 202721 | ** for the statement identified by the second argument. If successful, |
| 202722 | ** *pp is set to the requested statement handle and SQLITE_OK returned. |
| @@ -202839,24 +203110,24 @@ | |
| 202839 | assert( SizeofArray(azSql)==SizeofArray(p->aStmt) ); |
| 202840 | assert( eStmt<SizeofArray(azSql) && eStmt>=0 ); |
| 202841 | |
| 202842 | pStmt = p->aStmt[eStmt]; |
| 202843 | if( !pStmt ){ |
| 202844 | int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; |
| 202845 | char *zSql; |
| 202846 | if( eStmt==SQL_CONTENT_INSERT ){ |
| 202847 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); |
| 202848 | }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ |
| 202849 | f &= ~SQLITE_PREPARE_NO_VTAB; |
| 202850 | zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); |
| 202851 | }else{ |
| 202852 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); |
| 202853 | } |
| 202854 | if( !zSql ){ |
| 202855 | rc = SQLITE_NOMEM; |
| 202856 | }else{ |
| 202857 | rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL); |
| 202858 | sqlite3_free(zSql); |
| 202859 | assert( rc==SQLITE_OK || pStmt==0 ); |
| 202860 | p->aStmt[eStmt] = pStmt; |
| 202861 | } |
| 202862 | } |
| @@ -206019,11 +206290,11 @@ | |
| 206019 | /* Compose and prepare an SQL statement to loop through the content table */ |
| 206020 | char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); |
| 206021 | if( !zSql ){ |
| 206022 | rc = SQLITE_NOMEM; |
| 206023 | }else{ |
| 206024 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 206025 | sqlite3_free(zSql); |
| 206026 | } |
| 206027 | |
| 206028 | if( rc==SQLITE_OK ){ |
| 206029 | sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3; |
| @@ -207772,11 +208043,11 @@ | |
| 207772 | |
| 207773 | zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); |
| 207774 | if( !zSql ){ |
| 207775 | rc = SQLITE_NOMEM; |
| 207776 | }else{ |
| 207777 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 207778 | sqlite3_free(zSql); |
| 207779 | } |
| 207780 | |
| 207781 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 207782 | i64 iDocid = sqlite3_column_int64(pStmt, 0); |
| @@ -211177,11 +211448,14 @@ | |
| 211177 | */ |
| 211178 | #define JSON_JSON 0x01 /* Result is always JSON */ |
| 211179 | #define JSON_SQL 0x02 /* Result is always SQL */ |
| 211180 | #define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ |
| 211181 | #define JSON_ISSET 0x04 /* json_set(), not json_insert() */ |
| 211182 | #define JSON_BLOB 0x08 /* Use the BLOB output format */ |
| 211183 | |
| 211184 | |
| 211185 | /* A parsed JSON value. Lifecycle: |
| 211186 | ** |
| 211187 | ** 1. JSON comes in and is parsed into a JSONB value in aBlob. The |
| @@ -211223,10 +211497,11 @@ | |
| 211223 | /* Allowed values for JsonParse.eEdit */ |
| 211224 | #define JEDIT_DEL 1 /* Delete if exists */ |
| 211225 | #define JEDIT_REPL 2 /* Overwrite if exists */ |
| 211226 | #define JEDIT_INS 3 /* Insert if not exists */ |
| 211227 | #define JEDIT_SET 4 /* Insert or overwrite */ |
| 211228 | |
| 211229 | /* |
| 211230 | ** Maximum nesting depth of JSON for this implementation. |
| 211231 | ** |
| 211232 | ** This limit is needed to avoid a stack overflow in the recursive |
| @@ -213719,11 +213994,12 @@ | |
| 213719 | /* |
| 213720 | ** Error returns from jsonLookupStep() |
| 213721 | */ |
| 213722 | #define JSON_LOOKUP_ERROR 0xffffffff |
| 213723 | #define JSON_LOOKUP_NOTFOUND 0xfffffffe |
| 213724 | #define JSON_LOOKUP_PATHERROR 0xfffffffd |
| 213725 | #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) |
| 213726 | |
| 213727 | /* Forward declaration */ |
| 213728 | static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); |
| 213729 | |
| @@ -213748,11 +214024,11 @@ | |
| 213748 | ** using the substructure. |
| 213749 | */ |
| 213750 | static u32 jsonCreateEditSubstructure( |
| 213751 | JsonParse *pParse, /* The original JSONB that is being edited */ |
| 213752 | JsonParse *pIns, /* Populate this with the blob data to insert */ |
| 213753 | const char *zTail /* Tail of the path that determins substructure */ |
| 213754 | ){ |
| 213755 | static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; |
| 213756 | int rc; |
| 213757 | memset(pIns, 0, sizeof(*pIns)); |
| 213758 | pIns->db = pParse->db; |
| @@ -213783,13 +214059,13 @@ | |
| 213783 | ** label, before returning. |
| 213784 | ** |
| 213785 | ** Return one of the JSON_LOOKUP error codes if problems are seen. |
| 213786 | ** |
| 213787 | ** This routine will also modify the blob. If pParse->eEdit is one of |
| 213788 | ** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be |
| 213789 | ** made to the selected value. If an edit is performed, then the return |
| 213790 | ** value does not necessarily point to the select element. If an edit |
| 213791 | ** is performed, the return value is only useful for detecting error |
| 213792 | ** conditions. |
| 213793 | */ |
| 213794 | static u32 jsonLookupStep( |
| 213795 | JsonParse *pParse, /* The JSON to search */ |
| @@ -213811,10 +214087,17 @@ | |
| 213811 | iRoot = iLabel; |
| 213812 | } |
| 213813 | jsonBlobEdit(pParse, iRoot, sz, 0, 0); |
| 213814 | }else if( pParse->eEdit==JEDIT_INS ){ |
| 213815 | /* Already exists, so json_insert() is a no-op */ |
| 213816 | }else{ |
| 213817 | /* json_set() or json_replace() */ |
| 213818 | jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); |
| 213819 | } |
| 213820 | } |
| @@ -213882,10 +214165,14 @@ | |
| 213882 | u32 nIns; /* Total bytes to insert (label+value) */ |
| 213883 | JsonParse v; /* BLOB encoding of the value to be inserted */ |
| 213884 | JsonParse ix; /* Header of the label to be inserted */ |
| 213885 | testcase( pParse->eEdit==JEDIT_INS ); |
| 213886 | testcase( pParse->eEdit==JEDIT_SET ); |
| 213887 | memset(&ix, 0, sizeof(ix)); |
| 213888 | ix.db = pParse->db; |
| 213889 | jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); |
| 213890 | pParse->oom |= ix.oom; |
| 213891 | rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); |
| @@ -213909,32 +214196,36 @@ | |
| 213909 | jsonParseReset(&v); |
| 213910 | jsonParseReset(&ix); |
| 213911 | return rc; |
| 213912 | } |
| 213913 | }else if( zPath[0]=='[' ){ |
| 213914 | x = pParse->aBlob[iRoot] & 0x0f; |
| 213915 | if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; |
| 213916 | n = jsonbPayloadSize(pParse, iRoot, &sz); |
| 213917 | k = 0; |
| 213918 | i = 1; |
| 213919 | while( sqlite3Isdigit(zPath[i]) ){ |
| 213920 | k = k*10 + zPath[i] - '0'; |
| 213921 | i++; |
| 213922 | } |
| 213923 | if( i<2 || zPath[i]!=']' ){ |
| 213924 | if( zPath[1]=='#' ){ |
| 213925 | k = jsonbArrayCount(pParse, iRoot); |
| 213926 | i = 2; |
| 213927 | if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ |
| 213928 | unsigned int nn = 0; |
| 213929 | i = 3; |
| 213930 | do{ |
| 213931 | nn = nn*10 + zPath[i] - '0'; |
| 213932 | i++; |
| 213933 | }while( sqlite3Isdigit(zPath[i]) ); |
| 213934 | if( nn>k ) return JSON_LOOKUP_NOTFOUND; |
| 213935 | k -= nn; |
| 213936 | } |
| 213937 | if( zPath[i]!=']' ){ |
| 213938 | return JSON_LOOKUP_PATHERROR; |
| 213939 | } |
| 213940 | }else{ |
| @@ -213942,25 +214233,26 @@ | |
| 213942 | } |
| 213943 | } |
| 213944 | j = iRoot+n; |
| 213945 | iEnd = j+sz; |
| 213946 | while( j<iEnd ){ |
| 213947 | if( k==0 ){ |
| 213948 | rc = jsonLookupStep(pParse, j, &zPath[i+1], 0); |
| 213949 | if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); |
| 213950 | return rc; |
| 213951 | } |
| 213952 | k--; |
| 213953 | n = jsonbPayloadSize(pParse, j, &sz); |
| 213954 | if( n==0 ) return JSON_LOOKUP_ERROR; |
| 213955 | j += n+sz; |
| 213956 | } |
| 213957 | if( j>iEnd ) return JSON_LOOKUP_ERROR; |
| 213958 | if( k>0 ) return JSON_LOOKUP_NOTFOUND; |
| 213959 | if( pParse->eEdit>=JEDIT_INS ){ |
| 213960 | JsonParse v; |
| 213961 | testcase( pParse->eEdit==JEDIT_INS ); |
| 213962 | testcase( pParse->eEdit==JEDIT_SET ); |
| 213963 | rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); |
| 213964 | if( !JSON_LOOKUP_ISERROR(rc) |
| 213965 | && jsonBlobMakeEditable(pParse, v.nBlob) |
| 213966 | ){ |
| @@ -214281,13 +214573,19 @@ | |
| 214281 | ** If ctx is not NULL then push the error message into ctx and return NULL. |
| 214282 | ** If ctx is NULL, then return the text of the error message. |
| 214283 | */ |
| 214284 | static char *jsonBadPathError( |
| 214285 | sqlite3_context *ctx, /* The function call containing the error */ |
| 214286 | const char *zPath /* The path with the problem */ |
| 214287 | ){ |
| 214288 | char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); |
| 214289 | if( ctx==0 ) return zMsg; |
| 214290 | if( zMsg ){ |
| 214291 | sqlite3_result_error(ctx, zMsg, -1); |
| 214292 | sqlite3_free(zMsg); |
| 214293 | }else{ |
| @@ -214300,17 +214598,17 @@ | |
| 214300 | ** arguments come in pairs where each pair contains a JSON path and |
| 214301 | ** content to insert or set at that patch. Do the updates |
| 214302 | ** and return the result. |
| 214303 | ** |
| 214304 | ** The specific operation is determined by eEdit, which can be one |
| 214305 | ** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. |
| 214306 | */ |
| 214307 | static void jsonInsertIntoBlob( |
| 214308 | sqlite3_context *ctx, |
| 214309 | int argc, |
| 214310 | sqlite3_value **argv, |
| 214311 | int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ |
| 214312 | ){ |
| 214313 | int i; |
| 214314 | u32 rc = 0; |
| 214315 | const char *zPath = 0; |
| 214316 | int flgs; |
| @@ -214358,11 +214656,11 @@ | |
| 214358 | jsonInsertIntoBlob_patherror: |
| 214359 | jsonParseFree(p); |
| 214360 | if( rc==JSON_LOOKUP_ERROR ){ |
| 214361 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 214362 | }else{ |
| 214363 | jsonBadPathError(ctx, zPath); |
| 214364 | } |
| 214365 | return; |
| 214366 | } |
| 214367 | |
| 214368 | /* |
| @@ -214800,11 +215098,11 @@ | |
| 214800 | i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); |
| 214801 | if( JSON_LOOKUP_ISERROR(i) ){ |
| 214802 | if( i==JSON_LOOKUP_NOTFOUND ){ |
| 214803 | /* no-op */ |
| 214804 | }else if( i==JSON_LOOKUP_PATHERROR ){ |
| 214805 | jsonBadPathError(ctx, zPath); |
| 214806 | }else{ |
| 214807 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 214808 | } |
| 214809 | eErr = 1; |
| 214810 | i = 0; |
| @@ -214905,11 +215203,11 @@ | |
| 214905 | } |
| 214906 | jsonStringTerminate(&jx); |
| 214907 | j = jsonLookupStep(p, 0, jx.zBuf, 0); |
| 214908 | jsonStringReset(&jx); |
| 214909 | }else{ |
| 214910 | jsonBadPathError(ctx, zPath); |
| 214911 | goto json_extract_error; |
| 214912 | } |
| 214913 | if( j<p->nBlob ){ |
| 214914 | if( argc==2 ){ |
| 214915 | if( flags & JSON_JSON ){ |
| @@ -214940,11 +215238,11 @@ | |
| 214940 | } |
| 214941 | }else if( j==JSON_LOOKUP_ERROR ){ |
| 214942 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 214943 | goto json_extract_error; |
| 214944 | }else{ |
| 214945 | jsonBadPathError(ctx, zPath); |
| 214946 | goto json_extract_error; |
| 214947 | } |
| 214948 | } |
| 214949 | if( argc>2 ){ |
| 214950 | jsonAppendChar(&jx, ']'); |
| @@ -215269,11 +215567,11 @@ | |
| 215269 | rc = jsonLookupStep(p, 0, zPath+1, 0); |
| 215270 | if( JSON_LOOKUP_ISERROR(rc) ){ |
| 215271 | if( rc==JSON_LOOKUP_NOTFOUND ){ |
| 215272 | continue; /* No-op */ |
| 215273 | }else if( rc==JSON_LOOKUP_PATHERROR ){ |
| 215274 | jsonBadPathError(ctx, zPath); |
| 215275 | }else{ |
| 215276 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215277 | } |
| 215278 | goto json_remove_done; |
| 215279 | } |
| @@ -215281,11 +215579,11 @@ | |
| 215281 | jsonReturnParse(ctx, p); |
| 215282 | jsonParseFree(p); |
| 215283 | return; |
| 215284 | |
| 215285 | json_remove_patherror: |
| 215286 | jsonBadPathError(ctx, zPath); |
| 215287 | |
| 215288 | json_remove_done: |
| 215289 | jsonParseFree(p); |
| 215290 | return; |
| 215291 | } |
| @@ -215325,20 +215623,22 @@ | |
| 215325 | static void jsonSetFunc( |
| 215326 | sqlite3_context *ctx, |
| 215327 | int argc, |
| 215328 | sqlite3_value **argv |
| 215329 | ){ |
| 215330 | |
| 215331 | int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); |
| 215332 | int bIsSet = (flags&JSON_ISSET)!=0; |
| 215333 | |
| 215334 | if( argc<1 ) return; |
| 215335 | if( (argc&1)==0 ) { |
| 215336 | jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); |
| 215337 | return; |
| 215338 | } |
| 215339 | jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); |
| 215340 | } |
| 215341 | |
| 215342 | /* |
| 215343 | ** json_type(JSON) |
| 215344 | ** json_type(JSON, PATH) |
| @@ -215359,19 +215659,19 @@ | |
| 215359 | if( p==0 ) return; |
| 215360 | if( argc==2 ){ |
| 215361 | zPath = (const char*)sqlite3_value_text(argv[1]); |
| 215362 | if( zPath==0 ) goto json_type_done; |
| 215363 | if( zPath[0]!='$' ){ |
| 215364 | jsonBadPathError(ctx, zPath); |
| 215365 | goto json_type_done; |
| 215366 | } |
| 215367 | i = jsonLookupStep(p, 0, zPath+1, 0); |
| 215368 | if( JSON_LOOKUP_ISERROR(i) ){ |
| 215369 | if( i==JSON_LOOKUP_NOTFOUND ){ |
| 215370 | /* no-op */ |
| 215371 | }else if( i==JSON_LOOKUP_PATHERROR ){ |
| 215372 | jsonBadPathError(ctx, zPath); |
| 215373 | }else{ |
| 215374 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215375 | } |
| 215376 | goto json_type_done; |
| 215377 | } |
| @@ -215623,16 +215923,15 @@ | |
| 215623 | jsonAppendSqlValue(pStr, argv[0]); |
| 215624 | } |
| 215625 | } |
| 215626 | static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ |
| 215627 | JsonString *pStr; |
| 215628 | pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); |
| 215629 | if( pStr ){ |
| 215630 | int flags; |
| 215631 | pStr->pCtx = ctx; |
| 215632 | jsonAppendChar(pStr, ']'); |
| 215633 | flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); |
| 215634 | if( pStr->eErr ){ |
| 215635 | jsonReturnString(pStr, 0, 0); |
| 215636 | return; |
| 215637 | }else if( flags & JSON_BLOB ){ |
| 215638 | jsonReturnStringAsBlob(pStr); |
| @@ -215649,10 +215948,13 @@ | |
| 215649 | pStr->bStatic = 1; |
| 215650 | }else{ |
| 215651 | sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); |
| 215652 | jsonStringTrimOneChar(pStr); |
| 215653 | } |
| 215654 | }else{ |
| 215655 | sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); |
| 215656 | } |
| 215657 | sqlite3_result_subtype(ctx, JSON_SUBTYPE); |
| 215658 | } |
| @@ -215745,16 +216047,15 @@ | |
| 215745 | } |
| 215746 | } |
| 215747 | } |
| 215748 | static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ |
| 215749 | JsonString *pStr; |
| 215750 | pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); |
| 215751 | if( pStr ){ |
| 215752 | int flags; |
| 215753 | jsonAppendChar(pStr, '}'); |
| 215754 | pStr->pCtx = ctx; |
| 215755 | flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); |
| 215756 | if( pStr->eErr ){ |
| 215757 | jsonReturnString(pStr, 0, 0); |
| 215758 | return; |
| 215759 | }else if( flags & JSON_BLOB ){ |
| 215760 | jsonReturnStringAsBlob(pStr); |
| @@ -215771,10 +216072,13 @@ | |
| 215771 | pStr->bStatic = 1; |
| 215772 | }else{ |
| 215773 | sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); |
| 215774 | jsonStringTrimOneChar(pStr); |
| 215775 | } |
| 215776 | }else{ |
| 215777 | sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); |
| 215778 | } |
| 215779 | sqlite3_result_subtype(ctx, JSON_SUBTYPE); |
| 215780 | } |
| @@ -216271,11 +216575,11 @@ | |
| 216271 | if( idxNum==3 ){ |
| 216272 | zRoot = (const char*)sqlite3_value_text(argv[1]); |
| 216273 | if( zRoot==0 ) return SQLITE_OK; |
| 216274 | if( zRoot[0]!='$' ){ |
| 216275 | sqlite3_free(cur->pVtab->zErrMsg); |
| 216276 | cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); |
| 216277 | jsonEachCursorReset(p); |
| 216278 | return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 216279 | } |
| 216280 | p->nRoot = sqlite3Strlen30(zRoot); |
| 216281 | if( zRoot[1]==0 ){ |
| @@ -216289,11 +216593,11 @@ | |
| 216289 | p->eType = 0; |
| 216290 | p->iEnd = 0; |
| 216291 | return SQLITE_OK; |
| 216292 | } |
| 216293 | sqlite3_free(cur->pVtab->zErrMsg); |
| 216294 | cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); |
| 216295 | jsonEachCursorReset(p); |
| 216296 | return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 216297 | } |
| 216298 | if( p->sParse.iLabel ){ |
| 216299 | p->i = p->sParse.iLabel; |
| @@ -216379,10 +216683,12 @@ | |
| 216379 | /* | | | | | | */ |
| 216380 | JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), |
| 216381 | JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), |
| 216382 | JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), |
| 216383 | JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), |
| 216384 | JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), |
| 216385 | JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), |
| 216386 | JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), |
| 216387 | JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc), |
| 216388 | JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc), |
| @@ -220226,11 +220532,11 @@ | |
| 220226 | tree.nBytesPerCell = 8 + 8 * tree.nDim; |
| 220227 | node.zData = (u8 *)sqlite3_value_blob(apArg[1]); |
| 220228 | if( node.zData==0 ) return; |
| 220229 | nData = sqlite3_value_bytes(apArg[1]); |
| 220230 | if( nData<4 ) return; |
| 220231 | if( nData<NCELL(&node)*tree.nBytesPerCell ) return; |
| 220232 | |
| 220233 | pOut = sqlite3_str_new(0); |
| 220234 | for(ii=0; ii<NCELL(&node); ii++){ |
| 220235 | RtreeCell cell; |
| 220236 | int jj; |
| @@ -231218,10 +231524,11 @@ | |
| 231218 | struct carray_bind { |
| 231219 | void *aData; /* The data */ |
| 231220 | int nData; /* Number of elements */ |
| 231221 | int mFlags; /* Control flags */ |
| 231222 | void (*xDel)(void*); /* Destructor for aData */ |
| 231223 | }; |
| 231224 | |
| 231225 | |
| 231226 | /* carray_cursor is a subclass of sqlite3_vtab_cursor which will |
| 231227 | ** serve as the underlying representation of a cursor that scans |
| @@ -231550,26 +231857,38 @@ | |
| 231550 | ** Destructor for the carray_bind object |
| 231551 | */ |
| 231552 | static void carrayBindDel(void *pPtr){ |
| 231553 | carray_bind *p = (carray_bind*)pPtr; |
| 231554 | if( p->xDel!=SQLITE_STATIC ){ |
| 231555 | p->xDel(p->aData); |
| 231556 | } |
| 231557 | sqlite3_free(p); |
| 231558 | } |
| 231559 | |
| 231560 | /* |
| 231561 | ** Invoke this interface in order to bind to the single-argument |
| 231562 | ** version of CARRAY(). |
| 231563 | */ |
| 231564 | SQLITE_API int sqlite3_carray_bind( |
| 231565 | sqlite3_stmt *pStmt, |
| 231566 | int idx, |
| 231567 | void *aData, |
| 231568 | int nData, |
| 231569 | int mFlags, |
| 231570 | void (*xDestroy)(void*) |
| 231571 | ){ |
| 231572 | carray_bind *pNew = 0; |
| 231573 | int i; |
| 231574 | int rc = SQLITE_OK; |
| 231575 | |
| @@ -231642,23 +231961,41 @@ | |
| 231642 | } |
| 231643 | }else{ |
| 231644 | memcpy(pNew->aData, aData, sz); |
| 231645 | } |
| 231646 | pNew->xDel = sqlite3_free; |
| 231647 | }else{ |
| 231648 | pNew->aData = aData; |
| 231649 | pNew->xDel = xDestroy; |
| 231650 | } |
| 231651 | return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel); |
| 231652 | |
| 231653 | carray_bind_error: |
| 231654 | if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){ |
| 231655 | xDestroy(aData); |
| 231656 | } |
| 231657 | sqlite3_free(pNew); |
| 231658 | return rc; |
| 231659 | } |
| 231660 | |
| 231661 | /* |
| 231662 | ** Invoke this routine to register the carray() function. |
| 231663 | */ |
| 231664 | SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3 *db){ |
| @@ -232017,10 +232354,24 @@ | |
| 232017 | ** bytes read. |
| 232018 | */ |
| 232019 | static int sessionVarintGet(const u8 *aBuf, int *piVal){ |
| 232020 | return getVarint32(aBuf, *piVal); |
| 232021 | } |
| 232022 | |
| 232023 | /* Load an unaligned and unsigned 32-bit integer */ |
| 232024 | #define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) |
| 232025 | |
| 232026 | /* |
| @@ -232312,31 +232663,31 @@ | |
| 232312 | for(i=0; i<pTab->nCol; i++){ |
| 232313 | int eType = *a; |
| 232314 | int isPK = pTab->abPK[i]; |
| 232315 | if( bPkOnly && isPK==0 ) continue; |
| 232316 | |
| 232317 | /* It is not possible for eType to be SQLITE_NULL here. The session |
| 232318 | ** module does not record changes for rows with NULL values stored in |
| 232319 | ** primary key columns. */ |
| 232320 | assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT |
| 232321 | || eType==SQLITE_TEXT || eType==SQLITE_BLOB |
| 232322 | || eType==SQLITE_NULL || eType==0 |
| 232323 | ); |
| 232324 | assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) ); |
| 232325 | |
| 232326 | if( isPK ){ |
| 232327 | a++; |
| 232328 | h = sessionHashAppendType(h, eType); |
| 232329 | if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ |
| 232330 | h = sessionHashAppendI64(h, sessionGetI64(a)); |
| 232331 | a += 8; |
| 232332 | }else{ |
| 232333 | int n; |
| 232334 | a += sessionVarintGet(a, &n); |
| 232335 | h = sessionHashAppendBlob(h, n, a); |
| 232336 | a += n; |
| 232337 | } |
| 232338 | }else{ |
| 232339 | a += sessionSerialLen(a); |
| 232340 | } |
| 232341 | } |
| 232342 | return (h % nBucket); |
| @@ -234741,14 +235092,17 @@ | |
| 234741 | *pnChangeset = 0; |
| 234742 | *ppChangeset = 0; |
| 234743 | } |
| 234744 | |
| 234745 | if( pSession->rc ) return pSession->rc; |
| 234746 | rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0); |
| 234747 | if( rc!=SQLITE_OK ) return rc; |
| 234748 | |
| 234749 | sqlite3_mutex_enter(sqlite3_db_mutex(db)); |
| 234750 | |
| 234751 | for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ |
| 234752 | if( pTab->nEntry ){ |
| 234753 | const char *zName = pTab->zName; |
| 234754 | int i; /* Used to iterate through hash buckets */ |
| @@ -235227,11 +235581,12 @@ | |
| 235227 | |
| 235228 | if( rc==SQLITE_OK ){ |
| 235229 | u8 *aVal = &pIn->aData[pIn->iNext]; |
| 235230 | if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ |
| 235231 | int nByte; |
| 235232 | pIn->iNext += sessionVarintGet(aVal, &nByte); |
| 235233 | rc = sessionInputBuffer(pIn, nByte); |
| 235234 | if( rc==SQLITE_OK ){ |
| 235235 | if( nByte<0 || nByte>pIn->nData-pIn->iNext ){ |
| 235236 | rc = SQLITE_CORRUPT_BKPT; |
| 235237 | }else{ |
| @@ -235280,11 +235635,12 @@ | |
| 235280 | int nCol = 0; |
| 235281 | int nRead = 0; |
| 235282 | |
| 235283 | rc = sessionInputBuffer(pIn, 9); |
| 235284 | if( rc==SQLITE_OK ){ |
| 235285 | nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol); |
| 235286 | /* The hard upper limit for the number of columns in an SQLite |
| 235287 | ** database table is, according to sqliteLimit.h, 32676. So |
| 235288 | ** consider any table-header that purports to have more than 65536 |
| 235289 | ** columns to be corrupt. This is convenient because otherwise, |
| 235290 | ** if the (nCol>65536) condition below were omitted, a sufficiently |
| @@ -235300,12 +235656,19 @@ | |
| 235300 | |
| 235301 | while( rc==SQLITE_OK ){ |
| 235302 | while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){ |
| 235303 | nRead++; |
| 235304 | } |
| 235305 | if( (pIn->iNext + nRead)<pIn->nData ) break; |
| 235306 | rc = sessionInputBuffer(pIn, nRead + 100); |
| 235307 | } |
| 235308 | *pnByte = nRead+1; |
| 235309 | return rc; |
| 235310 | } |
| 235311 | |
| @@ -235433,14 +235796,14 @@ | |
| 235433 | sqlite3ValueFree(p->apValue[i]); |
| 235434 | } |
| 235435 | memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); |
| 235436 | } |
| 235437 | |
| 235438 | /* Make sure the buffer contains at least 10 bytes of input data, or all |
| 235439 | ** remaining data if there are less than 10 bytes available. This is |
| 235440 | ** sufficient either for the 'T' or 'P' byte and the varint that follows |
| 235441 | ** it, or for the two single byte values otherwise. */ |
| 235442 | p->rc = sessionInputBuffer(&p->in, 2); |
| 235443 | if( p->rc!=SQLITE_OK ) return p->rc; |
| 235444 | |
| 235445 | p->in.iCurrent = p->in.iNext; |
| 235446 | sessionDiscardData(&p->in); |
| @@ -235466,15 +235829,17 @@ | |
| 235466 | ** corrupt changeset. */ |
| 235467 | assert( p->in.iNext==1 || p->zTab ); |
| 235468 | return (p->rc = SQLITE_CORRUPT_BKPT); |
| 235469 | } |
| 235470 | |
| 235471 | p->op = op; |
| 235472 | p->bIndirect = p->in.aData[p->in.iNext++]; |
| 235473 | if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ |
| 235474 | return (p->rc = SQLITE_CORRUPT_BKPT); |
| 235475 | } |
| 235476 | |
| 235477 | if( paRec ){ |
| 235478 | int nVal; /* Number of values to buffer */ |
| 235479 | if( p->bPatchset==0 && op==SQLITE_UPDATE ){ |
| 235480 | nVal = p->nCol * 2; |
| @@ -239307,11 +239672,17 @@ | |
| 239307 | # define FLEXARRAY |
| 239308 | #else |
| 239309 | # define FLEXARRAY 1 |
| 239310 | #endif |
| 239311 | |
| 239312 | #endif |
| 239313 | |
| 239314 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 239315 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 239316 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| 239317 | #define FTS5_MAX_TOKEN_SIZE 32768 |
| @@ -253198,11 +253569,11 @@ | |
| 253198 | ){ |
| 253199 | const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); |
| 253200 | int iSegid = pSeg->pSeg->iSegid; |
| 253201 | u8 *aPg = pSeg->pLeaf->p; |
| 253202 | int nPg = pSeg->pLeaf->nn; |
| 253203 | int iPgIdx = pSeg->pLeaf->szLeaf; |
| 253204 | |
| 253205 | u64 iDelta = 0; |
| 253206 | int iNextOff = 0; |
| 253207 | int iOff = 0; |
| 253208 | int nIdx = 0; |
| @@ -253277,11 +253648,11 @@ | |
| 253277 | iStart = iSOP + (nPos/2); |
| 253278 | iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); |
| 253279 | iSOP += fts5GetVarint32(&aPg[iSOP], nPos); |
| 253280 | } |
| 253281 | assert_nc( iSOP==pSeg->iLeafOffset ); |
| 253282 | iNextOff = pSeg->iLeafOffset + pSeg->nPos; |
| 253283 | } |
| 253284 | } |
| 253285 | |
| 253286 | iOff = iStart; |
| 253287 | |
| @@ -253357,35 +253728,35 @@ | |
| 253357 | if( iNextOff!=iPgIdx ){ |
| 253358 | /* This is the only position-list associated with the term, and there |
| 253359 | ** is another term following it on this page. So the subsequent term |
| 253360 | ** needs to be moved to replace the term associated with the entry |
| 253361 | ** being removed. */ |
| 253362 | int nPrefix = 0; |
| 253363 | int nSuffix = 0; |
| 253364 | int nPrefix2 = 0; |
| 253365 | int nSuffix2 = 0; |
| 253366 | |
| 253367 | iDelKeyOff = iNextOff; |
| 253368 | iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); |
| 253369 | iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); |
| 253370 | |
| 253371 | if( iKey!=1 ){ |
| 253372 | iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); |
| 253373 | } |
| 253374 | iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); |
| 253375 | |
| 253376 | nPrefix = MIN(nPrefix, nPrefix2); |
| 253377 | nSuffix = (nPrefix2 + nSuffix2) - nPrefix; |
| 253378 | |
| 253379 | if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ |
| 253380 | FTS5_CORRUPT_IDX(p); |
| 253381 | }else{ |
| 253382 | if( iKey!=1 ){ |
| 253383 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); |
| 253384 | } |
| 253385 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); |
| 253386 | if( nPrefix2>pSeg->term.n ){ |
| 253387 | FTS5_CORRUPT_IDX(p); |
| 253388 | }else if( nPrefix2>nPrefix ){ |
| 253389 | memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); |
| 253390 | iOff += (nPrefix2-nPrefix); |
| 253391 | } |
| @@ -253412,11 +253783,11 @@ | |
| 253412 | Fts5Data *pTerm = fts5DataRead(p, iId); |
| 253413 | if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ |
| 253414 | u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; |
| 253415 | int nTermIdx = pTerm->nn - pTerm->szLeaf; |
| 253416 | int iTermIdx = 0; |
| 253417 | int iTermOff = 0; |
| 253418 | |
| 253419 | while( 1 ){ |
| 253420 | u32 iVal = 0; |
| 253421 | int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); |
| 253422 | iTermOff += iVal; |
| @@ -253423,16 +253794,19 @@ | |
| 253423 | if( (iTermIdx+nByte)>=nTermIdx ) break; |
| 253424 | iTermIdx += nByte; |
| 253425 | } |
| 253426 | nTermIdx = iTermIdx; |
| 253427 | |
| 253428 | memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); |
| 253429 | fts5PutU16(&pTerm->p[2], iTermOff); |
| 253430 | |
| 253431 | fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); |
| 253432 | if( nTermIdx==0 ){ |
| 253433 | fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); |
| 253434 | } |
| 253435 | } |
| 253436 | fts5DataRelease(pTerm); |
| 253437 | } |
| 253438 | } |
| @@ -253451,11 +253825,13 @@ | |
| 253451 | int nShift = iNextOff - iOff; /* Distance to move them */ |
| 253452 | |
| 253453 | int iPrevKeyOut = 0; |
| 253454 | int iKeyIn = 0; |
| 253455 | |
| 253456 | memmove(&aPg[iOff], &aPg[iNextOff], nMove); |
| 253457 | iPgIdx -= nShift; |
| 253458 | nPg = iPgIdx; |
| 253459 | fts5PutU16(&aPg[2], iPgIdx); |
| 253460 | |
| 253461 | for(iIdx=0; iIdx<nIdx; /* no-op */){ |
| @@ -253889,11 +254265,11 @@ | |
| 253889 | if( nMerge<0 ){ |
| 253890 | Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct); |
| 253891 | fts5StructureRelease(pStruct); |
| 253892 | pStruct = pNew; |
| 253893 | nMin = 1; |
| 253894 | nMerge = nMerge*-1; |
| 253895 | } |
| 253896 | if( pStruct && pStruct->nLevel ){ |
| 253897 | if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){ |
| 253898 | fts5StructureWrite(p, pStruct); |
| 253899 | } |
| @@ -261097,11 +261473,11 @@ | |
| 261097 | int nArg, /* Number of args */ |
| 261098 | sqlite3_value **apUnused /* Function arguments */ |
| 261099 | ){ |
| 261100 | assert( nArg==0 ); |
| 261101 | UNUSED_PARAM2(nArg, apUnused); |
| 261102 | sqlite3_result_text(pCtx, "fts5: 2025-12-11 18:16:39 e785a80e4100c368dca8d73cb662cff4d0fd76734fa0f3fa9b5754a380f7c746", -1, SQLITE_TRANSIENT); |
| 261103 | } |
| 261104 | |
| 261105 | /* |
| 261106 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 261107 | ** |
| 261108 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** c476d956d0bd3065cf894de6f9d393b999ff with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -467,14 +467,14 @@ | |
| 467 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 468 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 469 | */ |
| 470 | #define SQLITE_VERSION "3.52.0" |
| 471 | #define SQLITE_VERSION_NUMBER 3052000 |
| 472 | #define SQLITE_SOURCE_ID "2026-02-04 20:51:27 c476d956d0bd3065cf894de6f9d393b999ff7d2268a35f01a6d88804789ab58f" |
| 473 | #define SQLITE_SCM_BRANCH "trunk" |
| 474 | #define SQLITE_SCM_TAGS "" |
| 475 | #define SQLITE_SCM_DATETIME "2026-02-04T20:51:27.822Z" |
| 476 | |
| 477 | /* |
| 478 | ** CAPI3REF: Run-Time Library Version Numbers |
| 479 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 480 | ** |
| @@ -4752,16 +4752,32 @@ | |
| 4752 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4753 | ** messages on the global error log when it is not. If the test compile |
| 4754 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4755 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4756 | ** logs the error. |
| 4757 | ** |
| 4758 | ** [[SQLITE_PREPARE_FROM_DDL]] <dt>SQLITE_PREPARE_FROM_DDL</dt> |
| 4759 | ** <dd>The SQLITE_PREPARE_FROM_DDL flag causes the SQL compiler to behave as if |
| 4760 | ** the SQL statement is part of a database schema. This makes a difference |
| 4761 | ** when the [SQLITE_DBCONFIG_TRUSTED_SCHEMA] option is set to off. |
| 4762 | ** When this option is used and SQLITE_DBCONFIG_TRUSTED_SCHEMA is off, |
| 4763 | ** SQL functions may not be called unless they are tagged with |
| 4764 | ** [SQLITE_INNOCUOUS] and virtual tables may not be used unless tagged |
| 4765 | ** with [SQLITE_VTAB_INNOCUOUS]. Use the SQLITE_PREPARE_FROM_DDL option |
| 4766 | ** when preparing SQL that is derived from parts of the database |
| 4767 | ** schema. In particular, virtual table implementations that |
| 4768 | ** run SQL statements based on the arguments to their CREATE VIRTUAL |
| 4769 | ** TABLE statement should use [sqlite3_prepare_v3()] and set the |
| 4770 | ** SQLITE_PREPARE_FROM_DLL flag to prevent bypass of the |
| 4771 | ** [SQLITE_DBCONFIG_TRUSTED_SCHEMA] security checks. |
| 4772 | ** </dl> |
| 4773 | */ |
| 4774 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4775 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4776 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4777 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4778 | #define SQLITE_PREPARE_FROM_DDL 0x20 |
| 4779 | |
| 4780 | /* |
| 4781 | ** CAPI3REF: Compiling An SQL Statement |
| 4782 | ** KEYWORDS: {SQL statement compiler} |
| 4783 | ** METHOD: sqlite3 |
| @@ -4771,12 +4787,13 @@ | |
| 4787 | ** program using one of these routines. Or, in other words, these routines |
| 4788 | ** are constructors for the [prepared statement] object. |
| 4789 | ** |
| 4790 | ** The preferred routine to use is [sqlite3_prepare_v2()]. The |
| 4791 | ** [sqlite3_prepare()] interface is legacy and should be avoided. |
| 4792 | ** [sqlite3_prepare_v3()] has an extra |
| 4793 | ** [SQLITE_PREPARE_FROM_DDL|"prepFlags" option] that is some times |
| 4794 | ** needed for special purpose or to pass along security restrictions. |
| 4795 | ** |
| 4796 | ** The use of the UTF-8 interfaces is preferred, as SQLite currently |
| 4797 | ** does all parsing using UTF-8. The UTF-16 interfaces are provided |
| 4798 | ** as a convenience. The UTF-16 interfaces work by converting the |
| 4799 | ** input text into UTF-8, then invoking the corresponding UTF-8 interface. |
| @@ -5177,12 +5194,12 @@ | |
| 5194 | ** it should be a pointer to well-formed UTF8 text. |
| 5195 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 5196 | ** it should be a pointer to well-formed UTF16 text. |
| 5197 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 5198 | ** it should be a pointer to a well-formed unicode string that is |
| 5199 | ** either UTF8 if the sixth parameter is SQLITE_UTF8 or SQLITE_UTF8_ZT, |
| 5200 | ** or UTF16 otherwise. |
| 5201 | ** |
| 5202 | ** [[byte-order determination rules]] ^The byte-order of |
| 5203 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 5204 | ** found in the first character, which is removed, or in the absence of a BOM |
| 5205 | ** the byte order is the native byte order of the host |
| @@ -5224,14 +5241,19 @@ | |
| 5241 | ** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the |
| 5242 | ** object is to be copied prior to the return from sqlite3_bind_*(). ^The |
| 5243 | ** object and pointer to it must remain valid until then. ^SQLite will then |
| 5244 | ** manage the lifetime of its private copy. |
| 5245 | ** |
| 5246 | ** ^The sixth argument (the E argument) |
| 5247 | ** to sqlite3_bind_text64(S,K,Z,N,D,E) must be one of |
| 5248 | ** [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], |
| 5249 | ** or [SQLITE_UTF16LE] to specify the encoding of the text in the |
| 5250 | ** third parameter, Z. The special value [SQLITE_UTF8_ZT] means that the |
| 5251 | ** string argument is both UTF-8 encoded and is zero-terminated. In other |
| 5252 | ** words, SQLITE_UTF8_ZT means that the Z array is allocated to hold at |
| 5253 | ** least N+1 bytes and that the Z[N] byte is zero. If |
| 5254 | ** the E argument to sqlite3_bind_text64(S,K,Z,N,D,E) is not one of the |
| 5255 | ** allowed values shown above, or if the text encoding is different |
| 5256 | ** from the encoding specified by the sixth parameter, then the behavior |
| 5257 | ** is undefined. |
| 5258 | ** |
| 5259 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
| @@ -6094,17 +6116,63 @@ | |
| 6116 | /* |
| 6117 | ** CAPI3REF: Text Encodings |
| 6118 | ** |
| 6119 | ** These constants define integer codes that represent the various |
| 6120 | ** text encodings supported by SQLite. |
| 6121 | ** |
| 6122 | ** <dl> |
| 6123 | ** [[SQLITE_UTF8]] <dt>SQLITE_UTF8</dt><dd>Text is encoding as UTF-8</dd> |
| 6124 | ** |
| 6125 | ** [[SQLITE_UTF16LE]] <dt>SQLITE_UTF16LE</dt><dd>Text is encoding as UTF-16 |
| 6126 | ** with each code point being expressed "little endian" - the least significant |
| 6127 | ** byte first. This is the usual encoding, for example on Windows.</dd> |
| 6128 | ** |
| 6129 | ** [[SQLITE_UTF16BE]] <dt>SQLITE_UTF16BE</dt><dd>Text is encoding as UTF-16 |
| 6130 | ** with each code point being expressed "big endian" - the most significant |
| 6131 | ** byte first. This encoding is less common, but is still sometimes seen, |
| 6132 | ** specially on older systems. |
| 6133 | ** |
| 6134 | ** [[SQLITE_UTF16]] <dt>SQLITE_UTF16</dt><dd>Text is encoding as UTF-16 |
| 6135 | ** with each code point being expressed either little endian or as big |
| 6136 | ** endian, according to the native endianness of the host computer. |
| 6137 | ** |
| 6138 | ** [[SQLITE_ANY]] <dt>SQLITE_ANY</dt><dd>This encoding value may only be used |
| 6139 | ** to declare the preferred text for [application-defined SQL functions] |
| 6140 | ** created using [sqlite3_create_function()] and similar. If the preferred |
| 6141 | ** encoding (the 4th parameter to sqlite3_create_function() - the eTextRep |
| 6142 | ** parameter) is SQLITE_ANY, that indicates that the function does not have |
| 6143 | ** a preference regarding the text encoding of its parameters and can take |
| 6144 | ** any text encoding that the SQLite core find convenient to supply. This |
| 6145 | ** option is deprecated. Please do not use it in new applications. |
| 6146 | ** |
| 6147 | ** [[SQLITE_UTF16_ALIGNED]] <dt>SQLITE_UTF16_ALIGNED</dt><dd>This encoding |
| 6148 | ** value may be used as the 3rd parameter (the eTextRep parameter) to |
| 6149 | ** [sqlite3_create_collation()] and similar. This encoding value means |
| 6150 | ** that the application-defined collating sequence created expects its |
| 6151 | ** input strings to be in UTF16 in native byte order, and that the start |
| 6152 | ** of the strings must be aligned to a 2-byte boundary. |
| 6153 | ** |
| 6154 | ** [[SQLITE_UTF8_ZT]] <dt>SQLITE_UTF8_ZT</dt><dd>This option can only be |
| 6155 | ** used to specify the text encoding to strings input to [sqlite3_result_text64()] |
| 6156 | ** and [sqlite3_bind_text64()]. It means that the input string (call it "z") |
| 6157 | ** is UTF-8 encoded and that it is zero-terminated. If the length parameter |
| 6158 | ** (call it "n") is non-negative, this encoding option means that the caller |
| 6159 | ** guarantees that z array contains at least n+1 bytes and that the z[n] |
| 6160 | ** byte has a value of zero. |
| 6161 | ** This option gives the same output as SQLITE_UTF8, but can be more efficient |
| 6162 | ** by avoiding the need to make a copy of the input string, in some cases. |
| 6163 | ** However, if z is allocated to hold fewer than n+1 bytes or if the |
| 6164 | ** z[n] byte is not zero, undefined behavior may result. |
| 6165 | ** </dl> |
| 6166 | */ |
| 6167 | #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ |
| 6168 | #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ |
| 6169 | #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ |
| 6170 | #define SQLITE_UTF16 4 /* Use native byte order */ |
| 6171 | #define SQLITE_ANY 5 /* Deprecated */ |
| 6172 | #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ |
| 6173 | #define SQLITE_UTF8_ZT 16 /* Zero-terminated UTF8 */ |
| 6174 | |
| 6175 | /* |
| 6176 | ** CAPI3REF: Function Flags |
| 6177 | ** |
| 6178 | ** These constants may be ORed together with the |
| @@ -6606,14 +6674,18 @@ | |
| 6674 | ** use for client data is to provide a mechanism for wrapper libraries |
| 6675 | ** to store additional information about an SQLite database connection. |
| 6676 | ** |
| 6677 | ** There is no limit (other than available memory) on the number of different |
| 6678 | ** client data pointers (with different names) that can be attached to a |
| 6679 | ** single database connection. However, the current implementation stores |
| 6680 | ** the content on a linked list. Insert and retrieval performance will |
| 6681 | ** be proportional to the number of entries. The design use case, and |
| 6682 | ** the use case for which the implementation is optimized, is |
| 6683 | ** that an application will store only small number of client data names, |
| 6684 | ** typically just one or two. This interface is not intended to be a |
| 6685 | ** generalized key/value store for thousands or millions of keys. It |
| 6686 | ** will work for that, but performance might be disappointing. |
| 6687 | ** |
| 6688 | ** There is no way to enumerate the client data pointers |
| 6689 | ** associated with a database connection. The N parameter can be thought |
| 6690 | ** of as a secret key such that only code that knows the secret key is able |
| 6691 | ** to access the associated data. |
| @@ -6717,14 +6789,18 @@ | |
| 6789 | ** ^The sqlite3_result_text(), sqlite3_result_text16(), |
| 6790 | ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces |
| 6791 | ** set the return value of the application-defined function to be |
| 6792 | ** a text string which is represented as UTF-8, UTF-16 native byte order, |
| 6793 | ** UTF-16 little endian, or UTF-16 big endian, respectively. |
| 6794 | ** ^The sqlite3_result_text64(C,Z,N,D,E) interface sets the return value of an |
| 6795 | ** application-defined function to be a text string in an encoding |
| 6796 | ** specified the E parameter, which must be one |
| 6797 | ** of [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], |
| 6798 | ** or [SQLITE_UTF16LE]. ^The special value [SQLITE_UTF8_ZT] means that |
| 6799 | ** the result text is both UTF-8 and zero-terminated. In other words, |
| 6800 | ** SQLITE_UTF8_ZT means that the Z array holds at least N+1 byes and that |
| 6801 | ** the Z[N] is zero. |
| 6802 | ** ^SQLite takes the text result from the application from |
| 6803 | ** the 2nd parameter of the sqlite3_result_text* interfaces. |
| 6804 | ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces |
| 6805 | ** other than sqlite3_result_text64() is negative, then SQLite computes |
| 6806 | ** the string length itself by searching the 2nd parameter for the first |
| @@ -6807,11 +6883,11 @@ | |
| 6883 | SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); |
| 6884 | SQLITE_API void sqlite3_result_int(sqlite3_context*, int); |
| 6885 | SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); |
| 6886 | SQLITE_API void sqlite3_result_null(sqlite3_context*); |
| 6887 | SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); |
| 6888 | SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char *z, sqlite3_uint64 n, |
| 6889 | void(*)(void*), unsigned char encoding); |
| 6890 | SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); |
| 6891 | SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6892 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6893 | SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); |
| @@ -7746,22 +7822,22 @@ | |
| 7822 | ** ^This interface loads an SQLite extension library from the named file. |
| 7823 | ** |
| 7824 | ** ^The sqlite3_load_extension() interface attempts to load an |
| 7825 | ** [SQLite extension] library contained in the file zFile. If |
| 7826 | ** the file cannot be loaded directly, attempts are made to load |
| 7827 | ** with various operating-system specific filename extensions added. |
| 7828 | ** So for example, if "samplelib" cannot be loaded, then names like |
| 7829 | ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might |
| 7830 | ** be tried also. |
| 7831 | ** |
| 7832 | ** ^The entry point is zProc. |
| 7833 | ** ^(zProc may be 0, in which case SQLite will try to come up with an |
| 7834 | ** entry point name on its own. It first tries "sqlite3_extension_init". |
| 7835 | ** If that does not work, it tries names of the form "sqlite3_X_init" |
| 7836 | ** where X consists of the lower-case equivalent of all ASCII alphabetic |
| 7837 | ** characters or all ASCII alphanumeric characters in the filename from |
| 7838 | ** the last "/" to the first following "." and omitting any initial "lib".)^ |
| 7839 | ** ^The sqlite3_load_extension() interface returns |
| 7840 | ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. |
| 7841 | ** ^If an error occurs and pzErrMsg is not 0, then the |
| 7842 | ** [sqlite3_load_extension()] interface shall attempt to |
| 7843 | ** fill *pzErrMsg with error message text stored in memory |
| @@ -11495,23 +11571,45 @@ | |
| 11571 | #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ |
| 11572 | |
| 11573 | /* |
| 11574 | ** CAPI3REF: Bind array values to the CARRAY table-valued function |
| 11575 | ** |
| 11576 | ** The sqlite3_carray_bind_v2(S,I,P,N,F,X,D) interface binds an array value to |
| 11577 | ** parameter that is the first argument of the [carray() table-valued function]. |
| 11578 | ** The S parameter is a pointer to the [prepared statement] that uses the carray() |
| 11579 | ** functions. I is the parameter index to be bound. I must be the index of the |
| 11580 | ** parameter that is the first argument to the carray() table-valued function. |
| 11581 | ** P is a pointer to the array to be bound, and N is the number of elements in |
| 11582 | ** the array. The F argument is one of constants [SQLITE_CARRAY_INT32], |
| 11583 | ** [SQLITE_CARRAY_INT64], [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], |
| 11584 | ** or [SQLITE_CARRAY_BLOB] to indicate the datatype of the array P. |
| 11585 | ** |
| 11586 | ** If the X argument is not a NULL pointer or one of the special |
| 11587 | ** values [SQLITE_STATIC] or [SQLITE_TRANSIENT], then SQLite will invoke |
| 11588 | ** the function X with argument D when it is finished using the data in P. |
| 11589 | ** The call to X(D) is a destructor for the array P. The destructor X(D) |
| 11590 | ** is invoked even if the call to sqlite3_carray_bind() fails. If the X |
| 11591 | ** parameter is the special-case value [SQLITE_STATIC], then SQLite assumes |
| 11592 | ** that the data static and the destructor is never invoked. If the X |
| 11593 | ** parameter is the special-case value [SQLITE_TRANSIENT], then |
| 11594 | ** sqlite3_carray_bind_v2() makes its own private copy of the data prior |
| 11595 | ** to returning and never invokes the destructor X. |
| 11596 | ** |
| 11597 | ** The sqlite3_carray_bind() function works the same as sqlite_carray_bind_v2() |
| 11598 | ** with a D parameter set to P. In other words, |
| 11599 | ** sqlite3_carray_bind(S,I,P,N,F,X) is same as |
| 11600 | ** sqlite3_carray_bind(S,I,P,N,F,X,P). |
| 11601 | */ |
| 11602 | SQLITE_API int sqlite3_carray_bind_v2( |
| 11603 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11604 | int i, /* Parameter index */ |
| 11605 | void *aData, /* Pointer to array data */ |
| 11606 | int nData, /* Number of data elements */ |
| 11607 | int mFlags, /* CARRAY flags */ |
| 11608 | void (*xDel)(void*), /* Destructor for aData */ |
| 11609 | void *pDel /* Optional argument to xDel() */ |
| 11610 | ); |
| 11611 | SQLITE_API int sqlite3_carray_bind( |
| 11612 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11613 | int i, /* Parameter index */ |
| 11614 | void *aData, /* Pointer to array data */ |
| 11615 | int nData, /* Number of data elements */ |
| @@ -14352,10 +14450,31 @@ | |
| 14450 | #ifndef SQLITE_MAX_LENGTH |
| 14451 | # define SQLITE_MAX_LENGTH 1000000000 |
| 14452 | #endif |
| 14453 | #define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ |
| 14454 | |
| 14455 | /* |
| 14456 | ** Maximum size of any single memory allocation. |
| 14457 | ** |
| 14458 | ** This is not a limit on the total amount of memory used. This is |
| 14459 | ** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc(). |
| 14460 | ** |
| 14461 | ** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 |
| 14462 | ** This provides a 256-byte safety margin for defense against 32-bit |
| 14463 | ** signed integer overflow bugs when computing memory allocation sizes. |
| 14464 | ** Paranoid applications might want to reduce the maximum allocation size |
| 14465 | ** further for an even larger safety margin. 0x3fffffff or 0x0fffffff |
| 14466 | ** or even smaller would be reasonable upper bounds on the size of a memory |
| 14467 | ** allocations for most applications. |
| 14468 | */ |
| 14469 | #ifndef SQLITE_MAX_ALLOCATION_SIZE |
| 14470 | # define SQLITE_MAX_ALLOCATION_SIZE 2147483391 |
| 14471 | #endif |
| 14472 | #if SQLITE_MAX_ALLOCATION_SIZE>2147483391 |
| 14473 | # error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391 |
| 14474 | #endif |
| 14475 | |
| 14476 | /* |
| 14477 | ** This is the maximum number of |
| 14478 | ** |
| 14479 | ** * Columns in a table |
| 14480 | ** * Columns in an index |
| @@ -17529,11 +17648,11 @@ | |
| 17648 | |
| 17649 | /* |
| 17650 | ** Additional non-public SQLITE_PREPARE_* flags |
| 17651 | */ |
| 17652 | #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ |
| 17653 | #define SQLITE_PREPARE_MASK 0x3f /* Mask of public flags */ |
| 17654 | |
| 17655 | /* |
| 17656 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 17657 | ** for a description of what each of these routines does. |
| 17658 | */ |
| @@ -20202,31 +20321,17 @@ | |
| 20321 | }; |
| 20322 | |
| 20323 | /* |
| 20324 | ** An instance of the following structure contains all information |
| 20325 | ** needed to generate code for a single SELECT statement. |
| 20326 | */ |
| 20327 | struct Select { |
| 20328 | u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ |
| 20329 | LogEst nSelectRow; /* Estimated number of result rows */ |
| 20330 | u32 selFlags; /* Various SF_* values */ |
| 20331 | int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ |
| 20332 | u32 selId; /* Unique identifier number for this SELECT */ |
| 20333 | ExprList *pEList; /* The fields of the result */ |
| 20334 | SrcList *pSrc; /* The FROM clause */ |
| 20335 | Expr *pWhere; /* The WHERE clause */ |
| 20336 | ExprList *pGroupBy; /* The GROUP BY clause */ |
| 20337 | Expr *pHaving; /* The HAVING clause */ |
| @@ -20254,28 +20359,28 @@ | |
| 20359 | #define SF_Distinct 0x0000001 /* Output should be DISTINCT */ |
| 20360 | #define SF_All 0x0000002 /* Includes the ALL keyword */ |
| 20361 | #define SF_Resolved 0x0000004 /* Identifiers have been resolved */ |
| 20362 | #define SF_Aggregate 0x0000008 /* Contains agg functions or a GROUP BY */ |
| 20363 | #define SF_HasAgg 0x0000010 /* Contains aggregate functions */ |
| 20364 | /* 0x0000020 // available for reuse */ |
| 20365 | #define SF_Expanded 0x0000040 /* sqlite3SelectExpand() called on this */ |
| 20366 | #define SF_HasTypeInfo 0x0000080 /* FROM subqueries have Table metadata */ |
| 20367 | #define SF_Compound 0x0000100 /* Part of a compound query */ |
| 20368 | #define SF_Values 0x0000200 /* Synthesized from VALUES clause */ |
| 20369 | #define SF_MultiValue 0x0000400 /* Single VALUES term with multiple rows */ |
| 20370 | #define SF_NestedFrom 0x0000800 /* Part of a parenthesized FROM clause */ |
| 20371 | #define SF_MinMaxAgg 0x0001000 /* Aggregate containing min() or max() */ |
| 20372 | #define SF_Recursive 0x0002000 /* The recursive part of a recursive CTE */ |
| 20373 | #define SF_FixedLimit 0x0004000 /* nSelectRow set by a constant LIMIT */ |
| 20374 | /* 0x0008000 // available for reuse */ |
| 20375 | #define SF_Converted 0x0010000 /* By convertCompoundSelectToSubquery() */ |
| 20376 | #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */ |
| 20377 | #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */ |
| 20378 | #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ |
| 20379 | #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ |
| 20380 | #define SF_View 0x0200000 /* SELECT statement is a view */ |
| 20381 | /* 0x0400000 // available for reuse */ |
| 20382 | #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ |
| 20383 | #define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ |
| 20384 | #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ |
| 20385 | #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ |
| 20386 | #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ |
| @@ -20291,15 +20396,10 @@ | |
| 20396 | /* |
| 20397 | ** The results of a SELECT can be distributed in several ways, as defined |
| 20398 | ** by one of the following macros. The "SRT" prefix means "SELECT Result |
| 20399 | ** Type". |
| 20400 | ** |
| 20401 | ** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result |
| 20402 | ** set is not empty. |
| 20403 | ** |
| 20404 | ** SRT_Discard Throw the results away. This is used by SELECT |
| 20405 | ** statements within triggers whose only purpose is |
| @@ -20359,34 +20459,32 @@ | |
| 20459 | ** column returned by the SELECT is used as the integer |
| 20460 | ** key. If (pDest->iSDParm>0), then the table is an index |
| 20461 | ** table. (pDest->iSDParm) is the number of key columns in |
| 20462 | ** each index record in this case. |
| 20463 | */ |
| 20464 | #define SRT_Exists 1 /* Store 1 if the result is not empty */ |
| 20465 | #define SRT_Discard 2 /* Do not save the results anywhere */ |
| 20466 | #define SRT_DistFifo 3 /* Like SRT_Fifo, but unique results only */ |
| 20467 | #define SRT_DistQueue 4 /* Like SRT_Queue, but unique results only */ |
| 20468 | |
| 20469 | /* The DISTINCT clause is ignored for all of the above. Not that |
| 20470 | ** IgnorableDistinct() implies IgnorableOrderby() */ |
| 20471 | #define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue) |
| 20472 | |
| 20473 | #define SRT_Queue 5 /* Store result in an queue */ |
| 20474 | #define SRT_Fifo 6 /* Store result as data with an automatic rowid */ |
| 20475 | |
| 20476 | /* The ORDER BY clause is ignored for all of the above */ |
| 20477 | #define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo) |
| 20478 | |
| 20479 | #define SRT_Output 7 /* Output each row of result */ |
| 20480 | #define SRT_Mem 8 /* Store result in a memory cell */ |
| 20481 | #define SRT_Set 9 /* Store results as keys in an index */ |
| 20482 | #define SRT_EphemTab 10 /* Create transient tab and store like SRT_Table */ |
| 20483 | #define SRT_Coroutine 11 /* Generate a single row of result */ |
| 20484 | #define SRT_Table 12 /* Store result as data with an automatic rowid */ |
| 20485 | #define SRT_Upfrom 13 /* Store result as data with rowid */ |
| 20486 | |
| 20487 | /* |
| 20488 | ** An instance of this object describes where to put of the results of |
| 20489 | ** a SELECT statement. |
| 20490 | */ |
| @@ -21018,10 +21116,11 @@ | |
| 21116 | u16 mWFlags; /* Use-dependent flags */ |
| 21117 | union { /* Extra data for callback */ |
| 21118 | NameContext *pNC; /* Naming context */ |
| 21119 | int n; /* A counter */ |
| 21120 | int iCur; /* A cursor number */ |
| 21121 | int sz; /* String literal length */ |
| 21122 | SrcList *pSrcList; /* FROM clause */ |
| 21123 | struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ |
| 21124 | struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */ |
| 21125 | int *aiCol; /* array of column indexes */ |
| 21126 | struct IdxCover *pIdxCover; /* Check for index coverage */ |
| @@ -21801,10 +21900,11 @@ | |
| 21900 | SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); |
| 21901 | #endif |
| 21902 | SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*, Parse*); |
| 21903 | SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); |
| 21904 | SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); |
| 21905 | SQLITE_PRIVATE int sqlite3ExprIsLikeOperator(const Expr*); |
| 21906 | SQLITE_PRIVATE int sqlite3IsRowid(const char*); |
| 21907 | SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); |
| 21908 | SQLITE_PRIVATE void sqlite3GenerateRowDelete( |
| 21909 | Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); |
| 21910 | SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); |
| @@ -24489,10 +24589,11 @@ | |
| 24589 | SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); |
| 24590 | SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); |
| 24591 | SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); |
| 24592 | SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); |
| 24593 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); |
| 24594 | SQLITE_PRIVATE int sqlite3VdbeMemSetText(Mem*, const char*, i64, void(*)(void*)); |
| 24595 | SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); |
| 24596 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 24597 | # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 |
| 24598 | #else |
| 24599 | SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); |
| @@ -31335,31 +31436,10 @@ | |
| 31436 | sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); |
| 31437 | } |
| 31438 | *pp = p; |
| 31439 | } |
| 31440 | |
| 31441 | /* |
| 31442 | ** Allocate memory. This routine is like sqlite3_malloc() except that it |
| 31443 | ** assumes the memory subsystem has already been initialized. |
| 31444 | */ |
| 31445 | SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ |
| @@ -31579,12 +31659,11 @@ | |
| 31659 | } |
| 31660 | if( nBytes==0 ){ |
| 31661 | sqlite3_free(pOld); /* IMP: R-26507-47431 */ |
| 31662 | return 0; |
| 31663 | } |
| 31664 | if( nBytes>SQLITE_MAX_ALLOCATION_SIZE ){ |
| 31665 | return 0; |
| 31666 | } |
| 31667 | nOld = sqlite3MallocSize(pOld); |
| 31668 | /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second |
| 31669 | ** argument to xRealloc is always a value returned by a prior call to |
| @@ -48881,78 +48960,54 @@ | |
| 48960 | { "WriteFile", (SYSCALL)WriteFile, 0 }, |
| 48961 | |
| 48962 | #define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ |
| 48963 | LPOVERLAPPED))aSyscall[61].pCurrent) |
| 48964 | |
| 48965 | /* |
| 48966 | ** For WaitForSingleObject(), MSDN says: |
| 48967 | ** |
| 48968 | ** Minimum supported client: Windows XP [desktop apps | UWP apps] |
| 48969 | ** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] |
| 48970 | */ |
| 48971 | { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, |
| 48972 | |
| 48973 | #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ |
| 48974 | DWORD))aSyscall[62].pCurrent) |
| 48975 | |
| 48976 | #if !SQLITE_OS_WINCE |
| 48977 | { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, |
| 48978 | #else |
| 48979 | { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, |
| 48980 | #endif |
| 48981 | |
| 48982 | #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ |
| 48983 | BOOL))aSyscall[63].pCurrent) |
| 48984 | |
| 48985 | { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, |
| 48986 | |
| 48987 | #define osGetNativeSystemInfo ((VOID(WINAPI*)( \ |
| 48988 | LPSYSTEM_INFO))aSyscall[64].pCurrent) |
| 48989 | |
| 48990 | #if defined(SQLITE_WIN32_HAS_ANSI) |
| 48991 | { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, |
| 48992 | #else |
| 48993 | { "OutputDebugStringA", (SYSCALL)0, 0 }, |
| 48994 | #endif |
| 48995 | |
| 48996 | #define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[65].pCurrent) |
| 48997 | |
| 48998 | #if defined(SQLITE_WIN32_HAS_WIDE) |
| 48999 | { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, |
| 49000 | #else |
| 49001 | { "OutputDebugStringW", (SYSCALL)0, 0 }, |
| 49002 | #endif |
| 49003 | |
| 49004 | #define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[66].pCurrent) |
| 49005 | |
| 49006 | { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, |
| 49007 | |
| 49008 | #define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[67].pCurrent) |
| 49009 | |
| 49010 | /* |
| 49011 | ** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" |
| 49012 | ** is really just a macro that uses a compiler intrinsic (e.g. x64). |
| 49013 | ** So do not try to make this is into a redefinable interface. |
| @@ -48963,38 +49018,38 @@ | |
| 49018 | #define osInterlockedCompareExchange InterlockedCompareExchange |
| 49019 | #else |
| 49020 | { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, |
| 49021 | |
| 49022 | #define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ |
| 49023 | SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[68].pCurrent) |
| 49024 | #endif /* defined(InterlockedCompareExchange) */ |
| 49025 | |
| 49026 | #if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID |
| 49027 | { "UuidCreate", (SYSCALL)UuidCreate, 0 }, |
| 49028 | #else |
| 49029 | { "UuidCreate", (SYSCALL)0, 0 }, |
| 49030 | #endif |
| 49031 | |
| 49032 | #define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[69].pCurrent) |
| 49033 | |
| 49034 | #if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID |
| 49035 | { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, |
| 49036 | #else |
| 49037 | { "UuidCreateSequential", (SYSCALL)0, 0 }, |
| 49038 | #endif |
| 49039 | |
| 49040 | #define osUuidCreateSequential \ |
| 49041 | ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[70].pCurrent) |
| 49042 | |
| 49043 | #if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 |
| 49044 | { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, |
| 49045 | #else |
| 49046 | { "FlushViewOfFile", (SYSCALL)0, 0 }, |
| 49047 | #endif |
| 49048 | |
| 49049 | #define osFlushViewOfFile \ |
| 49050 | ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[71].pCurrent) |
| 49051 | |
| 49052 | /* |
| 49053 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() |
| 49054 | ** to implement blocking locks with timeouts. MSDN says: |
| 49055 | ** |
| @@ -49007,11 +49062,11 @@ | |
| 49062 | { "CreateEvent", (SYSCALL)0, 0 }, |
| 49063 | #endif |
| 49064 | |
| 49065 | #define osCreateEvent ( \ |
| 49066 | (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ |
| 49067 | aSyscall[72].pCurrent \ |
| 49068 | ) |
| 49069 | |
| 49070 | /* |
| 49071 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() |
| 49072 | ** for the case where a timeout expires and a lock request must be |
| @@ -49024,68 +49079,68 @@ | |
| 49079 | { "CancelIo", (SYSCALL)CancelIo, 0 }, |
| 49080 | #else |
| 49081 | { "CancelIo", (SYSCALL)0, 0 }, |
| 49082 | #endif |
| 49083 | |
| 49084 | #define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[73].pCurrent) |
| 49085 | |
| 49086 | #if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32) |
| 49087 | { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 }, |
| 49088 | #else |
| 49089 | { "GetModuleHandleW", (SYSCALL)0, 0 }, |
| 49090 | #endif |
| 49091 | |
| 49092 | #define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[74].pCurrent) |
| 49093 | |
| 49094 | #ifndef _WIN32 |
| 49095 | { "getenv", (SYSCALL)getenv, 0 }, |
| 49096 | #else |
| 49097 | { "getenv", (SYSCALL)0, 0 }, |
| 49098 | #endif |
| 49099 | |
| 49100 | #define osGetenv ((const char *(*)(const char *))aSyscall[75].pCurrent) |
| 49101 | |
| 49102 | #ifndef _WIN32 |
| 49103 | { "getcwd", (SYSCALL)getcwd, 0 }, |
| 49104 | #else |
| 49105 | { "getcwd", (SYSCALL)0, 0 }, |
| 49106 | #endif |
| 49107 | |
| 49108 | #define osGetcwd ((char*(*)(char*,size_t))aSyscall[76].pCurrent) |
| 49109 | |
| 49110 | #ifndef _WIN32 |
| 49111 | { "readlink", (SYSCALL)readlink, 0 }, |
| 49112 | #else |
| 49113 | { "readlink", (SYSCALL)0, 0 }, |
| 49114 | #endif |
| 49115 | |
| 49116 | #define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[77].pCurrent) |
| 49117 | |
| 49118 | #ifndef _WIN32 |
| 49119 | { "lstat", (SYSCALL)lstat, 0 }, |
| 49120 | #else |
| 49121 | { "lstat", (SYSCALL)0, 0 }, |
| 49122 | #endif |
| 49123 | |
| 49124 | #define osLstat ((int(*)(const char*,struct stat*))aSyscall[78].pCurrent) |
| 49125 | |
| 49126 | #ifndef _WIN32 |
| 49127 | { "__errno", (SYSCALL)__errno, 0 }, |
| 49128 | #else |
| 49129 | { "__errno", (SYSCALL)0, 0 }, |
| 49130 | #endif |
| 49131 | |
| 49132 | #define osErrno (*((int*(*)(void))aSyscall[79].pCurrent)()) |
| 49133 | |
| 49134 | #ifndef _WIN32 |
| 49135 | { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 }, |
| 49136 | #else |
| 49137 | { "cygwin_conv_path", (SYSCALL)0, 0 }, |
| 49138 | #endif |
| 49139 | |
| 49140 | #define osCygwin_conv_path ((size_t(*)(unsigned int, \ |
| 49141 | const void *, void *, size_t))aSyscall[80].pCurrent) |
| 49142 | |
| 49143 | }; /* End of the overrideable system calls */ |
| 49144 | |
| 49145 | /* |
| 49146 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| @@ -53659,10 +53714,11 @@ | |
| 53714 | attr = INVALID_FILE_ATTRIBUTES; |
| 53715 | }else{ |
| 53716 | attr = sAttrData.dwFileAttributes; |
| 53717 | } |
| 53718 | }else{ |
| 53719 | if( noRetry ) lastErrno = osGetLastError(); |
| 53720 | winLogIoerr(cnt, __LINE__); |
| 53721 | if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ |
| 53722 | sqlite3_free(zConverted); |
| 53723 | return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", |
| 53724 | zFilename); |
| @@ -54399,11 +54455,16 @@ | |
| 54455 | }; |
| 54456 | #endif |
| 54457 | |
| 54458 | /* Double-check that the aSyscall[] array has been constructed |
| 54459 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 54460 | assert( ArraySize(aSyscall)==81 ); |
| 54461 | assert( strcmp(aSyscall[0].zName,"AreFileApisANSI")==0 ); |
| 54462 | assert( strcmp(aSyscall[20].zName,"GetFileAttributesA")==0 ); |
| 54463 | assert( strcmp(aSyscall[40].zName,"HeapReAlloc")==0 ); |
| 54464 | assert( strcmp(aSyscall[60].zName,"WideCharToMultiByte")==0 ); |
| 54465 | assert( strcmp(aSyscall[80].zName,"cygwin_conv_path")==0 ); |
| 54466 | |
| 54467 | /* get memory map allocation granularity */ |
| 54468 | memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); |
| 54469 | osGetSystemInfo(&winSysInfo); |
| 54470 | assert( winSysInfo.dwAllocationGranularity>0 ); |
| @@ -77426,11 +77487,11 @@ | |
| 77487 | } |
| 77488 | assert( cursorHoldsMutex(pCur) ); |
| 77489 | |
| 77490 | getCellInfo(pCur); |
| 77491 | aPayload = pCur->info.pPayload; |
| 77492 | assert( (u64)offset+(u64)amt <= (u64)pCur->info.nPayload ); |
| 77493 | |
| 77494 | assert( aPayload > pPage->aData ); |
| 77495 | if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){ |
| 77496 | /* Trying to read or write past the end of the data is an error. The |
| 77497 | ** conditional above is really: |
| @@ -77983,11 +78044,11 @@ | |
| 78044 | SQLITE_PRIVATE int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes){ |
| 78045 | int rc; |
| 78046 | |
| 78047 | assert( cursorOwnsBtShared(pCur) ); |
| 78048 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 78049 | if( NEVER(pCur->eState==CURSOR_VALID) ){ |
| 78050 | *pRes = 0; |
| 78051 | return SQLITE_OK; |
| 78052 | } |
| 78053 | rc = moveToRoot(pCur); |
| 78054 | if( rc==SQLITE_EMPTY ){ |
| @@ -85876,10 +85937,88 @@ | |
| 85937 | #endif |
| 85938 | |
| 85939 | |
| 85940 | return SQLITE_OK; |
| 85941 | } |
| 85942 | |
| 85943 | /* Like sqlite3VdbeMemSetStr() except: |
| 85944 | ** |
| 85945 | ** enc is always SQLITE_UTF8 |
| 85946 | ** pMem->db is always non-NULL |
| 85947 | */ |
| 85948 | SQLITE_PRIVATE int sqlite3VdbeMemSetText( |
| 85949 | Mem *pMem, /* Memory cell to set to string value */ |
| 85950 | const char *z, /* String pointer */ |
| 85951 | i64 n, /* Bytes in string, or negative */ |
| 85952 | void (*xDel)(void*) /* Destructor function */ |
| 85953 | ){ |
| 85954 | i64 nByte = n; /* New value for pMem->n */ |
| 85955 | u16 flags; |
| 85956 | |
| 85957 | assert( pMem!=0 ); |
| 85958 | assert( pMem->db!=0 ); |
| 85959 | assert( sqlite3_mutex_held(pMem->db->mutex) ); |
| 85960 | assert( !sqlite3VdbeMemIsRowSet(pMem) ); |
| 85961 | |
| 85962 | /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ |
| 85963 | if( !z ){ |
| 85964 | sqlite3VdbeMemSetNull(pMem); |
| 85965 | return SQLITE_OK; |
| 85966 | } |
| 85967 | |
| 85968 | if( nByte<0 ){ |
| 85969 | nByte = strlen(z); |
| 85970 | flags = MEM_Str|MEM_Term; |
| 85971 | }else{ |
| 85972 | flags = MEM_Str; |
| 85973 | } |
| 85974 | if( nByte>(i64)pMem->db->aLimit[SQLITE_LIMIT_LENGTH] ){ |
| 85975 | if( xDel && xDel!=SQLITE_TRANSIENT ){ |
| 85976 | if( xDel==SQLITE_DYNAMIC ){ |
| 85977 | sqlite3DbFree(pMem->db, (void*)z); |
| 85978 | }else{ |
| 85979 | xDel((void*)z); |
| 85980 | } |
| 85981 | } |
| 85982 | sqlite3VdbeMemSetNull(pMem); |
| 85983 | return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); |
| 85984 | } |
| 85985 | |
| 85986 | /* The following block sets the new values of Mem.z and Mem.xDel. It |
| 85987 | ** also sets a flag in local variable "flags" to indicate the memory |
| 85988 | ** management (one of MEM_Dyn or MEM_Static). |
| 85989 | */ |
| 85990 | if( xDel==SQLITE_TRANSIENT ){ |
| 85991 | i64 nAlloc = nByte + 1; |
| 85992 | testcase( nAlloc==31 ); |
| 85993 | testcase( nAlloc==32 ); |
| 85994 | if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ |
| 85995 | return SQLITE_NOMEM_BKPT; |
| 85996 | } |
| 85997 | assert( pMem->z!=0 ); |
| 85998 | memcpy(pMem->z, z, nByte); |
| 85999 | pMem->z[nByte] = 0; |
| 86000 | }else{ |
| 86001 | sqlite3VdbeMemRelease(pMem); |
| 86002 | pMem->z = (char *)z; |
| 86003 | if( xDel==SQLITE_DYNAMIC ){ |
| 86004 | pMem->zMalloc = pMem->z; |
| 86005 | pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); |
| 86006 | pMem->xDel = 0; |
| 86007 | }else if( xDel==SQLITE_STATIC ){ |
| 86008 | pMem->xDel = xDel; |
| 86009 | flags |= MEM_Static; |
| 86010 | }else{ |
| 86011 | pMem->xDel = xDel; |
| 86012 | flags |= MEM_Dyn; |
| 86013 | } |
| 86014 | } |
| 86015 | pMem->flags = flags; |
| 86016 | pMem->n = (int)(nByte & 0x7fffffff); |
| 86017 | pMem->enc = SQLITE_UTF8; |
| 86018 | return SQLITE_OK; |
| 86019 | } |
| 86020 | |
| 86021 | /* |
| 86022 | ** Move data out of a btree key or data field and into a Mem structure. |
| 86023 | ** The data is payload from the entry that pCur is currently pointing |
| 86024 | ** to. offset and amt determine what portion of the data or key to retrieve. |
| @@ -85900,11 +86039,16 @@ | |
| 86039 | u32 amt, /* Number of bytes to return. */ |
| 86040 | Mem *pMem /* OUT: Return data in this Mem structure. */ |
| 86041 | ){ |
| 86042 | int rc; |
| 86043 | pMem->flags = MEM_Null; |
| 86044 | testcase( amt==SQLITE_MAX_ALLOCATION_SIZE-1 ); |
| 86045 | testcase( amt==SQLITE_MAX_ALLOCATION_SIZE ); |
| 86046 | if( amt>=SQLITE_MAX_ALLOCATION_SIZE ){ |
| 86047 | return SQLITE_NOMEM_BKPT; |
| 86048 | } |
| 86049 | if( (u64)amt + (u64)offset > (u64)sqlite3BtreeMaxRecordSize(pCur) ){ |
| 86050 | return SQLITE_CORRUPT_BKPT; |
| 86051 | } |
| 86052 | if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){ |
| 86053 | rc = sqlite3BtreePayload(pCur, offset, amt, pMem->z); |
| 86054 | if( rc==SQLITE_OK ){ |
| @@ -86568,10 +86712,15 @@ | |
| 86712 | ** the column value into *ppVal. If *ppVal is initially NULL then a new |
| 86713 | ** sqlite3_value object is allocated. |
| 86714 | ** |
| 86715 | ** If *ppVal is initially NULL then the caller is responsible for |
| 86716 | ** ensuring that the value written into *ppVal is eventually freed. |
| 86717 | ** |
| 86718 | ** If the buffer does not contain a well-formed record, this routine may |
| 86719 | ** read several bytes past the end of the buffer. Callers must therefore |
| 86720 | ** ensure that any buffer which may contain a corrupt record is padded |
| 86721 | ** with at least 8 bytes of addressable memory. |
| 86722 | */ |
| 86723 | SQLITE_PRIVATE int sqlite3Stat4Column( |
| 86724 | sqlite3 *db, /* Database handle */ |
| 86725 | const void *pRec, /* Pointer to buffer containing record */ |
| 86726 | int nRec, /* Size of buffer pRec in bytes */ |
| @@ -89584,11 +89733,11 @@ | |
| 89733 | assert( !zName || xDel!=SQLITE_DYNAMIC ); |
| 89734 | return SQLITE_NOMEM_BKPT; |
| 89735 | } |
| 89736 | assert( p->aColName!=0 ); |
| 89737 | pColName = &(p->aColName[idx+var*p->nResAlloc]); |
| 89738 | rc = sqlite3VdbeMemSetText(pColName, zName, -1, xDel); |
| 89739 | assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); |
| 89740 | return rc; |
| 89741 | } |
| 89742 | |
| 89743 | /* |
| @@ -92662,11 +92811,27 @@ | |
| 92811 | int n, /* Bytes in string, or negative */ |
| 92812 | u8 enc, /* Encoding of z. 0 for BLOBs */ |
| 92813 | void (*xDel)(void*) /* Destructor function */ |
| 92814 | ){ |
| 92815 | Mem *pOut = pCtx->pOut; |
| 92816 | int rc; |
| 92817 | if( enc==SQLITE_UTF8 ){ |
| 92818 | rc = sqlite3VdbeMemSetText(pOut, z, n, xDel); |
| 92819 | }else if( enc==SQLITE_UTF8_ZT ){ |
| 92820 | /* It is usually considered improper to assert() on an input. However, |
| 92821 | ** the following assert() is checking for inputs that are documented |
| 92822 | ** to result in undefined behavior. */ |
| 92823 | assert( z==0 |
| 92824 | || n<0 |
| 92825 | || n>pOut->db->aLimit[SQLITE_LIMIT_LENGTH] |
| 92826 | || z[n]==0 |
| 92827 | ); |
| 92828 | rc = sqlite3VdbeMemSetText(pOut, z, n, xDel); |
| 92829 | pOut->flags |= MEM_Term; |
| 92830 | }else{ |
| 92831 | rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); |
| 92832 | } |
| 92833 | if( rc ){ |
| 92834 | if( rc==SQLITE_TOOBIG ){ |
| 92835 | sqlite3_result_error_toobig(pCtx); |
| 92836 | }else{ |
| 92837 | /* The only errors possible from sqlite3VdbeMemSetStr are |
| @@ -92855,11 +93020,11 @@ | |
| 93020 | return; |
| 93021 | } |
| 93022 | #endif |
| 93023 | assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); |
| 93024 | assert( xDel!=SQLITE_DYNAMIC ); |
| 93025 | if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){ |
| 93026 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 93027 | n &= ~(u64)1; |
| 93028 | } |
| 93029 | if( n>0x7fffffff ){ |
| 93030 | (void)invokeValueDestructor(z, xDel, pCtx); |
| @@ -93315,11 +93480,11 @@ | |
| 93480 | if( rc==SQLITE_OK ){ |
| 93481 | u32 sz; /* Size of current row in bytes */ |
| 93482 | Mem sMem; /* Raw content of current row */ |
| 93483 | memset(&sMem, 0, sizeof(sMem)); |
| 93484 | sz = sqlite3BtreePayloadSize(pRhs->pCsr); |
| 93485 | rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,sz,&sMem); |
| 93486 | if( rc==SQLITE_OK ){ |
| 93487 | u8 *zBuf = (u8*)sMem.z; |
| 93488 | u32 iSerial; |
| 93489 | sqlite3_value *pOut = pRhs->pOut; |
| 93490 | int iOff = 1 + getVarint32(&zBuf[1], iSerial); |
| @@ -93964,17 +94129,29 @@ | |
| 94129 | rc = vdbeUnbind(p, (u32)(i-1)); |
| 94130 | if( rc==SQLITE_OK ){ |
| 94131 | assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ |
| 94132 | if( zData!=0 ){ |
| 94133 | pVar = &p->aVar[i-1]; |
| 94134 | if( encoding==SQLITE_UTF8 ){ |
| 94135 | rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel); |
| 94136 | }else if( encoding==SQLITE_UTF8_ZT ){ |
| 94137 | /* It is usually consider improper to assert() on an input. |
| 94138 | ** However, the following assert() is checking for inputs |
| 94139 | ** that are documented to result in undefined behavior. */ |
| 94140 | assert( zData==0 |
| 94141 | || nData<0 |
| 94142 | || nData>pVar->db->aLimit[SQLITE_LIMIT_LENGTH] |
| 94143 | || ((u8*)zData)[nData]==0 |
| 94144 | ); |
| 94145 | rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel); |
| 94146 | pVar->flags |= MEM_Term; |
| 94147 | }else{ |
| 94148 | rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); |
| 94149 | if( encoding==0 ) pVar->enc = ENC(p->db); |
| 94150 | } |
| 94151 | if( rc==SQLITE_OK && encoding!=0 ){ |
| 94152 | rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); |
| 94153 | } |
| 94154 | if( rc ){ |
| 94155 | sqlite3Error(p->db, rc); |
| 94156 | rc = sqlite3ApiExit(p->db, rc); |
| 94157 | } |
| @@ -94082,11 +94259,11 @@ | |
| 94259 | sqlite3_uint64 nData, |
| 94260 | void (*xDel)(void*), |
| 94261 | unsigned char enc |
| 94262 | ){ |
| 94263 | assert( xDel!=SQLITE_DYNAMIC ); |
| 94264 | if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){ |
| 94265 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 94266 | nData &= ~(u64)1; |
| 94267 | } |
| 94268 | return bindText(pStmt, i, zData, nData, xDel, enc); |
| 94269 | } |
| @@ -101780,24 +101957,19 @@ | |
| 101957 | rc = sqlite3VdbeSorterWrite(pC, pIn2); |
| 101958 | if( rc) goto abort_due_to_error; |
| 101959 | break; |
| 101960 | } |
| 101961 | |
| 101962 | /* Opcode: IdxDelete P1 P2 P3 * * |
| 101963 | ** Synopsis: key=r[P2@P3] |
| 101964 | ** |
| 101965 | ** The content of P3 registers starting at register P2 form |
| 101966 | ** an unpacked index key. This opcode removes that entry from the |
| 101967 | ** index opened by cursor P1. |
| 101968 | ** |
| 101969 | ** Raise an SQLITE_CORRUPT_INDEX error if no matching index entry is found |
| 101970 | ** and not in writable_schema mode. |
| 101971 | */ |
| 101972 | case OP_IdxDelete: { |
| 101973 | VdbeCursor *pC; |
| 101974 | BtCursor *pCrsr; |
| 101975 | int res; |
| @@ -101819,11 +101991,11 @@ | |
| 101991 | rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res); |
| 101992 | if( rc ) goto abort_due_to_error; |
| 101993 | if( res==0 ){ |
| 101994 | rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); |
| 101995 | if( rc ) goto abort_due_to_error; |
| 101996 | }else if( !sqlite3WritableSchema(db) ){ |
| 101997 | rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); |
| 101998 | goto abort_due_to_error; |
| 101999 | } |
| 102000 | assert( pC->deferredMoveto==0 ); |
| 102001 | pC->cacheStatus = CACHE_STALE; |
| @@ -110988,14 +111160,10 @@ | |
| 111160 | } |
| 111161 | } |
| 111162 | } |
| 111163 | #endif |
| 111164 | |
| 111165 | sNC.ncFlags |= NC_AllowAgg|NC_AllowWin; |
| 111166 | |
| 111167 | /* If this is a converted compound query, move the ORDER BY clause from |
| 111168 | ** the sub-query back to the parent query. At this point each term |
| 111169 | ** within the ORDER BY clause has been transformed to an integer value. |
| @@ -112575,11 +112743,13 @@ | |
| 112743 | const Expr *pExpr, /* The function invocation */ |
| 112744 | const FuncDef *pDef /* The function being invoked */ |
| 112745 | ){ |
| 112746 | assert( !IN_RENAME_OBJECT ); |
| 112747 | assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); |
| 112748 | if( ExprHasProperty(pExpr, EP_FromDDL) |
| 112749 | || pParse->prepFlags & SQLITE_PREPARE_FROM_DDL |
| 112750 | ){ |
| 112751 | if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0 |
| 112752 | || (pParse->db->flags & SQLITE_TrustedSchema)==0 |
| 112753 | ){ |
| 112754 | /* Functions prohibited in triggers and views if: |
| 112755 | ** (1) tagged with SQLITE_DIRECTONLY |
| @@ -113271,13 +113441,11 @@ | |
| 113441 | pNew->pNext = pNext; |
| 113442 | pNew->pPrior = 0; |
| 113443 | pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); |
| 113444 | pNew->iLimit = 0; |
| 113445 | pNew->iOffset = 0; |
| 113446 | pNew->selFlags = p->selFlags; |
| 113447 | pNew->nSelectRow = p->nSelectRow; |
| 113448 | pNew->pWith = sqlite3WithDup(db, p->pWith); |
| 113449 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 113450 | pNew->pWin = 0; |
| 113451 | pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); |
| @@ -115495,12 +115663,13 @@ | |
| 115663 | if( destIfFalse==destIfNull ){ |
| 115664 | /* Combine Step 3 and Step 5 into a single opcode */ |
| 115665 | if( ExprHasProperty(pExpr, EP_Subrtn) ){ |
| 115666 | const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); |
| 115667 | assert( pOp->opcode==OP_Once || pParse->nErr ); |
| 115668 | if( pOp->p3>0 ){ /* tag-202407032019 */ |
| 115669 | assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) |
| 115670 | || pParse->nErr ); |
| 115671 | sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, |
| 115672 | rLhs, nVector); VdbeCoverage(v); |
| 115673 | } |
| 115674 | } |
| 115675 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, |
| @@ -118687,11 +118856,14 @@ | |
| 118856 | if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break; |
| 118857 | } |
| 118858 | if( pIEpr==0 ) break; |
| 118859 | if( NEVER(!ExprUseYTab(pExpr)) ) break; |
| 118860 | for(i=0; i<pSrcList->nSrc; i++){ |
| 118861 | if( pSrcList->a[i].iCursor==pIEpr->iDataCur ){ |
| 118862 | testcase( i>0 ); |
| 118863 | break; |
| 118864 | } |
| 118865 | } |
| 118866 | if( i>=pSrcList->nSrc ) break; |
| 118867 | if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */ |
| 118868 | if( pParse->nErr ){ return WRC_Abort; } |
| 118869 | |
| @@ -131401,11 +131573,11 @@ | |
| 131573 | ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY |
| 131574 | ** virtual tables |
| 131575 | ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS |
| 131576 | ** virtual tables if PRAGMA trusted_schema=ON. |
| 131577 | */ |
| 131578 | if( (pParse->pToplevel!=0 || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL)) |
| 131579 | && pTab->u.vtab.p->eVtabRisk > |
| 131580 | ((pParse->db->flags & SQLITE_TrustedSchema)!=0) |
| 131581 | ){ |
| 131582 | sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", |
| 131583 | pTab->zName); |
| @@ -132239,11 +132411,10 @@ | |
| 132411 | VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); |
| 132412 | r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, |
| 132413 | &iPartIdxLabel, pPrior, r1); |
| 132414 | sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, |
| 132415 | pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
| 132416 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 132417 | pPrior = pIdx; |
| 132418 | } |
| 132419 | } |
| 132420 | |
| @@ -133586,11 +133757,11 @@ | |
| 133757 | }else{ |
| 133758 | goto unistr_error; |
| 133759 | } |
| 133760 | } |
| 133761 | zOut[j] = 0; |
| 133762 | sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8_ZT); |
| 133763 | return; |
| 133764 | |
| 133765 | unistr_error: |
| 133766 | sqlite3_free(zOut); |
| 133767 | sqlite3_result_error(context, "invalid Unicode escape", -1); |
| @@ -133679,11 +133850,11 @@ | |
| 133850 | *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); |
| 133851 | *zOut++ = 0x80 + (u8)(c & 0x3F); |
| 133852 | } \ |
| 133853 | } |
| 133854 | *zOut = 0; |
| 133855 | sqlite3_result_text64(context, (char*)z, zOut-z,sqlite3_free,SQLITE_UTF8_ZT); |
| 133856 | } |
| 133857 | |
| 133858 | /* |
| 133859 | ** The hex() function. Interpret the argument as a blob. Return |
| 133860 | ** a hexadecimal rendering as text. |
| @@ -133708,11 +133879,11 @@ | |
| 133879 | *(z++) = hexdigits[(c>>4)&0xf]; |
| 133880 | *(z++) = hexdigits[c&0xf]; |
| 133881 | } |
| 133882 | *z = 0; |
| 133883 | sqlite3_result_text64(context, zHex, (u64)(z-zHex), |
| 133884 | sqlite3_free, SQLITE_UTF8_ZT); |
| 133885 | } |
| 133886 | } |
| 133887 | |
| 133888 | /* |
| 133889 | ** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr |
| @@ -134046,11 +134217,11 @@ | |
| 134217 | } |
| 134218 | } |
| 134219 | } |
| 134220 | z[j] = 0; |
| 134221 | assert( j<=n ); |
| 134222 | sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8_ZT); |
| 134223 | } |
| 134224 | |
| 134225 | /* |
| 134226 | ** The CONCAT(...) function. Generate a string result that is the |
| 134227 | ** concatentation of all non-null arguments. |
| @@ -141234,10 +141405,12 @@ | |
| 141405 | int (*set_errmsg)(sqlite3*,int,const char*); |
| 141406 | int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int); |
| 141407 | /* Version 3.52.0 and later */ |
| 141408 | void (*str_truncate)(sqlite3_str*,int); |
| 141409 | void (*str_free)(sqlite3_str*); |
| 141410 | int (*carray_bind)(sqlite3_stmt*,int,void*,int,int,void(*)(void*)); |
| 141411 | int (*carray_bind_v2)(sqlite3_stmt*,int,void*,int,int,void(*)(void*),void*); |
| 141412 | }; |
| 141413 | |
| 141414 | /* |
| 141415 | ** This is the function signature used for all extension entry points. It |
| 141416 | ** is also defined in the file "loadext.c". |
| @@ -141575,10 +141748,12 @@ | |
| 141748 | #define sqlite3_set_errmsg sqlite3_api->set_errmsg |
| 141749 | #define sqlite3_db_status64 sqlite3_api->db_status64 |
| 141750 | /* Version 3.52.0 and later */ |
| 141751 | #define sqlite3_str_truncate sqlite3_api->str_truncate |
| 141752 | #define sqlite3_str_free sqlite3_api->str_free |
| 141753 | #define sqlite3_carray_bind sqlite3_api->carray_bind |
| 141754 | #define sqlite3_carray_bind_v2 sqlite3_api->carray_bind_v2 |
| 141755 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 141756 | |
| 141757 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 141758 | /* This case when the file really is being compiled as a loadable |
| 141759 | ** extension */ |
| @@ -142104,11 +142279,18 @@ | |
| 142279 | /* Version 3.51.0 and later */ |
| 142280 | sqlite3_set_errmsg, |
| 142281 | sqlite3_db_status64, |
| 142282 | /* Version 3.52.0 and later */ |
| 142283 | sqlite3_str_truncate, |
| 142284 | sqlite3_str_free, |
| 142285 | #ifdef SQLITE_ENABLE_CARRAY |
| 142286 | sqlite3_carray_bind, |
| 142287 | sqlite3_carray_bind_v2 |
| 142288 | #else |
| 142289 | 0, |
| 142290 | 0 |
| 142291 | #endif |
| 142292 | }; |
| 142293 | |
| 142294 | /* True if x is the directory separator character |
| 142295 | */ |
| 142296 | #if SQLITE_OS_WIN |
| @@ -142206,37 +142388,46 @@ | |
| 142388 | |
| 142389 | /* If no entry point was specified and the default legacy |
| 142390 | ** entry point name "sqlite3_extension_init" was not found, then |
| 142391 | ** construct an entry point name "sqlite3_X_init" where the X is |
| 142392 | ** replaced by the lowercase value of every ASCII alphabetic |
| 142393 | ** character in the filename after the last "/" up to the first ".", |
| 142394 | ** and skipping the first three characters if they are "lib". |
| 142395 | ** Examples: |
| 142396 | ** |
| 142397 | ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example_init |
| 142398 | ** C:/lib/mathfuncs.dll ==> sqlite3_mathfuncs_init |
| 142399 | ** |
| 142400 | ** If that still finds no entry point, repeat a second time but this |
| 142401 | ** time include both alphabetic and numeric characters up to the first |
| 142402 | ** ".". Example: |
| 142403 | ** |
| 142404 | ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example5_init |
| 142405 | */ |
| 142406 | if( xInit==0 && zProc==0 ){ |
| 142407 | int iFile, iEntry, c; |
| 142408 | int ncFile = sqlite3Strlen30(zFile); |
| 142409 | int cnt = 0; |
| 142410 | zAltEntry = sqlite3_malloc64(ncFile+30); |
| 142411 | if( zAltEntry==0 ){ |
| 142412 | sqlite3OsDlClose(pVfs, handle); |
| 142413 | return SQLITE_NOMEM_BKPT; |
| 142414 | } |
| 142415 | do{ |
| 142416 | memcpy(zAltEntry, "sqlite3_", 8); |
| 142417 | for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} |
| 142418 | iFile++; |
| 142419 | if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; |
| 142420 | for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ |
| 142421 | if( sqlite3Isalpha(c) || (cnt && sqlite3Isdigit(c)) ){ |
| 142422 | zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; |
| 142423 | } |
| 142424 | } |
| 142425 | memcpy(zAltEntry+iEntry, "_init", 6); |
| 142426 | zEntry = zAltEntry; |
| 142427 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 142428 | }while( xInit==0 && (++cnt)<2 ); |
| 142429 | } |
| 142430 | if( xInit==0 ){ |
| 142431 | if( pzErrMsg ){ |
| 142432 | nMsg += strlen(zEntry) + 300; |
| 142433 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| @@ -147518,12 +147709,10 @@ | |
| 147709 | pNew->op = TK_SELECT; |
| 147710 | pNew->selFlags = selFlags; |
| 147711 | pNew->iLimit = 0; |
| 147712 | pNew->iOffset = 0; |
| 147713 | pNew->selId = ++pParse->nSelect; |
| 147714 | pNew->nSelectRow = 0; |
| 147715 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); |
| 147716 | pNew->pSrc = pSrc; |
| 147717 | pNew->pWhere = pWhere; |
| 147718 | pNew->pGroupBy = pGroupBy; |
| @@ -148667,33 +148856,10 @@ | |
| 148856 | codeOffset(v, p->iOffset, iContinue); |
| 148857 | } |
| 148858 | } |
| 148859 | |
| 148860 | switch( eDest ){ |
| 148861 | /* Store the result as data using a unique key. |
| 148862 | */ |
| 148863 | case SRT_Fifo: |
| 148864 | case SRT_DistFifo: |
| 148865 | case SRT_Table: |
| @@ -149976,13 +150142,13 @@ | |
| 150142 | ** |
| 150143 | ** Space to hold the KeyInfo structure is obtained from malloc. The calling |
| 150144 | ** function is responsible for ensuring that this structure is eventually |
| 150145 | ** freed. |
| 150146 | */ |
| 150147 | static KeyInfo *multiSelectByMergeKeyInfo(Parse *pParse, Select *p, int nExtra){ |
| 150148 | ExprList *pOrderBy = p->pOrderBy; |
| 150149 | int nOrderBy = (pOrderBy!=0) ? pOrderBy->nExpr : 0; |
| 150150 | sqlite3 *db = pParse->db; |
| 150151 | KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); |
| 150152 | if( pRet ){ |
| 150153 | int i; |
| 150154 | for(i=0; i<nOrderBy; i++){ |
| @@ -150111,21 +150277,41 @@ | |
| 150277 | |
| 150278 | /* Allocate cursors for Current, Queue, and Distinct. */ |
| 150279 | regCurrent = ++pParse->nMem; |
| 150280 | sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol); |
| 150281 | if( pOrderBy ){ |
| 150282 | KeyInfo *pKeyInfo = multiSelectByMergeKeyInfo(pParse, p, 1); |
| 150283 | sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0, |
| 150284 | (char*)pKeyInfo, P4_KEYINFO); |
| 150285 | destQueue.pOrderBy = pOrderBy; |
| 150286 | }else{ |
| 150287 | sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol); |
| 150288 | } |
| 150289 | VdbeComment((v, "Queue table")); |
| 150290 | if( iDistinct ){ |
| 150291 | /* Generate an ephemeral table used to enforce distinctness on the |
| 150292 | ** output of the recursive part of the CTE. |
| 150293 | */ |
| 150294 | KeyInfo *pKeyInfo; /* Collating sequence for the result set */ |
| 150295 | CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ |
| 150296 | |
| 150297 | assert( p->pNext==0 ); |
| 150298 | assert( p->pEList!=0 ); |
| 150299 | nCol = p->pEList->nExpr; |
| 150300 | pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nCol, 1); |
| 150301 | if( pKeyInfo ){ |
| 150302 | for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){ |
| 150303 | *apColl = multiSelectCollSeq(pParse, p, i); |
| 150304 | if( 0==*apColl ){ |
| 150305 | *apColl = pParse->db->pDfltColl; |
| 150306 | } |
| 150307 | } |
| 150308 | sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iDistinct, nCol, 0, |
| 150309 | (void*)pKeyInfo, P4_KEYINFO); |
| 150310 | }else{ |
| 150311 | assert( pParse->nErr>0 ); |
| 150312 | } |
| 150313 | } |
| 150314 | |
| 150315 | /* Detach the ORDER BY clause from the compound SELECT */ |
| 150316 | p->pOrderBy = 0; |
| 150317 | |
| @@ -150196,11 +150382,11 @@ | |
| 150382 | return; |
| 150383 | } |
| 150384 | #endif /* SQLITE_OMIT_CTE */ |
| 150385 | |
| 150386 | /* Forward references */ |
| 150387 | static int multiSelectByMerge( |
| 150388 | Parse *pParse, /* Parsing context */ |
| 150389 | Select *p, /* The right-most of SELECTs to be coded */ |
| 150390 | SelectDest *pDest /* What to do with query results */ |
| 150391 | ); |
| 150392 | |
| @@ -150345,317 +150531,80 @@ | |
| 150531 | #ifndef SQLITE_OMIT_CTE |
| 150532 | if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){ |
| 150533 | generateWithRecursiveQuery(pParse, p, &dest); |
| 150534 | }else |
| 150535 | #endif |
| 150536 | if( p->pOrderBy ){ |
| 150537 | /* If the compound has an ORDER BY clause, then always use the merge |
| 150538 | ** algorithm. */ |
| 150539 | return multiSelectByMerge(pParse, p, pDest); |
| 150540 | }else if( p->op!=TK_ALL ){ |
| 150541 | /* If the compound is EXCEPT, INTERSECT, or UNION (anything other than |
| 150542 | ** UNION ALL) then also always use the merge algorithm. However, the |
| 150543 | ** multiSelectByMerge() routine requires that the compound have an |
| 150544 | ** ORDER BY clause, and it doesn't right now. So invent one first. */ |
| 150545 | Expr *pOne = sqlite3ExprInt32(db, 1); |
| 150546 | p->pOrderBy = sqlite3ExprListAppend(pParse, 0, pOne); |
| 150547 | if( pParse->nErr ) goto multi_select_end; |
| 150548 | assert( p->pOrderBy!=0 ); |
| 150549 | p->pOrderBy->a[0].u.x.iOrderByCol = 1; |
| 150550 | return multiSelectByMerge(pParse, p, pDest); |
| 150551 | }else{ |
| 150552 | /* For a UNION ALL compound without ORDER BY, simply run the left |
| 150553 | ** query, then run the right query */ |
| 150554 | int addr = 0; |
| 150555 | int nLimit = 0; /* Initialize to suppress harmless compiler warning */ |
| 150556 | |
| 150557 | #ifndef SQLITE_OMIT_EXPLAIN |
| 150558 | if( pPrior->pPrior==0 ){ |
| 150559 | ExplainQueryPlan((pParse, 1, "COMPOUND QUERY")); |
| 150560 | ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY")); |
| 150561 | } |
| 150562 | #endif |
| 150563 | assert( !pPrior->pLimit ); |
| 150564 | pPrior->iLimit = p->iLimit; |
| 150565 | pPrior->iOffset = p->iOffset; |
| 150566 | pPrior->pLimit = sqlite3ExprDup(db, p->pLimit, 0); |
| 150567 | TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); |
| 150568 | rc = sqlite3Select(pParse, pPrior, &dest); |
| 150569 | sqlite3ExprDelete(db, pPrior->pLimit); |
| 150570 | pPrior->pLimit = 0; |
| 150571 | if( rc ){ |
| 150572 | goto multi_select_end; |
| 150573 | } |
| 150574 | p->pPrior = 0; |
| 150575 | p->iLimit = pPrior->iLimit; |
| 150576 | p->iOffset = pPrior->iOffset; |
| 150577 | if( p->iLimit ){ |
| 150578 | addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); |
| 150579 | VdbeComment((v, "Jump ahead if LIMIT reached")); |
| 150580 | if( p->iOffset ){ |
| 150581 | sqlite3VdbeAddOp3(v, OP_OffsetLimit, |
| 150582 | p->iLimit, p->iOffset+1, p->iOffset); |
| 150583 | } |
| 150584 | } |
| 150585 | ExplainQueryPlan((pParse, 1, "UNION ALL")); |
| 150586 | TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); |
| 150587 | rc = sqlite3Select(pParse, p, &dest); |
| 150588 | testcase( rc!=SQLITE_OK ); |
| 150589 | pDelete = p->pPrior; |
| 150590 | p->pPrior = pPrior; |
| 150591 | p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); |
| 150592 | if( p->pLimit |
| 150593 | && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) |
| 150594 | && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) |
| 150595 | ){ |
| 150596 | p->nSelectRow = sqlite3LogEst((u64)nLimit); |
| 150597 | } |
| 150598 | if( addr ){ |
| 150599 | sqlite3VdbeJumpHere(v, addr); |
| 150600 | } |
| 150601 | #ifndef SQLITE_OMIT_EXPLAIN |
| 150602 | if( p->pNext==0 ){ |
| 150603 | ExplainQueryPlanPop(pParse); |
| 150604 | } |
| 150605 | #endif |
| 150606 | } |
| 150607 | |
| 150608 | multi_select_end: |
| 150609 | pDest->iSdst = dest.iSdst; |
| 150610 | pDest->nSdst = dest.nSdst; |
| @@ -150683,12 +150632,12 @@ | |
| 150632 | |
| 150633 | /* |
| 150634 | ** Code an output subroutine for a coroutine implementation of a |
| 150635 | ** SELECT statement. |
| 150636 | ** |
| 150637 | ** The data to be output is contained in an array of pIn->nSdst registers |
| 150638 | ** starting at register pIn->iSdst. pDest is where the output should |
| 150639 | ** be sent. |
| 150640 | ** |
| 150641 | ** regReturn is the number of the register holding the subroutine |
| 150642 | ** return address. |
| 150643 | ** |
| @@ -150713,10 +150662,12 @@ | |
| 150662 | ){ |
| 150663 | Vdbe *v = pParse->pVdbe; |
| 150664 | int iContinue; |
| 150665 | int addr; |
| 150666 | |
| 150667 | assert( pIn->eDest==SRT_Coroutine ); |
| 150668 | |
| 150669 | addr = sqlite3VdbeCurrentAddr(v); |
| 150670 | iContinue = sqlite3VdbeMakeLabel(pParse); |
| 150671 | |
| 150672 | /* Suppress duplicates for UNION, EXCEPT, and INTERSECT |
| 150673 | */ |
| @@ -150734,26 +150685,63 @@ | |
| 150685 | |
| 150686 | /* Suppress the first OFFSET entries if there is an OFFSET clause |
| 150687 | */ |
| 150688 | codeOffset(v, p->iOffset, iContinue); |
| 150689 | |
| 150690 | switch( pDest->eDest ){ |
| 150691 | /* Store the result as data using a unique key. |
| 150692 | */ |
| 150693 | case SRT_Fifo: |
| 150694 | case SRT_DistFifo: |
| 150695 | case SRT_Table: |
| 150696 | case SRT_EphemTab: { |
| 150697 | int r1 = sqlite3GetTempReg(pParse); |
| 150698 | int r2 = sqlite3GetTempReg(pParse); |
| 150699 | int iParm = pDest->iSDParm; |
| 150700 | testcase( pDest->eDest==SRT_Table ); |
| 150701 | testcase( pDest->eDest==SRT_EphemTab ); |
| 150702 | testcase( pDest->eDest==SRT_Fifo ); |
| 150703 | testcase( pDest->eDest==SRT_DistFifo ); |
| 150704 | sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1); |
| 150705 | #if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG) |
| 150706 | /* A destination of SRT_Table and a non-zero iSDParm2 parameter means |
| 150707 | ** that this is an "UPDATE ... FROM" on a virtual table or view. In this |
| 150708 | ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC. |
| 150709 | ** This does not affect operation in any way - it just allows MakeRecord |
| 150710 | ** to process OPFLAG_NOCHANGE values without an assert() failing. */ |
| 150711 | if( pDest->eDest==SRT_Table && pDest->iSDParm2 ){ |
| 150712 | sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); |
| 150713 | } |
| 150714 | #endif |
| 150715 | #ifndef SQLITE_OMIT_CTE |
| 150716 | if( pDest->eDest==SRT_DistFifo ){ |
| 150717 | /* If the destination is DistFifo, then cursor (iParm+1) is open |
| 150718 | ** on an ephemeral index that is used to enforce uniqueness on the |
| 150719 | ** total result. At this point, we are processing the setup portion |
| 150720 | ** of the recursive CTE using the merge algorithm, so the results are |
| 150721 | ** guaranteed to be unique anyhow. But we still need to populate the |
| 150722 | ** (iParm+1) cursor for use by the subsequent recursive phase. |
| 150723 | */ |
| 150724 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm+1, r1, |
| 150725 | pIn->iSdst, pIn->nSdst); |
| 150726 | } |
| 150727 | #endif |
| 150728 | sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2); |
| 150729 | sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2); |
| 150730 | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| 150731 | sqlite3ReleaseTempReg(pParse, r2); |
| 150732 | sqlite3ReleaseTempReg(pParse, r1); |
| 150733 | break; |
| 150734 | } |
| 150735 | |
| 150736 | /* If any row exist in the result set, record that fact and abort. |
| 150737 | */ |
| 150738 | case SRT_Exists: { |
| 150739 | sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm); |
| 150740 | /* The LIMIT clause will terminate the loop for us */ |
| 150741 | break; |
| 150742 | } |
| 150743 | |
| 150744 | #ifndef SQLITE_OMIT_SUBQUERY |
| 150745 | /* If we are creating a set for an "expr IN (SELECT ...)". |
| 150746 | */ |
| 150747 | case SRT_Set: { |
| @@ -150796,14 +150784,74 @@ | |
| 150784 | } |
| 150785 | sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pIn->nSdst); |
| 150786 | sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); |
| 150787 | break; |
| 150788 | } |
| 150789 | |
| 150790 | #ifndef SQLITE_OMIT_CTE |
| 150791 | /* Write the results into a priority queue that is order according to |
| 150792 | ** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an |
| 150793 | ** index with pSO->nExpr+2 columns. Build a key using pSO for the first |
| 150794 | ** pSO->nExpr columns, then make sure all keys are unique by adding a |
| 150795 | ** final OP_Sequence column. The last column is the record as a blob. |
| 150796 | */ |
| 150797 | case SRT_DistQueue: |
| 150798 | case SRT_Queue: { |
| 150799 | int nKey; |
| 150800 | int r1, r2, r3, ii; |
| 150801 | ExprList *pSO; |
| 150802 | int iParm = pDest->iSDParm; |
| 150803 | pSO = pDest->pOrderBy; |
| 150804 | assert( pSO ); |
| 150805 | nKey = pSO->nExpr; |
| 150806 | r1 = sqlite3GetTempReg(pParse); |
| 150807 | r2 = sqlite3GetTempRange(pParse, nKey+2); |
| 150808 | r3 = r2+nKey+1; |
| 150809 | |
| 150810 | #if 0 /* <-- Why the next block of code is commented out: (tag-20260125-a) |
| 150811 | ** |
| 150812 | ** If the destination is DistQueue, then cursor (iParm+1) is open |
| 150813 | ** on a second ephemeral index that holds all values previously |
| 150814 | ** added to the queue. This code only runs during the setup phase |
| 150815 | ** using the merge algorithm, and so the values here are already |
| 150816 | ** guaranteed to be unique. |
| 150817 | */ |
| 150818 | if( pDest->eDest==SRT_DistQueue ){ |
| 150819 | addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0, |
| 150820 | pIn->iSdst, pIn->nSdst); |
| 150821 | VdbeCoverage(v); |
| 150822 | } |
| 150823 | #endif |
| 150824 | sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r3); |
| 150825 | if( pDest->eDest==SRT_DistQueue ){ |
| 150826 | sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3); |
| 150827 | sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); |
| 150828 | } |
| 150829 | for(ii=0; ii<nKey; ii++){ |
| 150830 | sqlite3VdbeAddOp2(v, OP_SCopy, |
| 150831 | pIn->iSdst + pSO->a[ii].u.x.iOrderByCol - 1, |
| 150832 | r2+ii); |
| 150833 | } |
| 150834 | sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey); |
| 150835 | sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1); |
| 150836 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, r2, nKey+2); |
| 150837 | #if 0 /* tag-20260125-a */ |
| 150838 | if( addrTest ) sqlite3VdbeJumpHere(v, addrTest); |
| 150839 | #endif |
| 150840 | sqlite3ReleaseTempReg(pParse, r1); |
| 150841 | sqlite3ReleaseTempRange(pParse, r2, nKey+2); |
| 150842 | break; |
| 150843 | } |
| 150844 | #endif /* SQLITE_OMIT_CTE */ |
| 150845 | |
| 150846 | /* Ignore the output */ |
| 150847 | case SRT_Discard: { |
| 150848 | break; |
| 150849 | } |
| 150850 | |
| 150851 | /* If none of the above, then the result destination must be |
| 150852 | ** SRT_Output. |
| 150853 | ** |
| 150854 | ** For SRT_Output, results are stored in a sequence of registers. |
| 150855 | ** Then the OP_ResultRow opcode is used to cause sqlite3_step() to |
| 150856 | ** return the next row of result. |
| 150857 | */ |
| @@ -150827,12 +150875,13 @@ | |
| 150875 | |
| 150876 | return addr; |
| 150877 | } |
| 150878 | |
| 150879 | /* |
| 150880 | ** Generate code for a compound SELECT statement using a merge |
| 150881 | ** algorithm. The compound must have an ORDER BY clause for this |
| 150882 | ** to work. |
| 150883 | ** |
| 150884 | ** We assume a query of the following form: |
| 150885 | ** |
| 150886 | ** <selectA> <operator> <selectB> ORDER BY <orderbylist> |
| 150887 | ** |
| @@ -150845,11 +150894,11 @@ | |
| 150894 | ** outA: Move the output of the selectA coroutine into the output |
| 150895 | ** of the compound query. |
| 150896 | ** |
| 150897 | ** outB: Move the output of the selectB coroutine into the output |
| 150898 | ** of the compound query. (Only generated for UNION and |
| 150899 | ** UNION ALL. EXCEPT and INTERSECT never output a row that |
| 150900 | ** appears only in B.) |
| 150901 | ** |
| 150902 | ** AltB: Called when there is data from both coroutines and A<B. |
| 150903 | ** |
| 150904 | ** AeqB: Called when there is data from both coroutines and A==B. |
| @@ -150898,25 +150947,23 @@ | |
| 150947 | ** EofB: ... |
| 150948 | ** AltB: ... |
| 150949 | ** AeqB: ... |
| 150950 | ** AgtB: ... |
| 150951 | ** Init: initialize coroutine registers |
| 150952 | ** yield coA, on eof goto EofA |
| 150953 | ** yield coB, on eof goto EofB |
| 150954 | ** Cmpr: Compare A, B |
| 150955 | ** Jump AltB, AeqB, AgtB |
| 150956 | ** End: ... |
| 150957 | ** |
| 150958 | ** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not |
| 150959 | ** actually called using Gosub and they do not Return. EofA and EofB loop |
| 150960 | ** until all data is exhausted then jump to the "end" label. AltB, AeqB, |
| 150961 | ** and AgtB jump to either Cmpr or to one of EofA or EofB. |
| 150962 | */ |
| 150963 | #ifndef SQLITE_OMIT_COMPOUND_SELECT |
| 150964 | static int multiSelectByMerge( |
| 150965 | Parse *pParse, /* Parsing context */ |
| 150966 | Select *p, /* The right-most of SELECTs to be coded */ |
| 150967 | SelectDest *pDest /* What to do with query results */ |
| 150968 | ){ |
| 150969 | int i, j; /* Loop counters */ |
| @@ -150993,30 +151040,33 @@ | |
| 151040 | } |
| 151041 | } |
| 151042 | } |
| 151043 | |
| 151044 | /* Compute the comparison permutation and keyinfo that is used with |
| 151045 | ** the permutation to determine if the next row of results comes |
| 151046 | ** from selectA or selectB. Also add literal collations to the |
| 151047 | ** ORDER BY clause terms so that when selectA and selectB are |
| 151048 | ** evaluated, they use the correct collation. |
| 151049 | */ |
| 151050 | aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); |
| 151051 | if( aPermute ){ |
| 151052 | struct ExprList_item *pItem; |
| 151053 | int bKeep = 0; |
| 151054 | aPermute[0] = nOrderBy; |
| 151055 | for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ |
| 151056 | assert( pItem!=0 ); |
| 151057 | assert( pItem->u.x.iOrderByCol>0 ); |
| 151058 | assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); |
| 151059 | aPermute[i] = pItem->u.x.iOrderByCol - 1; |
| 151060 | if( aPermute[i]!=(u32)i-1 ) bKeep = 1; |
| 151061 | } |
| 151062 | if( bKeep==0 ){ |
| 151063 | sqlite3DbFreeNN(db, aPermute); |
| 151064 | aPermute = 0; |
| 151065 | } |
| 151066 | } |
| 151067 | pKeyMerge = multiSelectByMergeKeyInfo(pParse, p, 1); |
| 151068 | |
| 151069 | /* Allocate a range of temporary registers and the KeyInfo needed |
| 151070 | ** for the logic that removes duplicate result rows when the |
| 151071 | ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). |
| 151072 | */ |
| @@ -151091,11 +151141,11 @@ | |
| 151141 | /* Generate a coroutine to evaluate the SELECT statement to the |
| 151142 | ** left of the compound operator - the "A" select. |
| 151143 | */ |
| 151144 | addrSelectA = sqlite3VdbeCurrentAddr(v) + 1; |
| 151145 | addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA); |
| 151146 | VdbeComment((v, "SUBR: next-A")); |
| 151147 | pPrior->iLimit = regLimitA; |
| 151148 | ExplainQueryPlan((pParse, 1, "LEFT")); |
| 151149 | sqlite3Select(pParse, pPrior, &destA); |
| 151150 | sqlite3VdbeEndCoroutine(v, regAddrA); |
| 151151 | sqlite3VdbeJumpHere(v, addr1); |
| @@ -151103,11 +151153,11 @@ | |
| 151153 | /* Generate a coroutine to evaluate the SELECT statement on |
| 151154 | ** the right - the "B" select |
| 151155 | */ |
| 151156 | addrSelectB = sqlite3VdbeCurrentAddr(v) + 1; |
| 151157 | addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB); |
| 151158 | VdbeComment((v, "SUBR: next-B")); |
| 151159 | savedLimit = p->iLimit; |
| 151160 | savedOffset = p->iOffset; |
| 151161 | p->iLimit = regLimitB; |
| 151162 | p->iOffset = 0; |
| 151163 | ExplainQueryPlan((pParse, 1, "RIGHT")); |
| @@ -151117,20 +151167,20 @@ | |
| 151167 | sqlite3VdbeEndCoroutine(v, regAddrB); |
| 151168 | |
| 151169 | /* Generate a subroutine that outputs the current row of the A |
| 151170 | ** select as the next output row of the compound select. |
| 151171 | */ |
| 151172 | VdbeNoopComment((v, "SUBR: out-A")); |
| 151173 | addrOutA = generateOutputSubroutine(pParse, |
| 151174 | p, &destA, pDest, regOutA, |
| 151175 | regPrev, pKeyDup, labelEnd); |
| 151176 | |
| 151177 | /* Generate a subroutine that outputs the current row of the B |
| 151178 | ** select as the next output row of the compound select. |
| 151179 | */ |
| 151180 | if( op==TK_ALL || op==TK_UNION ){ |
| 151181 | VdbeNoopComment((v, "SUBR: out-B")); |
| 151182 | addrOutB = generateOutputSubroutine(pParse, |
| 151183 | p, &destB, pDest, regOutB, |
| 151184 | regPrev, pKeyDup, labelEnd); |
| 151185 | } |
| 151186 | sqlite3KeyInfoUnref(pKeyDup); |
| @@ -151139,14 +151189,16 @@ | |
| 151189 | ** are exhausted and only data in select B remains. |
| 151190 | */ |
| 151191 | if( op==TK_EXCEPT || op==TK_INTERSECT ){ |
| 151192 | addrEofA_noB = addrEofA = labelEnd; |
| 151193 | }else{ |
| 151194 | VdbeNoopComment((v, "SUBR: eof-A")); |
| 151195 | addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); |
| 151196 | VdbeComment((v, "out-B")); |
| 151197 | addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd); |
| 151198 | VdbeCoverage(v); |
| 151199 | VdbeComment((v, "next-B")); |
| 151200 | sqlite3VdbeGoto(v, addrEofA); |
| 151201 | p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); |
| 151202 | } |
| 151203 | |
| 151204 | /* Generate a subroutine to run when the results from select B |
| @@ -151154,21 +151206,24 @@ | |
| 151206 | */ |
| 151207 | if( op==TK_INTERSECT ){ |
| 151208 | addrEofB = addrEofA; |
| 151209 | if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; |
| 151210 | }else{ |
| 151211 | VdbeNoopComment((v, "SUBR: eof-B")); |
| 151212 | addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); |
| 151213 | VdbeComment((v, "out-A")); |
| 151214 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v); |
| 151215 | VdbeComment((v, "next-A")); |
| 151216 | sqlite3VdbeGoto(v, addrEofB); |
| 151217 | } |
| 151218 | |
| 151219 | /* Generate code to handle the case of A<B |
| 151220 | */ |
| 151221 | addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); |
| 151222 | VdbeComment((v, "out-A")); |
| 151223 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v); |
| 151224 | VdbeComment((v, "next-A")); |
| 151225 | sqlite3VdbeGoto(v, labelCmpr); |
| 151226 | |
| 151227 | /* Generate code to handle the case of A==B |
| 151228 | */ |
| 151229 | if( op==TK_ALL ){ |
| @@ -151175,40 +151230,52 @@ | |
| 151230 | addrAeqB = addrAltB; |
| 151231 | }else if( op==TK_INTERSECT ){ |
| 151232 | addrAeqB = addrAltB; |
| 151233 | addrAltB++; |
| 151234 | }else{ |
| 151235 | addrAeqB = addrAltB + 1; |
| 151236 | } |
| 151237 | |
| 151238 | /* Generate code to handle the case of A>B |
| 151239 | */ |
| 151240 | addrAgtB = sqlite3VdbeCurrentAddr(v); |
| 151241 | if( op==TK_ALL || op==TK_UNION ){ |
| 151242 | sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); |
| 151243 | VdbeComment((v, "out-B")); |
| 151244 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); |
| 151245 | VdbeComment((v, "next-B")); |
| 151246 | sqlite3VdbeGoto(v, labelCmpr); |
| 151247 | }else{ |
| 151248 | addrAgtB++; /* Just do next-B. Might as well use the next-B call |
| 151249 | ** in the next code block */ |
| 151250 | } |
| 151251 | |
| 151252 | /* This code runs once to initialize everything. |
| 151253 | */ |
| 151254 | sqlite3VdbeJumpHere(v, addr1); |
| 151255 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v); |
| 151256 | VdbeComment((v, "next-A")); |
| 151257 | /* v--- Also the A>B case for EXCEPT and INTERSECT */ |
| 151258 | sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); |
| 151259 | VdbeComment((v, "next-B")); |
| 151260 | |
| 151261 | /* Implement the main merge loop |
| 151262 | */ |
| 151263 | if( aPermute!=0 ){ |
| 151264 | sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); |
| 151265 | } |
| 151266 | sqlite3VdbeResolveLabel(v, labelCmpr); |
| 151267 | sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, |
| 151268 | (char*)pKeyMerge, P4_KEYINFO); |
| 151269 | if( aPermute!=0 ){ |
| 151270 | sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); |
| 151271 | } |
| 151272 | sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); |
| 151273 | VdbeCoverageIf(v, op==TK_ALL); |
| 151274 | VdbeCoverageIf(v, op==TK_UNION); |
| 151275 | VdbeCoverageIf(v, op==TK_EXCEPT); |
| 151276 | VdbeCoverageIf(v, op==TK_INTERSECT); |
| 151277 | |
| 151278 | /* Jump to the this point in order to terminate the query. |
| 151279 | */ |
| 151280 | sqlite3VdbeResolveLabel(v, labelEnd); |
| 151281 | |
| @@ -152121,11 +152188,11 @@ | |
| 152188 | pItem->fg.jointype |= (jointype & JT_LTORJ); |
| 152189 | memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); |
| 152190 | } |
| 152191 | pSubitem->fg.jointype |= jointype; |
| 152192 | |
| 152193 | /* Begin substituting subquery result set expressions for |
| 152194 | ** references to the iParent in the outer query. |
| 152195 | ** |
| 152196 | ** Example: |
| 152197 | ** |
| 152198 | ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; |
| @@ -152133,21 +152200,21 @@ | |
| 152200 | ** \_____________________ outer query ______________________________/ |
| 152201 | ** |
| 152202 | ** We look at every expression in the outer query and every place we see |
| 152203 | ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". |
| 152204 | */ |
| 152205 | if( pSub->pOrderBy ){ |
| 152206 | /* At this point, any non-zero iOrderByCol values indicate that the |
| 152207 | ** ORDER BY column expression is identical to the iOrderByCol'th |
| 152208 | ** expression returned by SELECT statement pSub. Since these values |
| 152209 | ** do not necessarily correspond to columns in SELECT statement pParent, |
| 152210 | ** zero them before transferring the ORDER BY clause. |
| 152211 | ** |
| 152212 | ** Not doing this may cause an error if a subsequent call to this |
| 152213 | ** function attempts to flatten a compound sub-query into pParent. |
| 152214 | ** See ticket [d11a6e908f]. |
| 152215 | */ |
| 152216 | ExprList *pOrderBy = pSub->pOrderBy; |
| 152217 | for(i=0; i<pOrderBy->nExpr; i++){ |
| 152218 | pOrderBy->a[i].u.x.iOrderByCol = 0; |
| 152219 | } |
| 152220 | assert( pParent->pOrderBy==0 ); |
| @@ -152995,18 +153062,18 @@ | |
| 153062 | ** These are rewritten as a subquery: |
| 153063 | ** |
| 153064 | ** SELECT * FROM (SELECT ... FROM t1 EXCEPT SELECT ... FROM t2) |
| 153065 | ** ORDER BY ... COLLATE ... |
| 153066 | ** |
| 153067 | ** This transformation is necessary because the multiSelectByMerge() routine |
| 153068 | ** above that generates the code for a compound SELECT with an ORDER BY clause |
| 153069 | ** uses a merge algorithm that requires the same collating sequence on the |
| 153070 | ** result columns as on the ORDER BY clause. See ticket |
| 153071 | ** http://sqlite.org/src/info/6709574d2a |
| 153072 | ** |
| 153073 | ** This transformation is only needed for EXCEPT, INTERSECT, and UNION. |
| 153074 | ** The UNION ALL operator works fine with multiSelectByMerge() even when |
| 153075 | ** there are COLLATE terms in the ORDER BY. |
| 153076 | */ |
| 153077 | static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ |
| 153078 | int i; |
| 153079 | Select *pNew; |
| @@ -153548,11 +153615,11 @@ | |
| 153615 | } |
| 153616 | sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1); |
| 153617 | } |
| 153618 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 153619 | else if( ALWAYS(IsVirtual(pTab)) |
| 153620 | && (pFrom->fg.fromDDL || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL)) |
| 153621 | && ALWAYS(pTab->u.vtab.p!=0) |
| 153622 | && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) |
| 153623 | ){ |
| 153624 | sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", |
| 153625 | pTab->zName); |
| @@ -154832,19 +154899,30 @@ | |
| 154899 | Expr *pSubWhere = pSub->pWhere; |
| 154900 | if( pSub->pSrc->nSrc==1 |
| 154901 | && (pSub->selFlags & SF_Aggregate)==0 |
| 154902 | && !pSub->pSrc->a[0].fg.isSubquery |
| 154903 | && pSub->pLimit==0 |
| 154904 | && pSub->pPrior==0 |
| 154905 | ){ |
| 154906 | /* Before combining the sub-select with the parent, renumber the |
| 154907 | ** cursor used by the subselect. This is because the EXISTS expression |
| 154908 | ** might be a copy of another EXISTS expression from somewhere |
| 154909 | ** else in the tree, and in this case it is important that it use |
| 154910 | ** a unique cursor number. */ |
| 154911 | sqlite3 *db = pParse->db; |
| 154912 | int *aCsrMap = sqlite3DbMallocZero(db, (pParse->nTab+2)*sizeof(int)); |
| 154913 | if( aCsrMap==0 ) return; |
| 154914 | aCsrMap[0] = (pParse->nTab+1); |
| 154915 | renumberCursors(pParse, pSub, -1, aCsrMap); |
| 154916 | sqlite3DbFree(db, aCsrMap); |
| 154917 | |
| 154918 | memset(pWhere, 0, sizeof(*pWhere)); |
| 154919 | pWhere->op = TK_INTEGER; |
| 154920 | pWhere->u.iValue = 1; |
| 154921 | ExprSetProperty(pWhere, EP_IntValue); |
| 154922 | assert( p->pWhere!=0 ); |
| 154923 | pSub->pSrc->a[0].fg.fromExists = 1; |
| 154924 | p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); |
| 154925 | if( pSubWhere ){ |
| 154926 | p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere); |
| 154927 | pSub->pWhere = 0; |
| 154928 | } |
| @@ -154969,10 +155047,34 @@ | |
| 155047 | memset(&sCtx, 0, sizeof(sCtx)); |
| 155048 | sCtx.pSrc = pSelect->pSrc; |
| 155049 | sqlite3WalkExprNN(&w, pSelect->pWhere); |
| 155050 | pSelect->selFlags &= ~SF_OnToWhere; |
| 155051 | } |
| 155052 | |
| 155053 | /* |
| 155054 | ** If p2 exists and p1 and p2 have the same number of terms, then change |
| 155055 | ** every term of p1 to have the same sort order as p2 and return true. |
| 155056 | ** |
| 155057 | ** If p2 is NULL or p1 and p2 are different lengths, then make no changes |
| 155058 | ** and return false. |
| 155059 | ** |
| 155060 | ** p1 must be non-NULL. |
| 155061 | */ |
| 155062 | static int sqlite3CopySortOrder(ExprList *p1, ExprList *p2){ |
| 155063 | assert( p1 ); |
| 155064 | if( p2 && p1->nExpr==p2->nExpr ){ |
| 155065 | int ii; |
| 155066 | for(ii=0; ii<p1->nExpr; ii++){ |
| 155067 | u8 sortFlags; |
| 155068 | sortFlags = p2->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; |
| 155069 | p1->a[ii].fg.sortFlags = sortFlags; |
| 155070 | } |
| 155071 | return 1; |
| 155072 | }else{ |
| 155073 | return 0; |
| 155074 | } |
| 155075 | } |
| 155076 | |
| 155077 | /* |
| 155078 | ** Generate byte-code for the SELECT statement given in the p argument. |
| 155079 | ** |
| 155080 | ** The results are returned according to the SelectDest structure. |
| @@ -155065,12 +155167,11 @@ | |
| 155167 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); |
| 155168 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); |
| 155169 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); |
| 155170 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue ); |
| 155171 | if( IgnorableDistinct(pDest) ){ |
| 155172 | assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Discard || |
| 155173 | pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); |
| 155174 | /* All of these destinations are also able to ignore the ORDER BY clause */ |
| 155175 | if( p->pOrderBy ){ |
| 155176 | #if TREETRACE_ENABLED |
| 155177 | TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); |
| @@ -155082,11 +155183,10 @@ | |
| 155183 | p->pOrderBy); |
| 155184 | testcase( pParse->earlyCleanup ); |
| 155185 | p->pOrderBy = 0; |
| 155186 | } |
| 155187 | p->selFlags &= ~(u32)SF_Distinct; |
| 155188 | } |
| 155189 | sqlite3SelectPrep(pParse, p, 0); |
| 155190 | if( pParse->nErr ){ |
| 155191 | goto select_end; |
| 155192 | } |
| @@ -155622,11 +155722,12 @@ | |
| 155722 | ** used for both the ORDER BY and DISTINCT processing. As originally |
| 155723 | ** written the query must use a temp-table for at least one of the ORDER |
| 155724 | ** BY and DISTINCT, and an index or separate temp-table for the other. |
| 155725 | */ |
| 155726 | if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct |
| 155727 | && sqlite3CopySortOrder(pEList, sSort.pOrderBy) |
| 155728 | && sqlite3ExprListCompare(pEList, sSort.pOrderBy, -1)==0 |
| 155729 | && OptimizationEnabled(db, SQLITE_GroupByOrder) |
| 155730 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 155731 | && p->pWin==0 |
| 155732 | #endif |
| 155733 | ){ |
| @@ -155836,25 +155937,14 @@ | |
| 155937 | ** in the correct order. It also may not - the GROUP BY might use a |
| 155938 | ** database index that causes rows to be grouped together as required |
| 155939 | ** but not actually sorted. Either way, record the fact that the |
| 155940 | ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp |
| 155941 | ** variable. */ |
| 155942 | if( sqlite3CopySortOrder(pGroupBy, sSort.pOrderBy) |
| 155943 | && sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 |
| 155944 | ){ |
| 155945 | orderByGrp = 1; |
| 155946 | } |
| 155947 | }else{ |
| 155948 | assert( 0==sqlite3LogEst(1) ); |
| 155949 | p->nSelectRow = 0; |
| 155950 | } |
| @@ -156849,15 +156939,20 @@ | |
| 156939 | } |
| 156940 | goto trigger_cleanup; |
| 156941 | } |
| 156942 | } |
| 156943 | |
| 156944 | /* NB: The SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES compile-time option is |
| 156945 | ** experimental and unsupported. Do not use it unless understand the |
| 156946 | ** implications and you cannot get by without this capability. */ |
| 156947 | #if !defined(SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES) /* Experimental */ |
| 156948 | /* Do not create a trigger on a system table */ |
| 156949 | if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ |
| 156950 | sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); |
| 156951 | goto trigger_cleanup; |
| 156952 | } |
| 156953 | #endif |
| 156954 | |
| 156955 | /* INSTEAD of triggers are only for views and views only support INSTEAD |
| 156956 | ** of triggers. |
| 156957 | */ |
| 156958 | if( IsView(pTab) && tr_tm!=TK_INSTEAD ){ |
| @@ -160118,13 +160213,15 @@ | |
| 160213 | if( rc!=SQLITE_OK ) goto end_of_vacuum; |
| 160214 | assert( (db->nDb-1)==nDb ); |
| 160215 | pDb = &db->aDb[nDb]; |
| 160216 | assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); |
| 160217 | pTemp = pDb->pBt; |
| 160218 | nRes = sqlite3BtreeGetRequestedReserve(pMain); |
| 160219 | if( pOut ){ |
| 160220 | sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); |
| 160221 | i64 sz = 0; |
| 160222 | const char *zFilename; |
| 160223 | if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ |
| 160224 | rc = SQLITE_ERROR; |
| 160225 | sqlite3SetString(pzErrMsg, db, "output file already exists"); |
| 160226 | goto end_of_vacuum; |
| 160227 | } |
| @@ -160132,12 +160229,20 @@ | |
| 160229 | |
| 160230 | /* For a VACUUM INTO, the pager-flags are set to the same values as |
| 160231 | ** they are for the database being vacuumed, except that PAGER_CACHESPILL |
| 160232 | ** is always set. */ |
| 160233 | pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); |
| 160234 | |
| 160235 | /* If the VACUUM INTO target file is a URI filename and if the |
| 160236 | ** "reserve=N" query parameter is present, reset the reserve to the |
| 160237 | ** amount specified, if the amount is within range */ |
| 160238 | zFilename = sqlite3BtreeGetFilename(pTemp); |
| 160239 | if( ALWAYS(zFilename) ){ |
| 160240 | int nNew = (int)sqlite3_uri_int64(zFilename, "reserve", nRes); |
| 160241 | if( nNew>=0 && nNew<=255 ) nRes = nNew; |
| 160242 | } |
| 160243 | } |
| 160244 | |
| 160245 | sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); |
| 160246 | sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); |
| 160247 | sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); |
| 160248 | |
| @@ -165646,10 +165751,38 @@ | |
| 165751 | sqlite3ValueFree(pVal); |
| 165752 | return rc; |
| 165753 | } |
| 165754 | #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ |
| 165755 | |
| 165756 | /* |
| 165757 | ** If pExpr is one of "like", "glob", "match", or "regexp", then |
| 165758 | ** return the corresponding SQLITE_INDEX_CONSTRAINT_xxxx value. |
| 165759 | ** If not, return 0. |
| 165760 | ** |
| 165761 | ** pExpr is guaranteed to be a TK_FUNCTION. |
| 165762 | */ |
| 165763 | SQLITE_PRIVATE int sqlite3ExprIsLikeOperator(const Expr *pExpr){ |
| 165764 | static const struct { |
| 165765 | const char *zOp; |
| 165766 | unsigned char eOp; |
| 165767 | } aOp[] = { |
| 165768 | { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, |
| 165769 | { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, |
| 165770 | { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, |
| 165771 | { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } |
| 165772 | }; |
| 165773 | int i; |
| 165774 | assert( pExpr->op==TK_FUNCTION ); |
| 165775 | assert( !ExprHasProperty(pExpr, EP_IntValue) ); |
| 165776 | for(i=0; i<ArraySize(aOp); i++){ |
| 165777 | if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){ |
| 165778 | return aOp[i].eOp; |
| 165779 | } |
| 165780 | } |
| 165781 | return 0; |
| 165782 | } |
| 165783 | |
| 165784 | |
| 165785 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 165786 | /* |
| 165787 | ** Check to see if the pExpr expression is a form that needs to be passed |
| 165788 | ** to the xBestIndex method of virtual tables. Forms of interest include: |
| @@ -165682,19 +165815,10 @@ | |
| 165815 | unsigned char *peOp2, /* OUT: 0 for MATCH, or else an op2 value */ |
| 165816 | Expr **ppLeft, /* Column expression to left of MATCH/op2 */ |
| 165817 | Expr **ppRight /* Expression to left of MATCH/op2 */ |
| 165818 | ){ |
| 165819 | if( pExpr->op==TK_FUNCTION ){ |
| 165820 | ExprList *pList; |
| 165821 | Expr *pCol; /* Column reference */ |
| 165822 | int i; |
| 165823 | |
| 165824 | assert( ExprUseXList(pExpr) ); |
| @@ -165710,20 +165834,15 @@ | |
| 165834 | ** vtab_column MATCH expression |
| 165835 | ** MATCH(expression,vtab_column) |
| 165836 | */ |
| 165837 | pCol = pList->a[1].pExpr; |
| 165838 | assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); |
| 165839 | if( ExprIsVtab(pCol) && (i = sqlite3ExprIsLikeOperator(pExpr))!=0 ){ |
| 165840 | *peOp2 = i; |
| 165841 | *ppRight = pList->a[0].pExpr; |
| 165842 | *ppLeft = pCol; |
| 165843 | return 1; |
| 165844 | } |
| 165845 | |
| 165846 | /* We can also match against the first column of overloaded |
| 165847 | ** functions where xFindFunction returns a value of at least |
| 165848 | ** SQLITE_INDEX_CONSTRAINT_FUNCTION. |
| @@ -165853,20 +165972,26 @@ | |
| 165972 | u16 eOp = pOne->eOperator | pTwo->eOperator; |
| 165973 | sqlite3 *db; /* Database connection (for malloc) */ |
| 165974 | Expr *pNew; /* New virtual expression */ |
| 165975 | int op; /* Operator for the combined expression */ |
| 165976 | int idxNew; /* Index in pWC of the next virtual term */ |
| 165977 | Expr *pA, *pB; /* Expressions associated with pOne and pTwo */ |
| 165978 | |
| 165979 | if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; |
| 165980 | if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; |
| 165981 | if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; |
| 165982 | if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp |
| 165983 | && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; |
| 165984 | pA = pOne->pExpr; |
| 165985 | pB = pTwo->pExpr; |
| 165986 | assert( pA->pLeft!=0 && pA->pRight!=0 ); |
| 165987 | assert( pB->pLeft!=0 && pB->pRight!=0 ); |
| 165988 | if( sqlite3ExprCompare(0,pA->pLeft, pB->pLeft, -1) ) return; |
| 165989 | if( sqlite3ExprCompare(0,pA->pRight, pB->pRight,-1) ) return; |
| 165990 | if( ExprHasProperty(pA,EP_Commuted)!=ExprHasProperty(pB,EP_Commuted) ){ |
| 165991 | return; |
| 165992 | } |
| 165993 | /* If we reach this point, it means the two subterms can be combined */ |
| 165994 | if( (eOp & (eOp-1))!=0 ){ |
| 165995 | if( eOp & (WO_LT|WO_LE) ){ |
| 165996 | eOp = WO_LE; |
| 165997 | }else{ |
| @@ -165873,11 +165998,11 @@ | |
| 165998 | assert( eOp & (WO_GT|WO_GE) ); |
| 165999 | eOp = WO_GE; |
| 166000 | } |
| 166001 | } |
| 166002 | db = pWC->pWInfo->pParse->db; |
| 166003 | pNew = sqlite3ExprDup(db, pA, 0); |
| 166004 | if( pNew==0 ) return; |
| 166005 | for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( op<TK_GE ); } |
| 166006 | pNew->op = op; |
| 166007 | idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); |
| 166008 | exprAnalyze(pSrc, pWC, idxNew); |
| @@ -168744,15 +168869,18 @@ | |
| 168869 | |
| 168870 | /* No matches cause a break out of the loop */ |
| 168871 | break; |
| 168872 | } |
| 168873 | if( i==n ){ |
| 168874 | int bSortByGroup = (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0; |
| 168875 | nOrderBy = n; |
| 168876 | if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ |
| 168877 | eDistinct = 2 + bSortByGroup; |
| 168878 | }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ |
| 168879 | eDistinct = 1 - bSortByGroup; |
| 168880 | }else if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ |
| 168881 | eDistinct = 3; |
| 168882 | } |
| 168883 | } |
| 168884 | } |
| 168885 | |
| 168886 | /* Allocate the sqlite3_index_info structure |
| @@ -170158,10 +170286,71 @@ | |
| 170286 | p->u.btree.pIndex = 0; |
| 170287 | } |
| 170288 | } |
| 170289 | return rc; |
| 170290 | } |
| 170291 | |
| 170292 | /* |
| 170293 | ** Callback for estLikePatternLength(). |
| 170294 | ** |
| 170295 | ** If this node is a string literal that is longer pWalker->sz, then set |
| 170296 | ** pWalker->sz to the byte length of that string literal. |
| 170297 | ** |
| 170298 | ** pWalker->eCode indicates how to count characters: |
| 170299 | ** |
| 170300 | ** eCode==0 Count as a GLOB pattern |
| 170301 | ** eCode==1 Count as a LIKE pattern |
| 170302 | */ |
| 170303 | static int exprNodePatternLengthEst(Walker *pWalker, Expr *pExpr){ |
| 170304 | if( pExpr->op==TK_STRING ){ |
| 170305 | int sz = 0; /* Pattern size in bytes */ |
| 170306 | u8 *z = (u8*)pExpr->u.zToken; /* The pattern */ |
| 170307 | u8 c; /* Next character of the pattern */ |
| 170308 | u8 c1, c2, c3; /* Wildcards */ |
| 170309 | if( pWalker->eCode ){ |
| 170310 | c1 = '%'; |
| 170311 | c2 = '_'; |
| 170312 | c3 = 0; |
| 170313 | }else{ |
| 170314 | c1 = '*'; |
| 170315 | c2 = '?'; |
| 170316 | c3 = '['; |
| 170317 | } |
| 170318 | while( (c = *(z++))!=0 ){ |
| 170319 | if( c==c3 ){ |
| 170320 | if( *z ) z++; |
| 170321 | while( *z && *z!=']' ) z++; |
| 170322 | }else if( c!=c1 && c!=c2 ){ |
| 170323 | sz++; |
| 170324 | } |
| 170325 | } |
| 170326 | if( sz>pWalker->u.sz ) pWalker->u.sz = sz; |
| 170327 | } |
| 170328 | return WRC_Continue; |
| 170329 | } |
| 170330 | |
| 170331 | /* |
| 170332 | ** Return the length of the longest string literal in the given |
| 170333 | ** expression. |
| 170334 | ** |
| 170335 | ** eCode indicates how to count characters: |
| 170336 | ** |
| 170337 | ** eCode==0 Count as a GLOB pattern |
| 170338 | ** eCode==1 Count as a LIKE pattern |
| 170339 | */ |
| 170340 | static int estLikePatternLength(Expr *p, u16 eCode){ |
| 170341 | Walker w; |
| 170342 | w.u.sz = 0; |
| 170343 | w.eCode = eCode; |
| 170344 | w.xExprCallback = exprNodePatternLengthEst; |
| 170345 | w.xSelectCallback = sqlite3SelectWalkFail; |
| 170346 | #ifdef SQLITE_DEBUG |
| 170347 | w.xSelectCallback2 = sqlite3SelectWalkAssert2; |
| 170348 | #endif |
| 170349 | sqlite3WalkExpr(&w, p); |
| 170350 | return w.u.sz; |
| 170351 | } |
| 170352 | |
| 170353 | /* |
| 170354 | ** Adjust the WhereLoop.nOut value downward to account for terms of the |
| 170355 | ** WHERE clause that reference the loop but which are not used by an |
| 170356 | ** index. |
| @@ -170187,10 +170376,17 @@ | |
| 170376 | ** of rows in the table. In other words, assume that x==EXPR will filter |
| 170377 | ** out at least 3 out of 4 rows. If EXPR is -1 or 0 or 1, then maybe the |
| 170378 | ** "x" column is boolean or else -1 or 0 or 1 is a common default value |
| 170379 | ** on the "x" column and so in that case only cap the output row estimate |
| 170380 | ** at 1/2 instead of 1/4. |
| 170381 | ** |
| 170382 | ** Heuristic 3: If there is a LIKE or GLOB (or REGEXP or MATCH) operator |
| 170383 | ** with a large constant pattern, then reduce the size of the search |
| 170384 | ** space according to the length of the pattern, under the theory that |
| 170385 | ** longer patterns are less likely to match. This heuristic was added |
| 170386 | ** to give better output-row count estimates when preparing queries for |
| 170387 | ** the Join-Order Benchmarks. See forum thread 2026-01-30T09:57:54z |
| 170388 | */ |
| 170389 | static void whereLoopOutputAdjust( |
| 170390 | WhereClause *pWC, /* The WHERE clause */ |
| 170391 | WhereLoop *pLoop, /* The loop to adjust downward */ |
| 170392 | LogEst nRow /* Number of rows in the entire table */ |
| @@ -170236,25 +170432,43 @@ | |
| 170432 | ** then use the probability provided by the application. */ |
| 170433 | pLoop->nOut += pTerm->truthProb; |
| 170434 | }else{ |
| 170435 | /* In the absence of explicit truth probabilities, use heuristics to |
| 170436 | ** guess a reasonable truth probability. */ |
| 170437 | Expr *pOpExpr = pTerm->pExpr; |
| 170438 | pLoop->nOut--; |
| 170439 | if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 |
| 170440 | && (pTerm->wtFlags & TERM_HIGHTRUTH)==0 /* tag-20200224-1 */ |
| 170441 | ){ |
| 170442 | Expr *pRight = pOpExpr->pRight; |
| 170443 | int k = 0; |
| 170444 | testcase( pOpExpr->op==TK_IS ); |
| 170445 | if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){ |
| 170446 | k = 10; |
| 170447 | }else{ |
| 170448 | k = 20; |
| 170449 | } |
| 170450 | if( iReduce<k ){ |
| 170451 | pTerm->wtFlags |= TERM_HEURTRUTH; |
| 170452 | iReduce = k; |
| 170453 | } |
| 170454 | }else |
| 170455 | if( ExprHasProperty(pOpExpr, EP_InfixFunc) |
| 170456 | && pOpExpr->op==TK_FUNCTION |
| 170457 | ){ |
| 170458 | int eOp; |
| 170459 | assert( ExprUseXList(pOpExpr) ); |
| 170460 | assert( pOpExpr->x.pList->nExpr>=2 ); |
| 170461 | eOp = sqlite3ExprIsLikeOperator(pOpExpr); |
| 170462 | if( ALWAYS(eOp>0) ){ |
| 170463 | int szPattern; |
| 170464 | Expr *pRHS = pOpExpr->x.pList->a[0].pExpr; |
| 170465 | eOp = eOp==SQLITE_INDEX_CONSTRAINT_LIKE; |
| 170466 | szPattern = estLikePatternLength(pRHS, eOp); |
| 170467 | if( szPattern>0 ){ |
| 170468 | pLoop->nOut -= szPattern*2; |
| 170469 | } |
| 170470 | } |
| 170471 | } |
| 170472 | } |
| 170473 | } |
| 170474 | } |
| @@ -170712,10 +170926,11 @@ | |
| 170926 | |
| 170927 | nOutUnadjusted = pNew->nOut; |
| 170928 | pNew->rRun += nInMul + nIn; |
| 170929 | pNew->nOut += nInMul + nIn; |
| 170930 | whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize); |
| 170931 | if( pSrc->fg.fromExists ) pNew->nOut = 0; |
| 170932 | rc = whereLoopInsert(pBuilder, pNew); |
| 170933 | |
| 170934 | if( pNew->wsFlags & WHERE_COLUMN_RANGE ){ |
| 170935 | pNew->nOut = saved_nOut; |
| 170936 | }else{ |
| @@ -171308,10 +171523,12 @@ | |
| 171523 | ApplyCostMultiplier(pNew->rRun, pTab->costMult); |
| 171524 | whereLoopOutputAdjust(pWC, pNew, rSize); |
| 171525 | if( pSrc->fg.isSubquery ){ |
| 171526 | if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE; |
| 171527 | pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; |
| 171528 | }else if( pSrc->fg.fromExists ){ |
| 171529 | pNew->nOut = 0; |
| 171530 | } |
| 171531 | rc = whereLoopInsert(pBuilder, pNew); |
| 171532 | pNew->nOut = rSize; |
| 171533 | if( rc ) break; |
| 171534 | }else{ |
| @@ -171410,10 +171627,11 @@ | |
| 171627 | /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN |
| 171628 | ** because the cursor used to access the index might not be |
| 171629 | ** positioned to the correct row during the right-join no-match |
| 171630 | ** loop. */ |
| 171631 | }else{ |
| 171632 | if( pSrc->fg.fromExists ) pNew->nOut = 0; |
| 171633 | rc = whereLoopInsert(pBuilder, pNew); |
| 171634 | } |
| 171635 | pNew->nOut = rSize; |
| 171636 | if( rc ) break; |
| 171637 | } |
| @@ -172072,11 +172290,11 @@ | |
| 172290 | SrcItem *pItem; |
| 172291 | SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; |
| 172292 | sqlite3 *db = pWInfo->pParse->db; |
| 172293 | int rc = SQLITE_OK; |
| 172294 | int bFirstPastRJ = 0; |
| 172295 | int hasRightCrossJoin = 0; |
| 172296 | WhereLoop *pNew; |
| 172297 | |
| 172298 | |
| 172299 | /* Loop over the tables in the join, from left to right */ |
| 172300 | pNew = pBuilder->pNew; |
| @@ -172099,19 +172317,38 @@ | |
| 172317 | /* Add prerequisites to prevent reordering of FROM clause terms |
| 172318 | ** across CROSS joins and outer joins. The bFirstPastRJ boolean |
| 172319 | ** prevents the right operand of a RIGHT JOIN from being swapped with |
| 172320 | ** other elements even further to the right. |
| 172321 | ** |
| 172322 | ** The hasRightCrossJoin flag prevent FROM-clause terms from moving |
| 172323 | ** from the right side of a LEFT JOIN or CROSS JOIN over to the |
| 172324 | ** left side of that same join. This is a required restriction in |
| 172325 | ** the case of LEFT JOIN - an incorrect answer may results if it is |
| 172326 | ** not enforced. This restriction is not required for CROSS JOIN. |
| 172327 | ** It is provided merely as a means of controlling join order, under |
| 172328 | ** the theory that no real-world queries that care about performance |
| 172329 | ** actually use the CROSS JOIN syntax. |
| 172330 | */ |
| 172331 | if( pItem->fg.jointype & (JT_LTORJ|JT_CROSS) ){ |
| 172332 | testcase( pItem->fg.jointype & JT_LTORJ ); |
| 172333 | testcase( pItem->fg.jointype & JT_CROSS ); |
| 172334 | hasRightCrossJoin = 1; |
| 172335 | } |
| 172336 | mPrereq |= mPrior; |
| 172337 | bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; |
| 172338 | }else if( pItem->fg.fromExists ){ |
| 172339 | /* joins that result from the EXISTS-to-JOIN optimization should not |
| 172340 | ** be moved to the left of any of their dependencies */ |
| 172341 | WhereClause *pWC = &pWInfo->sWC; |
| 172342 | WhereTerm *pTerm; |
| 172343 | int i; |
| 172344 | for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){ |
| 172345 | if( (pNew->maskSelf & pTerm->prereqAll)!=0 ){ |
| 172346 | mPrereq |= (pTerm->prereqAll & (pNew->maskSelf-1)); |
| 172347 | } |
| 172348 | } |
| 172349 | }else if( !hasRightCrossJoin ){ |
| 172350 | mPrereq = 0; |
| 172351 | } |
| 172352 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 172353 | if( IsVirtual(pItem->pSTab) ){ |
| 172354 | SrcItem *p; |
| @@ -172330,13 +172567,11 @@ | |
| 172567 | if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue; |
| 172568 | }else{ |
| 172569 | pLoop = pLast; |
| 172570 | } |
| 172571 | if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ |
| 172572 | if( pLoop->u.vtab.isOrdered && pWInfo->pOrderBy==pOrderBy ){ |
| 172573 | obSat = obDone; |
| 172574 | }else{ |
| 172575 | /* No further ORDER BY terms may be matched. So this call should |
| 172576 | ** return >=0, not -1. Clear isOrderDistinct to ensure it does so. */ |
| 172577 | isOrderDistinct = 0; |
| @@ -173673,10 +173908,11 @@ | |
| 173908 | pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); |
| 173909 | notReady &= ~pLoop->maskSelf; |
| 173910 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| 173911 | if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ |
| 173912 | pTerm->wtFlags |= TERM_CODED; |
| 173913 | pTerm->prereqAll = 0; |
| 173914 | } |
| 173915 | } |
| 173916 | if( i!=pWInfo->nLevel-1 ){ |
| 173917 | int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); |
| 173918 | memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); |
| @@ -174660,26 +174896,31 @@ | |
| 174896 | VdbeCoverageIf(v, op==OP_SeekGT); |
| 174897 | sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); |
| 174898 | } |
| 174899 | #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ |
| 174900 | } |
| 174901 | if( pTabList->a[pLevel->iFrom].fg.fromExists |
| 174902 | && (i==pWInfo->nLevel-1 |
| 174903 | || pTabList->a[pWInfo->a[i+1].iFrom].fg.fromExists==0) |
| 174904 | ){ |
| 174905 | /* This is an EXISTS-to-JOIN optimization which is either the |
| 174906 | ** inner-most loop, or the inner-most of a group of nested |
| 174907 | ** EXISTS-to-JOIN optimization loops. If this loop sees a successful |
| 174908 | ** row, it should break out of itself as well as other EXISTS-to-JOIN |
| 174909 | ** loops in which is is directly nested. */ |
| 174910 | int nOuter = 0; /* Nr of outer EXISTS that this one is nested within */ |
| 174911 | while( nOuter<i ){ |
| 174912 | if( !pTabList->a[pLevel[-nOuter-1].iFrom].fg.fromExists ) break; |
| 174913 | nOuter++; |
| 174914 | } |
| 174915 | testcase( nOuter>0 ); |
| 174916 | sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel[-nOuter].addrBrk); |
| 174917 | if( nOuter ){ |
| 174918 | VdbeComment((v, "EXISTS break %d..%d", i-nOuter, i)); |
| 174919 | }else{ |
| 174920 | VdbeComment((v, "EXISTS break %d", i)); |
| 174921 | } |
| 174922 | } |
| 174923 | sqlite3VdbeResolveLabel(v, pLevel->addrCont); |
| 174924 | if( pLevel->op!=OP_Noop ){ |
| 174925 | sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); |
| 174926 | sqlite3VdbeChangeP5(v, pLevel->p5); |
| @@ -184823,11 +185064,11 @@ | |
| 185064 | for(i=1; sqlite3Isdigit(z[i]); i++){} |
| 185065 | return i; |
| 185066 | } |
| 185067 | case CC_DOLLAR: |
| 185068 | case CC_VARALPHA: { |
| 185069 | i64 n = 0; |
| 185070 | testcase( z[0]=='$' ); testcase( z[0]=='@' ); |
| 185071 | testcase( z[0]==':' ); testcase( z[0]=='#' ); |
| 185072 | *tokenType = TK_VARIABLE; |
| 185073 | for(i=1; (c=z[i])!=0; i++){ |
| 185074 | if( IdChar(c) ){ |
| @@ -189448,10 +189689,16 @@ | |
| 189689 | /* |
| 189690 | ** Find existing client data. |
| 189691 | */ |
| 189692 | SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){ |
| 189693 | DbClientData *p; |
| 189694 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 189695 | if( !zName || !sqlite3SafetyCheckOk(db) ){ |
| 189696 | (void)SQLITE_MISUSE_BKPT; |
| 189697 | return 0; |
| 189698 | } |
| 189699 | #endif |
| 189700 | sqlite3_mutex_enter(db->mutex); |
| 189701 | for(p=db->pDbData; p; p=p->pNext){ |
| 189702 | if( strcmp(p->zName, zName)==0 ){ |
| 189703 | void *pResult = p->pData; |
| 189704 | sqlite3_mutex_leave(db->mutex); |
| @@ -192292,10 +192539,19 @@ | |
| 192539 | SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); |
| 192540 | |
| 192541 | #define fts3GetVarint32(p, piVal) ( \ |
| 192542 | (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \ |
| 192543 | ) |
| 192544 | |
| 192545 | SQLITE_PRIVATE int sqlite3Fts3PrepareStmt( |
| 192546 | Fts3Table *p, /* Prepare for this connection */ |
| 192547 | const char *zSql, /* SQL to prepare */ |
| 192548 | int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */ |
| 192549 | int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */ |
| 192550 | sqlite3_stmt **pp /* OUT: Prepared statement */ |
| 192551 | ); |
| 192552 | |
| 192553 | |
| 192554 | /* fts3.c */ |
| 192555 | SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...); |
| 192556 | SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); |
| 192557 | SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); |
| @@ -193900,13 +194156,11 @@ | |
| 194156 | p->pSeekStmt = 0; |
| 194157 | }else{ |
| 194158 | zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); |
| 194159 | if( !zSql ) return SQLITE_NOMEM; |
| 194160 | p->bLock++; |
| 194161 | rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt); |
| 194162 | p->bLock--; |
| 194163 | sqlite3_free(zSql); |
| 194164 | } |
| 194165 | if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1; |
| 194166 | } |
| @@ -195477,13 +195731,11 @@ | |
| 195731 | p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") |
| 195732 | ); |
| 195733 | } |
| 195734 | if( zSql ){ |
| 195735 | p->bLock++; |
| 195736 | rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt); |
| 195737 | p->bLock--; |
| 195738 | sqlite3_free(zSql); |
| 195739 | }else{ |
| 195740 | rc = SQLITE_NOMEM; |
| 195741 | } |
| @@ -196102,10 +196354,11 @@ | |
| 196354 | int rc = SQLITE_OK; |
| 196355 | int bOk = 0; |
| 196356 | |
| 196357 | UNUSED_PARAMETER(isQuick); |
| 196358 | rc = sqlite3Fts3IntegrityCheck(p, &bOk); |
| 196359 | assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK ); |
| 196360 | assert( rc!=SQLITE_CORRUPT_VTAB ); |
| 196361 | if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ |
| 196362 | *pzErr = sqlite3_mprintf("unable to validate the inverted index for" |
| 196363 | " FTS%d table %s.%s: %s", |
| 196364 | p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); |
| @@ -202713,10 +202966,28 @@ | |
| 202966 | #define SQL_SELECT_MXLEVEL 36 |
| 202967 | |
| 202968 | #define SQL_SELECT_LEVEL_RANGE2 37 |
| 202969 | #define SQL_UPDATE_LEVEL_IDX 38 |
| 202970 | #define SQL_UPDATE_LEVEL 39 |
| 202971 | |
| 202972 | /* |
| 202973 | ** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL |
| 202974 | ** is always set. |
| 202975 | */ |
| 202976 | SQLITE_PRIVATE int sqlite3Fts3PrepareStmt( |
| 202977 | Fts3Table *p, /* Prepare for this connection */ |
| 202978 | const char *zSql, /* SQL to prepare */ |
| 202979 | int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */ |
| 202980 | int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */ |
| 202981 | sqlite3_stmt **pp /* OUT: Prepared statement */ |
| 202982 | ){ |
| 202983 | int f = SQLITE_PREPARE_FROM_DDL |
| 202984 | |((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0) |
| 202985 | |(bPersist ? SQLITE_PREPARE_PERSISTENT : 0); |
| 202986 | |
| 202987 | return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL); |
| 202988 | } |
| 202989 | |
| 202990 | /* |
| 202991 | ** This function is used to obtain an SQLite prepared statement handle |
| 202992 | ** for the statement identified by the second argument. If successful, |
| 202993 | ** *pp is set to the requested statement handle and SQLITE_OK returned. |
| @@ -202839,24 +203110,24 @@ | |
| 203110 | assert( SizeofArray(azSql)==SizeofArray(p->aStmt) ); |
| 203111 | assert( eStmt<SizeofArray(azSql) && eStmt>=0 ); |
| 203112 | |
| 203113 | pStmt = p->aStmt[eStmt]; |
| 203114 | if( !pStmt ){ |
| 203115 | int bAllowVtab = 0; |
| 203116 | char *zSql; |
| 203117 | if( eStmt==SQL_CONTENT_INSERT ){ |
| 203118 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); |
| 203119 | }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ |
| 203120 | bAllowVtab = 1; |
| 203121 | zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); |
| 203122 | }else{ |
| 203123 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); |
| 203124 | } |
| 203125 | if( !zSql ){ |
| 203126 | rc = SQLITE_NOMEM; |
| 203127 | }else{ |
| 203128 | rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt); |
| 203129 | sqlite3_free(zSql); |
| 203130 | assert( rc==SQLITE_OK || pStmt==0 ); |
| 203131 | p->aStmt[eStmt] = pStmt; |
| 203132 | } |
| 203133 | } |
| @@ -206019,11 +206290,11 @@ | |
| 206290 | /* Compose and prepare an SQL statement to loop through the content table */ |
| 206291 | char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); |
| 206292 | if( !zSql ){ |
| 206293 | rc = SQLITE_NOMEM; |
| 206294 | }else{ |
| 206295 | rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt); |
| 206296 | sqlite3_free(zSql); |
| 206297 | } |
| 206298 | |
| 206299 | if( rc==SQLITE_OK ){ |
| 206300 | sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3; |
| @@ -207772,11 +208043,11 @@ | |
| 208043 | |
| 208044 | zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist); |
| 208045 | if( !zSql ){ |
| 208046 | rc = SQLITE_NOMEM; |
| 208047 | }else{ |
| 208048 | rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt); |
| 208049 | sqlite3_free(zSql); |
| 208050 | } |
| 208051 | |
| 208052 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 208053 | i64 iDocid = sqlite3_column_int64(pStmt, 0); |
| @@ -211177,11 +211448,14 @@ | |
| 211448 | */ |
| 211449 | #define JSON_JSON 0x01 /* Result is always JSON */ |
| 211450 | #define JSON_SQL 0x02 /* Result is always SQL */ |
| 211451 | #define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ |
| 211452 | #define JSON_ISSET 0x04 /* json_set(), not json_insert() */ |
| 211453 | #define JSON_AINS 0x08 /* json_array_insert(), not json_insert() */ |
| 211454 | #define JSON_BLOB 0x10 /* Use the BLOB output format */ |
| 211455 | |
| 211456 | #define JSON_INSERT_TYPE(X) (((X)&0xC)>>2) |
| 211457 | |
| 211458 | |
| 211459 | /* A parsed JSON value. Lifecycle: |
| 211460 | ** |
| 211461 | ** 1. JSON comes in and is parsed into a JSONB value in aBlob. The |
| @@ -211223,10 +211497,11 @@ | |
| 211497 | /* Allowed values for JsonParse.eEdit */ |
| 211498 | #define JEDIT_DEL 1 /* Delete if exists */ |
| 211499 | #define JEDIT_REPL 2 /* Overwrite if exists */ |
| 211500 | #define JEDIT_INS 3 /* Insert if not exists */ |
| 211501 | #define JEDIT_SET 4 /* Insert or overwrite */ |
| 211502 | #define JEDIT_AINS 5 /* array_insert() */ |
| 211503 | |
| 211504 | /* |
| 211505 | ** Maximum nesting depth of JSON for this implementation. |
| 211506 | ** |
| 211507 | ** This limit is needed to avoid a stack overflow in the recursive |
| @@ -213719,11 +213994,12 @@ | |
| 213994 | /* |
| 213995 | ** Error returns from jsonLookupStep() |
| 213996 | */ |
| 213997 | #define JSON_LOOKUP_ERROR 0xffffffff |
| 213998 | #define JSON_LOOKUP_NOTFOUND 0xfffffffe |
| 213999 | #define JSON_LOOKUP_NOTARRAY 0xfffffffd |
| 214000 | #define JSON_LOOKUP_PATHERROR 0xfffffffc |
| 214001 | #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) |
| 214002 | |
| 214003 | /* Forward declaration */ |
| 214004 | static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); |
| 214005 | |
| @@ -213748,11 +214024,11 @@ | |
| 214024 | ** using the substructure. |
| 214025 | */ |
| 214026 | static u32 jsonCreateEditSubstructure( |
| 214027 | JsonParse *pParse, /* The original JSONB that is being edited */ |
| 214028 | JsonParse *pIns, /* Populate this with the blob data to insert */ |
| 214029 | const char *zTail /* Tail of the path that determines substructure */ |
| 214030 | ){ |
| 214031 | static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; |
| 214032 | int rc; |
| 214033 | memset(pIns, 0, sizeof(*pIns)); |
| 214034 | pIns->db = pParse->db; |
| @@ -213783,13 +214059,13 @@ | |
| 214059 | ** label, before returning. |
| 214060 | ** |
| 214061 | ** Return one of the JSON_LOOKUP error codes if problems are seen. |
| 214062 | ** |
| 214063 | ** This routine will also modify the blob. If pParse->eEdit is one of |
| 214064 | ** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, JEDIT_SET, or JEDIT_AINS, then changes |
| 214065 | ** might be made to the selected value. If an edit is performed, then the |
| 214066 | ** return value does not necessarily point to the select element. If an edit |
| 214067 | ** is performed, the return value is only useful for detecting error |
| 214068 | ** conditions. |
| 214069 | */ |
| 214070 | static u32 jsonLookupStep( |
| 214071 | JsonParse *pParse, /* The JSON to search */ |
| @@ -213811,10 +214087,17 @@ | |
| 214087 | iRoot = iLabel; |
| 214088 | } |
| 214089 | jsonBlobEdit(pParse, iRoot, sz, 0, 0); |
| 214090 | }else if( pParse->eEdit==JEDIT_INS ){ |
| 214091 | /* Already exists, so json_insert() is a no-op */ |
| 214092 | }else if( pParse->eEdit==JEDIT_AINS ){ |
| 214093 | /* json_array_insert() */ |
| 214094 | if( zPath[-1]!=']' ){ |
| 214095 | return JSON_LOOKUP_NOTARRAY; |
| 214096 | }else{ |
| 214097 | jsonBlobEdit(pParse, iRoot, 0, pParse->aIns, pParse->nIns); |
| 214098 | } |
| 214099 | }else{ |
| 214100 | /* json_set() or json_replace() */ |
| 214101 | jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); |
| 214102 | } |
| 214103 | } |
| @@ -213882,10 +214165,14 @@ | |
| 214165 | u32 nIns; /* Total bytes to insert (label+value) */ |
| 214166 | JsonParse v; /* BLOB encoding of the value to be inserted */ |
| 214167 | JsonParse ix; /* Header of the label to be inserted */ |
| 214168 | testcase( pParse->eEdit==JEDIT_INS ); |
| 214169 | testcase( pParse->eEdit==JEDIT_SET ); |
| 214170 | testcase( pParse->eEdit==JEDIT_AINS ); |
| 214171 | if( pParse->eEdit==JEDIT_AINS && sqlite3_strglob("*]",&zPath[i])!=0 ){ |
| 214172 | return JSON_LOOKUP_NOTARRAY; |
| 214173 | } |
| 214174 | memset(&ix, 0, sizeof(ix)); |
| 214175 | ix.db = pParse->db; |
| 214176 | jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); |
| 214177 | pParse->oom |= ix.oom; |
| 214178 | rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); |
| @@ -213909,32 +214196,36 @@ | |
| 214196 | jsonParseReset(&v); |
| 214197 | jsonParseReset(&ix); |
| 214198 | return rc; |
| 214199 | } |
| 214200 | }else if( zPath[0]=='[' ){ |
| 214201 | u64 kk = 0; |
| 214202 | x = pParse->aBlob[iRoot] & 0x0f; |
| 214203 | if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; |
| 214204 | n = jsonbPayloadSize(pParse, iRoot, &sz); |
| 214205 | i = 1; |
| 214206 | while( sqlite3Isdigit(zPath[i]) ){ |
| 214207 | if( kk<0xffffffff ) kk = kk*10 + zPath[i] - '0'; |
| 214208 | /* ^^^^^^^^^^--- Allow kk to be bigger than any JSON array so that |
| 214209 | ** we get NOTFOUND instead of PATHERROR, without overflowing kk. */ |
| 214210 | i++; |
| 214211 | } |
| 214212 | if( i<2 || zPath[i]!=']' ){ |
| 214213 | if( zPath[1]=='#' ){ |
| 214214 | kk = jsonbArrayCount(pParse, iRoot); |
| 214215 | i = 2; |
| 214216 | if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ |
| 214217 | u64 nn = 0; |
| 214218 | i = 3; |
| 214219 | do{ |
| 214220 | if( nn<0xffffffff ) nn = nn*10 + zPath[i] - '0'; |
| 214221 | /* ^^^^^^^^^^--- Allow nn to be bigger than any JSON array to |
| 214222 | ** get NOTFOUND instead of PATHERROR, without overflowing nn. */ |
| 214223 | i++; |
| 214224 | }while( sqlite3Isdigit(zPath[i]) ); |
| 214225 | if( nn>kk ) return JSON_LOOKUP_NOTFOUND; |
| 214226 | kk -= nn; |
| 214227 | } |
| 214228 | if( zPath[i]!=']' ){ |
| 214229 | return JSON_LOOKUP_PATHERROR; |
| 214230 | } |
| 214231 | }else{ |
| @@ -213942,25 +214233,26 @@ | |
| 214233 | } |
| 214234 | } |
| 214235 | j = iRoot+n; |
| 214236 | iEnd = j+sz; |
| 214237 | while( j<iEnd ){ |
| 214238 | if( kk==0 ){ |
| 214239 | rc = jsonLookupStep(pParse, j, &zPath[i+1], 0); |
| 214240 | if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); |
| 214241 | return rc; |
| 214242 | } |
| 214243 | kk--; |
| 214244 | n = jsonbPayloadSize(pParse, j, &sz); |
| 214245 | if( n==0 ) return JSON_LOOKUP_ERROR; |
| 214246 | j += n+sz; |
| 214247 | } |
| 214248 | if( j>iEnd ) return JSON_LOOKUP_ERROR; |
| 214249 | if( kk>0 ) return JSON_LOOKUP_NOTFOUND; |
| 214250 | if( pParse->eEdit>=JEDIT_INS ){ |
| 214251 | JsonParse v; |
| 214252 | testcase( pParse->eEdit==JEDIT_INS ); |
| 214253 | testcase( pParse->eEdit==JEDIT_AINS ); |
| 214254 | testcase( pParse->eEdit==JEDIT_SET ); |
| 214255 | rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); |
| 214256 | if( !JSON_LOOKUP_ISERROR(rc) |
| 214257 | && jsonBlobMakeEditable(pParse, v.nBlob) |
| 214258 | ){ |
| @@ -214281,13 +214573,19 @@ | |
| 214573 | ** If ctx is not NULL then push the error message into ctx and return NULL. |
| 214574 | ** If ctx is NULL, then return the text of the error message. |
| 214575 | */ |
| 214576 | static char *jsonBadPathError( |
| 214577 | sqlite3_context *ctx, /* The function call containing the error */ |
| 214578 | const char *zPath, /* The path with the problem */ |
| 214579 | int rc /* Maybe JSON_LOOKUP_NOTARRAY */ |
| 214580 | ){ |
| 214581 | char *zMsg; |
| 214582 | if( rc==(int)JSON_LOOKUP_NOTARRAY ){ |
| 214583 | zMsg = sqlite3_mprintf("not an array element: %Q", zPath); |
| 214584 | }else{ |
| 214585 | zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); |
| 214586 | } |
| 214587 | if( ctx==0 ) return zMsg; |
| 214588 | if( zMsg ){ |
| 214589 | sqlite3_result_error(ctx, zMsg, -1); |
| 214590 | sqlite3_free(zMsg); |
| 214591 | }else{ |
| @@ -214300,17 +214598,17 @@ | |
| 214598 | ** arguments come in pairs where each pair contains a JSON path and |
| 214599 | ** content to insert or set at that patch. Do the updates |
| 214600 | ** and return the result. |
| 214601 | ** |
| 214602 | ** The specific operation is determined by eEdit, which can be one |
| 214603 | ** of JEDIT_INS, JEDIT_REPL, JEDIT_SET, or JEDIT_AINS. |
| 214604 | */ |
| 214605 | static void jsonInsertIntoBlob( |
| 214606 | sqlite3_context *ctx, |
| 214607 | int argc, |
| 214608 | sqlite3_value **argv, |
| 214609 | int eEdit /* JEDIT_INS, JEDIT_REPL, JEDIT_SET, JEDIT_AINS */ |
| 214610 | ){ |
| 214611 | int i; |
| 214612 | u32 rc = 0; |
| 214613 | const char *zPath = 0; |
| 214614 | int flgs; |
| @@ -214358,11 +214656,11 @@ | |
| 214656 | jsonInsertIntoBlob_patherror: |
| 214657 | jsonParseFree(p); |
| 214658 | if( rc==JSON_LOOKUP_ERROR ){ |
| 214659 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 214660 | }else{ |
| 214661 | jsonBadPathError(ctx, zPath, rc); |
| 214662 | } |
| 214663 | return; |
| 214664 | } |
| 214665 | |
| 214666 | /* |
| @@ -214800,11 +215098,11 @@ | |
| 215098 | i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); |
| 215099 | if( JSON_LOOKUP_ISERROR(i) ){ |
| 215100 | if( i==JSON_LOOKUP_NOTFOUND ){ |
| 215101 | /* no-op */ |
| 215102 | }else if( i==JSON_LOOKUP_PATHERROR ){ |
| 215103 | jsonBadPathError(ctx, zPath, 0); |
| 215104 | }else{ |
| 215105 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215106 | } |
| 215107 | eErr = 1; |
| 215108 | i = 0; |
| @@ -214905,11 +215203,11 @@ | |
| 215203 | } |
| 215204 | jsonStringTerminate(&jx); |
| 215205 | j = jsonLookupStep(p, 0, jx.zBuf, 0); |
| 215206 | jsonStringReset(&jx); |
| 215207 | }else{ |
| 215208 | jsonBadPathError(ctx, zPath, 0); |
| 215209 | goto json_extract_error; |
| 215210 | } |
| 215211 | if( j<p->nBlob ){ |
| 215212 | if( argc==2 ){ |
| 215213 | if( flags & JSON_JSON ){ |
| @@ -214940,11 +215238,11 @@ | |
| 215238 | } |
| 215239 | }else if( j==JSON_LOOKUP_ERROR ){ |
| 215240 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215241 | goto json_extract_error; |
| 215242 | }else{ |
| 215243 | jsonBadPathError(ctx, zPath, 0); |
| 215244 | goto json_extract_error; |
| 215245 | } |
| 215246 | } |
| 215247 | if( argc>2 ){ |
| 215248 | jsonAppendChar(&jx, ']'); |
| @@ -215269,11 +215567,11 @@ | |
| 215567 | rc = jsonLookupStep(p, 0, zPath+1, 0); |
| 215568 | if( JSON_LOOKUP_ISERROR(rc) ){ |
| 215569 | if( rc==JSON_LOOKUP_NOTFOUND ){ |
| 215570 | continue; /* No-op */ |
| 215571 | }else if( rc==JSON_LOOKUP_PATHERROR ){ |
| 215572 | jsonBadPathError(ctx, zPath, rc); |
| 215573 | }else{ |
| 215574 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215575 | } |
| 215576 | goto json_remove_done; |
| 215577 | } |
| @@ -215281,11 +215579,11 @@ | |
| 215579 | jsonReturnParse(ctx, p); |
| 215580 | jsonParseFree(p); |
| 215581 | return; |
| 215582 | |
| 215583 | json_remove_patherror: |
| 215584 | jsonBadPathError(ctx, zPath, 0); |
| 215585 | |
| 215586 | json_remove_done: |
| 215587 | jsonParseFree(p); |
| 215588 | return; |
| 215589 | } |
| @@ -215325,20 +215623,22 @@ | |
| 215623 | static void jsonSetFunc( |
| 215624 | sqlite3_context *ctx, |
| 215625 | int argc, |
| 215626 | sqlite3_value **argv |
| 215627 | ){ |
| 215628 | int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); |
| 215629 | int eInsType = JSON_INSERT_TYPE(flags); |
| 215630 | static const char *azInsType[] = { "insert", "set", "array_insert" }; |
| 215631 | static const u8 aEditType[] = { JEDIT_INS, JEDIT_SET, JEDIT_AINS }; |
| 215632 | |
| 215633 | if( argc<1 ) return; |
| 215634 | assert( eInsType>=0 && eInsType<=2 ); |
| 215635 | if( (argc&1)==0 ) { |
| 215636 | jsonWrongNumArgs(ctx, azInsType[eInsType]); |
| 215637 | return; |
| 215638 | } |
| 215639 | jsonInsertIntoBlob(ctx, argc, argv, aEditType[eInsType]); |
| 215640 | } |
| 215641 | |
| 215642 | /* |
| 215643 | ** json_type(JSON) |
| 215644 | ** json_type(JSON, PATH) |
| @@ -215359,19 +215659,19 @@ | |
| 215659 | if( p==0 ) return; |
| 215660 | if( argc==2 ){ |
| 215661 | zPath = (const char*)sqlite3_value_text(argv[1]); |
| 215662 | if( zPath==0 ) goto json_type_done; |
| 215663 | if( zPath[0]!='$' ){ |
| 215664 | jsonBadPathError(ctx, zPath, 0); |
| 215665 | goto json_type_done; |
| 215666 | } |
| 215667 | i = jsonLookupStep(p, 0, zPath+1, 0); |
| 215668 | if( JSON_LOOKUP_ISERROR(i) ){ |
| 215669 | if( i==JSON_LOOKUP_NOTFOUND ){ |
| 215670 | /* no-op */ |
| 215671 | }else if( i==JSON_LOOKUP_PATHERROR ){ |
| 215672 | jsonBadPathError(ctx, zPath, 0); |
| 215673 | }else{ |
| 215674 | sqlite3_result_error(ctx, "malformed JSON", -1); |
| 215675 | } |
| 215676 | goto json_type_done; |
| 215677 | } |
| @@ -215623,16 +215923,15 @@ | |
| 215923 | jsonAppendSqlValue(pStr, argv[0]); |
| 215924 | } |
| 215925 | } |
| 215926 | static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ |
| 215927 | JsonString *pStr; |
| 215928 | int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); |
| 215929 | pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); |
| 215930 | if( pStr ){ |
| 215931 | pStr->pCtx = ctx; |
| 215932 | jsonAppendChar(pStr, ']'); |
| 215933 | if( pStr->eErr ){ |
| 215934 | jsonReturnString(pStr, 0, 0); |
| 215935 | return; |
| 215936 | }else if( flags & JSON_BLOB ){ |
| 215937 | jsonReturnStringAsBlob(pStr); |
| @@ -215649,10 +215948,13 @@ | |
| 215948 | pStr->bStatic = 1; |
| 215949 | }else{ |
| 215950 | sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); |
| 215951 | jsonStringTrimOneChar(pStr); |
| 215952 | } |
| 215953 | }else if( flags & JSON_BLOB ){ |
| 215954 | static const u8 emptyArray = 0x0b; |
| 215955 | sqlite3_result_blob(ctx, &emptyArray, 1, SQLITE_STATIC); |
| 215956 | }else{ |
| 215957 | sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); |
| 215958 | } |
| 215959 | sqlite3_result_subtype(ctx, JSON_SUBTYPE); |
| 215960 | } |
| @@ -215745,16 +216047,15 @@ | |
| 216047 | } |
| 216048 | } |
| 216049 | } |
| 216050 | static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ |
| 216051 | JsonString *pStr; |
| 216052 | int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); |
| 216053 | pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); |
| 216054 | if( pStr ){ |
| 216055 | jsonAppendChar(pStr, '}'); |
| 216056 | pStr->pCtx = ctx; |
| 216057 | if( pStr->eErr ){ |
| 216058 | jsonReturnString(pStr, 0, 0); |
| 216059 | return; |
| 216060 | }else if( flags & JSON_BLOB ){ |
| 216061 | jsonReturnStringAsBlob(pStr); |
| @@ -215771,10 +216072,13 @@ | |
| 216072 | pStr->bStatic = 1; |
| 216073 | }else{ |
| 216074 | sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); |
| 216075 | jsonStringTrimOneChar(pStr); |
| 216076 | } |
| 216077 | }else if( flags & JSON_BLOB ){ |
| 216078 | static const unsigned char emptyObject = 0x0c; |
| 216079 | sqlite3_result_blob(ctx, &emptyObject, 1, SQLITE_STATIC); |
| 216080 | }else{ |
| 216081 | sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); |
| 216082 | } |
| 216083 | sqlite3_result_subtype(ctx, JSON_SUBTYPE); |
| 216084 | } |
| @@ -216271,11 +216575,11 @@ | |
| 216575 | if( idxNum==3 ){ |
| 216576 | zRoot = (const char*)sqlite3_value_text(argv[1]); |
| 216577 | if( zRoot==0 ) return SQLITE_OK; |
| 216578 | if( zRoot[0]!='$' ){ |
| 216579 | sqlite3_free(cur->pVtab->zErrMsg); |
| 216580 | cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); |
| 216581 | jsonEachCursorReset(p); |
| 216582 | return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 216583 | } |
| 216584 | p->nRoot = sqlite3Strlen30(zRoot); |
| 216585 | if( zRoot[1]==0 ){ |
| @@ -216289,11 +216593,11 @@ | |
| 216593 | p->eType = 0; |
| 216594 | p->iEnd = 0; |
| 216595 | return SQLITE_OK; |
| 216596 | } |
| 216597 | sqlite3_free(cur->pVtab->zErrMsg); |
| 216598 | cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); |
| 216599 | jsonEachCursorReset(p); |
| 216600 | return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
| 216601 | } |
| 216602 | if( p->sParse.iLabel ){ |
| 216603 | p->i = p->sParse.iLabel; |
| @@ -216379,10 +216683,12 @@ | |
| 216683 | /* | | | | | | */ |
| 216684 | JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), |
| 216685 | JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), |
| 216686 | JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), |
| 216687 | JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), |
| 216688 | JFUNCTION(json_array_insert, -1,1,1, 1,0,JSON_AINS, jsonSetFunc), |
| 216689 | JFUNCTION(jsonb_array_insert,-1,1,0, 1,1,JSON_AINS, jsonSetFunc), |
| 216690 | JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), |
| 216691 | JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), |
| 216692 | JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), |
| 216693 | JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc), |
| 216694 | JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc), |
| @@ -220226,11 +220532,11 @@ | |
| 220532 | tree.nBytesPerCell = 8 + 8 * tree.nDim; |
| 220533 | node.zData = (u8 *)sqlite3_value_blob(apArg[1]); |
| 220534 | if( node.zData==0 ) return; |
| 220535 | nData = sqlite3_value_bytes(apArg[1]); |
| 220536 | if( nData<4 ) return; |
| 220537 | if( nData<4+NCELL(&node)*tree.nBytesPerCell ) return; |
| 220538 | |
| 220539 | pOut = sqlite3_str_new(0); |
| 220540 | for(ii=0; ii<NCELL(&node); ii++){ |
| 220541 | RtreeCell cell; |
| 220542 | int jj; |
| @@ -231218,10 +231524,11 @@ | |
| 231524 | struct carray_bind { |
| 231525 | void *aData; /* The data */ |
| 231526 | int nData; /* Number of elements */ |
| 231527 | int mFlags; /* Control flags */ |
| 231528 | void (*xDel)(void*); /* Destructor for aData */ |
| 231529 | void *pDel; /* Alternative argument to xDel() */ |
| 231530 | }; |
| 231531 | |
| 231532 | |
| 231533 | /* carray_cursor is a subclass of sqlite3_vtab_cursor which will |
| 231534 | ** serve as the underlying representation of a cursor that scans |
| @@ -231550,26 +231857,38 @@ | |
| 231857 | ** Destructor for the carray_bind object |
| 231858 | */ |
| 231859 | static void carrayBindDel(void *pPtr){ |
| 231860 | carray_bind *p = (carray_bind*)pPtr; |
| 231861 | if( p->xDel!=SQLITE_STATIC ){ |
| 231862 | p->xDel(p->pDel); |
| 231863 | } |
| 231864 | sqlite3_free(p); |
| 231865 | } |
| 231866 | |
| 231867 | /* |
| 231868 | ** Invoke this interface in order to bind to the single-argument |
| 231869 | ** version of CARRAY(). |
| 231870 | ** |
| 231871 | ** pStmt The prepared statement to which to bind |
| 231872 | ** idx The index of the parameter of pStmt to which to bind |
| 231873 | ** aData The data to be bound |
| 231874 | ** nData The number of elements in aData |
| 231875 | ** mFlags One of SQLITE_CARRAY_xxxx indicating datatype of aData |
| 231876 | ** xDestroy Destructor for pDestroy or aData if pDestroy==NULL. |
| 231877 | ** pDestroy Invoke xDestroy on this pointer if not NULL |
| 231878 | ** |
| 231879 | ** The destructor is called pDestroy if pDestroy!=NULL, or against |
| 231880 | ** aData if pDestroy==NULL. |
| 231881 | */ |
| 231882 | SQLITE_API int sqlite3_carray_bind_v2( |
| 231883 | sqlite3_stmt *pStmt, |
| 231884 | int idx, |
| 231885 | void *aData, |
| 231886 | int nData, |
| 231887 | int mFlags, |
| 231888 | void (*xDestroy)(void*), |
| 231889 | void *pDestroy |
| 231890 | ){ |
| 231891 | carray_bind *pNew = 0; |
| 231892 | int i; |
| 231893 | int rc = SQLITE_OK; |
| 231894 | |
| @@ -231642,23 +231961,41 @@ | |
| 231961 | } |
| 231962 | }else{ |
| 231963 | memcpy(pNew->aData, aData, sz); |
| 231964 | } |
| 231965 | pNew->xDel = sqlite3_free; |
| 231966 | pNew->pDel = pNew->aData; |
| 231967 | }else{ |
| 231968 | pNew->aData = aData; |
| 231969 | pNew->xDel = xDestroy; |
| 231970 | pNew->pDel = pDestroy; |
| 231971 | } |
| 231972 | return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel); |
| 231973 | |
| 231974 | carray_bind_error: |
| 231975 | if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){ |
| 231976 | xDestroy(pDestroy); |
| 231977 | } |
| 231978 | sqlite3_free(pNew); |
| 231979 | return rc; |
| 231980 | } |
| 231981 | |
| 231982 | /* |
| 231983 | ** Invoke this interface in order to bind to the single-argument |
| 231984 | ** version of CARRAY(). Same as sqlite3_carray_bind_v2() with the |
| 231985 | ** pDestroy parameter set to NULL. |
| 231986 | */ |
| 231987 | SQLITE_API int sqlite3_carray_bind( |
| 231988 | sqlite3_stmt *pStmt, |
| 231989 | int idx, |
| 231990 | void *aData, |
| 231991 | int nData, |
| 231992 | int mFlags, |
| 231993 | void (*xDestroy)(void*) |
| 231994 | ){ |
| 231995 | return sqlite3_carray_bind_v2(pStmt,idx,aData,nData,mFlags,xDestroy,aData); |
| 231996 | } |
| 231997 | |
| 231998 | /* |
| 231999 | ** Invoke this routine to register the carray() function. |
| 232000 | */ |
| 232001 | SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3 *db){ |
| @@ -232017,10 +232354,24 @@ | |
| 232354 | ** bytes read. |
| 232355 | */ |
| 232356 | static int sessionVarintGet(const u8 *aBuf, int *piVal){ |
| 232357 | return getVarint32(aBuf, *piVal); |
| 232358 | } |
| 232359 | |
| 232360 | /* |
| 232361 | ** Read a varint value from buffer aBuf[], size nBuf bytes, into *piVal. |
| 232362 | ** Return the number of bytes read. |
| 232363 | */ |
| 232364 | static int sessionVarintGetSafe(const u8 *aBuf, int nBuf, int *piVal){ |
| 232365 | u8 aCopy[5]; |
| 232366 | const u8 *aRead = aBuf; |
| 232367 | if( nBuf<5 ){ |
| 232368 | memcpy(aCopy, aBuf, nBuf); |
| 232369 | aRead = aCopy; |
| 232370 | } |
| 232371 | return getVarint32(aRead, *piVal); |
| 232372 | } |
| 232373 | |
| 232374 | /* Load an unaligned and unsigned 32-bit integer */ |
| 232375 | #define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) |
| 232376 | |
| 232377 | /* |
| @@ -232312,31 +232663,31 @@ | |
| 232663 | for(i=0; i<pTab->nCol; i++){ |
| 232664 | int eType = *a; |
| 232665 | int isPK = pTab->abPK[i]; |
| 232666 | if( bPkOnly && isPK==0 ) continue; |
| 232667 | |
| 232668 | assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT |
| 232669 | || eType==SQLITE_TEXT || eType==SQLITE_BLOB |
| 232670 | || eType==SQLITE_NULL || eType==0 |
| 232671 | ); |
| 232672 | |
| 232673 | if( isPK ){ |
| 232674 | a++; |
| 232675 | h = sessionHashAppendType(h, eType); |
| 232676 | if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ |
| 232677 | h = sessionHashAppendI64(h, sessionGetI64(a)); |
| 232678 | a += 8; |
| 232679 | }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ |
| 232680 | int n; |
| 232681 | a += sessionVarintGet(a, &n); |
| 232682 | h = sessionHashAppendBlob(h, n, a); |
| 232683 | a += n; |
| 232684 | } |
| 232685 | /* It should not be possible for eType to be SQLITE_NULL or 0x00 here, |
| 232686 | ** as the session module does not record changes for rows with NULL |
| 232687 | ** values stored in primary key columns. But a corrupt changesets |
| 232688 | ** may contain such a value. */ |
| 232689 | }else{ |
| 232690 | a += sessionSerialLen(a); |
| 232691 | } |
| 232692 | } |
| 232693 | return (h % nBucket); |
| @@ -234741,14 +235092,17 @@ | |
| 235092 | *pnChangeset = 0; |
| 235093 | *ppChangeset = 0; |
| 235094 | } |
| 235095 | |
| 235096 | if( pSession->rc ) return pSession->rc; |
| 235097 | |
| 235098 | sqlite3_mutex_enter(sqlite3_db_mutex(db)); |
| 235099 | rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0); |
| 235100 | if( rc!=SQLITE_OK ){ |
| 235101 | sqlite3_mutex_leave(sqlite3_db_mutex(db)); |
| 235102 | return rc; |
| 235103 | } |
| 235104 | |
| 235105 | for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ |
| 235106 | if( pTab->nEntry ){ |
| 235107 | const char *zName = pTab->zName; |
| 235108 | int i; /* Used to iterate through hash buckets */ |
| @@ -235227,11 +235581,12 @@ | |
| 235581 | |
| 235582 | if( rc==SQLITE_OK ){ |
| 235583 | u8 *aVal = &pIn->aData[pIn->iNext]; |
| 235584 | if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ |
| 235585 | int nByte; |
| 235586 | int nRem = pIn->nData - pIn->iNext; |
| 235587 | pIn->iNext += sessionVarintGetSafe(aVal, nRem, &nByte); |
| 235588 | rc = sessionInputBuffer(pIn, nByte); |
| 235589 | if( rc==SQLITE_OK ){ |
| 235590 | if( nByte<0 || nByte>pIn->nData-pIn->iNext ){ |
| 235591 | rc = SQLITE_CORRUPT_BKPT; |
| 235592 | }else{ |
| @@ -235280,11 +235635,12 @@ | |
| 235635 | int nCol = 0; |
| 235636 | int nRead = 0; |
| 235637 | |
| 235638 | rc = sessionInputBuffer(pIn, 9); |
| 235639 | if( rc==SQLITE_OK ){ |
| 235640 | int nBuf = pIn->nData - pIn->iNext; |
| 235641 | nRead += sessionVarintGetSafe(&pIn->aData[pIn->iNext], nBuf, &nCol); |
| 235642 | /* The hard upper limit for the number of columns in an SQLite |
| 235643 | ** database table is, according to sqliteLimit.h, 32676. So |
| 235644 | ** consider any table-header that purports to have more than 65536 |
| 235645 | ** columns to be corrupt. This is convenient because otherwise, |
| 235646 | ** if the (nCol>65536) condition below were omitted, a sufficiently |
| @@ -235300,12 +235656,19 @@ | |
| 235656 | |
| 235657 | while( rc==SQLITE_OK ){ |
| 235658 | while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){ |
| 235659 | nRead++; |
| 235660 | } |
| 235661 | |
| 235662 | /* Break out of the loop if if the nul-terminator byte has been found. |
| 235663 | ** Otherwise, read some more input data and keep seeking. If there is |
| 235664 | ** no more input data, consider the changeset corrupt. */ |
| 235665 | if( (pIn->iNext + nRead)<pIn->nData ) break; |
| 235666 | rc = sessionInputBuffer(pIn, nRead + 100); |
| 235667 | if( rc==SQLITE_OK && (pIn->iNext + nRead)>=pIn->nData ){ |
| 235668 | rc = SQLITE_CORRUPT_BKPT; |
| 235669 | } |
| 235670 | } |
| 235671 | *pnByte = nRead+1; |
| 235672 | return rc; |
| 235673 | } |
| 235674 | |
| @@ -235433,14 +235796,14 @@ | |
| 235796 | sqlite3ValueFree(p->apValue[i]); |
| 235797 | } |
| 235798 | memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); |
| 235799 | } |
| 235800 | |
| 235801 | /* Make sure the buffer contains at least 2 bytes of input data, or all |
| 235802 | ** remaining data if there are less than 2 bytes available. This is |
| 235803 | ** sufficient either for the 'T' or 'P' byte that begins a new table, |
| 235804 | ** or for the "op" and "bIndirect" single bytes otherwise. */ |
| 235805 | p->rc = sessionInputBuffer(&p->in, 2); |
| 235806 | if( p->rc!=SQLITE_OK ) return p->rc; |
| 235807 | |
| 235808 | p->in.iCurrent = p->in.iNext; |
| 235809 | sessionDiscardData(&p->in); |
| @@ -235466,15 +235829,17 @@ | |
| 235829 | ** corrupt changeset. */ |
| 235830 | assert( p->in.iNext==1 || p->zTab ); |
| 235831 | return (p->rc = SQLITE_CORRUPT_BKPT); |
| 235832 | } |
| 235833 | |
| 235834 | if( (op!=SQLITE_UPDATE && op!=SQLITE_DELETE && op!=SQLITE_INSERT) |
| 235835 | || (p->in.iNext>=p->in.nData) |
| 235836 | ){ |
| 235837 | return (p->rc = SQLITE_CORRUPT_BKPT); |
| 235838 | } |
| 235839 | p->op = op; |
| 235840 | p->bIndirect = p->in.aData[p->in.iNext++]; |
| 235841 | |
| 235842 | if( paRec ){ |
| 235843 | int nVal; /* Number of values to buffer */ |
| 235844 | if( p->bPatchset==0 && op==SQLITE_UPDATE ){ |
| 235845 | nVal = p->nCol * 2; |
| @@ -239307,11 +239672,17 @@ | |
| 239672 | # define FLEXARRAY |
| 239673 | #else |
| 239674 | # define FLEXARRAY 1 |
| 239675 | #endif |
| 239676 | |
| 239677 | #endif /* SQLITE_AMALGAMATION */ |
| 239678 | |
| 239679 | /* |
| 239680 | ** Constants for the largest and smallest possible 32-bit signed integers. |
| 239681 | */ |
| 239682 | # define LARGEST_INT32 ((int)(0x7fffffff)) |
| 239683 | # define SMALLEST_INT32 ((int)((-1) - LARGEST_INT32)) |
| 239684 | |
| 239685 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 239686 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 239687 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| 239688 | #define FTS5_MAX_TOKEN_SIZE 32768 |
| @@ -253198,11 +253569,11 @@ | |
| 253569 | ){ |
| 253570 | const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); |
| 253571 | int iSegid = pSeg->pSeg->iSegid; |
| 253572 | u8 *aPg = pSeg->pLeaf->p; |
| 253573 | int nPg = pSeg->pLeaf->nn; |
| 253574 | int iPgIdx = pSeg->pLeaf->szLeaf; /* Offset of page footer */ |
| 253575 | |
| 253576 | u64 iDelta = 0; |
| 253577 | int iNextOff = 0; |
| 253578 | int iOff = 0; |
| 253579 | int nIdx = 0; |
| @@ -253277,11 +253648,11 @@ | |
| 253648 | iStart = iSOP + (nPos/2); |
| 253649 | iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); |
| 253650 | iSOP += fts5GetVarint32(&aPg[iSOP], nPos); |
| 253651 | } |
| 253652 | assert_nc( iSOP==pSeg->iLeafOffset ); |
| 253653 | iNextOff = iSOP + pSeg->nPos; |
| 253654 | } |
| 253655 | } |
| 253656 | |
| 253657 | iOff = iStart; |
| 253658 | |
| @@ -253357,35 +253728,35 @@ | |
| 253728 | if( iNextOff!=iPgIdx ){ |
| 253729 | /* This is the only position-list associated with the term, and there |
| 253730 | ** is another term following it on this page. So the subsequent term |
| 253731 | ** needs to be moved to replace the term associated with the entry |
| 253732 | ** being removed. */ |
| 253733 | u64 nPrefix = 0; |
| 253734 | u64 nSuffix = 0; |
| 253735 | u64 nPrefix2 = 0; |
| 253736 | u64 nSuffix2 = 0; |
| 253737 | |
| 253738 | iDelKeyOff = iNextOff; |
| 253739 | iNextOff += fts5GetVarint(&aPg[iNextOff], &nPrefix2); |
| 253740 | iNextOff += fts5GetVarint(&aPg[iNextOff], &nSuffix2); |
| 253741 | |
| 253742 | if( iKey!=1 ){ |
| 253743 | iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nPrefix); |
| 253744 | } |
| 253745 | iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nSuffix); |
| 253746 | |
| 253747 | nPrefix = MIN(nPrefix, nPrefix2); |
| 253748 | nSuffix = (nPrefix2 + nSuffix2) - nPrefix; |
| 253749 | |
| 253750 | if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){ |
| 253751 | FTS5_CORRUPT_IDX(p); |
| 253752 | }else{ |
| 253753 | if( iKey!=1 ){ |
| 253754 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); |
| 253755 | } |
| 253756 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); |
| 253757 | if( nPrefix2>(u64)pSeg->term.n ){ |
| 253758 | FTS5_CORRUPT_IDX(p); |
| 253759 | }else if( nPrefix2>nPrefix ){ |
| 253760 | memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); |
| 253761 | iOff += (nPrefix2-nPrefix); |
| 253762 | } |
| @@ -253412,11 +253783,11 @@ | |
| 253783 | Fts5Data *pTerm = fts5DataRead(p, iId); |
| 253784 | if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ |
| 253785 | u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; |
| 253786 | int nTermIdx = pTerm->nn - pTerm->szLeaf; |
| 253787 | int iTermIdx = 0; |
| 253788 | i64 iTermOff = 0; |
| 253789 | |
| 253790 | while( 1 ){ |
| 253791 | u32 iVal = 0; |
| 253792 | int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); |
| 253793 | iTermOff += iVal; |
| @@ -253423,16 +253794,19 @@ | |
| 253794 | if( (iTermIdx+nByte)>=nTermIdx ) break; |
| 253795 | iTermIdx += nByte; |
| 253796 | } |
| 253797 | nTermIdx = iTermIdx; |
| 253798 | |
| 253799 | if( iTermOff>pTerm->szLeaf ){ |
| 253800 | FTS5_CORRUPT_IDX(p); |
| 253801 | }else{ |
| 253802 | memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); |
| 253803 | fts5PutU16(&pTerm->p[2], iTermOff); |
| 253804 | fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); |
| 253805 | if( nTermIdx==0 ){ |
| 253806 | fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); |
| 253807 | } |
| 253808 | } |
| 253809 | } |
| 253810 | fts5DataRelease(pTerm); |
| 253811 | } |
| 253812 | } |
| @@ -253451,11 +253825,13 @@ | |
| 253825 | int nShift = iNextOff - iOff; /* Distance to move them */ |
| 253826 | |
| 253827 | int iPrevKeyOut = 0; |
| 253828 | int iKeyIn = 0; |
| 253829 | |
| 253830 | if( nMove>0 ){ |
| 253831 | memmove(&aPg[iOff], &aPg[iNextOff], nMove); |
| 253832 | } |
| 253833 | iPgIdx -= nShift; |
| 253834 | nPg = iPgIdx; |
| 253835 | fts5PutU16(&aPg[2], iPgIdx); |
| 253836 | |
| 253837 | for(iIdx=0; iIdx<nIdx; /* no-op */){ |
| @@ -253889,11 +254265,11 @@ | |
| 254265 | if( nMerge<0 ){ |
| 254266 | Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct); |
| 254267 | fts5StructureRelease(pStruct); |
| 254268 | pStruct = pNew; |
| 254269 | nMin = 1; |
| 254270 | nMerge = (nMerge==SMALLEST_INT32 ? LARGEST_INT32 : (nMerge*-1)); |
| 254271 | } |
| 254272 | if( pStruct && pStruct->nLevel ){ |
| 254273 | if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){ |
| 254274 | fts5StructureWrite(p, pStruct); |
| 254275 | } |
| @@ -261097,11 +261473,11 @@ | |
| 261473 | int nArg, /* Number of args */ |
| 261474 | sqlite3_value **apUnused /* Function arguments */ |
| 261475 | ){ |
| 261476 | assert( nArg==0 ); |
| 261477 | UNUSED_PARAM2(nArg, apUnused); |
| 261478 | sqlite3_result_text(pCtx, "fts5: 2026-02-04 18:10:49 e6902937ecdbeb449986469859b46631272fb0a9e7e1c31adea14cff072b6d67", -1, SQLITE_TRANSIENT); |
| 261479 | } |
| 261480 | |
| 261481 | /* |
| 261482 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 261483 | ** |
| 261484 |
+133
-35
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,14 +146,14 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.52.0" |
| 150 | 150 | #define SQLITE_VERSION_NUMBER 3052000 |
| 151 | -#define SQLITE_SOURCE_ID "2025-12-11 23:24:05 01409738afc2c0d5bdaa248ffb508aa5f36a66390f6b8e4834734529ee8ed2fa" | |
| 151 | +#define SQLITE_SOURCE_ID "2026-02-04 20:51:27 c476d956d0bd3065cf894de6f9d393b999ff7d2268a35f01a6d88804789ab58f" | |
| 152 | 152 | #define SQLITE_SCM_BRANCH "trunk" |
| 153 | 153 | #define SQLITE_SCM_TAGS "" |
| 154 | -#define SQLITE_SCM_DATETIME "2025-12-11T23:24:05.667Z" | |
| 154 | +#define SQLITE_SCM_DATETIME "2026-02-04T20:51:27.822Z" | |
| 155 | 155 | |
| 156 | 156 | /* |
| 157 | 157 | ** CAPI3REF: Run-Time Library Version Numbers |
| 158 | 158 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 159 | 159 | ** |
| @@ -4431,16 +4431,32 @@ | ||
| 4431 | 4431 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4432 | 4432 | ** messages on the global error log when it is not. If the test compile |
| 4433 | 4433 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4434 | 4434 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4435 | 4435 | ** logs the error. |
| 4436 | +** | |
| 4437 | +** [[SQLITE_PREPARE_FROM_DDL]] <dt>SQLITE_PREPARE_FROM_DDL</dt> | |
| 4438 | +** <dd>The SQLITE_PREPARE_FROM_DDL flag causes the SQL compiler to behave as if | |
| 4439 | +** the SQL statement is part of a database schema. This makes a difference | |
| 4440 | +** when the [SQLITE_DBCONFIG_TRUSTED_SCHEMA] option is set to off. | |
| 4441 | +** When this option is used and SQLITE_DBCONFIG_TRUSTED_SCHEMA is off, | |
| 4442 | +** SQL functions may not be called unless they are tagged with | |
| 4443 | +** [SQLITE_INNOCUOUS] and virtual tables may not be used unless tagged | |
| 4444 | +** with [SQLITE_VTAB_INNOCUOUS]. Use the SQLITE_PREPARE_FROM_DDL option | |
| 4445 | +** when preparing SQL that is derived from parts of the database | |
| 4446 | +** schema. In particular, virtual table implementations that | |
| 4447 | +** run SQL statements based on the arguments to their CREATE VIRTUAL | |
| 4448 | +** TABLE statement should use [sqlite3_prepare_v3()] and set the | |
| 4449 | +** SQLITE_PREPARE_FROM_DLL flag to prevent bypass of the | |
| 4450 | +** [SQLITE_DBCONFIG_TRUSTED_SCHEMA] security checks. | |
| 4436 | 4451 | ** </dl> |
| 4437 | 4452 | */ |
| 4438 | 4453 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4439 | 4454 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4440 | 4455 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4441 | 4456 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4457 | +#define SQLITE_PREPARE_FROM_DDL 0x20 | |
| 4442 | 4458 | |
| 4443 | 4459 | /* |
| 4444 | 4460 | ** CAPI3REF: Compiling An SQL Statement |
| 4445 | 4461 | ** KEYWORDS: {SQL statement compiler} |
| 4446 | 4462 | ** METHOD: sqlite3 |
| @@ -4450,12 +4466,13 @@ | ||
| 4450 | 4466 | ** program using one of these routines. Or, in other words, these routines |
| 4451 | 4467 | ** are constructors for the [prepared statement] object. |
| 4452 | 4468 | ** |
| 4453 | 4469 | ** The preferred routine to use is [sqlite3_prepare_v2()]. The |
| 4454 | 4470 | ** [sqlite3_prepare()] interface is legacy and should be avoided. |
| 4455 | -** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used | |
| 4456 | -** for special purposes. | |
| 4471 | +** [sqlite3_prepare_v3()] has an extra | |
| 4472 | +** [SQLITE_PREPARE_FROM_DDL|"prepFlags" option] that is some times | |
| 4473 | +** needed for special purpose or to pass along security restrictions. | |
| 4457 | 4474 | ** |
| 4458 | 4475 | ** The use of the UTF-8 interfaces is preferred, as SQLite currently |
| 4459 | 4476 | ** does all parsing using UTF-8. The UTF-16 interfaces are provided |
| 4460 | 4477 | ** as a convenience. The UTF-16 interfaces work by converting the |
| 4461 | 4478 | ** input text into UTF-8, then invoking the corresponding UTF-8 interface. |
| @@ -4856,12 +4873,12 @@ | ||
| 4856 | 4873 | ** it should be a pointer to well-formed UTF8 text. |
| 4857 | 4874 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 4858 | 4875 | ** it should be a pointer to well-formed UTF16 text. |
| 4859 | 4876 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 4860 | 4877 | ** it should be a pointer to a well-formed unicode string that is |
| 4861 | -** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 | |
| 4862 | -** otherwise. | |
| 4878 | +** either UTF8 if the sixth parameter is SQLITE_UTF8 or SQLITE_UTF8_ZT, | |
| 4879 | +** or UTF16 otherwise. | |
| 4863 | 4880 | ** |
| 4864 | 4881 | ** [[byte-order determination rules]] ^The byte-order of |
| 4865 | 4882 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 4866 | 4883 | ** found in the first character, which is removed, or in the absence of a BOM |
| 4867 | 4884 | ** the byte order is the native byte order of the host |
| @@ -4903,14 +4920,19 @@ | ||
| 4903 | 4920 | ** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the |
| 4904 | 4921 | ** object is to be copied prior to the return from sqlite3_bind_*(). ^The |
| 4905 | 4922 | ** object and pointer to it must remain valid until then. ^SQLite will then |
| 4906 | 4923 | ** manage the lifetime of its private copy. |
| 4907 | 4924 | ** |
| 4908 | -** ^The sixth argument to sqlite3_bind_text64() must be one of | |
| 4909 | -** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] | |
| 4910 | -** to specify the encoding of the text in the third parameter. If | |
| 4911 | -** the sixth argument to sqlite3_bind_text64() is not one of the | |
| 4925 | +** ^The sixth argument (the E argument) | |
| 4926 | +** to sqlite3_bind_text64(S,K,Z,N,D,E) must be one of | |
| 4927 | +** [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], | |
| 4928 | +** or [SQLITE_UTF16LE] to specify the encoding of the text in the | |
| 4929 | +** third parameter, Z. The special value [SQLITE_UTF8_ZT] means that the | |
| 4930 | +** string argument is both UTF-8 encoded and is zero-terminated. In other | |
| 4931 | +** words, SQLITE_UTF8_ZT means that the Z array is allocated to hold at | |
| 4932 | +** least N+1 bytes and that the Z[N] byte is zero. If | |
| 4933 | +** the E argument to sqlite3_bind_text64(S,K,Z,N,D,E) is not one of the | |
| 4912 | 4934 | ** allowed values shown above, or if the text encoding is different |
| 4913 | 4935 | ** from the encoding specified by the sixth parameter, then the behavior |
| 4914 | 4936 | ** is undefined. |
| 4915 | 4937 | ** |
| 4916 | 4938 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
| @@ -5773,17 +5795,63 @@ | ||
| 5773 | 5795 | /* |
| 5774 | 5796 | ** CAPI3REF: Text Encodings |
| 5775 | 5797 | ** |
| 5776 | 5798 | ** These constants define integer codes that represent the various |
| 5777 | 5799 | ** text encodings supported by SQLite. |
| 5800 | +** | |
| 5801 | +** <dl> | |
| 5802 | +** [[SQLITE_UTF8]] <dt>SQLITE_UTF8</dt><dd>Text is encoding as UTF-8</dd> | |
| 5803 | +** | |
| 5804 | +** [[SQLITE_UTF16LE]] <dt>SQLITE_UTF16LE</dt><dd>Text is encoding as UTF-16 | |
| 5805 | +** with each code point being expressed "little endian" - the least significant | |
| 5806 | +** byte first. This is the usual encoding, for example on Windows.</dd> | |
| 5807 | +** | |
| 5808 | +** [[SQLITE_UTF16BE]] <dt>SQLITE_UTF16BE</dt><dd>Text is encoding as UTF-16 | |
| 5809 | +** with each code point being expressed "big endian" - the most significant | |
| 5810 | +** byte first. This encoding is less common, but is still sometimes seen, | |
| 5811 | +** specially on older systems. | |
| 5812 | +** | |
| 5813 | +** [[SQLITE_UTF16]] <dt>SQLITE_UTF16</dt><dd>Text is encoding as UTF-16 | |
| 5814 | +** with each code point being expressed either little endian or as big | |
| 5815 | +** endian, according to the native endianness of the host computer. | |
| 5816 | +** | |
| 5817 | +** [[SQLITE_ANY]] <dt>SQLITE_ANY</dt><dd>This encoding value may only be used | |
| 5818 | +** to declare the preferred text for [application-defined SQL functions] | |
| 5819 | +** created using [sqlite3_create_function()] and similar. If the preferred | |
| 5820 | +** encoding (the 4th parameter to sqlite3_create_function() - the eTextRep | |
| 5821 | +** parameter) is SQLITE_ANY, that indicates that the function does not have | |
| 5822 | +** a preference regarding the text encoding of its parameters and can take | |
| 5823 | +** any text encoding that the SQLite core find convenient to supply. This | |
| 5824 | +** option is deprecated. Please do not use it in new applications. | |
| 5825 | +** | |
| 5826 | +** [[SQLITE_UTF16_ALIGNED]] <dt>SQLITE_UTF16_ALIGNED</dt><dd>This encoding | |
| 5827 | +** value may be used as the 3rd parameter (the eTextRep parameter) to | |
| 5828 | +** [sqlite3_create_collation()] and similar. This encoding value means | |
| 5829 | +** that the application-defined collating sequence created expects its | |
| 5830 | +** input strings to be in UTF16 in native byte order, and that the start | |
| 5831 | +** of the strings must be aligned to a 2-byte boundary. | |
| 5832 | +** | |
| 5833 | +** [[SQLITE_UTF8_ZT]] <dt>SQLITE_UTF8_ZT</dt><dd>This option can only be | |
| 5834 | +** used to specify the text encoding to strings input to [sqlite3_result_text64()] | |
| 5835 | +** and [sqlite3_bind_text64()]. It means that the input string (call it "z") | |
| 5836 | +** is UTF-8 encoded and that it is zero-terminated. If the length parameter | |
| 5837 | +** (call it "n") is non-negative, this encoding option means that the caller | |
| 5838 | +** guarantees that z array contains at least n+1 bytes and that the z[n] | |
| 5839 | +** byte has a value of zero. | |
| 5840 | +** This option gives the same output as SQLITE_UTF8, but can be more efficient | |
| 5841 | +** by avoiding the need to make a copy of the input string, in some cases. | |
| 5842 | +** However, if z is allocated to hold fewer than n+1 bytes or if the | |
| 5843 | +** z[n] byte is not zero, undefined behavior may result. | |
| 5844 | +** </dl> | |
| 5778 | 5845 | */ |
| 5779 | 5846 | #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ |
| 5780 | 5847 | #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ |
| 5781 | 5848 | #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ |
| 5782 | 5849 | #define SQLITE_UTF16 4 /* Use native byte order */ |
| 5783 | 5850 | #define SQLITE_ANY 5 /* Deprecated */ |
| 5784 | 5851 | #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ |
| 5852 | +#define SQLITE_UTF8_ZT 16 /* Zero-terminated UTF8 */ | |
| 5785 | 5853 | |
| 5786 | 5854 | /* |
| 5787 | 5855 | ** CAPI3REF: Function Flags |
| 5788 | 5856 | ** |
| 5789 | 5857 | ** These constants may be ORed together with the |
| @@ -6285,14 +6353,18 @@ | ||
| 6285 | 6353 | ** use for client data is to provide a mechanism for wrapper libraries |
| 6286 | 6354 | ** to store additional information about an SQLite database connection. |
| 6287 | 6355 | ** |
| 6288 | 6356 | ** There is no limit (other than available memory) on the number of different |
| 6289 | 6357 | ** client data pointers (with different names) that can be attached to a |
| 6290 | -** single database connection. However, the implementation is optimized | |
| 6291 | -** for the case of having only one or two different client data names. | |
| 6292 | -** Applications and wrapper libraries are discouraged from using more than | |
| 6293 | -** one client data name each. | |
| 6358 | +** single database connection. However, the current implementation stores | |
| 6359 | +** the content on a linked list. Insert and retrieval performance will | |
| 6360 | +** be proportional to the number of entries. The design use case, and | |
| 6361 | +** the use case for which the implementation is optimized, is | |
| 6362 | +** that an application will store only small number of client data names, | |
| 6363 | +** typically just one or two. This interface is not intended to be a | |
| 6364 | +** generalized key/value store for thousands or millions of keys. It | |
| 6365 | +** will work for that, but performance might be disappointing. | |
| 6294 | 6366 | ** |
| 6295 | 6367 | ** There is no way to enumerate the client data pointers |
| 6296 | 6368 | ** associated with a database connection. The N parameter can be thought |
| 6297 | 6369 | ** of as a secret key such that only code that knows the secret key is able |
| 6298 | 6370 | ** to access the associated data. |
| @@ -6396,14 +6468,18 @@ | ||
| 6396 | 6468 | ** ^The sqlite3_result_text(), sqlite3_result_text16(), |
| 6397 | 6469 | ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces |
| 6398 | 6470 | ** set the return value of the application-defined function to be |
| 6399 | 6471 | ** a text string which is represented as UTF-8, UTF-16 native byte order, |
| 6400 | 6472 | ** UTF-16 little endian, or UTF-16 big endian, respectively. |
| 6401 | -** ^The sqlite3_result_text64() interface sets the return value of an | |
| 6473 | +** ^The sqlite3_result_text64(C,Z,N,D,E) interface sets the return value of an | |
| 6402 | 6474 | ** application-defined function to be a text string in an encoding |
| 6403 | -** specified by the fifth (and last) parameter, which must be one | |
| 6404 | -** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. | |
| 6475 | +** specified the E parameter, which must be one | |
| 6476 | +** of [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], | |
| 6477 | +** or [SQLITE_UTF16LE]. ^The special value [SQLITE_UTF8_ZT] means that | |
| 6478 | +** the result text is both UTF-8 and zero-terminated. In other words, | |
| 6479 | +** SQLITE_UTF8_ZT means that the Z array holds at least N+1 byes and that | |
| 6480 | +** the Z[N] is zero. | |
| 6405 | 6481 | ** ^SQLite takes the text result from the application from |
| 6406 | 6482 | ** the 2nd parameter of the sqlite3_result_text* interfaces. |
| 6407 | 6483 | ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces |
| 6408 | 6484 | ** other than sqlite3_result_text64() is negative, then SQLite computes |
| 6409 | 6485 | ** the string length itself by searching the 2nd parameter for the first |
| @@ -6486,11 +6562,11 @@ | ||
| 6486 | 6562 | SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); |
| 6487 | 6563 | SQLITE_API void sqlite3_result_int(sqlite3_context*, int); |
| 6488 | 6564 | SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); |
| 6489 | 6565 | SQLITE_API void sqlite3_result_null(sqlite3_context*); |
| 6490 | 6566 | SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); |
| 6491 | -SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, | |
| 6567 | +SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char *z, sqlite3_uint64 n, | |
| 6492 | 6568 | void(*)(void*), unsigned char encoding); |
| 6493 | 6569 | SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); |
| 6494 | 6570 | SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6495 | 6571 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6496 | 6572 | SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); |
| @@ -7425,22 +7501,22 @@ | ||
| 7425 | 7501 | ** ^This interface loads an SQLite extension library from the named file. |
| 7426 | 7502 | ** |
| 7427 | 7503 | ** ^The sqlite3_load_extension() interface attempts to load an |
| 7428 | 7504 | ** [SQLite extension] library contained in the file zFile. If |
| 7429 | 7505 | ** the file cannot be loaded directly, attempts are made to load |
| 7430 | -** with various operating-system specific extensions added. | |
| 7506 | +** with various operating-system specific filename extensions added. | |
| 7431 | 7507 | ** So for example, if "samplelib" cannot be loaded, then names like |
| 7432 | 7508 | ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might |
| 7433 | 7509 | ** be tried also. |
| 7434 | 7510 | ** |
| 7435 | 7511 | ** ^The entry point is zProc. |
| 7436 | 7512 | ** ^(zProc may be 0, in which case SQLite will try to come up with an |
| 7437 | 7513 | ** entry point name on its own. It first tries "sqlite3_extension_init". |
| 7438 | -** If that does not work, it constructs a name "sqlite3_X_init" where | |
| 7439 | -** X consists of the lower-case equivalent of all ASCII alphabetic | |
| 7440 | -** characters in the filename from the last "/" to the first following | |
| 7441 | -** "." and omitting any initial "lib".)^ | |
| 7514 | +** If that does not work, it tries names of the form "sqlite3_X_init" | |
| 7515 | +** where X consists of the lower-case equivalent of all ASCII alphabetic | |
| 7516 | +** characters or all ASCII alphanumeric characters in the filename from | |
| 7517 | +** the last "/" to the first following "." and omitting any initial "lib".)^ | |
| 7442 | 7518 | ** ^The sqlite3_load_extension() interface returns |
| 7443 | 7519 | ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. |
| 7444 | 7520 | ** ^If an error occurs and pzErrMsg is not 0, then the |
| 7445 | 7521 | ** [sqlite3_load_extension()] interface shall attempt to |
| 7446 | 7522 | ** fill *pzErrMsg with error message text stored in memory |
| @@ -11174,23 +11250,45 @@ | ||
| 11174 | 11250 | #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ |
| 11175 | 11251 | |
| 11176 | 11252 | /* |
| 11177 | 11253 | ** CAPI3REF: Bind array values to the CARRAY table-valued function |
| 11178 | 11254 | ** |
| 11179 | -** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to | |
| 11180 | -** one of the first argument of the [carray() table-valued function]. The | |
| 11181 | -** S parameter is a pointer to the [prepared statement] that uses the carray() | |
| 11182 | -** functions. I is the parameter index to be bound. P is a pointer to the | |
| 11183 | -** array to be bound, and N is the number of eements in the array. The | |
| 11184 | -** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], | |
| 11185 | -** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to | |
| 11186 | -** indicate the datatype of the array being bound. The X argument is not a | |
| 11187 | -** NULL pointer, then SQLite will invoke the function X on the P parameter | |
| 11188 | -** after it has finished using P, even if the call to | |
| 11189 | -** sqlite3_carray_bind() fails. The special-case finalizer | |
| 11190 | -** SQLITE_TRANSIENT has no effect here. | |
| 11255 | +** The sqlite3_carray_bind_v2(S,I,P,N,F,X,D) interface binds an array value to | |
| 11256 | +** parameter that is the first argument of the [carray() table-valued function]. | |
| 11257 | +** The S parameter is a pointer to the [prepared statement] that uses the carray() | |
| 11258 | +** functions. I is the parameter index to be bound. I must be the index of the | |
| 11259 | +** parameter that is the first argument to the carray() table-valued function. | |
| 11260 | +** P is a pointer to the array to be bound, and N is the number of elements in | |
| 11261 | +** the array. The F argument is one of constants [SQLITE_CARRAY_INT32], | |
| 11262 | +** [SQLITE_CARRAY_INT64], [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], | |
| 11263 | +** or [SQLITE_CARRAY_BLOB] to indicate the datatype of the array P. | |
| 11264 | +** | |
| 11265 | +** If the X argument is not a NULL pointer or one of the special | |
| 11266 | +** values [SQLITE_STATIC] or [SQLITE_TRANSIENT], then SQLite will invoke | |
| 11267 | +** the function X with argument D when it is finished using the data in P. | |
| 11268 | +** The call to X(D) is a destructor for the array P. The destructor X(D) | |
| 11269 | +** is invoked even if the call to sqlite3_carray_bind() fails. If the X | |
| 11270 | +** parameter is the special-case value [SQLITE_STATIC], then SQLite assumes | |
| 11271 | +** that the data static and the destructor is never invoked. If the X | |
| 11272 | +** parameter is the special-case value [SQLITE_TRANSIENT], then | |
| 11273 | +** sqlite3_carray_bind_v2() makes its own private copy of the data prior | |
| 11274 | +** to returning and never invokes the destructor X. | |
| 11275 | +** | |
| 11276 | +** The sqlite3_carray_bind() function works the same as sqlite_carray_bind_v2() | |
| 11277 | +** with a D parameter set to P. In other words, | |
| 11278 | +** sqlite3_carray_bind(S,I,P,N,F,X) is same as | |
| 11279 | +** sqlite3_carray_bind(S,I,P,N,F,X,P). | |
| 11191 | 11280 | */ |
| 11281 | +SQLITE_API int sqlite3_carray_bind_v2( | |
| 11282 | + sqlite3_stmt *pStmt, /* Statement to be bound */ | |
| 11283 | + int i, /* Parameter index */ | |
| 11284 | + void *aData, /* Pointer to array data */ | |
| 11285 | + int nData, /* Number of data elements */ | |
| 11286 | + int mFlags, /* CARRAY flags */ | |
| 11287 | + void (*xDel)(void*), /* Destructor for aData */ | |
| 11288 | + void *pDel /* Optional argument to xDel() */ | |
| 11289 | +); | |
| 11192 | 11290 | SQLITE_API int sqlite3_carray_bind( |
| 11193 | 11291 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11194 | 11292 | int i, /* Parameter index */ |
| 11195 | 11293 | void *aData, /* Pointer to array data */ |
| 11196 | 11294 | int nData, /* Number of data elements */ |
| 11197 | 11295 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,14 +146,14 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.52.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3052000 |
| 151 | #define SQLITE_SOURCE_ID "2025-12-11 23:24:05 01409738afc2c0d5bdaa248ffb508aa5f36a66390f6b8e4834734529ee8ed2fa" |
| 152 | #define SQLITE_SCM_BRANCH "trunk" |
| 153 | #define SQLITE_SCM_TAGS "" |
| 154 | #define SQLITE_SCM_DATETIME "2025-12-11T23:24:05.667Z" |
| 155 | |
| 156 | /* |
| 157 | ** CAPI3REF: Run-Time Library Version Numbers |
| 158 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 159 | ** |
| @@ -4431,16 +4431,32 @@ | |
| 4431 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4432 | ** messages on the global error log when it is not. If the test compile |
| 4433 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4434 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4435 | ** logs the error. |
| 4436 | ** </dl> |
| 4437 | */ |
| 4438 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4439 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4440 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4441 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4442 | |
| 4443 | /* |
| 4444 | ** CAPI3REF: Compiling An SQL Statement |
| 4445 | ** KEYWORDS: {SQL statement compiler} |
| 4446 | ** METHOD: sqlite3 |
| @@ -4450,12 +4466,13 @@ | |
| 4450 | ** program using one of these routines. Or, in other words, these routines |
| 4451 | ** are constructors for the [prepared statement] object. |
| 4452 | ** |
| 4453 | ** The preferred routine to use is [sqlite3_prepare_v2()]. The |
| 4454 | ** [sqlite3_prepare()] interface is legacy and should be avoided. |
| 4455 | ** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used |
| 4456 | ** for special purposes. |
| 4457 | ** |
| 4458 | ** The use of the UTF-8 interfaces is preferred, as SQLite currently |
| 4459 | ** does all parsing using UTF-8. The UTF-16 interfaces are provided |
| 4460 | ** as a convenience. The UTF-16 interfaces work by converting the |
| 4461 | ** input text into UTF-8, then invoking the corresponding UTF-8 interface. |
| @@ -4856,12 +4873,12 @@ | |
| 4856 | ** it should be a pointer to well-formed UTF8 text. |
| 4857 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 4858 | ** it should be a pointer to well-formed UTF16 text. |
| 4859 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 4860 | ** it should be a pointer to a well-formed unicode string that is |
| 4861 | ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 |
| 4862 | ** otherwise. |
| 4863 | ** |
| 4864 | ** [[byte-order determination rules]] ^The byte-order of |
| 4865 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 4866 | ** found in the first character, which is removed, or in the absence of a BOM |
| 4867 | ** the byte order is the native byte order of the host |
| @@ -4903,14 +4920,19 @@ | |
| 4903 | ** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the |
| 4904 | ** object is to be copied prior to the return from sqlite3_bind_*(). ^The |
| 4905 | ** object and pointer to it must remain valid until then. ^SQLite will then |
| 4906 | ** manage the lifetime of its private copy. |
| 4907 | ** |
| 4908 | ** ^The sixth argument to sqlite3_bind_text64() must be one of |
| 4909 | ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] |
| 4910 | ** to specify the encoding of the text in the third parameter. If |
| 4911 | ** the sixth argument to sqlite3_bind_text64() is not one of the |
| 4912 | ** allowed values shown above, or if the text encoding is different |
| 4913 | ** from the encoding specified by the sixth parameter, then the behavior |
| 4914 | ** is undefined. |
| 4915 | ** |
| 4916 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
| @@ -5773,17 +5795,63 @@ | |
| 5773 | /* |
| 5774 | ** CAPI3REF: Text Encodings |
| 5775 | ** |
| 5776 | ** These constants define integer codes that represent the various |
| 5777 | ** text encodings supported by SQLite. |
| 5778 | */ |
| 5779 | #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ |
| 5780 | #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ |
| 5781 | #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ |
| 5782 | #define SQLITE_UTF16 4 /* Use native byte order */ |
| 5783 | #define SQLITE_ANY 5 /* Deprecated */ |
| 5784 | #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ |
| 5785 | |
| 5786 | /* |
| 5787 | ** CAPI3REF: Function Flags |
| 5788 | ** |
| 5789 | ** These constants may be ORed together with the |
| @@ -6285,14 +6353,18 @@ | |
| 6285 | ** use for client data is to provide a mechanism for wrapper libraries |
| 6286 | ** to store additional information about an SQLite database connection. |
| 6287 | ** |
| 6288 | ** There is no limit (other than available memory) on the number of different |
| 6289 | ** client data pointers (with different names) that can be attached to a |
| 6290 | ** single database connection. However, the implementation is optimized |
| 6291 | ** for the case of having only one or two different client data names. |
| 6292 | ** Applications and wrapper libraries are discouraged from using more than |
| 6293 | ** one client data name each. |
| 6294 | ** |
| 6295 | ** There is no way to enumerate the client data pointers |
| 6296 | ** associated with a database connection. The N parameter can be thought |
| 6297 | ** of as a secret key such that only code that knows the secret key is able |
| 6298 | ** to access the associated data. |
| @@ -6396,14 +6468,18 @@ | |
| 6396 | ** ^The sqlite3_result_text(), sqlite3_result_text16(), |
| 6397 | ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces |
| 6398 | ** set the return value of the application-defined function to be |
| 6399 | ** a text string which is represented as UTF-8, UTF-16 native byte order, |
| 6400 | ** UTF-16 little endian, or UTF-16 big endian, respectively. |
| 6401 | ** ^The sqlite3_result_text64() interface sets the return value of an |
| 6402 | ** application-defined function to be a text string in an encoding |
| 6403 | ** specified by the fifth (and last) parameter, which must be one |
| 6404 | ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. |
| 6405 | ** ^SQLite takes the text result from the application from |
| 6406 | ** the 2nd parameter of the sqlite3_result_text* interfaces. |
| 6407 | ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces |
| 6408 | ** other than sqlite3_result_text64() is negative, then SQLite computes |
| 6409 | ** the string length itself by searching the 2nd parameter for the first |
| @@ -6486,11 +6562,11 @@ | |
| 6486 | SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); |
| 6487 | SQLITE_API void sqlite3_result_int(sqlite3_context*, int); |
| 6488 | SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); |
| 6489 | SQLITE_API void sqlite3_result_null(sqlite3_context*); |
| 6490 | SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); |
| 6491 | SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, |
| 6492 | void(*)(void*), unsigned char encoding); |
| 6493 | SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); |
| 6494 | SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6495 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6496 | SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); |
| @@ -7425,22 +7501,22 @@ | |
| 7425 | ** ^This interface loads an SQLite extension library from the named file. |
| 7426 | ** |
| 7427 | ** ^The sqlite3_load_extension() interface attempts to load an |
| 7428 | ** [SQLite extension] library contained in the file zFile. If |
| 7429 | ** the file cannot be loaded directly, attempts are made to load |
| 7430 | ** with various operating-system specific extensions added. |
| 7431 | ** So for example, if "samplelib" cannot be loaded, then names like |
| 7432 | ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might |
| 7433 | ** be tried also. |
| 7434 | ** |
| 7435 | ** ^The entry point is zProc. |
| 7436 | ** ^(zProc may be 0, in which case SQLite will try to come up with an |
| 7437 | ** entry point name on its own. It first tries "sqlite3_extension_init". |
| 7438 | ** If that does not work, it constructs a name "sqlite3_X_init" where |
| 7439 | ** X consists of the lower-case equivalent of all ASCII alphabetic |
| 7440 | ** characters in the filename from the last "/" to the first following |
| 7441 | ** "." and omitting any initial "lib".)^ |
| 7442 | ** ^The sqlite3_load_extension() interface returns |
| 7443 | ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. |
| 7444 | ** ^If an error occurs and pzErrMsg is not 0, then the |
| 7445 | ** [sqlite3_load_extension()] interface shall attempt to |
| 7446 | ** fill *pzErrMsg with error message text stored in memory |
| @@ -11174,23 +11250,45 @@ | |
| 11174 | #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ |
| 11175 | |
| 11176 | /* |
| 11177 | ** CAPI3REF: Bind array values to the CARRAY table-valued function |
| 11178 | ** |
| 11179 | ** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to |
| 11180 | ** one of the first argument of the [carray() table-valued function]. The |
| 11181 | ** S parameter is a pointer to the [prepared statement] that uses the carray() |
| 11182 | ** functions. I is the parameter index to be bound. P is a pointer to the |
| 11183 | ** array to be bound, and N is the number of eements in the array. The |
| 11184 | ** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], |
| 11185 | ** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to |
| 11186 | ** indicate the datatype of the array being bound. The X argument is not a |
| 11187 | ** NULL pointer, then SQLite will invoke the function X on the P parameter |
| 11188 | ** after it has finished using P, even if the call to |
| 11189 | ** sqlite3_carray_bind() fails. The special-case finalizer |
| 11190 | ** SQLITE_TRANSIENT has no effect here. |
| 11191 | */ |
| 11192 | SQLITE_API int sqlite3_carray_bind( |
| 11193 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11194 | int i, /* Parameter index */ |
| 11195 | void *aData, /* Pointer to array data */ |
| 11196 | int nData, /* Number of data elements */ |
| 11197 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,14 +146,14 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.52.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3052000 |
| 151 | #define SQLITE_SOURCE_ID "2026-02-04 20:51:27 c476d956d0bd3065cf894de6f9d393b999ff7d2268a35f01a6d88804789ab58f" |
| 152 | #define SQLITE_SCM_BRANCH "trunk" |
| 153 | #define SQLITE_SCM_TAGS "" |
| 154 | #define SQLITE_SCM_DATETIME "2026-02-04T20:51:27.822Z" |
| 155 | |
| 156 | /* |
| 157 | ** CAPI3REF: Run-Time Library Version Numbers |
| 158 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 159 | ** |
| @@ -4431,16 +4431,32 @@ | |
| 4431 | ** compiles to see if some SQL syntax is well-formed, without generating |
| 4432 | ** messages on the global error log when it is not. If the test compile |
| 4433 | ** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4434 | ** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4435 | ** logs the error. |
| 4436 | ** |
| 4437 | ** [[SQLITE_PREPARE_FROM_DDL]] <dt>SQLITE_PREPARE_FROM_DDL</dt> |
| 4438 | ** <dd>The SQLITE_PREPARE_FROM_DDL flag causes the SQL compiler to behave as if |
| 4439 | ** the SQL statement is part of a database schema. This makes a difference |
| 4440 | ** when the [SQLITE_DBCONFIG_TRUSTED_SCHEMA] option is set to off. |
| 4441 | ** When this option is used and SQLITE_DBCONFIG_TRUSTED_SCHEMA is off, |
| 4442 | ** SQL functions may not be called unless they are tagged with |
| 4443 | ** [SQLITE_INNOCUOUS] and virtual tables may not be used unless tagged |
| 4444 | ** with [SQLITE_VTAB_INNOCUOUS]. Use the SQLITE_PREPARE_FROM_DDL option |
| 4445 | ** when preparing SQL that is derived from parts of the database |
| 4446 | ** schema. In particular, virtual table implementations that |
| 4447 | ** run SQL statements based on the arguments to their CREATE VIRTUAL |
| 4448 | ** TABLE statement should use [sqlite3_prepare_v3()] and set the |
| 4449 | ** SQLITE_PREPARE_FROM_DLL flag to prevent bypass of the |
| 4450 | ** [SQLITE_DBCONFIG_TRUSTED_SCHEMA] security checks. |
| 4451 | ** </dl> |
| 4452 | */ |
| 4453 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4454 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4455 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4456 | #define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4457 | #define SQLITE_PREPARE_FROM_DDL 0x20 |
| 4458 | |
| 4459 | /* |
| 4460 | ** CAPI3REF: Compiling An SQL Statement |
| 4461 | ** KEYWORDS: {SQL statement compiler} |
| 4462 | ** METHOD: sqlite3 |
| @@ -4450,12 +4466,13 @@ | |
| 4466 | ** program using one of these routines. Or, in other words, these routines |
| 4467 | ** are constructors for the [prepared statement] object. |
| 4468 | ** |
| 4469 | ** The preferred routine to use is [sqlite3_prepare_v2()]. The |
| 4470 | ** [sqlite3_prepare()] interface is legacy and should be avoided. |
| 4471 | ** [sqlite3_prepare_v3()] has an extra |
| 4472 | ** [SQLITE_PREPARE_FROM_DDL|"prepFlags" option] that is some times |
| 4473 | ** needed for special purpose or to pass along security restrictions. |
| 4474 | ** |
| 4475 | ** The use of the UTF-8 interfaces is preferred, as SQLite currently |
| 4476 | ** does all parsing using UTF-8. The UTF-16 interfaces are provided |
| 4477 | ** as a convenience. The UTF-16 interfaces work by converting the |
| 4478 | ** input text into UTF-8, then invoking the corresponding UTF-8 interface. |
| @@ -4856,12 +4873,12 @@ | |
| 4873 | ** it should be a pointer to well-formed UTF8 text. |
| 4874 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 4875 | ** it should be a pointer to well-formed UTF16 text. |
| 4876 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 4877 | ** it should be a pointer to a well-formed unicode string that is |
| 4878 | ** either UTF8 if the sixth parameter is SQLITE_UTF8 or SQLITE_UTF8_ZT, |
| 4879 | ** or UTF16 otherwise. |
| 4880 | ** |
| 4881 | ** [[byte-order determination rules]] ^The byte-order of |
| 4882 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 4883 | ** found in the first character, which is removed, or in the absence of a BOM |
| 4884 | ** the byte order is the native byte order of the host |
| @@ -4903,14 +4920,19 @@ | |
| 4920 | ** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the |
| 4921 | ** object is to be copied prior to the return from sqlite3_bind_*(). ^The |
| 4922 | ** object and pointer to it must remain valid until then. ^SQLite will then |
| 4923 | ** manage the lifetime of its private copy. |
| 4924 | ** |
| 4925 | ** ^The sixth argument (the E argument) |
| 4926 | ** to sqlite3_bind_text64(S,K,Z,N,D,E) must be one of |
| 4927 | ** [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], |
| 4928 | ** or [SQLITE_UTF16LE] to specify the encoding of the text in the |
| 4929 | ** third parameter, Z. The special value [SQLITE_UTF8_ZT] means that the |
| 4930 | ** string argument is both UTF-8 encoded and is zero-terminated. In other |
| 4931 | ** words, SQLITE_UTF8_ZT means that the Z array is allocated to hold at |
| 4932 | ** least N+1 bytes and that the Z[N] byte is zero. If |
| 4933 | ** the E argument to sqlite3_bind_text64(S,K,Z,N,D,E) is not one of the |
| 4934 | ** allowed values shown above, or if the text encoding is different |
| 4935 | ** from the encoding specified by the sixth parameter, then the behavior |
| 4936 | ** is undefined. |
| 4937 | ** |
| 4938 | ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
| @@ -5773,17 +5795,63 @@ | |
| 5795 | /* |
| 5796 | ** CAPI3REF: Text Encodings |
| 5797 | ** |
| 5798 | ** These constants define integer codes that represent the various |
| 5799 | ** text encodings supported by SQLite. |
| 5800 | ** |
| 5801 | ** <dl> |
| 5802 | ** [[SQLITE_UTF8]] <dt>SQLITE_UTF8</dt><dd>Text is encoding as UTF-8</dd> |
| 5803 | ** |
| 5804 | ** [[SQLITE_UTF16LE]] <dt>SQLITE_UTF16LE</dt><dd>Text is encoding as UTF-16 |
| 5805 | ** with each code point being expressed "little endian" - the least significant |
| 5806 | ** byte first. This is the usual encoding, for example on Windows.</dd> |
| 5807 | ** |
| 5808 | ** [[SQLITE_UTF16BE]] <dt>SQLITE_UTF16BE</dt><dd>Text is encoding as UTF-16 |
| 5809 | ** with each code point being expressed "big endian" - the most significant |
| 5810 | ** byte first. This encoding is less common, but is still sometimes seen, |
| 5811 | ** specially on older systems. |
| 5812 | ** |
| 5813 | ** [[SQLITE_UTF16]] <dt>SQLITE_UTF16</dt><dd>Text is encoding as UTF-16 |
| 5814 | ** with each code point being expressed either little endian or as big |
| 5815 | ** endian, according to the native endianness of the host computer. |
| 5816 | ** |
| 5817 | ** [[SQLITE_ANY]] <dt>SQLITE_ANY</dt><dd>This encoding value may only be used |
| 5818 | ** to declare the preferred text for [application-defined SQL functions] |
| 5819 | ** created using [sqlite3_create_function()] and similar. If the preferred |
| 5820 | ** encoding (the 4th parameter to sqlite3_create_function() - the eTextRep |
| 5821 | ** parameter) is SQLITE_ANY, that indicates that the function does not have |
| 5822 | ** a preference regarding the text encoding of its parameters and can take |
| 5823 | ** any text encoding that the SQLite core find convenient to supply. This |
| 5824 | ** option is deprecated. Please do not use it in new applications. |
| 5825 | ** |
| 5826 | ** [[SQLITE_UTF16_ALIGNED]] <dt>SQLITE_UTF16_ALIGNED</dt><dd>This encoding |
| 5827 | ** value may be used as the 3rd parameter (the eTextRep parameter) to |
| 5828 | ** [sqlite3_create_collation()] and similar. This encoding value means |
| 5829 | ** that the application-defined collating sequence created expects its |
| 5830 | ** input strings to be in UTF16 in native byte order, and that the start |
| 5831 | ** of the strings must be aligned to a 2-byte boundary. |
| 5832 | ** |
| 5833 | ** [[SQLITE_UTF8_ZT]] <dt>SQLITE_UTF8_ZT</dt><dd>This option can only be |
| 5834 | ** used to specify the text encoding to strings input to [sqlite3_result_text64()] |
| 5835 | ** and [sqlite3_bind_text64()]. It means that the input string (call it "z") |
| 5836 | ** is UTF-8 encoded and that it is zero-terminated. If the length parameter |
| 5837 | ** (call it "n") is non-negative, this encoding option means that the caller |
| 5838 | ** guarantees that z array contains at least n+1 bytes and that the z[n] |
| 5839 | ** byte has a value of zero. |
| 5840 | ** This option gives the same output as SQLITE_UTF8, but can be more efficient |
| 5841 | ** by avoiding the need to make a copy of the input string, in some cases. |
| 5842 | ** However, if z is allocated to hold fewer than n+1 bytes or if the |
| 5843 | ** z[n] byte is not zero, undefined behavior may result. |
| 5844 | ** </dl> |
| 5845 | */ |
| 5846 | #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ |
| 5847 | #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ |
| 5848 | #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ |
| 5849 | #define SQLITE_UTF16 4 /* Use native byte order */ |
| 5850 | #define SQLITE_ANY 5 /* Deprecated */ |
| 5851 | #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ |
| 5852 | #define SQLITE_UTF8_ZT 16 /* Zero-terminated UTF8 */ |
| 5853 | |
| 5854 | /* |
| 5855 | ** CAPI3REF: Function Flags |
| 5856 | ** |
| 5857 | ** These constants may be ORed together with the |
| @@ -6285,14 +6353,18 @@ | |
| 6353 | ** use for client data is to provide a mechanism for wrapper libraries |
| 6354 | ** to store additional information about an SQLite database connection. |
| 6355 | ** |
| 6356 | ** There is no limit (other than available memory) on the number of different |
| 6357 | ** client data pointers (with different names) that can be attached to a |
| 6358 | ** single database connection. However, the current implementation stores |
| 6359 | ** the content on a linked list. Insert and retrieval performance will |
| 6360 | ** be proportional to the number of entries. The design use case, and |
| 6361 | ** the use case for which the implementation is optimized, is |
| 6362 | ** that an application will store only small number of client data names, |
| 6363 | ** typically just one or two. This interface is not intended to be a |
| 6364 | ** generalized key/value store for thousands or millions of keys. It |
| 6365 | ** will work for that, but performance might be disappointing. |
| 6366 | ** |
| 6367 | ** There is no way to enumerate the client data pointers |
| 6368 | ** associated with a database connection. The N parameter can be thought |
| 6369 | ** of as a secret key such that only code that knows the secret key is able |
| 6370 | ** to access the associated data. |
| @@ -6396,14 +6468,18 @@ | |
| 6468 | ** ^The sqlite3_result_text(), sqlite3_result_text16(), |
| 6469 | ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces |
| 6470 | ** set the return value of the application-defined function to be |
| 6471 | ** a text string which is represented as UTF-8, UTF-16 native byte order, |
| 6472 | ** UTF-16 little endian, or UTF-16 big endian, respectively. |
| 6473 | ** ^The sqlite3_result_text64(C,Z,N,D,E) interface sets the return value of an |
| 6474 | ** application-defined function to be a text string in an encoding |
| 6475 | ** specified the E parameter, which must be one |
| 6476 | ** of [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], |
| 6477 | ** or [SQLITE_UTF16LE]. ^The special value [SQLITE_UTF8_ZT] means that |
| 6478 | ** the result text is both UTF-8 and zero-terminated. In other words, |
| 6479 | ** SQLITE_UTF8_ZT means that the Z array holds at least N+1 byes and that |
| 6480 | ** the Z[N] is zero. |
| 6481 | ** ^SQLite takes the text result from the application from |
| 6482 | ** the 2nd parameter of the sqlite3_result_text* interfaces. |
| 6483 | ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces |
| 6484 | ** other than sqlite3_result_text64() is negative, then SQLite computes |
| 6485 | ** the string length itself by searching the 2nd parameter for the first |
| @@ -6486,11 +6562,11 @@ | |
| 6562 | SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); |
| 6563 | SQLITE_API void sqlite3_result_int(sqlite3_context*, int); |
| 6564 | SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); |
| 6565 | SQLITE_API void sqlite3_result_null(sqlite3_context*); |
| 6566 | SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); |
| 6567 | SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char *z, sqlite3_uint64 n, |
| 6568 | void(*)(void*), unsigned char encoding); |
| 6569 | SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); |
| 6570 | SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6571 | SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); |
| 6572 | SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); |
| @@ -7425,22 +7501,22 @@ | |
| 7501 | ** ^This interface loads an SQLite extension library from the named file. |
| 7502 | ** |
| 7503 | ** ^The sqlite3_load_extension() interface attempts to load an |
| 7504 | ** [SQLite extension] library contained in the file zFile. If |
| 7505 | ** the file cannot be loaded directly, attempts are made to load |
| 7506 | ** with various operating-system specific filename extensions added. |
| 7507 | ** So for example, if "samplelib" cannot be loaded, then names like |
| 7508 | ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might |
| 7509 | ** be tried also. |
| 7510 | ** |
| 7511 | ** ^The entry point is zProc. |
| 7512 | ** ^(zProc may be 0, in which case SQLite will try to come up with an |
| 7513 | ** entry point name on its own. It first tries "sqlite3_extension_init". |
| 7514 | ** If that does not work, it tries names of the form "sqlite3_X_init" |
| 7515 | ** where X consists of the lower-case equivalent of all ASCII alphabetic |
| 7516 | ** characters or all ASCII alphanumeric characters in the filename from |
| 7517 | ** the last "/" to the first following "." and omitting any initial "lib".)^ |
| 7518 | ** ^The sqlite3_load_extension() interface returns |
| 7519 | ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. |
| 7520 | ** ^If an error occurs and pzErrMsg is not 0, then the |
| 7521 | ** [sqlite3_load_extension()] interface shall attempt to |
| 7522 | ** fill *pzErrMsg with error message text stored in memory |
| @@ -11174,23 +11250,45 @@ | |
| 11250 | #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ |
| 11251 | |
| 11252 | /* |
| 11253 | ** CAPI3REF: Bind array values to the CARRAY table-valued function |
| 11254 | ** |
| 11255 | ** The sqlite3_carray_bind_v2(S,I,P,N,F,X,D) interface binds an array value to |
| 11256 | ** parameter that is the first argument of the [carray() table-valued function]. |
| 11257 | ** The S parameter is a pointer to the [prepared statement] that uses the carray() |
| 11258 | ** functions. I is the parameter index to be bound. I must be the index of the |
| 11259 | ** parameter that is the first argument to the carray() table-valued function. |
| 11260 | ** P is a pointer to the array to be bound, and N is the number of elements in |
| 11261 | ** the array. The F argument is one of constants [SQLITE_CARRAY_INT32], |
| 11262 | ** [SQLITE_CARRAY_INT64], [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], |
| 11263 | ** or [SQLITE_CARRAY_BLOB] to indicate the datatype of the array P. |
| 11264 | ** |
| 11265 | ** If the X argument is not a NULL pointer or one of the special |
| 11266 | ** values [SQLITE_STATIC] or [SQLITE_TRANSIENT], then SQLite will invoke |
| 11267 | ** the function X with argument D when it is finished using the data in P. |
| 11268 | ** The call to X(D) is a destructor for the array P. The destructor X(D) |
| 11269 | ** is invoked even if the call to sqlite3_carray_bind() fails. If the X |
| 11270 | ** parameter is the special-case value [SQLITE_STATIC], then SQLite assumes |
| 11271 | ** that the data static and the destructor is never invoked. If the X |
| 11272 | ** parameter is the special-case value [SQLITE_TRANSIENT], then |
| 11273 | ** sqlite3_carray_bind_v2() makes its own private copy of the data prior |
| 11274 | ** to returning and never invokes the destructor X. |
| 11275 | ** |
| 11276 | ** The sqlite3_carray_bind() function works the same as sqlite_carray_bind_v2() |
| 11277 | ** with a D parameter set to P. In other words, |
| 11278 | ** sqlite3_carray_bind(S,I,P,N,F,X) is same as |
| 11279 | ** sqlite3_carray_bind(S,I,P,N,F,X,P). |
| 11280 | */ |
| 11281 | SQLITE_API int sqlite3_carray_bind_v2( |
| 11282 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11283 | int i, /* Parameter index */ |
| 11284 | void *aData, /* Pointer to array data */ |
| 11285 | int nData, /* Number of data elements */ |
| 11286 | int mFlags, /* CARRAY flags */ |
| 11287 | void (*xDel)(void*), /* Destructor for aData */ |
| 11288 | void *pDel /* Optional argument to xDel() */ |
| 11289 | ); |
| 11290 | SQLITE_API int sqlite3_carray_bind( |
| 11291 | sqlite3_stmt *pStmt, /* Statement to be bound */ |
| 11292 | int i, /* Parameter index */ |
| 11293 | void *aData, /* Pointer to array data */ |
| 11294 | int nData, /* Number of data elements */ |
| 11295 |
+23
| --- src/accordion.js | ||
| +++ src/accordion.js | ||
| @@ -22,10 +22,27 @@ | ||
| 22 | 22 | ** calculated. That's why setting `maxHeight' to `scrollHeight' is considered |
| 23 | 23 | ** "good enough" only during animation, but cleared afterwards. |
| 24 | 24 | ** |
| 25 | 25 | ** https://fossil-scm.org/forum/forumpost/66d7075f40 |
| 26 | 26 | ** https://fossil-scm.org/home/timeline?r=accordion-fix |
| 27 | +** | |
| 28 | +** To work around the missing support for `overflow-y: clip', this script uses a | |
| 29 | +** fallback and sets `overflow-y: hidden' during the accordion panel animations. | |
| 30 | +** That's because if `overflow-y: hidden' is set statically from the stylesheet, | |
| 31 | +** the shadow of the selected or current timeline entries in the context section | |
| 32 | +** of `/info' pages is still truncated on the right (which is strange, as that's | |
| 33 | +** not the "y" direction) in all major browsers. Otherwise, the stylesheet might | |
| 34 | +** define the fallback using the `@supports(…)' at-rule: | |
| 35 | +** | |
| 36 | +** .accordion_panel { | |
| 37 | +** overflow-y: hidden; | |
| 38 | +** } | |
| 39 | +** @supports(overflow-y: clip) { | |
| 40 | +** .accordion_panel { | |
| 41 | +** overflow-y: clip; | |
| 42 | +** } | |
| 43 | +** } | |
| 27 | 44 | */ |
| 28 | 45 | var acc_svgdata = ["data:image/svg+xml,"+ |
| 29 | 46 | "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+ |
| 30 | 47 | "%3Cpath style='fill:black;opacity:0' d='M16,16H0V0h16v16z'/%3E"+ |
| 31 | 48 | "%3Cpath style='fill:rgb(240,240,240)' d='M14,14H2V2h12v12z'/%3E"+ |
| @@ -52,15 +69,21 @@ | ||
| 52 | 69 | var x = this.nextElementSibling; |
| 53 | 70 | if( this.classList.contains("accordion_closed") ){ |
| 54 | 71 | x.style.maxHeight = x.scrollHeight + "px"; |
| 55 | 72 | setTimeout(function(){ |
| 56 | 73 | x.style.maxHeight = ""; |
| 74 | + if( !window.CSS || !window.CSS.supports("overflow: clip") ){ | |
| 75 | + x.style.overflowY = ""; | |
| 76 | + } | |
| 57 | 77 | },250); // default.css: .accordion_panel { transition-duration } |
| 58 | 78 | }else{ |
| 59 | 79 | x.style.maxHeight = x.scrollHeight + "px"; |
| 80 | + if( !window.CSS || !window.CSS.supports("overflow: clip") ){ | |
| 81 | + x.style.overflowY = "hidden"; | |
| 82 | + } | |
| 60 | 83 | setTimeout(function(){ |
| 61 | 84 | x.style.maxHeight = "0"; |
| 62 | 85 | },1); |
| 63 | 86 | } |
| 64 | 87 | this.classList.toggle("accordion_closed"); |
| 65 | 88 | }); |
| 66 | 89 | } |
| 67 | 90 |
| --- src/accordion.js | |
| +++ src/accordion.js | |
| @@ -22,10 +22,27 @@ | |
| 22 | ** calculated. That's why setting `maxHeight' to `scrollHeight' is considered |
| 23 | ** "good enough" only during animation, but cleared afterwards. |
| 24 | ** |
| 25 | ** https://fossil-scm.org/forum/forumpost/66d7075f40 |
| 26 | ** https://fossil-scm.org/home/timeline?r=accordion-fix |
| 27 | */ |
| 28 | var acc_svgdata = ["data:image/svg+xml,"+ |
| 29 | "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+ |
| 30 | "%3Cpath style='fill:black;opacity:0' d='M16,16H0V0h16v16z'/%3E"+ |
| 31 | "%3Cpath style='fill:rgb(240,240,240)' d='M14,14H2V2h12v12z'/%3E"+ |
| @@ -52,15 +69,21 @@ | |
| 52 | var x = this.nextElementSibling; |
| 53 | if( this.classList.contains("accordion_closed") ){ |
| 54 | x.style.maxHeight = x.scrollHeight + "px"; |
| 55 | setTimeout(function(){ |
| 56 | x.style.maxHeight = ""; |
| 57 | },250); // default.css: .accordion_panel { transition-duration } |
| 58 | }else{ |
| 59 | x.style.maxHeight = x.scrollHeight + "px"; |
| 60 | setTimeout(function(){ |
| 61 | x.style.maxHeight = "0"; |
| 62 | },1); |
| 63 | } |
| 64 | this.classList.toggle("accordion_closed"); |
| 65 | }); |
| 66 | } |
| 67 |
| --- src/accordion.js | |
| +++ src/accordion.js | |
| @@ -22,10 +22,27 @@ | |
| 22 | ** calculated. That's why setting `maxHeight' to `scrollHeight' is considered |
| 23 | ** "good enough" only during animation, but cleared afterwards. |
| 24 | ** |
| 25 | ** https://fossil-scm.org/forum/forumpost/66d7075f40 |
| 26 | ** https://fossil-scm.org/home/timeline?r=accordion-fix |
| 27 | ** |
| 28 | ** To work around the missing support for `overflow-y: clip', this script uses a |
| 29 | ** fallback and sets `overflow-y: hidden' during the accordion panel animations. |
| 30 | ** That's because if `overflow-y: hidden' is set statically from the stylesheet, |
| 31 | ** the shadow of the selected or current timeline entries in the context section |
| 32 | ** of `/info' pages is still truncated on the right (which is strange, as that's |
| 33 | ** not the "y" direction) in all major browsers. Otherwise, the stylesheet might |
| 34 | ** define the fallback using the `@supports(…)' at-rule: |
| 35 | ** |
| 36 | ** .accordion_panel { |
| 37 | ** overflow-y: hidden; |
| 38 | ** } |
| 39 | ** @supports(overflow-y: clip) { |
| 40 | ** .accordion_panel { |
| 41 | ** overflow-y: clip; |
| 42 | ** } |
| 43 | ** } |
| 44 | */ |
| 45 | var acc_svgdata = ["data:image/svg+xml,"+ |
| 46 | "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+ |
| 47 | "%3Cpath style='fill:black;opacity:0' d='M16,16H0V0h16v16z'/%3E"+ |
| 48 | "%3Cpath style='fill:rgb(240,240,240)' d='M14,14H2V2h12v12z'/%3E"+ |
| @@ -52,15 +69,21 @@ | |
| 69 | var x = this.nextElementSibling; |
| 70 | if( this.classList.contains("accordion_closed") ){ |
| 71 | x.style.maxHeight = x.scrollHeight + "px"; |
| 72 | setTimeout(function(){ |
| 73 | x.style.maxHeight = ""; |
| 74 | if( !window.CSS || !window.CSS.supports("overflow: clip") ){ |
| 75 | x.style.overflowY = ""; |
| 76 | } |
| 77 | },250); // default.css: .accordion_panel { transition-duration } |
| 78 | }else{ |
| 79 | x.style.maxHeight = x.scrollHeight + "px"; |
| 80 | if( !window.CSS || !window.CSS.supports("overflow: clip") ){ |
| 81 | x.style.overflowY = "hidden"; |
| 82 | } |
| 83 | setTimeout(function(){ |
| 84 | x.style.maxHeight = "0"; |
| 85 | },1); |
| 86 | } |
| 87 | this.classList.toggle("accordion_closed"); |
| 88 | }); |
| 89 | } |
| 90 |
+2
-2
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -353,11 +353,11 @@ | ||
| 353 | 353 | ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
| 354 | 354 | ** option does not appear on the command line then the "ignore-glob" setting |
| 355 | 355 | ** is used. If the --clean option does not appear on the command line then |
| 356 | 356 | ** the "clean-glob" setting is used. |
| 357 | 357 | ** |
| 358 | -** When attempting to explicitly add files on the commandline, and if those | |
| 358 | +** When attempting to explicitly add files on the command line, and if those | |
| 359 | 359 | ** match "ignore-glob", a confirmation is asked first. This can be prevented |
| 360 | 360 | ** using the -f|--force option. |
| 361 | 361 | ** |
| 362 | 362 | ** The --case-sensitive option determines whether or not filenames should |
| 363 | 363 | ** be treated case sensitive or not. If the option is not given, the default |
| @@ -476,11 +476,11 @@ | ||
| 476 | 476 | } |
| 477 | 477 | glob_free(pIgnore); |
| 478 | 478 | glob_free(pClean); |
| 479 | 479 | |
| 480 | 480 | /** Check for Windows-reserved names and warn or exit, as |
| 481 | - ** appopriate. Note that the 'add' internal machinery already | |
| 481 | + ** appropriate. Note that the 'add' internal machinery already | |
| 482 | 482 | ** _silently_ skips over any names for which |
| 483 | 483 | ** file_is_reserved_name() returns true or which is in the |
| 484 | 484 | ** fossil_reserved_name() list. We do not need to warn for those, |
| 485 | 485 | ** as they're outright verboten. */ |
| 486 | 486 | if(db_exists("SELECT 1 FROM sfile WHERE win_reserved(pathname)")){ |
| 487 | 487 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -353,11 +353,11 @@ | |
| 353 | ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
| 354 | ** option does not appear on the command line then the "ignore-glob" setting |
| 355 | ** is used. If the --clean option does not appear on the command line then |
| 356 | ** the "clean-glob" setting is used. |
| 357 | ** |
| 358 | ** When attempting to explicitly add files on the commandline, and if those |
| 359 | ** match "ignore-glob", a confirmation is asked first. This can be prevented |
| 360 | ** using the -f|--force option. |
| 361 | ** |
| 362 | ** The --case-sensitive option determines whether or not filenames should |
| 363 | ** be treated case sensitive or not. If the option is not given, the default |
| @@ -476,11 +476,11 @@ | |
| 476 | } |
| 477 | glob_free(pIgnore); |
| 478 | glob_free(pClean); |
| 479 | |
| 480 | /** Check for Windows-reserved names and warn or exit, as |
| 481 | ** appopriate. Note that the 'add' internal machinery already |
| 482 | ** _silently_ skips over any names for which |
| 483 | ** file_is_reserved_name() returns true or which is in the |
| 484 | ** fossil_reserved_name() list. We do not need to warn for those, |
| 485 | ** as they're outright verboten. */ |
| 486 | if(db_exists("SELECT 1 FROM sfile WHERE win_reserved(pathname)")){ |
| 487 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -353,11 +353,11 @@ | |
| 353 | ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
| 354 | ** option does not appear on the command line then the "ignore-glob" setting |
| 355 | ** is used. If the --clean option does not appear on the command line then |
| 356 | ** the "clean-glob" setting is used. |
| 357 | ** |
| 358 | ** When attempting to explicitly add files on the command line, and if those |
| 359 | ** match "ignore-glob", a confirmation is asked first. This can be prevented |
| 360 | ** using the -f|--force option. |
| 361 | ** |
| 362 | ** The --case-sensitive option determines whether or not filenames should |
| 363 | ** be treated case sensitive or not. If the option is not given, the default |
| @@ -476,11 +476,11 @@ | |
| 476 | } |
| 477 | glob_free(pIgnore); |
| 478 | glob_free(pClean); |
| 479 | |
| 480 | /** Check for Windows-reserved names and warn or exit, as |
| 481 | ** appropriate. Note that the 'add' internal machinery already |
| 482 | ** _silently_ skips over any names for which |
| 483 | ** file_is_reserved_name() returns true or which is in the |
| 484 | ** fossil_reserved_name() list. We do not need to warn for those, |
| 485 | ** as they're outright verboten. */ |
| 486 | if(db_exists("SELECT 1 FROM sfile WHERE win_reserved(pathname)")){ |
| 487 |
+1
-1
| --- src/ajax.c | ||
| +++ src/ajax.c | ||
| @@ -395,11 +395,11 @@ | ||
| 395 | 395 | const AjaxRoute routes[] = { |
| 396 | 396 | /* Keep these sorted by zName (for bsearch()) */ |
| 397 | 397 | {"preview-text", ajax_route_preview_text, 0, 1 |
| 398 | 398 | /* Note that this does not require write permissions in the repo. |
| 399 | 399 | ** It should arguably require write permissions but doing means |
| 400 | - ** that /chat does not work without checkin permissions: | |
| 400 | + ** that /chat does not work without check-in permissions: | |
| 401 | 401 | ** |
| 402 | 402 | ** https://fossil-scm.org/forum/forumpost/ed4a762b3a557898 |
| 403 | 403 | ** |
| 404 | 404 | ** This particular route is used by /fileedit and /chat, whereas |
| 405 | 405 | ** /wikiedit uses a simpler wiki-specific route. |
| 406 | 406 |
| --- src/ajax.c | |
| +++ src/ajax.c | |
| @@ -395,11 +395,11 @@ | |
| 395 | const AjaxRoute routes[] = { |
| 396 | /* Keep these sorted by zName (for bsearch()) */ |
| 397 | {"preview-text", ajax_route_preview_text, 0, 1 |
| 398 | /* Note that this does not require write permissions in the repo. |
| 399 | ** It should arguably require write permissions but doing means |
| 400 | ** that /chat does not work without checkin permissions: |
| 401 | ** |
| 402 | ** https://fossil-scm.org/forum/forumpost/ed4a762b3a557898 |
| 403 | ** |
| 404 | ** This particular route is used by /fileedit and /chat, whereas |
| 405 | ** /wikiedit uses a simpler wiki-specific route. |
| 406 |
| --- src/ajax.c | |
| +++ src/ajax.c | |
| @@ -395,11 +395,11 @@ | |
| 395 | const AjaxRoute routes[] = { |
| 396 | /* Keep these sorted by zName (for bsearch()) */ |
| 397 | {"preview-text", ajax_route_preview_text, 0, 1 |
| 398 | /* Note that this does not require write permissions in the repo. |
| 399 | ** It should arguably require write permissions but doing means |
| 400 | ** that /chat does not work without check-in permissions: |
| 401 | ** |
| 402 | ** https://fossil-scm.org/forum/forumpost/ed4a762b3a557898 |
| 403 | ** |
| 404 | ** This particular route is used by /fileedit and /chat, whereas |
| 405 | ** /wikiedit uses a simpler wiki-specific route. |
| 406 |
+7
-7
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -664,11 +664,11 @@ | ||
| 664 | 664 | return p; |
| 665 | 665 | } |
| 666 | 666 | |
| 667 | 667 | /* |
| 668 | 668 | ** Scan the header of the email message in pMsg looking for the |
| 669 | -** (first) occurrance of zField. Fill pValue with the content of | |
| 669 | +** (first) occurrence of zField. Fill pValue with the content of | |
| 670 | 670 | ** that field. |
| 671 | 671 | ** |
| 672 | 672 | ** This routine initializes pValue. Any prior content of pValue is |
| 673 | 673 | ** discarded (leaked). |
| 674 | 674 | ** |
| @@ -710,11 +710,11 @@ | ||
| 710 | 710 | /* |
| 711 | 711 | ** Determine whether or not the input string is a valid email address. |
| 712 | 712 | ** Only look at character up to but not including the first \000 or |
| 713 | 713 | ** the first cTerm character, whichever comes first. |
| 714 | 714 | ** |
| 715 | -** Return the length of the email addresss string in bytes if the email | |
| 715 | +** Return the length of the email address string in bytes if the email | |
| 716 | 716 | ** address is valid. If the email address is misformed, return 0. |
| 717 | 717 | */ |
| 718 | 718 | int email_address_is_valid(const char *z, char cTerm){ |
| 719 | 719 | int i; |
| 720 | 720 | int nAt = 0; |
| @@ -2259,11 +2259,11 @@ | ||
| 2259 | 2259 | ** Users visit this page to be delisted from email alerts. |
| 2260 | 2260 | ** |
| 2261 | 2261 | ** If a valid subscriber code is supplied in the name= query parameter, |
| 2262 | 2262 | ** then that subscriber is delisted. |
| 2263 | 2263 | ** |
| 2264 | -** Otherwise, If the users is logged in, then they are redirected | |
| 2264 | +** Otherwise, if the users are logged in, then they are redirected | |
| 2265 | 2265 | ** to the /alerts page where they have an unsubscribe button. |
| 2266 | 2266 | ** |
| 2267 | 2267 | ** Non-logged-in users with no name= query parameter are invited to enter |
| 2268 | 2268 | ** an email address to which will be sent the unsubscribe link that |
| 2269 | 2269 | ** contains the correct subscriber code. |
| @@ -2606,11 +2606,11 @@ | ||
| 2606 | 2606 | /* |
| 2607 | 2607 | ** Compute a string that is appropriate for the EmailEvent.zPriors field |
| 2608 | 2608 | ** for a particular forum post. |
| 2609 | 2609 | ** |
| 2610 | 2610 | ** This string is an encode list of sender names and rids for all ancestors |
| 2611 | -** of the fpdi post - the post that fpid answer, the post that that parent | |
| 2611 | +** of the fpid post - the post that fpid answers, the post that parent | |
| 2612 | 2612 | ** post answers, and so forth back up to the root post. Duplicates sender |
| 2613 | 2613 | ** names are omitted. |
| 2614 | 2614 | ** |
| 2615 | 2615 | ** The EmailEvent.zPriors field is used to screen events for people who |
| 2616 | 2616 | ** only want to see replies to their own posts or to specific posts. |
| @@ -2955,12 +2955,12 @@ | ||
| 2955 | 2955 | Blob *pBody, /* Write email body here */ |
| 2956 | 2956 | const char *zCode, /* The subscriber code */ |
| 2957 | 2957 | int lastContact, /* Last contact (days since 1970) */ |
| 2958 | 2958 | const char *zEAddr, /* Subscriber email address. Send to this. */ |
| 2959 | 2959 | const char *zSub, /* Subscription codes */ |
| 2960 | - const char *zRepoName, /* Name of the sending Fossil repostory */ | |
| 2961 | - const char *zUrl /* URL for the sending Fossil repostory */ | |
| 2960 | + const char *zRepoName, /* Name of the sending Fossil repository */ | |
| 2961 | + const char *zUrl /* URL for the sending Fossil repository */ | |
| 2962 | 2962 | ){ |
| 2963 | 2963 | blob_appendf(pHdr,"To: <%s>\r\n", zEAddr); |
| 2964 | 2964 | blob_appendf(pHdr,"Subject: %s Subscription to %s expires soon\r\n", |
| 2965 | 2965 | zRepoName, zUrl); |
| 2966 | 2966 | blob_appendf(pBody, |
| @@ -3466,11 +3466,11 @@ | ||
| 3466 | 3466 | @ </form> |
| 3467 | 3467 | style_finish_page(); |
| 3468 | 3468 | } |
| 3469 | 3469 | |
| 3470 | 3470 | /* |
| 3471 | -** Send an annoucement message described by query parameter. | |
| 3471 | +** Send an announcement message described by query parameter. | |
| 3472 | 3472 | ** Permission to do this has already been verified. |
| 3473 | 3473 | */ |
| 3474 | 3474 | static char *alert_send_announcement(void){ |
| 3475 | 3475 | AlertSender *pSender; |
| 3476 | 3476 | char *zErr; |
| 3477 | 3477 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -664,11 +664,11 @@ | |
| 664 | return p; |
| 665 | } |
| 666 | |
| 667 | /* |
| 668 | ** Scan the header of the email message in pMsg looking for the |
| 669 | ** (first) occurrance of zField. Fill pValue with the content of |
| 670 | ** that field. |
| 671 | ** |
| 672 | ** This routine initializes pValue. Any prior content of pValue is |
| 673 | ** discarded (leaked). |
| 674 | ** |
| @@ -710,11 +710,11 @@ | |
| 710 | /* |
| 711 | ** Determine whether or not the input string is a valid email address. |
| 712 | ** Only look at character up to but not including the first \000 or |
| 713 | ** the first cTerm character, whichever comes first. |
| 714 | ** |
| 715 | ** Return the length of the email addresss string in bytes if the email |
| 716 | ** address is valid. If the email address is misformed, return 0. |
| 717 | */ |
| 718 | int email_address_is_valid(const char *z, char cTerm){ |
| 719 | int i; |
| 720 | int nAt = 0; |
| @@ -2259,11 +2259,11 @@ | |
| 2259 | ** Users visit this page to be delisted from email alerts. |
| 2260 | ** |
| 2261 | ** If a valid subscriber code is supplied in the name= query parameter, |
| 2262 | ** then that subscriber is delisted. |
| 2263 | ** |
| 2264 | ** Otherwise, If the users is logged in, then they are redirected |
| 2265 | ** to the /alerts page where they have an unsubscribe button. |
| 2266 | ** |
| 2267 | ** Non-logged-in users with no name= query parameter are invited to enter |
| 2268 | ** an email address to which will be sent the unsubscribe link that |
| 2269 | ** contains the correct subscriber code. |
| @@ -2606,11 +2606,11 @@ | |
| 2606 | /* |
| 2607 | ** Compute a string that is appropriate for the EmailEvent.zPriors field |
| 2608 | ** for a particular forum post. |
| 2609 | ** |
| 2610 | ** This string is an encode list of sender names and rids for all ancestors |
| 2611 | ** of the fpdi post - the post that fpid answer, the post that that parent |
| 2612 | ** post answers, and so forth back up to the root post. Duplicates sender |
| 2613 | ** names are omitted. |
| 2614 | ** |
| 2615 | ** The EmailEvent.zPriors field is used to screen events for people who |
| 2616 | ** only want to see replies to their own posts or to specific posts. |
| @@ -2955,12 +2955,12 @@ | |
| 2955 | Blob *pBody, /* Write email body here */ |
| 2956 | const char *zCode, /* The subscriber code */ |
| 2957 | int lastContact, /* Last contact (days since 1970) */ |
| 2958 | const char *zEAddr, /* Subscriber email address. Send to this. */ |
| 2959 | const char *zSub, /* Subscription codes */ |
| 2960 | const char *zRepoName, /* Name of the sending Fossil repostory */ |
| 2961 | const char *zUrl /* URL for the sending Fossil repostory */ |
| 2962 | ){ |
| 2963 | blob_appendf(pHdr,"To: <%s>\r\n", zEAddr); |
| 2964 | blob_appendf(pHdr,"Subject: %s Subscription to %s expires soon\r\n", |
| 2965 | zRepoName, zUrl); |
| 2966 | blob_appendf(pBody, |
| @@ -3466,11 +3466,11 @@ | |
| 3466 | @ </form> |
| 3467 | style_finish_page(); |
| 3468 | } |
| 3469 | |
| 3470 | /* |
| 3471 | ** Send an annoucement message described by query parameter. |
| 3472 | ** Permission to do this has already been verified. |
| 3473 | */ |
| 3474 | static char *alert_send_announcement(void){ |
| 3475 | AlertSender *pSender; |
| 3476 | char *zErr; |
| 3477 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -664,11 +664,11 @@ | |
| 664 | return p; |
| 665 | } |
| 666 | |
| 667 | /* |
| 668 | ** Scan the header of the email message in pMsg looking for the |
| 669 | ** (first) occurrence of zField. Fill pValue with the content of |
| 670 | ** that field. |
| 671 | ** |
| 672 | ** This routine initializes pValue. Any prior content of pValue is |
| 673 | ** discarded (leaked). |
| 674 | ** |
| @@ -710,11 +710,11 @@ | |
| 710 | /* |
| 711 | ** Determine whether or not the input string is a valid email address. |
| 712 | ** Only look at character up to but not including the first \000 or |
| 713 | ** the first cTerm character, whichever comes first. |
| 714 | ** |
| 715 | ** Return the length of the email address string in bytes if the email |
| 716 | ** address is valid. If the email address is misformed, return 0. |
| 717 | */ |
| 718 | int email_address_is_valid(const char *z, char cTerm){ |
| 719 | int i; |
| 720 | int nAt = 0; |
| @@ -2259,11 +2259,11 @@ | |
| 2259 | ** Users visit this page to be delisted from email alerts. |
| 2260 | ** |
| 2261 | ** If a valid subscriber code is supplied in the name= query parameter, |
| 2262 | ** then that subscriber is delisted. |
| 2263 | ** |
| 2264 | ** Otherwise, if the users are logged in, then they are redirected |
| 2265 | ** to the /alerts page where they have an unsubscribe button. |
| 2266 | ** |
| 2267 | ** Non-logged-in users with no name= query parameter are invited to enter |
| 2268 | ** an email address to which will be sent the unsubscribe link that |
| 2269 | ** contains the correct subscriber code. |
| @@ -2606,11 +2606,11 @@ | |
| 2606 | /* |
| 2607 | ** Compute a string that is appropriate for the EmailEvent.zPriors field |
| 2608 | ** for a particular forum post. |
| 2609 | ** |
| 2610 | ** This string is an encode list of sender names and rids for all ancestors |
| 2611 | ** of the fpid post - the post that fpid answers, the post that parent |
| 2612 | ** post answers, and so forth back up to the root post. Duplicates sender |
| 2613 | ** names are omitted. |
| 2614 | ** |
| 2615 | ** The EmailEvent.zPriors field is used to screen events for people who |
| 2616 | ** only want to see replies to their own posts or to specific posts. |
| @@ -2955,12 +2955,12 @@ | |
| 2955 | Blob *pBody, /* Write email body here */ |
| 2956 | const char *zCode, /* The subscriber code */ |
| 2957 | int lastContact, /* Last contact (days since 1970) */ |
| 2958 | const char *zEAddr, /* Subscriber email address. Send to this. */ |
| 2959 | const char *zSub, /* Subscription codes */ |
| 2960 | const char *zRepoName, /* Name of the sending Fossil repository */ |
| 2961 | const char *zUrl /* URL for the sending Fossil repository */ |
| 2962 | ){ |
| 2963 | blob_appendf(pHdr,"To: <%s>\r\n", zEAddr); |
| 2964 | blob_appendf(pHdr,"Subject: %s Subscription to %s expires soon\r\n", |
| 2965 | zRepoName, zUrl); |
| 2966 | blob_appendf(pBody, |
| @@ -3466,11 +3466,11 @@ | |
| 3466 | @ </form> |
| 3467 | style_finish_page(); |
| 3468 | } |
| 3469 | |
| 3470 | /* |
| 3471 | ** Send an announcement message described by query parameter. |
| 3472 | ** Permission to do this has already been verified. |
| 3473 | */ |
| 3474 | static char *alert_send_announcement(void){ |
| 3475 | AlertSender *pSender; |
| 3476 | char *zErr; |
| 3477 |
+18
-5
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -157,19 +157,22 @@ | ||
| 157 | 157 | ** when one of the following commands are run against the repository: |
| 158 | 158 | ** clone, info, pull, push, or sync. Even previously ignored repositories |
| 159 | 159 | ** are added back to the list of repositories by these commands. |
| 160 | 160 | ** |
| 161 | 161 | ** Options: |
| 162 | -** --dry-run If given, display instead of run actions | |
| 162 | +** --dry-run Just display commands that would have run | |
| 163 | 163 | ** --showfile Show the repository or check-out being operated upon |
| 164 | 164 | ** --stop-on-error Halt immediately if any subprocess fails |
| 165 | +** -s|--stop Shorthand for "--stop-on-error" | |
| 165 | 166 | */ |
| 166 | 167 | void all_cmd(void){ |
| 167 | 168 | Stmt q; |
| 168 | 169 | const char *zCmd; |
| 169 | - char *zSyscmd; | |
| 170 | + char *zSyscmd = 0; | |
| 170 | 171 | Blob extra; |
| 172 | + int bHalted = 0; | |
| 173 | + int rc = 0; | |
| 171 | 174 | int useCheckouts = 0; |
| 172 | 175 | int quiet = 0; |
| 173 | 176 | int dryRunFlag = 0; |
| 174 | 177 | int showFile = find_option("showfile",0,0)!=0; |
| 175 | 178 | int stopOnError; |
| @@ -176,10 +179,11 @@ | ||
| 176 | 179 | int nToDel = 0; |
| 177 | 180 | int showLabel = 0; |
| 178 | 181 | |
| 179 | 182 | (void)find_option("dontstop",0,0); /* Legacy. Now the default */ |
| 180 | 183 | stopOnError = find_option("stop-on-error",0,0)!=0; |
| 184 | + if( find_option("stop","s",0)!=0 ) stopOnError = 1; | |
| 181 | 185 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 182 | 186 | if( !dryRunFlag ){ |
| 183 | 187 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 184 | 188 | } |
| 185 | 189 | |
| @@ -330,10 +334,12 @@ | ||
| 330 | 334 | zCmd = "sync -autourl -R"; |
| 331 | 335 | collect_argument(&extra, "share-links",0); |
| 332 | 336 | collect_argument(&extra, "verbose","v"); |
| 333 | 337 | collect_argument(&extra, "unversioned","u"); |
| 334 | 338 | collect_argument(&extra, "all",0); |
| 339 | + collect_argument(&extra, "quiet","q"); | |
| 340 | + collect_argument(&extra, "ping",0); | |
| 335 | 341 | }else if( fossil_strcmp(zCmd, "test-integrity")==0 ){ |
| 336 | 342 | collect_argument(&extra, "db-only", "d"); |
| 337 | 343 | collect_argument(&extra, "parse", 0); |
| 338 | 344 | collect_argument(&extra, "quick", "q"); |
| 339 | 345 | zCmd = "test-integrity"; |
| @@ -465,11 +471,10 @@ | ||
| 465 | 471 | " ORDER BY 1" |
| 466 | 472 | ); |
| 467 | 473 | } |
| 468 | 474 | db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1"); |
| 469 | 475 | while( db_step(&q)==SQLITE_ROW ){ |
| 470 | - int rc; | |
| 471 | 476 | const char *zFilename = db_column_text(&q, 0); |
| 472 | 477 | const char *zInode = db_column_text(&q,2); |
| 473 | 478 | #if !USE_SEE |
| 474 | 479 | if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue; |
| 475 | 480 | #endif |
| @@ -502,18 +507,21 @@ | ||
| 502 | 507 | if( !quiet || dryRunFlag ){ |
| 503 | 508 | fossil_print("%s\n", zSyscmd); |
| 504 | 509 | fflush(stdout); |
| 505 | 510 | } |
| 506 | 511 | rc = dryRunFlag ? 0 : fossil_system(zSyscmd); |
| 507 | - free(zSyscmd); | |
| 508 | 512 | if( rc ){ |
| 509 | - if( stopOnError ) break; | |
| 513 | + if( stopOnError ){ | |
| 514 | + bHalted = 1; | |
| 515 | + break; | |
| 516 | + } | |
| 510 | 517 | /* If there is an error, pause briefly, but do not stop. The brief |
| 511 | 518 | ** pause is so that if the prior command failed with Ctrl-C then there |
| 512 | 519 | ** will be time to stop the whole thing with a second Ctrl-C. */ |
| 513 | 520 | sqlite3_sleep(330); |
| 514 | 521 | } |
| 522 | + fossil_free(zSyscmd); | |
| 515 | 523 | } |
| 516 | 524 | db_finalize(&q); |
| 517 | 525 | |
| 518 | 526 | blob_reset(&extra); |
| 519 | 527 | |
| @@ -528,6 +536,11 @@ | ||
| 528 | 536 | db_unprotect(PROTECT_CONFIG); |
| 529 | 537 | db_multi_exec("%s", zSql /*safe-for-%s*/ ); |
| 530 | 538 | db_protect_pop(); |
| 531 | 539 | } |
| 532 | 540 | } |
| 541 | + | |
| 542 | + if( stopOnError && bHalted ){ | |
| 543 | + fossil_fatal("STOPPED: non-zero result code (%d) from\nSTOPPED: %s", | |
| 544 | + rc, zSyscmd); | |
| 545 | + } | |
| 533 | 546 | } |
| 534 | 547 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -157,19 +157,22 @@ | |
| 157 | ** when one of the following commands are run against the repository: |
| 158 | ** clone, info, pull, push, or sync. Even previously ignored repositories |
| 159 | ** are added back to the list of repositories by these commands. |
| 160 | ** |
| 161 | ** Options: |
| 162 | ** --dry-run If given, display instead of run actions |
| 163 | ** --showfile Show the repository or check-out being operated upon |
| 164 | ** --stop-on-error Halt immediately if any subprocess fails |
| 165 | */ |
| 166 | void all_cmd(void){ |
| 167 | Stmt q; |
| 168 | const char *zCmd; |
| 169 | char *zSyscmd; |
| 170 | Blob extra; |
| 171 | int useCheckouts = 0; |
| 172 | int quiet = 0; |
| 173 | int dryRunFlag = 0; |
| 174 | int showFile = find_option("showfile",0,0)!=0; |
| 175 | int stopOnError; |
| @@ -176,10 +179,11 @@ | |
| 176 | int nToDel = 0; |
| 177 | int showLabel = 0; |
| 178 | |
| 179 | (void)find_option("dontstop",0,0); /* Legacy. Now the default */ |
| 180 | stopOnError = find_option("stop-on-error",0,0)!=0; |
| 181 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 182 | if( !dryRunFlag ){ |
| 183 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 184 | } |
| 185 | |
| @@ -330,10 +334,12 @@ | |
| 330 | zCmd = "sync -autourl -R"; |
| 331 | collect_argument(&extra, "share-links",0); |
| 332 | collect_argument(&extra, "verbose","v"); |
| 333 | collect_argument(&extra, "unversioned","u"); |
| 334 | collect_argument(&extra, "all",0); |
| 335 | }else if( fossil_strcmp(zCmd, "test-integrity")==0 ){ |
| 336 | collect_argument(&extra, "db-only", "d"); |
| 337 | collect_argument(&extra, "parse", 0); |
| 338 | collect_argument(&extra, "quick", "q"); |
| 339 | zCmd = "test-integrity"; |
| @@ -465,11 +471,10 @@ | |
| 465 | " ORDER BY 1" |
| 466 | ); |
| 467 | } |
| 468 | db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1"); |
| 469 | while( db_step(&q)==SQLITE_ROW ){ |
| 470 | int rc; |
| 471 | const char *zFilename = db_column_text(&q, 0); |
| 472 | const char *zInode = db_column_text(&q,2); |
| 473 | #if !USE_SEE |
| 474 | if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue; |
| 475 | #endif |
| @@ -502,18 +507,21 @@ | |
| 502 | if( !quiet || dryRunFlag ){ |
| 503 | fossil_print("%s\n", zSyscmd); |
| 504 | fflush(stdout); |
| 505 | } |
| 506 | rc = dryRunFlag ? 0 : fossil_system(zSyscmd); |
| 507 | free(zSyscmd); |
| 508 | if( rc ){ |
| 509 | if( stopOnError ) break; |
| 510 | /* If there is an error, pause briefly, but do not stop. The brief |
| 511 | ** pause is so that if the prior command failed with Ctrl-C then there |
| 512 | ** will be time to stop the whole thing with a second Ctrl-C. */ |
| 513 | sqlite3_sleep(330); |
| 514 | } |
| 515 | } |
| 516 | db_finalize(&q); |
| 517 | |
| 518 | blob_reset(&extra); |
| 519 | |
| @@ -528,6 +536,11 @@ | |
| 528 | db_unprotect(PROTECT_CONFIG); |
| 529 | db_multi_exec("%s", zSql /*safe-for-%s*/ ); |
| 530 | db_protect_pop(); |
| 531 | } |
| 532 | } |
| 533 | } |
| 534 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -157,19 +157,22 @@ | |
| 157 | ** when one of the following commands are run against the repository: |
| 158 | ** clone, info, pull, push, or sync. Even previously ignored repositories |
| 159 | ** are added back to the list of repositories by these commands. |
| 160 | ** |
| 161 | ** Options: |
| 162 | ** --dry-run Just display commands that would have run |
| 163 | ** --showfile Show the repository or check-out being operated upon |
| 164 | ** --stop-on-error Halt immediately if any subprocess fails |
| 165 | ** -s|--stop Shorthand for "--stop-on-error" |
| 166 | */ |
| 167 | void all_cmd(void){ |
| 168 | Stmt q; |
| 169 | const char *zCmd; |
| 170 | char *zSyscmd = 0; |
| 171 | Blob extra; |
| 172 | int bHalted = 0; |
| 173 | int rc = 0; |
| 174 | int useCheckouts = 0; |
| 175 | int quiet = 0; |
| 176 | int dryRunFlag = 0; |
| 177 | int showFile = find_option("showfile",0,0)!=0; |
| 178 | int stopOnError; |
| @@ -176,10 +179,11 @@ | |
| 179 | int nToDel = 0; |
| 180 | int showLabel = 0; |
| 181 | |
| 182 | (void)find_option("dontstop",0,0); /* Legacy. Now the default */ |
| 183 | stopOnError = find_option("stop-on-error",0,0)!=0; |
| 184 | if( find_option("stop","s",0)!=0 ) stopOnError = 1; |
| 185 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 186 | if( !dryRunFlag ){ |
| 187 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 188 | } |
| 189 | |
| @@ -330,10 +334,12 @@ | |
| 334 | zCmd = "sync -autourl -R"; |
| 335 | collect_argument(&extra, "share-links",0); |
| 336 | collect_argument(&extra, "verbose","v"); |
| 337 | collect_argument(&extra, "unversioned","u"); |
| 338 | collect_argument(&extra, "all",0); |
| 339 | collect_argument(&extra, "quiet","q"); |
| 340 | collect_argument(&extra, "ping",0); |
| 341 | }else if( fossil_strcmp(zCmd, "test-integrity")==0 ){ |
| 342 | collect_argument(&extra, "db-only", "d"); |
| 343 | collect_argument(&extra, "parse", 0); |
| 344 | collect_argument(&extra, "quick", "q"); |
| 345 | zCmd = "test-integrity"; |
| @@ -465,11 +471,10 @@ | |
| 471 | " ORDER BY 1" |
| 472 | ); |
| 473 | } |
| 474 | db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1"); |
| 475 | while( db_step(&q)==SQLITE_ROW ){ |
| 476 | const char *zFilename = db_column_text(&q, 0); |
| 477 | const char *zInode = db_column_text(&q,2); |
| 478 | #if !USE_SEE |
| 479 | if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue; |
| 480 | #endif |
| @@ -502,18 +507,21 @@ | |
| 507 | if( !quiet || dryRunFlag ){ |
| 508 | fossil_print("%s\n", zSyscmd); |
| 509 | fflush(stdout); |
| 510 | } |
| 511 | rc = dryRunFlag ? 0 : fossil_system(zSyscmd); |
| 512 | if( rc ){ |
| 513 | if( stopOnError ){ |
| 514 | bHalted = 1; |
| 515 | break; |
| 516 | } |
| 517 | /* If there is an error, pause briefly, but do not stop. The brief |
| 518 | ** pause is so that if the prior command failed with Ctrl-C then there |
| 519 | ** will be time to stop the whole thing with a second Ctrl-C. */ |
| 520 | sqlite3_sleep(330); |
| 521 | } |
| 522 | fossil_free(zSyscmd); |
| 523 | } |
| 524 | db_finalize(&q); |
| 525 | |
| 526 | blob_reset(&extra); |
| 527 | |
| @@ -528,6 +536,11 @@ | |
| 536 | db_unprotect(PROTECT_CONFIG); |
| 537 | db_multi_exec("%s", zSql /*safe-for-%s*/ ); |
| 538 | db_protect_pop(); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | if( stopOnError && bHalted ){ |
| 543 | fossil_fatal("STOPPED: non-zero result code (%d) from\nSTOPPED: %s", |
| 544 | rc, zSyscmd); |
| 545 | } |
| 546 | } |
| 547 |
+1
-1
| --- src/backlink.c | ||
| +++ src/backlink.c | ||
| @@ -338,11 +338,11 @@ | ||
| 338 | 338 | blob_reset(&in); |
| 339 | 339 | } |
| 340 | 340 | |
| 341 | 341 | /* |
| 342 | 342 | ** Transform mimetype string into an integer code. |
| 343 | -** NOTE: In the sake of compatability empty string is parsed as MT_UNKNOWN; | |
| 343 | +** NOTE: In the sake of compatibility empty string is parsed as MT_UNKNOWN; | |
| 344 | 344 | ** it is yet unclear whether it can safely be changed to MT_NONE. |
| 345 | 345 | */ |
| 346 | 346 | int parse_mimetype(const char* zMimetype){ |
| 347 | 347 | if( zMimetype==0 ) return MT_NONE; |
| 348 | 348 | if( strstr(zMimetype,"wiki")!=0 ) return MT_WIKI; |
| 349 | 349 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -338,11 +338,11 @@ | |
| 338 | blob_reset(&in); |
| 339 | } |
| 340 | |
| 341 | /* |
| 342 | ** Transform mimetype string into an integer code. |
| 343 | ** NOTE: In the sake of compatability empty string is parsed as MT_UNKNOWN; |
| 344 | ** it is yet unclear whether it can safely be changed to MT_NONE. |
| 345 | */ |
| 346 | int parse_mimetype(const char* zMimetype){ |
| 347 | if( zMimetype==0 ) return MT_NONE; |
| 348 | if( strstr(zMimetype,"wiki")!=0 ) return MT_WIKI; |
| 349 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -338,11 +338,11 @@ | |
| 338 | blob_reset(&in); |
| 339 | } |
| 340 | |
| 341 | /* |
| 342 | ** Transform mimetype string into an integer code. |
| 343 | ** NOTE: In the sake of compatibility empty string is parsed as MT_UNKNOWN; |
| 344 | ** it is yet unclear whether it can safely be changed to MT_NONE. |
| 345 | */ |
| 346 | int parse_mimetype(const char* zMimetype){ |
| 347 | if( zMimetype==0 ) return MT_NONE; |
| 348 | if( strstr(zMimetype,"wiki")!=0 ) return MT_WIKI; |
| 349 |
+1
-1
| --- src/bisect.c | ||
| +++ src/bisect.c | ||
| @@ -171,11 +171,11 @@ | ||
| 171 | 171 | |
| 172 | 172 | /* |
| 173 | 173 | ** Append a new entry to the bisect log. Update the bisect-good or |
| 174 | 174 | ** bisect-bad values as appropriate. |
| 175 | 175 | ** |
| 176 | -** The bisect-log consists of a list of token. Each token is an | |
| 176 | +** The bisect-log consists of a list of tokens. Each token is an | |
| 177 | 177 | ** integer RID of a check-in. The RID is negative for "bad" check-ins |
| 178 | 178 | ** and positive for "good" check-ins. |
| 179 | 179 | */ |
| 180 | 180 | static void bisect_append_log(int rid){ |
| 181 | 181 | if( rid<0 ){ |
| 182 | 182 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -171,11 +171,11 @@ | |
| 171 | |
| 172 | /* |
| 173 | ** Append a new entry to the bisect log. Update the bisect-good or |
| 174 | ** bisect-bad values as appropriate. |
| 175 | ** |
| 176 | ** The bisect-log consists of a list of token. Each token is an |
| 177 | ** integer RID of a check-in. The RID is negative for "bad" check-ins |
| 178 | ** and positive for "good" check-ins. |
| 179 | */ |
| 180 | static void bisect_append_log(int rid){ |
| 181 | if( rid<0 ){ |
| 182 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -171,11 +171,11 @@ | |
| 171 | |
| 172 | /* |
| 173 | ** Append a new entry to the bisect log. Update the bisect-good or |
| 174 | ** bisect-bad values as appropriate. |
| 175 | ** |
| 176 | ** The bisect-log consists of a list of tokens. Each token is an |
| 177 | ** integer RID of a check-in. The RID is negative for "bad" check-ins |
| 178 | ** and positive for "good" check-ins. |
| 179 | */ |
| 180 | static void bisect_append_log(int rid){ |
| 181 | if( rid<0 ){ |
| 182 |
+18
-7
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -744,15 +744,26 @@ | ||
| 744 | 744 | void blob_rewind(Blob *p){ |
| 745 | 745 | p->iCursor = 0; |
| 746 | 746 | } |
| 747 | 747 | |
| 748 | 748 | /* |
| 749 | -** Truncate a blob back to zero length | |
| 749 | +** Truncate a blob to the specified length in bytes. | |
| 750 | 750 | */ |
| 751 | 751 | void blob_truncate(Blob *p, int sz){ |
| 752 | 752 | if( sz>=0 && sz<(int)(p->nUsed) ) p->nUsed = sz; |
| 753 | 753 | } |
| 754 | + | |
| 755 | +/* | |
| 756 | +** Truncate a blob to the specified length in bytes. If truncation | |
| 757 | +** results in an incomplete UTF-8 sequence at the end, remove up | |
| 758 | +** to three more bytes back to the last complete UTF-8 sequence. | |
| 759 | +*/ | |
| 760 | +void blob_truncate_utf8(Blob *p, int sz){ | |
| 761 | + if( sz>=0 && sz<(int)(p->nUsed) ){ | |
| 762 | + p->nUsed = utf8_nearest_codepoint(p->aData,sz); | |
| 763 | + } | |
| 764 | +} | |
| 754 | 765 | |
| 755 | 766 | /* |
| 756 | 767 | ** Seek the cursor in a blob to the indicated offset. |
| 757 | 768 | */ |
| 758 | 769 | int blob_seek(Blob *p, int offset, int whence){ |
| @@ -782,11 +793,11 @@ | ||
| 782 | 793 | ** end-of-file. |
| 783 | 794 | ** |
| 784 | 795 | ** The cursor of pFrom is left pointing at the first byte past the |
| 785 | 796 | ** \n that terminated the line. |
| 786 | 797 | ** |
| 787 | -** pTo will be an ephermeral blob. If pFrom changes, it might alter | |
| 798 | +** pTo will be an ephemeral blob. If pFrom changes, it might alter | |
| 788 | 799 | ** pTo as well. |
| 789 | 800 | */ |
| 790 | 801 | int blob_line(Blob *pFrom, Blob *pTo){ |
| 791 | 802 | char *aData = pFrom->aData; |
| 792 | 803 | int n = pFrom->nUsed; |
| @@ -825,11 +836,11 @@ | ||
| 825 | 836 | ** whitespace is ignored. |
| 826 | 837 | ** |
| 827 | 838 | ** The cursor of pFrom is left pointing at the first character past |
| 828 | 839 | ** the end of the token. |
| 829 | 840 | ** |
| 830 | -** pTo will be an ephermeral blob. If pFrom changes, it might alter | |
| 841 | +** pTo will be an ephemeral blob. If pFrom changes, it might alter | |
| 831 | 842 | ** pTo as well. |
| 832 | 843 | */ |
| 833 | 844 | int blob_token(Blob *pFrom, Blob *pTo){ |
| 834 | 845 | char *aData = pFrom->aData; |
| 835 | 846 | int n = pFrom->nUsed; |
| @@ -853,11 +864,11 @@ | ||
| 853 | 864 | ** (ignoring double '') or by the end of the string |
| 854 | 865 | ** |
| 855 | 866 | ** The cursor of pFrom is left pointing at the first character past |
| 856 | 867 | ** the end of the token. |
| 857 | 868 | ** |
| 858 | -** pTo will be an ephermeral blob. If pFrom changes, it might alter | |
| 869 | +** pTo will be an ephemeral blob. If pFrom changes, it might alter | |
| 859 | 870 | ** pTo as well. |
| 860 | 871 | */ |
| 861 | 872 | int blob_sqltoken(Blob *pFrom, Blob *pTo){ |
| 862 | 873 | char *aData = pFrom->aData; |
| 863 | 874 | int n = pFrom->nUsed; |
| @@ -881,11 +892,11 @@ | ||
| 881 | 892 | return pTo->nUsed; |
| 882 | 893 | } |
| 883 | 894 | |
| 884 | 895 | /* |
| 885 | 896 | ** Extract everything from the current cursor to the end of the blob |
| 886 | -** into a new blob. The new blob is an ephemerial reference to the | |
| 897 | +** into a new blob. The new blob is an ephemeral reference to the | |
| 887 | 898 | ** original blob. The cursor of the original blob is unchanged. |
| 888 | 899 | */ |
| 889 | 900 | int blob_tail(Blob *pFrom, Blob *pTo){ |
| 890 | 901 | int iCursor = pFrom->iCursor; |
| 891 | 902 | blob_extract(pFrom, pFrom->nUsed-pFrom->iCursor, pTo); |
| @@ -976,11 +987,11 @@ | ||
| 976 | 987 | ** Options: |
| 977 | 988 | ** -y|--side-by-side Show diff of INPUTFILE and output side-by-side |
| 978 | 989 | ** -W|--width N Width of lines in side-by-side diff |
| 979 | 990 | */ |
| 980 | 991 | void test_strip_comment_lines_cmd(void){ |
| 981 | - Blob f, h; /* unitialized */ | |
| 992 | + Blob f, h; /* uninitialized */ | |
| 982 | 993 | Blob out; |
| 983 | 994 | DiffConfig dCfg; |
| 984 | 995 | int sbs = 0; |
| 985 | 996 | const char *z; |
| 986 | 997 | int w = 0; |
| @@ -1806,11 +1817,11 @@ | ||
| 1806 | 1817 | ** unusual characters as an argument for testing. |
| 1807 | 1818 | ** |
| 1808 | 1819 | ** --compare HEX ASCII Verify that argument ASCII is identical to |
| 1809 | 1820 | ** to decoded HEX. |
| 1810 | 1821 | ** |
| 1811 | -** --fuzz N Run N fuzz cases. Each cases is a call | |
| 1822 | +** --fuzz N Run N fuzz cases. Each case is a call | |
| 1812 | 1823 | ** to "fossil test-escaped-arg --compare HEX ARG" |
| 1813 | 1824 | ** where HEX and ARG are the same argument. |
| 1814 | 1825 | ** The argument is chosen at random. |
| 1815 | 1826 | */ |
| 1816 | 1827 | void test_escaped_arg_command(void){ |
| 1817 | 1828 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -744,15 +744,26 @@ | |
| 744 | void blob_rewind(Blob *p){ |
| 745 | p->iCursor = 0; |
| 746 | } |
| 747 | |
| 748 | /* |
| 749 | ** Truncate a blob back to zero length |
| 750 | */ |
| 751 | void blob_truncate(Blob *p, int sz){ |
| 752 | if( sz>=0 && sz<(int)(p->nUsed) ) p->nUsed = sz; |
| 753 | } |
| 754 | |
| 755 | /* |
| 756 | ** Seek the cursor in a blob to the indicated offset. |
| 757 | */ |
| 758 | int blob_seek(Blob *p, int offset, int whence){ |
| @@ -782,11 +793,11 @@ | |
| 782 | ** end-of-file. |
| 783 | ** |
| 784 | ** The cursor of pFrom is left pointing at the first byte past the |
| 785 | ** \n that terminated the line. |
| 786 | ** |
| 787 | ** pTo will be an ephermeral blob. If pFrom changes, it might alter |
| 788 | ** pTo as well. |
| 789 | */ |
| 790 | int blob_line(Blob *pFrom, Blob *pTo){ |
| 791 | char *aData = pFrom->aData; |
| 792 | int n = pFrom->nUsed; |
| @@ -825,11 +836,11 @@ | |
| 825 | ** whitespace is ignored. |
| 826 | ** |
| 827 | ** The cursor of pFrom is left pointing at the first character past |
| 828 | ** the end of the token. |
| 829 | ** |
| 830 | ** pTo will be an ephermeral blob. If pFrom changes, it might alter |
| 831 | ** pTo as well. |
| 832 | */ |
| 833 | int blob_token(Blob *pFrom, Blob *pTo){ |
| 834 | char *aData = pFrom->aData; |
| 835 | int n = pFrom->nUsed; |
| @@ -853,11 +864,11 @@ | |
| 853 | ** (ignoring double '') or by the end of the string |
| 854 | ** |
| 855 | ** The cursor of pFrom is left pointing at the first character past |
| 856 | ** the end of the token. |
| 857 | ** |
| 858 | ** pTo will be an ephermeral blob. If pFrom changes, it might alter |
| 859 | ** pTo as well. |
| 860 | */ |
| 861 | int blob_sqltoken(Blob *pFrom, Blob *pTo){ |
| 862 | char *aData = pFrom->aData; |
| 863 | int n = pFrom->nUsed; |
| @@ -881,11 +892,11 @@ | |
| 881 | return pTo->nUsed; |
| 882 | } |
| 883 | |
| 884 | /* |
| 885 | ** Extract everything from the current cursor to the end of the blob |
| 886 | ** into a new blob. The new blob is an ephemerial reference to the |
| 887 | ** original blob. The cursor of the original blob is unchanged. |
| 888 | */ |
| 889 | int blob_tail(Blob *pFrom, Blob *pTo){ |
| 890 | int iCursor = pFrom->iCursor; |
| 891 | blob_extract(pFrom, pFrom->nUsed-pFrom->iCursor, pTo); |
| @@ -976,11 +987,11 @@ | |
| 976 | ** Options: |
| 977 | ** -y|--side-by-side Show diff of INPUTFILE and output side-by-side |
| 978 | ** -W|--width N Width of lines in side-by-side diff |
| 979 | */ |
| 980 | void test_strip_comment_lines_cmd(void){ |
| 981 | Blob f, h; /* unitialized */ |
| 982 | Blob out; |
| 983 | DiffConfig dCfg; |
| 984 | int sbs = 0; |
| 985 | const char *z; |
| 986 | int w = 0; |
| @@ -1806,11 +1817,11 @@ | |
| 1806 | ** unusual characters as an argument for testing. |
| 1807 | ** |
| 1808 | ** --compare HEX ASCII Verify that argument ASCII is identical to |
| 1809 | ** to decoded HEX. |
| 1810 | ** |
| 1811 | ** --fuzz N Run N fuzz cases. Each cases is a call |
| 1812 | ** to "fossil test-escaped-arg --compare HEX ARG" |
| 1813 | ** where HEX and ARG are the same argument. |
| 1814 | ** The argument is chosen at random. |
| 1815 | */ |
| 1816 | void test_escaped_arg_command(void){ |
| 1817 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -744,15 +744,26 @@ | |
| 744 | void blob_rewind(Blob *p){ |
| 745 | p->iCursor = 0; |
| 746 | } |
| 747 | |
| 748 | /* |
| 749 | ** Truncate a blob to the specified length in bytes. |
| 750 | */ |
| 751 | void blob_truncate(Blob *p, int sz){ |
| 752 | if( sz>=0 && sz<(int)(p->nUsed) ) p->nUsed = sz; |
| 753 | } |
| 754 | |
| 755 | /* |
| 756 | ** Truncate a blob to the specified length in bytes. If truncation |
| 757 | ** results in an incomplete UTF-8 sequence at the end, remove up |
| 758 | ** to three more bytes back to the last complete UTF-8 sequence. |
| 759 | */ |
| 760 | void blob_truncate_utf8(Blob *p, int sz){ |
| 761 | if( sz>=0 && sz<(int)(p->nUsed) ){ |
| 762 | p->nUsed = utf8_nearest_codepoint(p->aData,sz); |
| 763 | } |
| 764 | } |
| 765 | |
| 766 | /* |
| 767 | ** Seek the cursor in a blob to the indicated offset. |
| 768 | */ |
| 769 | int blob_seek(Blob *p, int offset, int whence){ |
| @@ -782,11 +793,11 @@ | |
| 793 | ** end-of-file. |
| 794 | ** |
| 795 | ** The cursor of pFrom is left pointing at the first byte past the |
| 796 | ** \n that terminated the line. |
| 797 | ** |
| 798 | ** pTo will be an ephemeral blob. If pFrom changes, it might alter |
| 799 | ** pTo as well. |
| 800 | */ |
| 801 | int blob_line(Blob *pFrom, Blob *pTo){ |
| 802 | char *aData = pFrom->aData; |
| 803 | int n = pFrom->nUsed; |
| @@ -825,11 +836,11 @@ | |
| 836 | ** whitespace is ignored. |
| 837 | ** |
| 838 | ** The cursor of pFrom is left pointing at the first character past |
| 839 | ** the end of the token. |
| 840 | ** |
| 841 | ** pTo will be an ephemeral blob. If pFrom changes, it might alter |
| 842 | ** pTo as well. |
| 843 | */ |
| 844 | int blob_token(Blob *pFrom, Blob *pTo){ |
| 845 | char *aData = pFrom->aData; |
| 846 | int n = pFrom->nUsed; |
| @@ -853,11 +864,11 @@ | |
| 864 | ** (ignoring double '') or by the end of the string |
| 865 | ** |
| 866 | ** The cursor of pFrom is left pointing at the first character past |
| 867 | ** the end of the token. |
| 868 | ** |
| 869 | ** pTo will be an ephemeral blob. If pFrom changes, it might alter |
| 870 | ** pTo as well. |
| 871 | */ |
| 872 | int blob_sqltoken(Blob *pFrom, Blob *pTo){ |
| 873 | char *aData = pFrom->aData; |
| 874 | int n = pFrom->nUsed; |
| @@ -881,11 +892,11 @@ | |
| 892 | return pTo->nUsed; |
| 893 | } |
| 894 | |
| 895 | /* |
| 896 | ** Extract everything from the current cursor to the end of the blob |
| 897 | ** into a new blob. The new blob is an ephemeral reference to the |
| 898 | ** original blob. The cursor of the original blob is unchanged. |
| 899 | */ |
| 900 | int blob_tail(Blob *pFrom, Blob *pTo){ |
| 901 | int iCursor = pFrom->iCursor; |
| 902 | blob_extract(pFrom, pFrom->nUsed-pFrom->iCursor, pTo); |
| @@ -976,11 +987,11 @@ | |
| 987 | ** Options: |
| 988 | ** -y|--side-by-side Show diff of INPUTFILE and output side-by-side |
| 989 | ** -W|--width N Width of lines in side-by-side diff |
| 990 | */ |
| 991 | void test_strip_comment_lines_cmd(void){ |
| 992 | Blob f, h; /* uninitialized */ |
| 993 | Blob out; |
| 994 | DiffConfig dCfg; |
| 995 | int sbs = 0; |
| 996 | const char *z; |
| 997 | int w = 0; |
| @@ -1806,11 +1817,11 @@ | |
| 1817 | ** unusual characters as an argument for testing. |
| 1818 | ** |
| 1819 | ** --compare HEX ASCII Verify that argument ASCII is identical to |
| 1820 | ** to decoded HEX. |
| 1821 | ** |
| 1822 | ** --fuzz N Run N fuzz cases. Each case is a call |
| 1823 | ** to "fossil test-escaped-arg --compare HEX ARG" |
| 1824 | ** where HEX and ARG are the same argument. |
| 1825 | ** The argument is chosen at random. |
| 1826 | */ |
| 1827 | void test_escaped_arg_command(void){ |
| 1828 |
+7
-7
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -232,11 +232,11 @@ | ||
| 232 | 232 | ** |
| 233 | 233 | ** name Name of the branch |
| 234 | 234 | ** mtime Time of last check-in on this branch |
| 235 | 235 | ** isclosed True if the branch is closed |
| 236 | 236 | ** mergeto Another branch this branch was merged into |
| 237 | -** nckin Number of checkins on this branch | |
| 237 | +** nckin Number of check-ins on this branch | |
| 238 | 238 | ** ckin Hash of the last check-in on this branch |
| 239 | 239 | ** isprivate True if the branch is private |
| 240 | 240 | ** bgclr Background color for this branch |
| 241 | 241 | */ |
| 242 | 242 | static const char createBrlistQuery[] = |
| @@ -558,11 +558,11 @@ | ||
| 558 | 558 | } |
| 559 | 559 | |
| 560 | 560 | /* |
| 561 | 561 | ** Implementation of (branch close|reopen) subcommands. nStartAtArg is |
| 562 | 562 | ** the g.argv index to start reading branch/check-in names. The given |
| 563 | -** checkins are closed if fClose is true, else their "closed" tag (if | |
| 563 | +** check-ins are closed if fClose is true, else their "closed" tag (if | |
| 564 | 564 | ** any) is cancelled. Fails fatally on error. |
| 565 | 565 | */ |
| 566 | 566 | static void branch_cmd_close(int nStartAtArg, int fClose){ |
| 567 | 567 | int argPos = nStartAtArg; /* g.argv pos with first branch name */ |
| 568 | 568 | char * zUuid = 0; /* Resolved branch UUID. */ |
| @@ -1116,17 +1116,17 @@ | ||
| 1116 | 1116 | style_finish_page(); |
| 1117 | 1117 | } |
| 1118 | 1118 | |
| 1119 | 1119 | /* |
| 1120 | 1120 | ** Generate a multichoice submenu for the few recent active branches. zName is |
| 1121 | -** the query parameter used to select the current checkin. zCI is optional and | |
| 1122 | -** represent the currently selected checkin, so if it is a checkin hash | |
| 1121 | +** the query parameter used to select the current check-in. zCI is optional and | |
| 1122 | +** represent the currently selected check-in, so if it is a check-in hash | |
| 1123 | 1123 | ** instead of a branch, it can be part of the multichoice menu. |
| 1124 | 1124 | */ |
| 1125 | 1125 | void generate_branch_submenu_multichoice( |
| 1126 | 1126 | const char* zName, /* Query parameter name */ |
| 1127 | - const char* zCI /* Current checkin */ | |
| 1127 | + const char* zCI /* Current check-in */ | |
| 1128 | 1128 | ){ |
| 1129 | 1129 | Stmt q; |
| 1130 | 1130 | const int brFlags = BRL_ORDERBY_MTIME | BRL_OPEN_ONLY; |
| 1131 | 1131 | static const char *zBranchMenuList[32*2]; /* 2 per entries */ |
| 1132 | 1132 | const int nLimit = count(zBranchMenuList)/2; |
| @@ -1134,18 +1134,18 @@ | ||
| 1134 | 1134 | |
| 1135 | 1135 | if( zName == 0 ) zName = "ci"; |
| 1136 | 1136 | |
| 1137 | 1137 | branch_prepare_list_query(&q, brFlags, 0, nLimit, 0); |
| 1138 | 1138 | zBranchMenuList[i++] = ""; |
| 1139 | - zBranchMenuList[i++] = "All Checkins"; | |
| 1139 | + zBranchMenuList[i++] = "All Check-ins"; | |
| 1140 | 1140 | |
| 1141 | 1141 | if( zCI ){ |
| 1142 | 1142 | zCI = fossil_strdup(zCI); |
| 1143 | 1143 | zBranchMenuList[i++] = zCI; |
| 1144 | 1144 | zBranchMenuList[i++] = zCI; |
| 1145 | 1145 | } |
| 1146 | - /* If current checkin is not "tip", add it to the list */ | |
| 1146 | + /* If current check-in is not "tip", add it to the list */ | |
| 1147 | 1147 | if( zCI==0 || strcmp(zCI, "tip") ){ |
| 1148 | 1148 | zBranchMenuList[i++] = "tip"; |
| 1149 | 1149 | zBranchMenuList[i++] = "tip"; |
| 1150 | 1150 | } |
| 1151 | 1151 | while( i/2 < nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1152 | 1152 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -232,11 +232,11 @@ | |
| 232 | ** |
| 233 | ** name Name of the branch |
| 234 | ** mtime Time of last check-in on this branch |
| 235 | ** isclosed True if the branch is closed |
| 236 | ** mergeto Another branch this branch was merged into |
| 237 | ** nckin Number of checkins on this branch |
| 238 | ** ckin Hash of the last check-in on this branch |
| 239 | ** isprivate True if the branch is private |
| 240 | ** bgclr Background color for this branch |
| 241 | */ |
| 242 | static const char createBrlistQuery[] = |
| @@ -558,11 +558,11 @@ | |
| 558 | } |
| 559 | |
| 560 | /* |
| 561 | ** Implementation of (branch close|reopen) subcommands. nStartAtArg is |
| 562 | ** the g.argv index to start reading branch/check-in names. The given |
| 563 | ** checkins are closed if fClose is true, else their "closed" tag (if |
| 564 | ** any) is cancelled. Fails fatally on error. |
| 565 | */ |
| 566 | static void branch_cmd_close(int nStartAtArg, int fClose){ |
| 567 | int argPos = nStartAtArg; /* g.argv pos with first branch name */ |
| 568 | char * zUuid = 0; /* Resolved branch UUID. */ |
| @@ -1116,17 +1116,17 @@ | |
| 1116 | style_finish_page(); |
| 1117 | } |
| 1118 | |
| 1119 | /* |
| 1120 | ** Generate a multichoice submenu for the few recent active branches. zName is |
| 1121 | ** the query parameter used to select the current checkin. zCI is optional and |
| 1122 | ** represent the currently selected checkin, so if it is a checkin hash |
| 1123 | ** instead of a branch, it can be part of the multichoice menu. |
| 1124 | */ |
| 1125 | void generate_branch_submenu_multichoice( |
| 1126 | const char* zName, /* Query parameter name */ |
| 1127 | const char* zCI /* Current checkin */ |
| 1128 | ){ |
| 1129 | Stmt q; |
| 1130 | const int brFlags = BRL_ORDERBY_MTIME | BRL_OPEN_ONLY; |
| 1131 | static const char *zBranchMenuList[32*2]; /* 2 per entries */ |
| 1132 | const int nLimit = count(zBranchMenuList)/2; |
| @@ -1134,18 +1134,18 @@ | |
| 1134 | |
| 1135 | if( zName == 0 ) zName = "ci"; |
| 1136 | |
| 1137 | branch_prepare_list_query(&q, brFlags, 0, nLimit, 0); |
| 1138 | zBranchMenuList[i++] = ""; |
| 1139 | zBranchMenuList[i++] = "All Checkins"; |
| 1140 | |
| 1141 | if( zCI ){ |
| 1142 | zCI = fossil_strdup(zCI); |
| 1143 | zBranchMenuList[i++] = zCI; |
| 1144 | zBranchMenuList[i++] = zCI; |
| 1145 | } |
| 1146 | /* If current checkin is not "tip", add it to the list */ |
| 1147 | if( zCI==0 || strcmp(zCI, "tip") ){ |
| 1148 | zBranchMenuList[i++] = "tip"; |
| 1149 | zBranchMenuList[i++] = "tip"; |
| 1150 | } |
| 1151 | while( i/2 < nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1152 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -232,11 +232,11 @@ | |
| 232 | ** |
| 233 | ** name Name of the branch |
| 234 | ** mtime Time of last check-in on this branch |
| 235 | ** isclosed True if the branch is closed |
| 236 | ** mergeto Another branch this branch was merged into |
| 237 | ** nckin Number of check-ins on this branch |
| 238 | ** ckin Hash of the last check-in on this branch |
| 239 | ** isprivate True if the branch is private |
| 240 | ** bgclr Background color for this branch |
| 241 | */ |
| 242 | static const char createBrlistQuery[] = |
| @@ -558,11 +558,11 @@ | |
| 558 | } |
| 559 | |
| 560 | /* |
| 561 | ** Implementation of (branch close|reopen) subcommands. nStartAtArg is |
| 562 | ** the g.argv index to start reading branch/check-in names. The given |
| 563 | ** check-ins are closed if fClose is true, else their "closed" tag (if |
| 564 | ** any) is cancelled. Fails fatally on error. |
| 565 | */ |
| 566 | static void branch_cmd_close(int nStartAtArg, int fClose){ |
| 567 | int argPos = nStartAtArg; /* g.argv pos with first branch name */ |
| 568 | char * zUuid = 0; /* Resolved branch UUID. */ |
| @@ -1116,17 +1116,17 @@ | |
| 1116 | style_finish_page(); |
| 1117 | } |
| 1118 | |
| 1119 | /* |
| 1120 | ** Generate a multichoice submenu for the few recent active branches. zName is |
| 1121 | ** the query parameter used to select the current check-in. zCI is optional and |
| 1122 | ** represent the currently selected check-in, so if it is a check-in hash |
| 1123 | ** instead of a branch, it can be part of the multichoice menu. |
| 1124 | */ |
| 1125 | void generate_branch_submenu_multichoice( |
| 1126 | const char* zName, /* Query parameter name */ |
| 1127 | const char* zCI /* Current check-in */ |
| 1128 | ){ |
| 1129 | Stmt q; |
| 1130 | const int brFlags = BRL_ORDERBY_MTIME | BRL_OPEN_ONLY; |
| 1131 | static const char *zBranchMenuList[32*2]; /* 2 per entries */ |
| 1132 | const int nLimit = count(zBranchMenuList)/2; |
| @@ -1134,18 +1134,18 @@ | |
| 1134 | |
| 1135 | if( zName == 0 ) zName = "ci"; |
| 1136 | |
| 1137 | branch_prepare_list_query(&q, brFlags, 0, nLimit, 0); |
| 1138 | zBranchMenuList[i++] = ""; |
| 1139 | zBranchMenuList[i++] = "All Check-ins"; |
| 1140 | |
| 1141 | if( zCI ){ |
| 1142 | zCI = fossil_strdup(zCI); |
| 1143 | zBranchMenuList[i++] = zCI; |
| 1144 | zBranchMenuList[i++] = zCI; |
| 1145 | } |
| 1146 | /* If current check-in is not "tip", add it to the list */ |
| 1147 | if( zCI==0 || strcmp(zCI, "tip") ){ |
| 1148 | zBranchMenuList[i++] = "tip"; |
| 1149 | zBranchMenuList[i++] = "tip"; |
| 1150 | } |
| 1151 | while( i/2 < nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1152 |
+1
-1
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -735,11 +735,11 @@ | ||
| 735 | 735 | } fjs[] = { |
| 736 | 736 | /* This list ordering isn't strictly important. */ |
| 737 | 737 | {"confirmer", 0, 0}, |
| 738 | 738 | {"copybutton", 0, "dom\0"}, |
| 739 | 739 | {"diff", 0, "dom\0fetch\0storage\0" |
| 740 | - /* maintenance note: "diff" needs "storage" for storing the the | |
| 740 | + /* maintenance note: "diff" needs "storage" for storing the | |
| 741 | 741 | ** sbs-sync-scroll toggle. */}, |
| 742 | 742 | {"dom", 0, 0}, |
| 743 | 743 | {"fetch", 0, 0}, |
| 744 | 744 | {"numbered-lines", 0, "popupwidget\0copybutton\0"}, |
| 745 | 745 | {"pikchr", 0, "dom\0"}, |
| 746 | 746 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -735,11 +735,11 @@ | |
| 735 | } fjs[] = { |
| 736 | /* This list ordering isn't strictly important. */ |
| 737 | {"confirmer", 0, 0}, |
| 738 | {"copybutton", 0, "dom\0"}, |
| 739 | {"diff", 0, "dom\0fetch\0storage\0" |
| 740 | /* maintenance note: "diff" needs "storage" for storing the the |
| 741 | ** sbs-sync-scroll toggle. */}, |
| 742 | {"dom", 0, 0}, |
| 743 | {"fetch", 0, 0}, |
| 744 | {"numbered-lines", 0, "popupwidget\0copybutton\0"}, |
| 745 | {"pikchr", 0, "dom\0"}, |
| 746 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -735,11 +735,11 @@ | |
| 735 | } fjs[] = { |
| 736 | /* This list ordering isn't strictly important. */ |
| 737 | {"confirmer", 0, 0}, |
| 738 | {"copybutton", 0, "dom\0"}, |
| 739 | {"diff", 0, "dom\0fetch\0storage\0" |
| 740 | /* maintenance note: "diff" needs "storage" for storing the |
| 741 | ** sbs-sync-scroll toggle. */}, |
| 742 | {"dom", 0, 0}, |
| 743 | {"fetch", 0, 0}, |
| 744 | {"numbered-lines", 0, "popupwidget\0copybutton\0"}, |
| 745 | {"pikchr", 0, "dom\0"}, |
| 746 |
+2
-2
| --- src/cache.c | ||
| +++ src/cache.c | ||
| @@ -70,11 +70,11 @@ | ||
| 70 | 70 | "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);" |
| 71 | 71 | "CREATE TABLE IF NOT EXISTS cache(" |
| 72 | 72 | "key TEXT PRIMARY KEY," /* Key used to access the cache */ |
| 73 | 73 | "id INT REFERENCES blob," /* The cache content */ |
| 74 | 74 | "sz INT," /* Size of content in bytes */ |
| 75 | - "tm INT," /* Last access time (unix timestampe) */ | |
| 75 | + "tm INT," /* Last access time (unix timestamp) */ | |
| 76 | 76 | "nref INT" /* Number of uses */ |
| 77 | 77 | ");" |
| 78 | 78 | "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN" |
| 79 | 79 | " DELETE FROM blob WHERE id=OLD.id;" |
| 80 | 80 | "END;", |
| @@ -437,11 +437,11 @@ | ||
| 437 | 437 | @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br> |
| 438 | 438 | @ size: %,lld(sqlite3_column_int64(pStmt,1)), |
| 439 | 439 | @ hit-count: %d(sqlite3_column_int(pStmt,2)), |
| 440 | 440 | @ last-access: %s(sqlite3_column_text(pStmt,3))Z \ |
| 441 | 441 | if( zHash ){ |
| 442 | - @ → %z(href("%R/timeline?c=%S",zHash))checkin info</a>\ | |
| 442 | + @ → %z(href("%R/timeline?c=%S",zHash))check-in info</a>\ | |
| 443 | 443 | fossil_free(zHash); |
| 444 | 444 | } |
| 445 | 445 | @ </p></li> |
| 446 | 446 | nEntry++; |
| 447 | 447 | } |
| 448 | 448 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -70,11 +70,11 @@ | |
| 70 | "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);" |
| 71 | "CREATE TABLE IF NOT EXISTS cache(" |
| 72 | "key TEXT PRIMARY KEY," /* Key used to access the cache */ |
| 73 | "id INT REFERENCES blob," /* The cache content */ |
| 74 | "sz INT," /* Size of content in bytes */ |
| 75 | "tm INT," /* Last access time (unix timestampe) */ |
| 76 | "nref INT" /* Number of uses */ |
| 77 | ");" |
| 78 | "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN" |
| 79 | " DELETE FROM blob WHERE id=OLD.id;" |
| 80 | "END;", |
| @@ -437,11 +437,11 @@ | |
| 437 | @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br> |
| 438 | @ size: %,lld(sqlite3_column_int64(pStmt,1)), |
| 439 | @ hit-count: %d(sqlite3_column_int(pStmt,2)), |
| 440 | @ last-access: %s(sqlite3_column_text(pStmt,3))Z \ |
| 441 | if( zHash ){ |
| 442 | @ → %z(href("%R/timeline?c=%S",zHash))checkin info</a>\ |
| 443 | fossil_free(zHash); |
| 444 | } |
| 445 | @ </p></li> |
| 446 | nEntry++; |
| 447 | } |
| 448 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -70,11 +70,11 @@ | |
| 70 | "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);" |
| 71 | "CREATE TABLE IF NOT EXISTS cache(" |
| 72 | "key TEXT PRIMARY KEY," /* Key used to access the cache */ |
| 73 | "id INT REFERENCES blob," /* The cache content */ |
| 74 | "sz INT," /* Size of content in bytes */ |
| 75 | "tm INT," /* Last access time (unix timestamp) */ |
| 76 | "nref INT" /* Number of uses */ |
| 77 | ");" |
| 78 | "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN" |
| 79 | " DELETE FROM blob WHERE id=OLD.id;" |
| 80 | "END;", |
| @@ -437,11 +437,11 @@ | |
| 437 | @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br> |
| 438 | @ size: %,lld(sqlite3_column_int64(pStmt,1)), |
| 439 | @ hit-count: %d(sqlite3_column_int(pStmt,2)), |
| 440 | @ last-access: %s(sqlite3_column_text(pStmt,3))Z \ |
| 441 | if( zHash ){ |
| 442 | @ → %z(href("%R/timeline?c=%S",zHash))check-in info</a>\ |
| 443 | fossil_free(zHash); |
| 444 | } |
| 445 | @ </p></li> |
| 446 | nEntry++; |
| 447 | } |
| 448 |
+3
-3
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -509,11 +509,11 @@ | ||
| 509 | 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | 510 | x &= 0x7fffffff; |
| 511 | 511 | return x; |
| 512 | 512 | } |
| 513 | 513 | |
| 514 | -/* The SQL that will rotate the the captcha-secret. */ | |
| 514 | +/* The SQL that will rotate the captcha-secret. */ | |
| 515 | 515 | static const char captchaSecretRotationSql[] = |
| 516 | 516 | @ SAVEPOINT rotate; |
| 517 | 517 | @ DELETE FROM config |
| 518 | 518 | @ WHERE name GLOB 'captcha-secret-*' |
| 519 | 519 | @ AND mtime<unixepoch('now','-6 hours'); |
| @@ -529,11 +529,11 @@ | ||
| 529 | 529 | ; |
| 530 | 530 | |
| 531 | 531 | |
| 532 | 532 | /* |
| 533 | 533 | ** Create a new random captcha-secret. Rotate the old one into |
| 534 | -** the captcha-secret-N backups. Purge captch-secret-N backups | |
| 534 | +** the captcha-secret-N backups. Purge captcha-secret-N backups | |
| 535 | 535 | ** older than 6 hours. |
| 536 | 536 | ** |
| 537 | 537 | ** Do this on the current database and in all other databases of |
| 538 | 538 | ** the same login group. |
| 539 | 539 | */ |
| @@ -559,11 +559,11 @@ | ||
| 559 | 559 | fossil_free(zSql); |
| 560 | 560 | } |
| 561 | 561 | |
| 562 | 562 | /* |
| 563 | 563 | ** Return the value of the N-th more recent captcha-secret. The |
| 564 | -** most recent captch-secret is 0. Others are prior captcha-secrets | |
| 564 | +** most recent captcha-secret is 0. Others are prior captcha-secrets | |
| 565 | 565 | ** that have expired, but are retained for a limited period of time |
| 566 | 566 | ** so that pending anonymous login cookies and/or captcha dialogs |
| 567 | 567 | ** don't malfunction when the captcha-secret changes. |
| 568 | 568 | ** |
| 569 | 569 | ** Clients should start by using the 0-th captcha-secret. Only if |
| 570 | 570 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -509,11 +509,11 @@ | |
| 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | x &= 0x7fffffff; |
| 511 | return x; |
| 512 | } |
| 513 | |
| 514 | /* The SQL that will rotate the the captcha-secret. */ |
| 515 | static const char captchaSecretRotationSql[] = |
| 516 | @ SAVEPOINT rotate; |
| 517 | @ DELETE FROM config |
| 518 | @ WHERE name GLOB 'captcha-secret-*' |
| 519 | @ AND mtime<unixepoch('now','-6 hours'); |
| @@ -529,11 +529,11 @@ | |
| 529 | ; |
| 530 | |
| 531 | |
| 532 | /* |
| 533 | ** Create a new random captcha-secret. Rotate the old one into |
| 534 | ** the captcha-secret-N backups. Purge captch-secret-N backups |
| 535 | ** older than 6 hours. |
| 536 | ** |
| 537 | ** Do this on the current database and in all other databases of |
| 538 | ** the same login group. |
| 539 | */ |
| @@ -559,11 +559,11 @@ | |
| 559 | fossil_free(zSql); |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | ** Return the value of the N-th more recent captcha-secret. The |
| 564 | ** most recent captch-secret is 0. Others are prior captcha-secrets |
| 565 | ** that have expired, but are retained for a limited period of time |
| 566 | ** so that pending anonymous login cookies and/or captcha dialogs |
| 567 | ** don't malfunction when the captcha-secret changes. |
| 568 | ** |
| 569 | ** Clients should start by using the 0-th captcha-secret. Only if |
| 570 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -509,11 +509,11 @@ | |
| 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | x &= 0x7fffffff; |
| 511 | return x; |
| 512 | } |
| 513 | |
| 514 | /* The SQL that will rotate the captcha-secret. */ |
| 515 | static const char captchaSecretRotationSql[] = |
| 516 | @ SAVEPOINT rotate; |
| 517 | @ DELETE FROM config |
| 518 | @ WHERE name GLOB 'captcha-secret-*' |
| 519 | @ AND mtime<unixepoch('now','-6 hours'); |
| @@ -529,11 +529,11 @@ | |
| 529 | ; |
| 530 | |
| 531 | |
| 532 | /* |
| 533 | ** Create a new random captcha-secret. Rotate the old one into |
| 534 | ** the captcha-secret-N backups. Purge captcha-secret-N backups |
| 535 | ** older than 6 hours. |
| 536 | ** |
| 537 | ** Do this on the current database and in all other databases of |
| 538 | ** the same login group. |
| 539 | */ |
| @@ -559,11 +559,11 @@ | |
| 559 | fossil_free(zSql); |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | ** Return the value of the N-th more recent captcha-secret. The |
| 564 | ** most recent captcha-secret is 0. Others are prior captcha-secrets |
| 565 | ** that have expired, but are retained for a limited period of time |
| 566 | ** so that pending anonymous login cookies and/or captcha dialogs |
| 567 | ** don't malfunction when the captcha-secret changes. |
| 568 | ** |
| 569 | ** Clients should start by using the 0-th captcha-secret. Only if |
| 570 |
+7
-7
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -13,11 +13,11 @@ | ||
| 13 | 13 | ** [email protected] |
| 14 | 14 | ** http://www.hwaci.com/drh/ |
| 15 | 15 | ** |
| 16 | 16 | ******************************************************************************* |
| 17 | 17 | ** |
| 18 | -** This file began as a set of C functions and procedures used intepret | |
| 18 | +** This file began as a set of C functions and procedures used to interpret | |
| 19 | 19 | ** CGI environment variables for Fossil web pages that were invoked by |
| 20 | 20 | ** CGI. That's where the file name comes from. But over the years it |
| 21 | 21 | ** has grown to incorporate lots of related functionality, including: |
| 22 | 22 | ** |
| 23 | 23 | ** * Interpreting CGI environment variables when Fossil is run as |
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** |
| 147 | 147 | ** Do not confuse the content header with the HTTP header. The content header |
| 148 | 148 | ** is generated by downstream code. The HTTP header is generated by the |
| 149 | 149 | ** cgi_reply() routine below. |
| 150 | 150 | ** |
| 151 | -** The content header and contenty body are *approximately* the <head> | |
| 151 | +** The content header and content body are *approximately* the <head> | |
| 152 | 152 | ** element and the <body> elements for HTML replies. However this is only |
| 153 | 153 | ** approximate. The content header also includes parts of <body> that |
| 154 | 154 | ** show the banner and menu bar at the top of each page. Also note that |
| 155 | 155 | ** not all replies are HTML, but there can still be separate header and |
| 156 | 156 | ** body sections of the content. |
| @@ -942,12 +942,12 @@ | ||
| 942 | 942 | } |
| 943 | 943 | return cnt; |
| 944 | 944 | } |
| 945 | 945 | |
| 946 | 946 | /* |
| 947 | -** Add an environment varaible value to the parameter set. The zName | |
| 948 | -** portion is fixed but a copy is be made of zValue. | |
| 947 | +** Add an environment variable value to the parameter set. The zName | |
| 948 | +** portion is fixed but a copy is made of zValue. | |
| 949 | 949 | */ |
| 950 | 950 | void cgi_setenv(const char *zName, const char *zValue){ |
| 951 | 951 | cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0); |
| 952 | 952 | } |
| 953 | 953 | |
| @@ -968,11 +968,11 @@ | ||
| 968 | 968 | ** Each parameter is of the form NAME=VALUE. Both the NAME and the |
| 969 | 969 | ** VALUE may be url-encoded ("+" for space, "%HH" for other special |
| 970 | 970 | ** characters). But this routine assumes that NAME contains no |
| 971 | 971 | ** special character and therefore does not decode it. |
| 972 | 972 | ** |
| 973 | -** If NAME begins with another other than a lower-case letter then | |
| 973 | +** If NAME begins with something other than a lower-case letter then | |
| 974 | 974 | ** the entire NAME=VALUE term is ignored. Hence: |
| 975 | 975 | ** |
| 976 | 976 | ** * cookies and query parameters that have uppercase names |
| 977 | 977 | ** are ignored. |
| 978 | 978 | ** |
| @@ -1682,11 +1682,11 @@ | ||
| 1682 | 1682 | ** be extended in the future. |
| 1683 | 1683 | ** |
| 1684 | 1684 | ** Checks are omitted for any logged-in user. |
| 1685 | 1685 | ** |
| 1686 | 1686 | ** This is the primary defense against attack. Fossil should easily be |
| 1687 | -** proof against SQL injection and XSS attacks even without without this | |
| 1687 | +** proof against SQL injection and XSS attacks even without this | |
| 1688 | 1688 | ** routine. Rather, this is an attempt to avoid denial-of-service caused |
| 1689 | 1689 | ** by persistent spiders that hammer the server with dozens or hundreds of |
| 1690 | 1690 | ** probes per seconds as they look for vulnerabilities. In other |
| 1691 | 1691 | ** words, this is an effort to reduce the CPU load imposed by malicious |
| 1692 | 1692 | ** spiders. Though those routine might help make attacks harder, it is |
| @@ -2830,11 +2830,11 @@ | ||
| 2830 | 2830 | if( fossil_system(zBrowser)<0 ){ |
| 2831 | 2831 | fossil_warning("cannot start browser: %s\n", zBrowser); |
| 2832 | 2832 | } |
| 2833 | 2833 | } |
| 2834 | 2834 | |
| 2835 | - /* What for incomming requests. For each request, fork() a child process | |
| 2835 | + /* What for incoming requests. For each request, fork() a child process | |
| 2836 | 2836 | ** to deal with that request. The child process returns. The parent |
| 2837 | 2837 | ** keeps on listening and never returns. |
| 2838 | 2838 | */ |
| 2839 | 2839 | while( 1 ){ |
| 2840 | 2840 | #if FOSSIL_MAX_CONNECTIONS>0 |
| 2841 | 2841 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -13,11 +13,11 @@ | |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file began as a set of C functions and procedures used intepret |
| 19 | ** CGI environment variables for Fossil web pages that were invoked by |
| 20 | ** CGI. That's where the file name comes from. But over the years it |
| 21 | ** has grown to incorporate lots of related functionality, including: |
| 22 | ** |
| 23 | ** * Interpreting CGI environment variables when Fossil is run as |
| @@ -146,11 +146,11 @@ | |
| 146 | ** |
| 147 | ** Do not confuse the content header with the HTTP header. The content header |
| 148 | ** is generated by downstream code. The HTTP header is generated by the |
| 149 | ** cgi_reply() routine below. |
| 150 | ** |
| 151 | ** The content header and contenty body are *approximately* the <head> |
| 152 | ** element and the <body> elements for HTML replies. However this is only |
| 153 | ** approximate. The content header also includes parts of <body> that |
| 154 | ** show the banner and menu bar at the top of each page. Also note that |
| 155 | ** not all replies are HTML, but there can still be separate header and |
| 156 | ** body sections of the content. |
| @@ -942,12 +942,12 @@ | |
| 942 | } |
| 943 | return cnt; |
| 944 | } |
| 945 | |
| 946 | /* |
| 947 | ** Add an environment varaible value to the parameter set. The zName |
| 948 | ** portion is fixed but a copy is be made of zValue. |
| 949 | */ |
| 950 | void cgi_setenv(const char *zName, const char *zValue){ |
| 951 | cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0); |
| 952 | } |
| 953 | |
| @@ -968,11 +968,11 @@ | |
| 968 | ** Each parameter is of the form NAME=VALUE. Both the NAME and the |
| 969 | ** VALUE may be url-encoded ("+" for space, "%HH" for other special |
| 970 | ** characters). But this routine assumes that NAME contains no |
| 971 | ** special character and therefore does not decode it. |
| 972 | ** |
| 973 | ** If NAME begins with another other than a lower-case letter then |
| 974 | ** the entire NAME=VALUE term is ignored. Hence: |
| 975 | ** |
| 976 | ** * cookies and query parameters that have uppercase names |
| 977 | ** are ignored. |
| 978 | ** |
| @@ -1682,11 +1682,11 @@ | |
| 1682 | ** be extended in the future. |
| 1683 | ** |
| 1684 | ** Checks are omitted for any logged-in user. |
| 1685 | ** |
| 1686 | ** This is the primary defense against attack. Fossil should easily be |
| 1687 | ** proof against SQL injection and XSS attacks even without without this |
| 1688 | ** routine. Rather, this is an attempt to avoid denial-of-service caused |
| 1689 | ** by persistent spiders that hammer the server with dozens or hundreds of |
| 1690 | ** probes per seconds as they look for vulnerabilities. In other |
| 1691 | ** words, this is an effort to reduce the CPU load imposed by malicious |
| 1692 | ** spiders. Though those routine might help make attacks harder, it is |
| @@ -2830,11 +2830,11 @@ | |
| 2830 | if( fossil_system(zBrowser)<0 ){ |
| 2831 | fossil_warning("cannot start browser: %s\n", zBrowser); |
| 2832 | } |
| 2833 | } |
| 2834 | |
| 2835 | /* What for incomming requests. For each request, fork() a child process |
| 2836 | ** to deal with that request. The child process returns. The parent |
| 2837 | ** keeps on listening and never returns. |
| 2838 | */ |
| 2839 | while( 1 ){ |
| 2840 | #if FOSSIL_MAX_CONNECTIONS>0 |
| 2841 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -13,11 +13,11 @@ | |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file began as a set of C functions and procedures used to interpret |
| 19 | ** CGI environment variables for Fossil web pages that were invoked by |
| 20 | ** CGI. That's where the file name comes from. But over the years it |
| 21 | ** has grown to incorporate lots of related functionality, including: |
| 22 | ** |
| 23 | ** * Interpreting CGI environment variables when Fossil is run as |
| @@ -146,11 +146,11 @@ | |
| 146 | ** |
| 147 | ** Do not confuse the content header with the HTTP header. The content header |
| 148 | ** is generated by downstream code. The HTTP header is generated by the |
| 149 | ** cgi_reply() routine below. |
| 150 | ** |
| 151 | ** The content header and content body are *approximately* the <head> |
| 152 | ** element and the <body> elements for HTML replies. However this is only |
| 153 | ** approximate. The content header also includes parts of <body> that |
| 154 | ** show the banner and menu bar at the top of each page. Also note that |
| 155 | ** not all replies are HTML, but there can still be separate header and |
| 156 | ** body sections of the content. |
| @@ -942,12 +942,12 @@ | |
| 942 | } |
| 943 | return cnt; |
| 944 | } |
| 945 | |
| 946 | /* |
| 947 | ** Add an environment variable value to the parameter set. The zName |
| 948 | ** portion is fixed but a copy is made of zValue. |
| 949 | */ |
| 950 | void cgi_setenv(const char *zName, const char *zValue){ |
| 951 | cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0); |
| 952 | } |
| 953 | |
| @@ -968,11 +968,11 @@ | |
| 968 | ** Each parameter is of the form NAME=VALUE. Both the NAME and the |
| 969 | ** VALUE may be url-encoded ("+" for space, "%HH" for other special |
| 970 | ** characters). But this routine assumes that NAME contains no |
| 971 | ** special character and therefore does not decode it. |
| 972 | ** |
| 973 | ** If NAME begins with something other than a lower-case letter then |
| 974 | ** the entire NAME=VALUE term is ignored. Hence: |
| 975 | ** |
| 976 | ** * cookies and query parameters that have uppercase names |
| 977 | ** are ignored. |
| 978 | ** |
| @@ -1682,11 +1682,11 @@ | |
| 1682 | ** be extended in the future. |
| 1683 | ** |
| 1684 | ** Checks are omitted for any logged-in user. |
| 1685 | ** |
| 1686 | ** This is the primary defense against attack. Fossil should easily be |
| 1687 | ** proof against SQL injection and XSS attacks even without this |
| 1688 | ** routine. Rather, this is an attempt to avoid denial-of-service caused |
| 1689 | ** by persistent spiders that hammer the server with dozens or hundreds of |
| 1690 | ** probes per seconds as they look for vulnerabilities. In other |
| 1691 | ** words, this is an effort to reduce the CPU load imposed by malicious |
| 1692 | ** spiders. Though those routine might help make attacks harder, it is |
| @@ -2830,11 +2830,11 @@ | |
| 2830 | if( fossil_system(zBrowser)<0 ){ |
| 2831 | fossil_warning("cannot start browser: %s\n", zBrowser); |
| 2832 | } |
| 2833 | } |
| 2834 | |
| 2835 | /* What for incoming requests. For each request, fork() a child process |
| 2836 | ** to deal with that request. The child process returns. The parent |
| 2837 | ** keeps on listening and never returns. |
| 2838 | */ |
| 2839 | while( 1 ){ |
| 2840 | #if FOSSIL_MAX_CONNECTIONS>0 |
| 2841 |
+1
-1
| --- src/chat.c | ||
| +++ src/chat.c | ||
| @@ -443,11 +443,11 @@ | ||
| 443 | 443 | const char *zUserName; |
| 444 | 444 | login_check_credentials(); |
| 445 | 445 | if( 0==g.perm.Chat ) { |
| 446 | 446 | chat_emit_permissions_error(0); |
| 447 | 447 | return; |
| 448 | - }else if( 0==cgi_csrf_safe(1) ){ | |
| 448 | + }else if( g.eAuthMethod==AUTH_COOKIE && 0==cgi_csrf_safe(1) ){ | |
| 449 | 449 | chat_emit_csrf_error(); |
| 450 | 450 | return; |
| 451 | 451 | } |
| 452 | 452 | zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; |
| 453 | 453 | nByte = atoi(PD("file:bytes","0")); |
| 454 | 454 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | const char *zUserName; |
| 444 | login_check_credentials(); |
| 445 | if( 0==g.perm.Chat ) { |
| 446 | chat_emit_permissions_error(0); |
| 447 | return; |
| 448 | }else if( 0==cgi_csrf_safe(1) ){ |
| 449 | chat_emit_csrf_error(); |
| 450 | return; |
| 451 | } |
| 452 | zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; |
| 453 | nByte = atoi(PD("file:bytes","0")); |
| 454 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | const char *zUserName; |
| 444 | login_check_credentials(); |
| 445 | if( 0==g.perm.Chat ) { |
| 446 | chat_emit_permissions_error(0); |
| 447 | return; |
| 448 | }else if( g.eAuthMethod==AUTH_COOKIE && 0==cgi_csrf_safe(1) ){ |
| 449 | chat_emit_csrf_error(); |
| 450 | return; |
| 451 | } |
| 452 | zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; |
| 453 | nByte = atoi(PD("file:bytes","0")); |
| 454 |
+32
-9
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -484,11 +484,11 @@ | ||
| 484 | 484 | {"no-merge", C_MERGE }, {"no-classify", C_CLASSIFY }, |
| 485 | 485 | }; |
| 486 | 486 | |
| 487 | 487 | Blob report = BLOB_INITIALIZER; |
| 488 | 488 | enum {CHANGES, STATUS} command = *g.argv[1]=='s' ? STATUS : CHANGES; |
| 489 | - /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ | |
| 489 | + /* --sha1sum is an undocumented alias for --hash for backwards compatibility */ | |
| 490 | 490 | int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 491 | 491 | int showHdr = command==CHANGES && find_option("header", 0, 0); |
| 492 | 492 | int verboseFlag = command==CHANGES && find_option("verbose", "v", 0); |
| 493 | 493 | const char *zIgnoreFlag = find_option("ignore", 0, 1); |
| 494 | 494 | unsigned scanFlags = 0; |
| @@ -714,16 +714,17 @@ | ||
| 714 | 714 | } |
| 715 | 715 | |
| 716 | 716 | /* |
| 717 | 717 | ** Take care of -r version of ls command |
| 718 | 718 | */ |
| 719 | -static void ls_cmd_rev( | |
| 719 | +void ls_cmd_rev( | |
| 720 | 720 | const char *zRev, /* Revision string given */ |
| 721 | 721 | int verboseFlag, /* Verbose flag given */ |
| 722 | 722 | int showAge, /* Age flag given */ |
| 723 | 723 | int showFileHash, /* Show file hash flag given */ |
| 724 | 724 | int showCkinHash, /* Show check-in hash flag given */ |
| 725 | + int showCkinInfo, /* Show check-in infos */ | |
| 725 | 726 | int timeOrder, /* Order by time flag given */ |
| 726 | 727 | int treeFmt /* Show output in the tree format */ |
| 727 | 728 | ){ |
| 728 | 729 | Stmt q; |
| 729 | 730 | char *zOrderBy = "pathname COLLATE nocase"; |
| @@ -763,22 +764,36 @@ | ||
| 763 | 764 | if( timeOrder ){ |
| 764 | 765 | zOrderBy = "mtime DESC"; |
| 765 | 766 | } |
| 766 | 767 | |
| 767 | 768 | compute_fileage(rid,0); |
| 768 | - db_prepare(&q, | |
| 769 | + if( showCkinInfo ){ | |
| 770 | + db_prepare(&q, | |
| 771 | + "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" | |
| 772 | + " bfh.size, fileage.uuid, bch.uuid,\n" | |
| 773 | + " coalesce(e.ecomment, e.comment), coalesce(e.euser, e.user)\n" | |
| 774 | + " FROM fileage, blob bfh, blob bch, event e\n" | |
| 775 | + " WHERE bfh.rid=fileage.fid AND bch.rid=fileage.mid\n" | |
| 776 | + " AND e.objid = fileage.mid %s\n" | |
| 777 | + " ORDER BY %s;", | |
| 778 | + blob_sql_text(&where), | |
| 779 | + zOrderBy /*safe-for-%s*/ | |
| 780 | + ); | |
| 781 | + }else{ | |
| 782 | + db_prepare(&q, | |
| 769 | 783 | "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" |
| 770 | 784 | " bfh.size, fileage.uuid %s\n" |
| 771 | 785 | " FROM fileage, blob bfh %s\n" |
| 772 | 786 | " WHERE bfh.rid=fileage.fid %s %s\n" |
| 773 | - " ORDER BY %s;", | |
| 787 | + " ORDER BY %s;", | |
| 774 | 788 | showCkinHash ? ", bch.uuid" : "", |
| 775 | 789 | showCkinHash ? ", blob bch" : "", |
| 776 | 790 | showCkinHash ? "\n AND bch.rid=fileage.mid" : "", |
| 777 | 791 | blob_sql_text(&where), |
| 778 | 792 | zOrderBy /*safe-for-%s*/ |
| 779 | - ); | |
| 793 | + ); | |
| 794 | + } | |
| 780 | 795 | blob_reset(&where); |
| 781 | 796 | if( treeFmt ) blob_init(&out, 0, 0); |
| 782 | 797 | |
| 783 | 798 | while( db_step(&q)==SQLITE_ROW ){ |
| 784 | 799 | const char *zTime = db_column_text(&q,0); |
| @@ -785,11 +800,18 @@ | ||
| 785 | 800 | const char *zFile = db_column_text(&q,1); |
| 786 | 801 | int size = db_column_int(&q,2); |
| 787 | 802 | if( treeFmt ){ |
| 788 | 803 | blob_appendf(&out, "%s\n", zFile); |
| 789 | 804 | }else if( verboseFlag ){ |
| 790 | - if( showFileHash ){ | |
| 805 | + if( showCkinInfo ){ | |
| 806 | + const char *zUuidC = db_column_text(&q,4); | |
| 807 | + const char *zComm = db_column_text(&q,5); | |
| 808 | + const char *zUser = db_column_text(&q,6); | |
| 809 | + fossil_print("%s [%S] %12s ", zTime, zUuidC, zUser); | |
| 810 | + if( showCkinInfo==2 ) fossil_print("%-20.20s ", zComm); | |
| 811 | + fossil_print("%s\n", zFile); | |
| 812 | + }else if( showFileHash ){ | |
| 791 | 813 | const char *zUuidF = db_column_text(&q,3); |
| 792 | 814 | fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidF, zFile); |
| 793 | 815 | }else if( showCkinHash ){ |
| 794 | 816 | const char *zUuidC = db_column_text(&q,4); |
| 795 | 817 | fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidC, zFile); |
| @@ -887,11 +909,12 @@ | ||
| 887 | 909 | } |
| 888 | 910 | |
| 889 | 911 | if( zRev!=0 ){ |
| 890 | 912 | db_find_and_open_repository(0, 0); |
| 891 | 913 | verify_all_options(); |
| 892 | - ls_cmd_rev(zRev,verboseFlag,showAge,showFHash,showCHash,timeOrder,treeFmt); | |
| 914 | + ls_cmd_rev(zRev, verboseFlag, showAge, showFHash, showCHash, 0, timeOrder, | |
| 915 | + treeFmt); | |
| 893 | 916 | return; |
| 894 | 917 | }else if( find_option("R",0,1)!=0 ){ |
| 895 | 918 | fossil_fatal("the -r is required in addition to -R"); |
| 896 | 919 | } |
| 897 | 920 | |
| @@ -1010,11 +1033,11 @@ | ||
| 1010 | 1033 | |
| 1011 | 1034 | zRev = find_option("r","r",1); |
| 1012 | 1035 | if( zRev==0 ) zRev = "current"; |
| 1013 | 1036 | db_find_and_open_repository(0, 0); |
| 1014 | 1037 | verify_all_options(); |
| 1015 | - ls_cmd_rev(zRev,0,0,0,0,0,1); | |
| 1038 | + ls_cmd_rev(zRev,0,0,0,0,0,0,1); | |
| 1016 | 1039 | } |
| 1017 | 1040 | |
| 1018 | 1041 | /* |
| 1019 | 1042 | ** COMMAND: extras |
| 1020 | 1043 | ** |
| @@ -2562,11 +2585,11 @@ | ||
| 2562 | 2585 | char *zNewBranch = 0; /* The branch name after update */ |
| 2563 | 2586 | int ckComFlgs; /* Flags passed to verify_comment() */ |
| 2564 | 2587 | |
| 2565 | 2588 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2566 | 2589 | url_proxy_options(); |
| 2567 | - /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ | |
| 2590 | + /* --sha1sum is an undocumented alias for --hash for backwards compatibility */ | |
| 2568 | 2591 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2569 | 2592 | noSign = find_option("nosign",0,0)!=0; |
| 2570 | 2593 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 2571 | 2594 | privateFlag = find_option("private",0,0)!=0; |
| 2572 | 2595 | forceDelta = find_option("delta",0,0)!=0; |
| 2573 | 2596 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -484,11 +484,11 @@ | |
| 484 | {"no-merge", C_MERGE }, {"no-classify", C_CLASSIFY }, |
| 485 | }; |
| 486 | |
| 487 | Blob report = BLOB_INITIALIZER; |
| 488 | enum {CHANGES, STATUS} command = *g.argv[1]=='s' ? STATUS : CHANGES; |
| 489 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 490 | int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 491 | int showHdr = command==CHANGES && find_option("header", 0, 0); |
| 492 | int verboseFlag = command==CHANGES && find_option("verbose", "v", 0); |
| 493 | const char *zIgnoreFlag = find_option("ignore", 0, 1); |
| 494 | unsigned scanFlags = 0; |
| @@ -714,16 +714,17 @@ | |
| 714 | } |
| 715 | |
| 716 | /* |
| 717 | ** Take care of -r version of ls command |
| 718 | */ |
| 719 | static void ls_cmd_rev( |
| 720 | const char *zRev, /* Revision string given */ |
| 721 | int verboseFlag, /* Verbose flag given */ |
| 722 | int showAge, /* Age flag given */ |
| 723 | int showFileHash, /* Show file hash flag given */ |
| 724 | int showCkinHash, /* Show check-in hash flag given */ |
| 725 | int timeOrder, /* Order by time flag given */ |
| 726 | int treeFmt /* Show output in the tree format */ |
| 727 | ){ |
| 728 | Stmt q; |
| 729 | char *zOrderBy = "pathname COLLATE nocase"; |
| @@ -763,22 +764,36 @@ | |
| 763 | if( timeOrder ){ |
| 764 | zOrderBy = "mtime DESC"; |
| 765 | } |
| 766 | |
| 767 | compute_fileage(rid,0); |
| 768 | db_prepare(&q, |
| 769 | "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" |
| 770 | " bfh.size, fileage.uuid %s\n" |
| 771 | " FROM fileage, blob bfh %s\n" |
| 772 | " WHERE bfh.rid=fileage.fid %s %s\n" |
| 773 | " ORDER BY %s;", |
| 774 | showCkinHash ? ", bch.uuid" : "", |
| 775 | showCkinHash ? ", blob bch" : "", |
| 776 | showCkinHash ? "\n AND bch.rid=fileage.mid" : "", |
| 777 | blob_sql_text(&where), |
| 778 | zOrderBy /*safe-for-%s*/ |
| 779 | ); |
| 780 | blob_reset(&where); |
| 781 | if( treeFmt ) blob_init(&out, 0, 0); |
| 782 | |
| 783 | while( db_step(&q)==SQLITE_ROW ){ |
| 784 | const char *zTime = db_column_text(&q,0); |
| @@ -785,11 +800,18 @@ | |
| 785 | const char *zFile = db_column_text(&q,1); |
| 786 | int size = db_column_int(&q,2); |
| 787 | if( treeFmt ){ |
| 788 | blob_appendf(&out, "%s\n", zFile); |
| 789 | }else if( verboseFlag ){ |
| 790 | if( showFileHash ){ |
| 791 | const char *zUuidF = db_column_text(&q,3); |
| 792 | fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidF, zFile); |
| 793 | }else if( showCkinHash ){ |
| 794 | const char *zUuidC = db_column_text(&q,4); |
| 795 | fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidC, zFile); |
| @@ -887,11 +909,12 @@ | |
| 887 | } |
| 888 | |
| 889 | if( zRev!=0 ){ |
| 890 | db_find_and_open_repository(0, 0); |
| 891 | verify_all_options(); |
| 892 | ls_cmd_rev(zRev,verboseFlag,showAge,showFHash,showCHash,timeOrder,treeFmt); |
| 893 | return; |
| 894 | }else if( find_option("R",0,1)!=0 ){ |
| 895 | fossil_fatal("the -r is required in addition to -R"); |
| 896 | } |
| 897 | |
| @@ -1010,11 +1033,11 @@ | |
| 1010 | |
| 1011 | zRev = find_option("r","r",1); |
| 1012 | if( zRev==0 ) zRev = "current"; |
| 1013 | db_find_and_open_repository(0, 0); |
| 1014 | verify_all_options(); |
| 1015 | ls_cmd_rev(zRev,0,0,0,0,0,1); |
| 1016 | } |
| 1017 | |
| 1018 | /* |
| 1019 | ** COMMAND: extras |
| 1020 | ** |
| @@ -2562,11 +2585,11 @@ | |
| 2562 | char *zNewBranch = 0; /* The branch name after update */ |
| 2563 | int ckComFlgs; /* Flags passed to verify_comment() */ |
| 2564 | |
| 2565 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2566 | url_proxy_options(); |
| 2567 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2568 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2569 | noSign = find_option("nosign",0,0)!=0; |
| 2570 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 2571 | privateFlag = find_option("private",0,0)!=0; |
| 2572 | forceDelta = find_option("delta",0,0)!=0; |
| 2573 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -484,11 +484,11 @@ | |
| 484 | {"no-merge", C_MERGE }, {"no-classify", C_CLASSIFY }, |
| 485 | }; |
| 486 | |
| 487 | Blob report = BLOB_INITIALIZER; |
| 488 | enum {CHANGES, STATUS} command = *g.argv[1]=='s' ? STATUS : CHANGES; |
| 489 | /* --sha1sum is an undocumented alias for --hash for backwards compatibility */ |
| 490 | int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 491 | int showHdr = command==CHANGES && find_option("header", 0, 0); |
| 492 | int verboseFlag = command==CHANGES && find_option("verbose", "v", 0); |
| 493 | const char *zIgnoreFlag = find_option("ignore", 0, 1); |
| 494 | unsigned scanFlags = 0; |
| @@ -714,16 +714,17 @@ | |
| 714 | } |
| 715 | |
| 716 | /* |
| 717 | ** Take care of -r version of ls command |
| 718 | */ |
| 719 | void ls_cmd_rev( |
| 720 | const char *zRev, /* Revision string given */ |
| 721 | int verboseFlag, /* Verbose flag given */ |
| 722 | int showAge, /* Age flag given */ |
| 723 | int showFileHash, /* Show file hash flag given */ |
| 724 | int showCkinHash, /* Show check-in hash flag given */ |
| 725 | int showCkinInfo, /* Show check-in infos */ |
| 726 | int timeOrder, /* Order by time flag given */ |
| 727 | int treeFmt /* Show output in the tree format */ |
| 728 | ){ |
| 729 | Stmt q; |
| 730 | char *zOrderBy = "pathname COLLATE nocase"; |
| @@ -763,22 +764,36 @@ | |
| 764 | if( timeOrder ){ |
| 765 | zOrderBy = "mtime DESC"; |
| 766 | } |
| 767 | |
| 768 | compute_fileage(rid,0); |
| 769 | if( showCkinInfo ){ |
| 770 | db_prepare(&q, |
| 771 | "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" |
| 772 | " bfh.size, fileage.uuid, bch.uuid,\n" |
| 773 | " coalesce(e.ecomment, e.comment), coalesce(e.euser, e.user)\n" |
| 774 | " FROM fileage, blob bfh, blob bch, event e\n" |
| 775 | " WHERE bfh.rid=fileage.fid AND bch.rid=fileage.mid\n" |
| 776 | " AND e.objid = fileage.mid %s\n" |
| 777 | " ORDER BY %s;", |
| 778 | blob_sql_text(&where), |
| 779 | zOrderBy /*safe-for-%s*/ |
| 780 | ); |
| 781 | }else{ |
| 782 | db_prepare(&q, |
| 783 | "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" |
| 784 | " bfh.size, fileage.uuid %s\n" |
| 785 | " FROM fileage, blob bfh %s\n" |
| 786 | " WHERE bfh.rid=fileage.fid %s %s\n" |
| 787 | " ORDER BY %s;", |
| 788 | showCkinHash ? ", bch.uuid" : "", |
| 789 | showCkinHash ? ", blob bch" : "", |
| 790 | showCkinHash ? "\n AND bch.rid=fileage.mid" : "", |
| 791 | blob_sql_text(&where), |
| 792 | zOrderBy /*safe-for-%s*/ |
| 793 | ); |
| 794 | } |
| 795 | blob_reset(&where); |
| 796 | if( treeFmt ) blob_init(&out, 0, 0); |
| 797 | |
| 798 | while( db_step(&q)==SQLITE_ROW ){ |
| 799 | const char *zTime = db_column_text(&q,0); |
| @@ -785,11 +800,18 @@ | |
| 800 | const char *zFile = db_column_text(&q,1); |
| 801 | int size = db_column_int(&q,2); |
| 802 | if( treeFmt ){ |
| 803 | blob_appendf(&out, "%s\n", zFile); |
| 804 | }else if( verboseFlag ){ |
| 805 | if( showCkinInfo ){ |
| 806 | const char *zUuidC = db_column_text(&q,4); |
| 807 | const char *zComm = db_column_text(&q,5); |
| 808 | const char *zUser = db_column_text(&q,6); |
| 809 | fossil_print("%s [%S] %12s ", zTime, zUuidC, zUser); |
| 810 | if( showCkinInfo==2 ) fossil_print("%-20.20s ", zComm); |
| 811 | fossil_print("%s\n", zFile); |
| 812 | }else if( showFileHash ){ |
| 813 | const char *zUuidF = db_column_text(&q,3); |
| 814 | fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidF, zFile); |
| 815 | }else if( showCkinHash ){ |
| 816 | const char *zUuidC = db_column_text(&q,4); |
| 817 | fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidC, zFile); |
| @@ -887,11 +909,12 @@ | |
| 909 | } |
| 910 | |
| 911 | if( zRev!=0 ){ |
| 912 | db_find_and_open_repository(0, 0); |
| 913 | verify_all_options(); |
| 914 | ls_cmd_rev(zRev, verboseFlag, showAge, showFHash, showCHash, 0, timeOrder, |
| 915 | treeFmt); |
| 916 | return; |
| 917 | }else if( find_option("R",0,1)!=0 ){ |
| 918 | fossil_fatal("the -r is required in addition to -R"); |
| 919 | } |
| 920 | |
| @@ -1010,11 +1033,11 @@ | |
| 1033 | |
| 1034 | zRev = find_option("r","r",1); |
| 1035 | if( zRev==0 ) zRev = "current"; |
| 1036 | db_find_and_open_repository(0, 0); |
| 1037 | verify_all_options(); |
| 1038 | ls_cmd_rev(zRev,0,0,0,0,0,0,1); |
| 1039 | } |
| 1040 | |
| 1041 | /* |
| 1042 | ** COMMAND: extras |
| 1043 | ** |
| @@ -2562,11 +2585,11 @@ | |
| 2585 | char *zNewBranch = 0; /* The branch name after update */ |
| 2586 | int ckComFlgs; /* Flags passed to verify_comment() */ |
| 2587 | |
| 2588 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2589 | url_proxy_options(); |
| 2590 | /* --sha1sum is an undocumented alias for --hash for backwards compatibility */ |
| 2591 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2592 | noSign = find_option("nosign",0,0)!=0; |
| 2593 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 2594 | privateFlag = find_option("private",0,0)!=0; |
| 2595 | forceDelta = find_option("delta",0,0)!=0; |
| 2596 |
+19
-21
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -438,43 +438,40 @@ | ||
| 438 | 438 | ** COMMAND: get |
| 439 | 439 | ** |
| 440 | 440 | ** Usage: %fossil get URL ?VERSION? ?OPTIONS? |
| 441 | 441 | ** |
| 442 | 442 | ** Download a single check-in from a remote repository named URL and |
| 443 | -** unpack all of the files locally. The check-in is identified by VERSION. | |
| 444 | -** | |
| 445 | -** URL can be a traditional URL like one of: | |
| 443 | +** unpack all of the files into a subdirectory. The specific check-in | |
| 444 | +** to download is identified by VERSION. If VERSION is omitted, the | |
| 445 | +** latest trunk check-in is used. The URL can be a traditional "https:", | |
| 446 | +** "ssh:", or "file:" URL similar to the examples shown below, or it can | |
| 447 | +** be the name of a local repository/ | |
| 446 | 448 | ** |
| 447 | 449 | ** * https://domain.com/project |
| 448 | 450 | ** * ssh://my-server/project.fossil |
| 449 | 451 | ** * file:/home/user/Fossils/project.fossil |
| 450 | 452 | ** |
| 451 | -** Or URL can be just the name of a local repository without the "file:" | |
| 452 | -** prefix. | |
| 453 | -** | |
| 454 | 453 | ** This command works by downloading an SQL archive of the requested |
| 455 | 454 | ** check-in and then extracting all the files from the archive. |
| 456 | 455 | ** |
| 457 | 456 | ** Options: |
| 458 | -** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ." | |
| 459 | -** to extract into the local directory. | |
| 460 | -** | |
| 461 | -** -f|--force Overwrite existing files | |
| 462 | -** | |
| 463 | -** --list List all the files that would have been checked | |
| 464 | -** out but do not actually write anything to the | |
| 465 | -** filesystem. | |
| 466 | -** | |
| 467 | -** --sqlar ARCHIVE Store the check-out in an SQL-archive rather | |
| 468 | -** than unpacking them into separate files. | |
| 469 | -** | |
| 470 | -** -v|--verbose Show all files as they are extracted | |
| 457 | +** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ." to | |
| 458 | +** extract into the local directory. If this option is | |
| 459 | +** omitted, Fossil invents a subdirectory name derived | |
| 460 | +** from base filename in the URL and from the VERSION. | |
| 461 | +** -f|--force Overwrite existing files | |
| 462 | +** --list List all the files that would have been checked | |
| 463 | +** out but do not actually write anything to the | |
| 464 | +** filesystem. | |
| 465 | +** --sqlar ARCHIVE Leave the check-out in an SQL-archive named ARCHIVE | |
| 466 | +** rather than unpacking into separate files. | |
| 467 | +** -v|--verbose Show all files as they are extracted | |
| 471 | 468 | */ |
| 472 | 469 | void get_cmd(void){ |
| 473 | 470 | int forceFlag = find_option("force","f",0)!=0; |
| 474 | 471 | int bVerbose = find_option("verbose","v",0)!=0; |
| 475 | - int bQuiet = find_option("quiet","q",0)!=0; | |
| 472 | + int bQuiet = g.fQuiet; | |
| 476 | 473 | int bDebug = find_option("debug",0,0)!=0; |
| 477 | 474 | int bList = find_option("list",0,0)!=0; |
| 478 | 475 | const char *zSqlArchive = find_option("sqlar",0,1); |
| 479 | 476 | const char *z; |
| 480 | 477 | char *zDest = 0; /* Where to store results */ |
| @@ -596,11 +593,11 @@ | ||
| 596 | 593 | } |
| 597 | 594 | if( rc!=SQLITE_OK ){ |
| 598 | 595 | fossil_fatal("Cannot create an in-memory database: %s", |
| 599 | 596 | sqlite3_errmsg(db)); |
| 600 | 597 | } |
| 601 | - zSql = mprintf("SELECT name, mode, sz, data FROM sqlar" | |
| 598 | + zSql = mprintf("SELECT name, mode, sz, data, mtime FROM sqlar" | |
| 602 | 599 | " WHERE name GLOB '%q*'", zDest); |
| 603 | 600 | rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 604 | 601 | fossil_free(zSql); |
| 605 | 602 | if( rc!=0 ){ |
| 606 | 603 | fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db)); |
| @@ -636,10 +633,11 @@ | ||
| 636 | 633 | blob_write_to_file(&file, zFilename); |
| 637 | 634 | if( mode & 0x40 ){ |
| 638 | 635 | file_setexe(zFilename, 1); |
| 639 | 636 | } |
| 640 | 637 | blob_zero(&file); |
| 638 | + file_set_mtime(zFilename, sqlite3_column_int64(pStmt,4)); | |
| 641 | 639 | if( bVerbose ){ |
| 642 | 640 | fossil_print("%s\n", zFilename); |
| 643 | 641 | } |
| 644 | 642 | } |
| 645 | 643 | } |
| 646 | 644 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -438,43 +438,40 @@ | |
| 438 | ** COMMAND: get |
| 439 | ** |
| 440 | ** Usage: %fossil get URL ?VERSION? ?OPTIONS? |
| 441 | ** |
| 442 | ** Download a single check-in from a remote repository named URL and |
| 443 | ** unpack all of the files locally. The check-in is identified by VERSION. |
| 444 | ** |
| 445 | ** URL can be a traditional URL like one of: |
| 446 | ** |
| 447 | ** * https://domain.com/project |
| 448 | ** * ssh://my-server/project.fossil |
| 449 | ** * file:/home/user/Fossils/project.fossil |
| 450 | ** |
| 451 | ** Or URL can be just the name of a local repository without the "file:" |
| 452 | ** prefix. |
| 453 | ** |
| 454 | ** This command works by downloading an SQL archive of the requested |
| 455 | ** check-in and then extracting all the files from the archive. |
| 456 | ** |
| 457 | ** Options: |
| 458 | ** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ." |
| 459 | ** to extract into the local directory. |
| 460 | ** |
| 461 | ** -f|--force Overwrite existing files |
| 462 | ** |
| 463 | ** --list List all the files that would have been checked |
| 464 | ** out but do not actually write anything to the |
| 465 | ** filesystem. |
| 466 | ** |
| 467 | ** --sqlar ARCHIVE Store the check-out in an SQL-archive rather |
| 468 | ** than unpacking them into separate files. |
| 469 | ** |
| 470 | ** -v|--verbose Show all files as they are extracted |
| 471 | */ |
| 472 | void get_cmd(void){ |
| 473 | int forceFlag = find_option("force","f",0)!=0; |
| 474 | int bVerbose = find_option("verbose","v",0)!=0; |
| 475 | int bQuiet = find_option("quiet","q",0)!=0; |
| 476 | int bDebug = find_option("debug",0,0)!=0; |
| 477 | int bList = find_option("list",0,0)!=0; |
| 478 | const char *zSqlArchive = find_option("sqlar",0,1); |
| 479 | const char *z; |
| 480 | char *zDest = 0; /* Where to store results */ |
| @@ -596,11 +593,11 @@ | |
| 596 | } |
| 597 | if( rc!=SQLITE_OK ){ |
| 598 | fossil_fatal("Cannot create an in-memory database: %s", |
| 599 | sqlite3_errmsg(db)); |
| 600 | } |
| 601 | zSql = mprintf("SELECT name, mode, sz, data FROM sqlar" |
| 602 | " WHERE name GLOB '%q*'", zDest); |
| 603 | rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 604 | fossil_free(zSql); |
| 605 | if( rc!=0 ){ |
| 606 | fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db)); |
| @@ -636,10 +633,11 @@ | |
| 636 | blob_write_to_file(&file, zFilename); |
| 637 | if( mode & 0x40 ){ |
| 638 | file_setexe(zFilename, 1); |
| 639 | } |
| 640 | blob_zero(&file); |
| 641 | if( bVerbose ){ |
| 642 | fossil_print("%s\n", zFilename); |
| 643 | } |
| 644 | } |
| 645 | } |
| 646 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -438,43 +438,40 @@ | |
| 438 | ** COMMAND: get |
| 439 | ** |
| 440 | ** Usage: %fossil get URL ?VERSION? ?OPTIONS? |
| 441 | ** |
| 442 | ** Download a single check-in from a remote repository named URL and |
| 443 | ** unpack all of the files into a subdirectory. The specific check-in |
| 444 | ** to download is identified by VERSION. If VERSION is omitted, the |
| 445 | ** latest trunk check-in is used. The URL can be a traditional "https:", |
| 446 | ** "ssh:", or "file:" URL similar to the examples shown below, or it can |
| 447 | ** be the name of a local repository/ |
| 448 | ** |
| 449 | ** * https://domain.com/project |
| 450 | ** * ssh://my-server/project.fossil |
| 451 | ** * file:/home/user/Fossils/project.fossil |
| 452 | ** |
| 453 | ** This command works by downloading an SQL archive of the requested |
| 454 | ** check-in and then extracting all the files from the archive. |
| 455 | ** |
| 456 | ** Options: |
| 457 | ** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ." to |
| 458 | ** extract into the local directory. If this option is |
| 459 | ** omitted, Fossil invents a subdirectory name derived |
| 460 | ** from base filename in the URL and from the VERSION. |
| 461 | ** -f|--force Overwrite existing files |
| 462 | ** --list List all the files that would have been checked |
| 463 | ** out but do not actually write anything to the |
| 464 | ** filesystem. |
| 465 | ** --sqlar ARCHIVE Leave the check-out in an SQL-archive named ARCHIVE |
| 466 | ** rather than unpacking into separate files. |
| 467 | ** -v|--verbose Show all files as they are extracted |
| 468 | */ |
| 469 | void get_cmd(void){ |
| 470 | int forceFlag = find_option("force","f",0)!=0; |
| 471 | int bVerbose = find_option("verbose","v",0)!=0; |
| 472 | int bQuiet = g.fQuiet; |
| 473 | int bDebug = find_option("debug",0,0)!=0; |
| 474 | int bList = find_option("list",0,0)!=0; |
| 475 | const char *zSqlArchive = find_option("sqlar",0,1); |
| 476 | const char *z; |
| 477 | char *zDest = 0; /* Where to store results */ |
| @@ -596,11 +593,11 @@ | |
| 593 | } |
| 594 | if( rc!=SQLITE_OK ){ |
| 595 | fossil_fatal("Cannot create an in-memory database: %s", |
| 596 | sqlite3_errmsg(db)); |
| 597 | } |
| 598 | zSql = mprintf("SELECT name, mode, sz, data, mtime FROM sqlar" |
| 599 | " WHERE name GLOB '%q*'", zDest); |
| 600 | rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 601 | fossil_free(zSql); |
| 602 | if( rc!=0 ){ |
| 603 | fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db)); |
| @@ -636,10 +633,11 @@ | |
| 633 | blob_write_to_file(&file, zFilename); |
| 634 | if( mode & 0x40 ){ |
| 635 | file_setexe(zFilename, 1); |
| 636 | } |
| 637 | blob_zero(&file); |
| 638 | file_set_mtime(zFilename, sqlite3_column_int64(pStmt,4)); |
| 639 | if( bVerbose ){ |
| 640 | fossil_print("%s\n", zFilename); |
| 641 | } |
| 642 | } |
| 643 | } |
| 644 |
+1
-1
| --- src/color.c | ||
| +++ src/color.c | ||
| @@ -215,11 +215,11 @@ | ||
| 215 | 215 | |
| 216 | 216 | /* |
| 217 | 217 | ** SETTING: raw-bgcolor boolean default=off |
| 218 | 218 | ** |
| 219 | 219 | ** Fossil usually tries to adjust user-specified background colors |
| 220 | -** for checkins so that the text is readable and so that the color | |
| 220 | +** for check-ins so that the text is readable and so that the color | |
| 221 | 221 | ** is not too garish. This setting disables that filter. When |
| 222 | 222 | ** this setting is on, the user-selected background colors are shown |
| 223 | 223 | ** exactly as requested. |
| 224 | 224 | */ |
| 225 | 225 | |
| 226 | 226 |
| --- src/color.c | |
| +++ src/color.c | |
| @@ -215,11 +215,11 @@ | |
| 215 | |
| 216 | /* |
| 217 | ** SETTING: raw-bgcolor boolean default=off |
| 218 | ** |
| 219 | ** Fossil usually tries to adjust user-specified background colors |
| 220 | ** for checkins so that the text is readable and so that the color |
| 221 | ** is not too garish. This setting disables that filter. When |
| 222 | ** this setting is on, the user-selected background colors are shown |
| 223 | ** exactly as requested. |
| 224 | */ |
| 225 | |
| 226 |
| --- src/color.c | |
| +++ src/color.c | |
| @@ -215,11 +215,11 @@ | |
| 215 | |
| 216 | /* |
| 217 | ** SETTING: raw-bgcolor boolean default=off |
| 218 | ** |
| 219 | ** Fossil usually tries to adjust user-specified background colors |
| 220 | ** for check-ins so that the text is readable and so that the color |
| 221 | ** is not too garish. This setting disables that filter. When |
| 222 | ** this setting is on, the user-selected background colors are shown |
| 223 | ** exactly as requested. |
| 224 | */ |
| 225 | |
| 226 |
+1
-1
| --- src/comformat.c | ||
| +++ src/comformat.c | ||
| @@ -750,11 +750,11 @@ | ||
| 750 | 750 | ** handling the value of a C-card from a manifest. |
| 751 | 751 | ** --file FILE Omit the TEXT argument and read the comment text |
| 752 | 752 | ** from FILE. |
| 753 | 753 | ** --indent Number of spaces to indent (default (-1) is to |
| 754 | 754 | ** auto-detect). Zero means no indent. |
| 755 | -** --orig FILE Take the value for the ORIGTEXT argumetn from FILE. | |
| 755 | +** --orig FILE Take the value for the ORIGTEXT argument from FILE. | |
| 756 | 756 | ** --origbreak Attempt to break when the original comment text |
| 757 | 757 | ** is detected. |
| 758 | 758 | ** --trimcrlf Enable trimming of leading/trailing CR/LF. |
| 759 | 759 | ** --trimspace Enable trimming of leading/trailing spaces. |
| 760 | 760 | ** --whitespace Keep all internal whitespace. |
| 761 | 761 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -750,11 +750,11 @@ | |
| 750 | ** handling the value of a C-card from a manifest. |
| 751 | ** --file FILE Omit the TEXT argument and read the comment text |
| 752 | ** from FILE. |
| 753 | ** --indent Number of spaces to indent (default (-1) is to |
| 754 | ** auto-detect). Zero means no indent. |
| 755 | ** --orig FILE Take the value for the ORIGTEXT argumetn from FILE. |
| 756 | ** --origbreak Attempt to break when the original comment text |
| 757 | ** is detected. |
| 758 | ** --trimcrlf Enable trimming of leading/trailing CR/LF. |
| 759 | ** --trimspace Enable trimming of leading/trailing spaces. |
| 760 | ** --whitespace Keep all internal whitespace. |
| 761 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -750,11 +750,11 @@ | |
| 750 | ** handling the value of a C-card from a manifest. |
| 751 | ** --file FILE Omit the TEXT argument and read the comment text |
| 752 | ** from FILE. |
| 753 | ** --indent Number of spaces to indent (default (-1) is to |
| 754 | ** auto-detect). Zero means no indent. |
| 755 | ** --orig FILE Take the value for the ORIGTEXT argument from FILE. |
| 756 | ** --origbreak Attempt to break when the original comment text |
| 757 | ** is detected. |
| 758 | ** --trimcrlf Enable trimming of leading/trailing CR/LF. |
| 759 | ** --trimspace Enable trimming of leading/trailing spaces. |
| 760 | ** --whitespace Keep all internal whitespace. |
| 761 |
+1
-1
| --- src/config.h | ||
| +++ src/config.h | ||
| @@ -39,11 +39,11 @@ | ||
| 39 | 39 | |
| 40 | 40 | #ifdef HAVE_AUTOCONFIG_H |
| 41 | 41 | #include "autoconfig.h" |
| 42 | 42 | #endif |
| 43 | 43 | |
| 44 | -/* Enable the hardened SHA1 implemenation by default */ | |
| 44 | +/* Enable the hardened SHA1 implementation by default */ | |
| 45 | 45 | #ifndef FOSSIL_HARDENED_SHA1 |
| 46 | 46 | # define FOSSIL_HARDENED_SHA1 1 |
| 47 | 47 | #endif |
| 48 | 48 | |
| 49 | 49 | #ifndef _RC_COMPILE_ |
| 50 | 50 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -39,11 +39,11 @@ | |
| 39 | |
| 40 | #ifdef HAVE_AUTOCONFIG_H |
| 41 | #include "autoconfig.h" |
| 42 | #endif |
| 43 | |
| 44 | /* Enable the hardened SHA1 implemenation by default */ |
| 45 | #ifndef FOSSIL_HARDENED_SHA1 |
| 46 | # define FOSSIL_HARDENED_SHA1 1 |
| 47 | #endif |
| 48 | |
| 49 | #ifndef _RC_COMPILE_ |
| 50 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -39,11 +39,11 @@ | |
| 39 | |
| 40 | #ifdef HAVE_AUTOCONFIG_H |
| 41 | #include "autoconfig.h" |
| 42 | #endif |
| 43 | |
| 44 | /* Enable the hardened SHA1 implementation by default */ |
| 45 | #ifndef FOSSIL_HARDENED_SHA1 |
| 46 | # define FOSSIL_HARDENED_SHA1 1 |
| 47 | #endif |
| 48 | |
| 49 | #ifndef _RC_COMPILE_ |
| 50 |
+1
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -376,11 +376,11 @@ | ||
| 376 | 376 | ** NAME-specific notes: |
| 377 | 377 | ** |
| 378 | 378 | ** - /reportftm's $MTIME is in Julian, not the Unix epoch. |
| 379 | 379 | */ |
| 380 | 380 | void configure_receive(const char *zName, Blob *pContent, int groupMask){ |
| 381 | - int checkMask; /* Masks for which we must first check existance of tables */ | |
| 381 | + int checkMask; /* Masks for which we must first check existence of tables */ | |
| 382 | 382 | |
| 383 | 383 | checkMask = CONFIGSET_SCRIBER; |
| 384 | 384 | if( zName[0]=='/' ){ |
| 385 | 385 | /* The new format */ |
| 386 | 386 | char *azToken[24]; |
| 387 | 387 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -376,11 +376,11 @@ | |
| 376 | ** NAME-specific notes: |
| 377 | ** |
| 378 | ** - /reportftm's $MTIME is in Julian, not the Unix epoch. |
| 379 | */ |
| 380 | void configure_receive(const char *zName, Blob *pContent, int groupMask){ |
| 381 | int checkMask; /* Masks for which we must first check existance of tables */ |
| 382 | |
| 383 | checkMask = CONFIGSET_SCRIBER; |
| 384 | if( zName[0]=='/' ){ |
| 385 | /* The new format */ |
| 386 | char *azToken[24]; |
| 387 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -376,11 +376,11 @@ | |
| 376 | ** NAME-specific notes: |
| 377 | ** |
| 378 | ** - /reportftm's $MTIME is in Julian, not the Unix epoch. |
| 379 | */ |
| 380 | void configure_receive(const char *zName, Blob *pContent, int groupMask){ |
| 381 | int checkMask; /* Masks for which we must first check existence of tables */ |
| 382 | |
| 383 | checkMask = CONFIGSET_SCRIBER; |
| 384 | if( zName[0]=='/' ){ |
| 385 | /* The new format */ |
| 386 | char *azToken[24]; |
| 387 |
+8
-8
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -496,11 +496,11 @@ | ||
| 496 | 496 | ** zUuid is the UUID of the artifact, if it is specified. When srcId is |
| 497 | 497 | ** specified then zUuid must always be specified. If srcId is zero, |
| 498 | 498 | ** and zUuid is zero then the correct zUuid is computed from pBlob. |
| 499 | 499 | ** |
| 500 | 500 | ** If the record already exists but is a phantom, the pBlob content |
| 501 | -** is inserted and the phatom becomes a real record. | |
| 501 | +** is inserted and the phantom becomes a real record. | |
| 502 | 502 | ** |
| 503 | 503 | ** The original content of pBlob is not disturbed. The caller continues |
| 504 | 504 | ** to be responsible for pBlob. This routine does *not* take over |
| 505 | 505 | ** responsibility for freeing pBlob. |
| 506 | 506 | */ |
| @@ -651,11 +651,11 @@ | ||
| 651 | 651 | ** pBlob is uncompressed and is not deltaed. It is exactly the content |
| 652 | 652 | ** to be inserted. |
| 653 | 653 | ** |
| 654 | 654 | ** The original content of pBlob is not disturbed. The caller continues |
| 655 | 655 | ** to be responsible for pBlob. This routine does *not* take over |
| 656 | -** responsiblity for freeing pBlob. | |
| 656 | +** responsibility for freeing pBlob. | |
| 657 | 657 | */ |
| 658 | 658 | int content_put(Blob *pBlob){ |
| 659 | 659 | return content_put_ex(pBlob, 0, 0, 0, 0); |
| 660 | 660 | } |
| 661 | 661 | |
| @@ -793,11 +793,11 @@ | ||
| 793 | 793 | } |
| 794 | 794 | |
| 795 | 795 | /* |
| 796 | 796 | ** Try to change the storage of rid so that it is a delta from one |
| 797 | 797 | ** of the artifacts given in aSrc[0]..aSrc[nSrc-1]. The aSrc[*] that |
| 798 | -** gives the smallest delta is choosen. | |
| 798 | +** gives the smallest delta is chosen. | |
| 799 | 799 | ** |
| 800 | 800 | ** If rid is already a delta from some other place then no |
| 801 | 801 | ** conversion occurs and this is a no-op unless force==1. If force==1, |
| 802 | 802 | ** then nSrc must also be 1. |
| 803 | 803 | ** |
| @@ -864,11 +864,11 @@ | ||
| 864 | 864 | int srcid = aSrc[i]; |
| 865 | 865 | if( srcid==rid ) continue; |
| 866 | 866 | if( content_is_private(srcid) && !content_is_private(rid) ) continue; |
| 867 | 867 | |
| 868 | 868 | /* Compute all ancestors of srcid and make sure rid is not one of them. |
| 869 | - ** If rid is an ancestor of srcid, then making rid a descendent of srcid | |
| 869 | + ** If rid is an ancestor of srcid, then making rid a descendant of srcid | |
| 870 | 870 | ** would create a delta loop. */ |
| 871 | 871 | s = srcid; |
| 872 | 872 | while( (s = delta_source_rid(s))>0 ){ |
| 873 | 873 | if( s==rid ){ |
| 874 | 874 | content_undelta(srcid); |
| @@ -965,11 +965,11 @@ | ||
| 965 | 965 | ** Options: |
| 966 | 966 | ** -d|--db-only Run "PRAGMA integrity_check" on the database only. |
| 967 | 967 | ** No other validation is performed. |
| 968 | 968 | ** --parse Parse all manifests, wikis, tickets, events, and |
| 969 | 969 | ** so forth, reporting any errors found. |
| 970 | -** -q|--quick Run "PRAGMA quick_check" on the database only. | |
| 970 | +** --quick Run "PRAGMA quick_check" on the database only. | |
| 971 | 971 | ** No other validation is performed. |
| 972 | 972 | */ |
| 973 | 973 | void test_integrity(void){ |
| 974 | 974 | Stmt q; |
| 975 | 975 | Blob content; |
| @@ -979,11 +979,11 @@ | ||
| 979 | 979 | int total; |
| 980 | 980 | int nCA = 0; |
| 981 | 981 | int anCA[10]; |
| 982 | 982 | int bParse = find_option("parse",0,0)!=0; |
| 983 | 983 | int bDbOnly = find_option("db-only","d",0)!=0; |
| 984 | - int bQuick = find_option("quick","q",0)!=0; | |
| 984 | + int bQuick = find_option("quick",0,0)!=0; | |
| 985 | 985 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); |
| 986 | 986 | if( bDbOnly || bQuick ){ |
| 987 | 987 | const char *zType = bQuick ? "quick" : "integrity"; |
| 988 | 988 | char *zRes; |
| 989 | 989 | zRes = db_text(0,"PRAGMA repository.%s_check", zType/*safe-for-%s*/); |
| @@ -1199,11 +1199,11 @@ | ||
| 1199 | 1199 | ** all references are satisfied. Report any referenced artifacts |
| 1200 | 1200 | ** that are missing or shunned. |
| 1201 | 1201 | ** |
| 1202 | 1202 | ** Options: |
| 1203 | 1203 | ** --notshunned Do not report shunned artifacts |
| 1204 | -** --quiet Only show output if there are errors | |
| 1204 | +** -q|--quiet Only show output if there are errors | |
| 1205 | 1205 | */ |
| 1206 | 1206 | void test_missing(void){ |
| 1207 | 1207 | Stmt q; |
| 1208 | 1208 | Blob content; |
| 1209 | 1209 | int nErr = 0; |
| @@ -1212,11 +1212,11 @@ | ||
| 1212 | 1212 | Manifest *p; |
| 1213 | 1213 | unsigned flags = 0; |
| 1214 | 1214 | int quietFlag; |
| 1215 | 1215 | |
| 1216 | 1216 | if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; |
| 1217 | - quietFlag = find_option("quiet","q",0)!=0; | |
| 1217 | + quietFlag = g.fQuiet; | |
| 1218 | 1218 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1219 | 1219 | db_prepare(&q, |
| 1220 | 1220 | "SELECT mid FROM mlink UNION " |
| 1221 | 1221 | "SELECT srcid FROM tagxref WHERE srcid>0 UNION " |
| 1222 | 1222 | "SELECT rid FROM tagxref UNION " |
| 1223 | 1223 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -496,11 +496,11 @@ | |
| 496 | ** zUuid is the UUID of the artifact, if it is specified. When srcId is |
| 497 | ** specified then zUuid must always be specified. If srcId is zero, |
| 498 | ** and zUuid is zero then the correct zUuid is computed from pBlob. |
| 499 | ** |
| 500 | ** If the record already exists but is a phantom, the pBlob content |
| 501 | ** is inserted and the phatom becomes a real record. |
| 502 | ** |
| 503 | ** The original content of pBlob is not disturbed. The caller continues |
| 504 | ** to be responsible for pBlob. This routine does *not* take over |
| 505 | ** responsibility for freeing pBlob. |
| 506 | */ |
| @@ -651,11 +651,11 @@ | |
| 651 | ** pBlob is uncompressed and is not deltaed. It is exactly the content |
| 652 | ** to be inserted. |
| 653 | ** |
| 654 | ** The original content of pBlob is not disturbed. The caller continues |
| 655 | ** to be responsible for pBlob. This routine does *not* take over |
| 656 | ** responsiblity for freeing pBlob. |
| 657 | */ |
| 658 | int content_put(Blob *pBlob){ |
| 659 | return content_put_ex(pBlob, 0, 0, 0, 0); |
| 660 | } |
| 661 | |
| @@ -793,11 +793,11 @@ | |
| 793 | } |
| 794 | |
| 795 | /* |
| 796 | ** Try to change the storage of rid so that it is a delta from one |
| 797 | ** of the artifacts given in aSrc[0]..aSrc[nSrc-1]. The aSrc[*] that |
| 798 | ** gives the smallest delta is choosen. |
| 799 | ** |
| 800 | ** If rid is already a delta from some other place then no |
| 801 | ** conversion occurs and this is a no-op unless force==1. If force==1, |
| 802 | ** then nSrc must also be 1. |
| 803 | ** |
| @@ -864,11 +864,11 @@ | |
| 864 | int srcid = aSrc[i]; |
| 865 | if( srcid==rid ) continue; |
| 866 | if( content_is_private(srcid) && !content_is_private(rid) ) continue; |
| 867 | |
| 868 | /* Compute all ancestors of srcid and make sure rid is not one of them. |
| 869 | ** If rid is an ancestor of srcid, then making rid a descendent of srcid |
| 870 | ** would create a delta loop. */ |
| 871 | s = srcid; |
| 872 | while( (s = delta_source_rid(s))>0 ){ |
| 873 | if( s==rid ){ |
| 874 | content_undelta(srcid); |
| @@ -965,11 +965,11 @@ | |
| 965 | ** Options: |
| 966 | ** -d|--db-only Run "PRAGMA integrity_check" on the database only. |
| 967 | ** No other validation is performed. |
| 968 | ** --parse Parse all manifests, wikis, tickets, events, and |
| 969 | ** so forth, reporting any errors found. |
| 970 | ** -q|--quick Run "PRAGMA quick_check" on the database only. |
| 971 | ** No other validation is performed. |
| 972 | */ |
| 973 | void test_integrity(void){ |
| 974 | Stmt q; |
| 975 | Blob content; |
| @@ -979,11 +979,11 @@ | |
| 979 | int total; |
| 980 | int nCA = 0; |
| 981 | int anCA[10]; |
| 982 | int bParse = find_option("parse",0,0)!=0; |
| 983 | int bDbOnly = find_option("db-only","d",0)!=0; |
| 984 | int bQuick = find_option("quick","q",0)!=0; |
| 985 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); |
| 986 | if( bDbOnly || bQuick ){ |
| 987 | const char *zType = bQuick ? "quick" : "integrity"; |
| 988 | char *zRes; |
| 989 | zRes = db_text(0,"PRAGMA repository.%s_check", zType/*safe-for-%s*/); |
| @@ -1199,11 +1199,11 @@ | |
| 1199 | ** all references are satisfied. Report any referenced artifacts |
| 1200 | ** that are missing or shunned. |
| 1201 | ** |
| 1202 | ** Options: |
| 1203 | ** --notshunned Do not report shunned artifacts |
| 1204 | ** --quiet Only show output if there are errors |
| 1205 | */ |
| 1206 | void test_missing(void){ |
| 1207 | Stmt q; |
| 1208 | Blob content; |
| 1209 | int nErr = 0; |
| @@ -1212,11 +1212,11 @@ | |
| 1212 | Manifest *p; |
| 1213 | unsigned flags = 0; |
| 1214 | int quietFlag; |
| 1215 | |
| 1216 | if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; |
| 1217 | quietFlag = find_option("quiet","q",0)!=0; |
| 1218 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1219 | db_prepare(&q, |
| 1220 | "SELECT mid FROM mlink UNION " |
| 1221 | "SELECT srcid FROM tagxref WHERE srcid>0 UNION " |
| 1222 | "SELECT rid FROM tagxref UNION " |
| 1223 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -496,11 +496,11 @@ | |
| 496 | ** zUuid is the UUID of the artifact, if it is specified. When srcId is |
| 497 | ** specified then zUuid must always be specified. If srcId is zero, |
| 498 | ** and zUuid is zero then the correct zUuid is computed from pBlob. |
| 499 | ** |
| 500 | ** If the record already exists but is a phantom, the pBlob content |
| 501 | ** is inserted and the phantom becomes a real record. |
| 502 | ** |
| 503 | ** The original content of pBlob is not disturbed. The caller continues |
| 504 | ** to be responsible for pBlob. This routine does *not* take over |
| 505 | ** responsibility for freeing pBlob. |
| 506 | */ |
| @@ -651,11 +651,11 @@ | |
| 651 | ** pBlob is uncompressed and is not deltaed. It is exactly the content |
| 652 | ** to be inserted. |
| 653 | ** |
| 654 | ** The original content of pBlob is not disturbed. The caller continues |
| 655 | ** to be responsible for pBlob. This routine does *not* take over |
| 656 | ** responsibility for freeing pBlob. |
| 657 | */ |
| 658 | int content_put(Blob *pBlob){ |
| 659 | return content_put_ex(pBlob, 0, 0, 0, 0); |
| 660 | } |
| 661 | |
| @@ -793,11 +793,11 @@ | |
| 793 | } |
| 794 | |
| 795 | /* |
| 796 | ** Try to change the storage of rid so that it is a delta from one |
| 797 | ** of the artifacts given in aSrc[0]..aSrc[nSrc-1]. The aSrc[*] that |
| 798 | ** gives the smallest delta is chosen. |
| 799 | ** |
| 800 | ** If rid is already a delta from some other place then no |
| 801 | ** conversion occurs and this is a no-op unless force==1. If force==1, |
| 802 | ** then nSrc must also be 1. |
| 803 | ** |
| @@ -864,11 +864,11 @@ | |
| 864 | int srcid = aSrc[i]; |
| 865 | if( srcid==rid ) continue; |
| 866 | if( content_is_private(srcid) && !content_is_private(rid) ) continue; |
| 867 | |
| 868 | /* Compute all ancestors of srcid and make sure rid is not one of them. |
| 869 | ** If rid is an ancestor of srcid, then making rid a descendant of srcid |
| 870 | ** would create a delta loop. */ |
| 871 | s = srcid; |
| 872 | while( (s = delta_source_rid(s))>0 ){ |
| 873 | if( s==rid ){ |
| 874 | content_undelta(srcid); |
| @@ -965,11 +965,11 @@ | |
| 965 | ** Options: |
| 966 | ** -d|--db-only Run "PRAGMA integrity_check" on the database only. |
| 967 | ** No other validation is performed. |
| 968 | ** --parse Parse all manifests, wikis, tickets, events, and |
| 969 | ** so forth, reporting any errors found. |
| 970 | ** --quick Run "PRAGMA quick_check" on the database only. |
| 971 | ** No other validation is performed. |
| 972 | */ |
| 973 | void test_integrity(void){ |
| 974 | Stmt q; |
| 975 | Blob content; |
| @@ -979,11 +979,11 @@ | |
| 979 | int total; |
| 980 | int nCA = 0; |
| 981 | int anCA[10]; |
| 982 | int bParse = find_option("parse",0,0)!=0; |
| 983 | int bDbOnly = find_option("db-only","d",0)!=0; |
| 984 | int bQuick = find_option("quick",0,0)!=0; |
| 985 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); |
| 986 | if( bDbOnly || bQuick ){ |
| 987 | const char *zType = bQuick ? "quick" : "integrity"; |
| 988 | char *zRes; |
| 989 | zRes = db_text(0,"PRAGMA repository.%s_check", zType/*safe-for-%s*/); |
| @@ -1199,11 +1199,11 @@ | |
| 1199 | ** all references are satisfied. Report any referenced artifacts |
| 1200 | ** that are missing or shunned. |
| 1201 | ** |
| 1202 | ** Options: |
| 1203 | ** --notshunned Do not report shunned artifacts |
| 1204 | ** -q|--quiet Only show output if there are errors |
| 1205 | */ |
| 1206 | void test_missing(void){ |
| 1207 | Stmt q; |
| 1208 | Blob content; |
| 1209 | int nErr = 0; |
| @@ -1212,11 +1212,11 @@ | |
| 1212 | Manifest *p; |
| 1213 | unsigned flags = 0; |
| 1214 | int quietFlag; |
| 1215 | |
| 1216 | if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; |
| 1217 | quietFlag = g.fQuiet; |
| 1218 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1219 | db_prepare(&q, |
| 1220 | "SELECT mid FROM mlink UNION " |
| 1221 | "SELECT srcid FROM tagxref WHERE srcid>0 UNION " |
| 1222 | "SELECT rid FROM tagxref UNION " |
| 1223 |
M
src/db.c
+9
-9
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1718,11 +1718,11 @@ | ||
| 1718 | 1718 | savedKeySize = n; |
| 1719 | 1719 | } |
| 1720 | 1720 | |
| 1721 | 1721 | /* |
| 1722 | 1722 | ** This function arranges for the database encryption key to be securely |
| 1723 | -** saved in non-pagable memory (on platforms where this is possible). | |
| 1723 | +** saved in non-pageable memory (on platforms where this is possible). | |
| 1724 | 1724 | */ |
| 1725 | 1725 | static void db_save_encryption_key( |
| 1726 | 1726 | Blob *pKey |
| 1727 | 1727 | ){ |
| 1728 | 1728 | void *p = NULL; |
| @@ -2531,11 +2531,11 @@ | ||
| 2531 | 2531 | if( file_access(zDbName, F_OK) ) return 0; |
| 2532 | 2532 | lsize = file_size(zDbName, ExtFILE); |
| 2533 | 2533 | if( lsize%1024!=0 || lsize<4096 ) return 0; |
| 2534 | 2534 | db_open_or_attach(zDbName, "localdb"); |
| 2535 | 2535 | |
| 2536 | - /* Check to see if the check-out database has the lastest schema changes. | |
| 2536 | + /* Check to see if the check-out database has the latest schema changes. | |
| 2537 | 2537 | ** The most recent schema change (2019-01-19) is the addition of the |
| 2538 | 2538 | ** vmerge.mhash and vfile.mhash fields. If the schema has the vmerge.mhash |
| 2539 | 2539 | ** column, assume everything else is up-to-date. |
| 2540 | 2540 | */ |
| 2541 | 2541 | if( db_table_has_column("localdb","vmerge","mhash") ){ |
| @@ -2784,11 +2784,11 @@ | ||
| 2784 | 2784 | |
| 2785 | 2785 | /* Additional checks that occur when opening the check-out database */ |
| 2786 | 2786 | if( g.localOpen ){ |
| 2787 | 2787 | |
| 2788 | 2788 | /* If the repository database that was just opened has been |
| 2789 | - ** eplaced by a clone of the same project, with different RID | |
| 2789 | + ** replaced by a clone of the same project, with different RID | |
| 2790 | 2790 | ** values, then renumber the RID values stored in various tables |
| 2791 | 2791 | ** of the check-out database, so that the repository and check-out |
| 2792 | 2792 | ** databases align. |
| 2793 | 2793 | */ |
| 2794 | 2794 | if( !db_fingerprint_ok() ){ |
| @@ -3655,12 +3655,12 @@ | ||
| 3655 | 3655 | ** non-versioned value exist and are not equal, then a warning message |
| 3656 | 3656 | ** might be generated. |
| 3657 | 3657 | ** |
| 3658 | 3658 | ** zCkin is normally NULL. In that case, the versioned setting is |
| 3659 | 3659 | ** take from the local check-out, if a local checkout exists, or from |
| 3660 | -** checkin named by the g.zOpenRevision global variable. If zCkin is | |
| 3661 | -** not NULL, then zCkin is the name of the specific checkin from which | |
| 3660 | +** check-in named by the g.zOpenRevision global variable. If zCkin is | |
| 3661 | +** not NULL, then zCkin is the name of the specific check-in from which | |
| 3662 | 3662 | ** versioned setting value is taken. When zCkin is not NULL, the cache |
| 3663 | 3663 | ** is bypassed. |
| 3664 | 3664 | */ |
| 3665 | 3665 | char *db_get_versioned( |
| 3666 | 3666 | const char *zName, |
| @@ -4569,11 +4569,11 @@ | ||
| 4569 | 4569 | ** |
| 4570 | 4570 | ** var is the name of the internal configuration name for db_(un)set. |
| 4571 | 4571 | ** If var is 0, the settings name is used. |
| 4572 | 4572 | ** |
| 4573 | 4573 | ** width is the length for the edit field on the behavior page, 0 is |
| 4574 | -** used for on/off checkboxes. A negative value indicates that that | |
| 4574 | +** used for on/off checkboxes. A negative value indicates that the | |
| 4575 | 4575 | ** page should not render this setting. Such values may be rendered |
| 4576 | 4576 | ** separately/manually on another page, e.g., /setup_access, and are |
| 4577 | 4577 | ** exposed via the CLI settings command. |
| 4578 | 4578 | ** |
| 4579 | 4579 | ** The behaviour page doesn't use a special layout. It lists all |
| @@ -4855,11 +4855,11 @@ | ||
| 4855 | 4855 | */ |
| 4856 | 4856 | /* |
| 4857 | 4857 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4858 | 4858 | ** The value is a list of pathnames parsed according to the same rules as |
| 4859 | 4859 | ** the *-glob settings. On update and checkout commands, if no directory |
| 4860 | -** exists with that name, an empty directory will be be created, even if | |
| 4860 | +** exists with that name, an empty directory will be created, even if | |
| 4861 | 4861 | ** it must create one or more parent directories. |
| 4862 | 4862 | */ |
| 4863 | 4863 | /* |
| 4864 | 4864 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4865 | 4865 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| @@ -5576,17 +5576,17 @@ | ||
| 5576 | 5576 | fossil_print("Config database: %s\n", g.zConfigDbName); |
| 5577 | 5577 | } |
| 5578 | 5578 | |
| 5579 | 5579 | /* |
| 5580 | 5580 | ** Compute a "fingerprint" on the repository. A fingerprint is used |
| 5581 | -** to verify that that the repository has not been replaced by a clone | |
| 5581 | +** to verify that the repository has not been replaced by a clone | |
| 5582 | 5582 | ** of the same repository. More precisely, a fingerprint is used to |
| 5583 | 5583 | ** verify that the mapping between SHA3 hashes and RID values is unchanged. |
| 5584 | 5584 | ** |
| 5585 | 5585 | ** The check-out database ("localdb") stores RID values. When associating |
| 5586 | 5586 | ** a check-out database against a repository database, it is useful to verify |
| 5587 | -** the fingerprint so that we know tha the RID values in the check-out | |
| 5587 | +** the fingerprint so that we know that the RID values in the check-out | |
| 5588 | 5588 | ** database still correspond to the correct entries in the BLOB table of |
| 5589 | 5589 | ** the repository. |
| 5590 | 5590 | ** |
| 5591 | 5591 | ** The fingerprint is based on the RCVFROM table. When constructing a |
| 5592 | 5592 | ** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to |
| 5593 | 5593 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1718,11 +1718,11 @@ | |
| 1718 | savedKeySize = n; |
| 1719 | } |
| 1720 | |
| 1721 | /* |
| 1722 | ** This function arranges for the database encryption key to be securely |
| 1723 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1724 | */ |
| 1725 | static void db_save_encryption_key( |
| 1726 | Blob *pKey |
| 1727 | ){ |
| 1728 | void *p = NULL; |
| @@ -2531,11 +2531,11 @@ | |
| 2531 | if( file_access(zDbName, F_OK) ) return 0; |
| 2532 | lsize = file_size(zDbName, ExtFILE); |
| 2533 | if( lsize%1024!=0 || lsize<4096 ) return 0; |
| 2534 | db_open_or_attach(zDbName, "localdb"); |
| 2535 | |
| 2536 | /* Check to see if the check-out database has the lastest schema changes. |
| 2537 | ** The most recent schema change (2019-01-19) is the addition of the |
| 2538 | ** vmerge.mhash and vfile.mhash fields. If the schema has the vmerge.mhash |
| 2539 | ** column, assume everything else is up-to-date. |
| 2540 | */ |
| 2541 | if( db_table_has_column("localdb","vmerge","mhash") ){ |
| @@ -2784,11 +2784,11 @@ | |
| 2784 | |
| 2785 | /* Additional checks that occur when opening the check-out database */ |
| 2786 | if( g.localOpen ){ |
| 2787 | |
| 2788 | /* If the repository database that was just opened has been |
| 2789 | ** eplaced by a clone of the same project, with different RID |
| 2790 | ** values, then renumber the RID values stored in various tables |
| 2791 | ** of the check-out database, so that the repository and check-out |
| 2792 | ** databases align. |
| 2793 | */ |
| 2794 | if( !db_fingerprint_ok() ){ |
| @@ -3655,12 +3655,12 @@ | |
| 3655 | ** non-versioned value exist and are not equal, then a warning message |
| 3656 | ** might be generated. |
| 3657 | ** |
| 3658 | ** zCkin is normally NULL. In that case, the versioned setting is |
| 3659 | ** take from the local check-out, if a local checkout exists, or from |
| 3660 | ** checkin named by the g.zOpenRevision global variable. If zCkin is |
| 3661 | ** not NULL, then zCkin is the name of the specific checkin from which |
| 3662 | ** versioned setting value is taken. When zCkin is not NULL, the cache |
| 3663 | ** is bypassed. |
| 3664 | */ |
| 3665 | char *db_get_versioned( |
| 3666 | const char *zName, |
| @@ -4569,11 +4569,11 @@ | |
| 4569 | ** |
| 4570 | ** var is the name of the internal configuration name for db_(un)set. |
| 4571 | ** If var is 0, the settings name is used. |
| 4572 | ** |
| 4573 | ** width is the length for the edit field on the behavior page, 0 is |
| 4574 | ** used for on/off checkboxes. A negative value indicates that that |
| 4575 | ** page should not render this setting. Such values may be rendered |
| 4576 | ** separately/manually on another page, e.g., /setup_access, and are |
| 4577 | ** exposed via the CLI settings command. |
| 4578 | ** |
| 4579 | ** The behaviour page doesn't use a special layout. It lists all |
| @@ -4855,11 +4855,11 @@ | |
| 4855 | */ |
| 4856 | /* |
| 4857 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4858 | ** The value is a list of pathnames parsed according to the same rules as |
| 4859 | ** the *-glob settings. On update and checkout commands, if no directory |
| 4860 | ** exists with that name, an empty directory will be be created, even if |
| 4861 | ** it must create one or more parent directories. |
| 4862 | */ |
| 4863 | /* |
| 4864 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4865 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| @@ -5576,17 +5576,17 @@ | |
| 5576 | fossil_print("Config database: %s\n", g.zConfigDbName); |
| 5577 | } |
| 5578 | |
| 5579 | /* |
| 5580 | ** Compute a "fingerprint" on the repository. A fingerprint is used |
| 5581 | ** to verify that that the repository has not been replaced by a clone |
| 5582 | ** of the same repository. More precisely, a fingerprint is used to |
| 5583 | ** verify that the mapping between SHA3 hashes and RID values is unchanged. |
| 5584 | ** |
| 5585 | ** The check-out database ("localdb") stores RID values. When associating |
| 5586 | ** a check-out database against a repository database, it is useful to verify |
| 5587 | ** the fingerprint so that we know tha the RID values in the check-out |
| 5588 | ** database still correspond to the correct entries in the BLOB table of |
| 5589 | ** the repository. |
| 5590 | ** |
| 5591 | ** The fingerprint is based on the RCVFROM table. When constructing a |
| 5592 | ** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to |
| 5593 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1718,11 +1718,11 @@ | |
| 1718 | savedKeySize = n; |
| 1719 | } |
| 1720 | |
| 1721 | /* |
| 1722 | ** This function arranges for the database encryption key to be securely |
| 1723 | ** saved in non-pageable memory (on platforms where this is possible). |
| 1724 | */ |
| 1725 | static void db_save_encryption_key( |
| 1726 | Blob *pKey |
| 1727 | ){ |
| 1728 | void *p = NULL; |
| @@ -2531,11 +2531,11 @@ | |
| 2531 | if( file_access(zDbName, F_OK) ) return 0; |
| 2532 | lsize = file_size(zDbName, ExtFILE); |
| 2533 | if( lsize%1024!=0 || lsize<4096 ) return 0; |
| 2534 | db_open_or_attach(zDbName, "localdb"); |
| 2535 | |
| 2536 | /* Check to see if the check-out database has the latest schema changes. |
| 2537 | ** The most recent schema change (2019-01-19) is the addition of the |
| 2538 | ** vmerge.mhash and vfile.mhash fields. If the schema has the vmerge.mhash |
| 2539 | ** column, assume everything else is up-to-date. |
| 2540 | */ |
| 2541 | if( db_table_has_column("localdb","vmerge","mhash") ){ |
| @@ -2784,11 +2784,11 @@ | |
| 2784 | |
| 2785 | /* Additional checks that occur when opening the check-out database */ |
| 2786 | if( g.localOpen ){ |
| 2787 | |
| 2788 | /* If the repository database that was just opened has been |
| 2789 | ** replaced by a clone of the same project, with different RID |
| 2790 | ** values, then renumber the RID values stored in various tables |
| 2791 | ** of the check-out database, so that the repository and check-out |
| 2792 | ** databases align. |
| 2793 | */ |
| 2794 | if( !db_fingerprint_ok() ){ |
| @@ -3655,12 +3655,12 @@ | |
| 3655 | ** non-versioned value exist and are not equal, then a warning message |
| 3656 | ** might be generated. |
| 3657 | ** |
| 3658 | ** zCkin is normally NULL. In that case, the versioned setting is |
| 3659 | ** take from the local check-out, if a local checkout exists, or from |
| 3660 | ** check-in named by the g.zOpenRevision global variable. If zCkin is |
| 3661 | ** not NULL, then zCkin is the name of the specific check-in from which |
| 3662 | ** versioned setting value is taken. When zCkin is not NULL, the cache |
| 3663 | ** is bypassed. |
| 3664 | */ |
| 3665 | char *db_get_versioned( |
| 3666 | const char *zName, |
| @@ -4569,11 +4569,11 @@ | |
| 4569 | ** |
| 4570 | ** var is the name of the internal configuration name for db_(un)set. |
| 4571 | ** If var is 0, the settings name is used. |
| 4572 | ** |
| 4573 | ** width is the length for the edit field on the behavior page, 0 is |
| 4574 | ** used for on/off checkboxes. A negative value indicates that the |
| 4575 | ** page should not render this setting. Such values may be rendered |
| 4576 | ** separately/manually on another page, e.g., /setup_access, and are |
| 4577 | ** exposed via the CLI settings command. |
| 4578 | ** |
| 4579 | ** The behaviour page doesn't use a special layout. It lists all |
| @@ -4855,11 +4855,11 @@ | |
| 4855 | */ |
| 4856 | /* |
| 4857 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4858 | ** The value is a list of pathnames parsed according to the same rules as |
| 4859 | ** the *-glob settings. On update and checkout commands, if no directory |
| 4860 | ** exists with that name, an empty directory will be created, even if |
| 4861 | ** it must create one or more parent directories. |
| 4862 | */ |
| 4863 | /* |
| 4864 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4865 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| @@ -5576,17 +5576,17 @@ | |
| 5576 | fossil_print("Config database: %s\n", g.zConfigDbName); |
| 5577 | } |
| 5578 | |
| 5579 | /* |
| 5580 | ** Compute a "fingerprint" on the repository. A fingerprint is used |
| 5581 | ** to verify that the repository has not been replaced by a clone |
| 5582 | ** of the same repository. More precisely, a fingerprint is used to |
| 5583 | ** verify that the mapping between SHA3 hashes and RID values is unchanged. |
| 5584 | ** |
| 5585 | ** The check-out database ("localdb") stores RID values. When associating |
| 5586 | ** a check-out database against a repository database, it is useful to verify |
| 5587 | ** the fingerprint so that we know that the RID values in the check-out |
| 5588 | ** database still correspond to the correct entries in the BLOB table of |
| 5589 | ** the repository. |
| 5590 | ** |
| 5591 | ** The fingerprint is based on the RCVFROM table. When constructing a |
| 5592 | ** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to |
| 5593 |
+19
-1
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1231,11 +1231,11 @@ | ||
| 1231 | 1231 | } |
| 1232 | 1232 | .accordion_closed > .accordion_btn_plus { |
| 1233 | 1233 | display: inline-block; |
| 1234 | 1234 | } |
| 1235 | 1235 | .accordion_panel { |
| 1236 | - overflow: hidden; | |
| 1236 | + overflow-y: clip; | |
| 1237 | 1237 | transition: max-height 0.25s ease-out; |
| 1238 | 1238 | } |
| 1239 | 1239 | .error { |
| 1240 | 1240 | color: darkred; |
| 1241 | 1241 | background: yellow; |
| @@ -1975,10 +1975,28 @@ | ||
| 1975 | 1975 | } |
| 1976 | 1976 | div.markdown span.notescope:hover, |
| 1977 | 1977 | div.markdown span.notescope:target { |
| 1978 | 1978 | border-bottom: 2px solid gold; |
| 1979 | 1979 | } |
| 1980 | + | |
| 1981 | +/* Cause <dd> elements to be aligned complete to the | |
| 1982 | +** right of their <dt> on help pages. */ | |
| 1983 | +dl.helpOptions { | |
| 1984 | + display: grid; | |
| 1985 | + grid-template-columns: max-content 1fr; | |
| 1986 | + column-gap: 1rem; | |
| 1987 | +} | |
| 1988 | +dl.helpOptions > dt { | |
| 1989 | + grid-column: 1; | |
| 1990 | +} | |
| 1991 | +dl.helpOptions > dd { | |
| 1992 | + grid-column: 2; | |
| 1993 | + margin: 0; | |
| 1994 | +} | |
| 1995 | +div.helpPage blockquote { | |
| 1996 | + margin-left: 0.2em; | |
| 1997 | +} | |
| 1980 | 1998 | |
| 1981 | 1999 | /* Objects in the "desktoponly" class are invisible on mobile */ |
| 1982 | 2000 | @media screen and (max-width: 600px) { |
| 1983 | 2001 | .desktoponly { |
| 1984 | 2002 | display: none; |
| 1985 | 2003 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1231,11 +1231,11 @@ | |
| 1231 | } |
| 1232 | .accordion_closed > .accordion_btn_plus { |
| 1233 | display: inline-block; |
| 1234 | } |
| 1235 | .accordion_panel { |
| 1236 | overflow: hidden; |
| 1237 | transition: max-height 0.25s ease-out; |
| 1238 | } |
| 1239 | .error { |
| 1240 | color: darkred; |
| 1241 | background: yellow; |
| @@ -1975,10 +1975,28 @@ | |
| 1975 | } |
| 1976 | div.markdown span.notescope:hover, |
| 1977 | div.markdown span.notescope:target { |
| 1978 | border-bottom: 2px solid gold; |
| 1979 | } |
| 1980 | |
| 1981 | /* Objects in the "desktoponly" class are invisible on mobile */ |
| 1982 | @media screen and (max-width: 600px) { |
| 1983 | .desktoponly { |
| 1984 | display: none; |
| 1985 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1231,11 +1231,11 @@ | |
| 1231 | } |
| 1232 | .accordion_closed > .accordion_btn_plus { |
| 1233 | display: inline-block; |
| 1234 | } |
| 1235 | .accordion_panel { |
| 1236 | overflow-y: clip; |
| 1237 | transition: max-height 0.25s ease-out; |
| 1238 | } |
| 1239 | .error { |
| 1240 | color: darkred; |
| 1241 | background: yellow; |
| @@ -1975,10 +1975,28 @@ | |
| 1975 | } |
| 1976 | div.markdown span.notescope:hover, |
| 1977 | div.markdown span.notescope:target { |
| 1978 | border-bottom: 2px solid gold; |
| 1979 | } |
| 1980 | |
| 1981 | /* Cause <dd> elements to be aligned complete to the |
| 1982 | ** right of their <dt> on help pages. */ |
| 1983 | dl.helpOptions { |
| 1984 | display: grid; |
| 1985 | grid-template-columns: max-content 1fr; |
| 1986 | column-gap: 1rem; |
| 1987 | } |
| 1988 | dl.helpOptions > dt { |
| 1989 | grid-column: 1; |
| 1990 | } |
| 1991 | dl.helpOptions > dd { |
| 1992 | grid-column: 2; |
| 1993 | margin: 0; |
| 1994 | } |
| 1995 | div.helpPage blockquote { |
| 1996 | margin-left: 0.2em; |
| 1997 | } |
| 1998 | |
| 1999 | /* Objects in the "desktoponly" class are invisible on mobile */ |
| 2000 | @media screen and (max-width: 600px) { |
| 2001 | .desktoponly { |
| 2002 | display: none; |
| 2003 |
+3
-3
| --- src/delta.c | ||
| +++ src/delta.c | ||
| @@ -316,11 +316,11 @@ | ||
| 316 | 316 | ** |
| 317 | 317 | ** The last term is of the form |
| 318 | 318 | ** |
| 319 | 319 | ** NNN; |
| 320 | 320 | ** |
| 321 | -** In this case, NNN is a 32-bit bigendian checksum of the output file | |
| 321 | +** In this case, NNN is a 32-bit big endian checksum of the output file | |
| 322 | 322 | ** that can be used to verify that the delta applied correctly. All |
| 323 | 323 | ** numbers are in base-64. |
| 324 | 324 | ** |
| 325 | 325 | ** Pure text files generate a pure text delta. Binary files generate a |
| 326 | 326 | ** delta that may contain some binary data. |
| @@ -572,12 +572,12 @@ | ||
| 572 | 572 | int lenSrc, /* Length of the source file */ |
| 573 | 573 | const char *zDelta, /* Delta to apply to the pattern */ |
| 574 | 574 | int lenDelta, /* Length of the delta */ |
| 575 | 575 | char *zOut /* Write the output into this preallocated buffer */ |
| 576 | 576 | ){ |
| 577 | - unsigned int limit; | |
| 578 | - unsigned int total = 0; | |
| 577 | + sqlite3_uint64 limit; | |
| 578 | + sqlite3_uint64 total = 0; | |
| 579 | 579 | #ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST |
| 580 | 580 | char *zOrigOut = zOut; |
| 581 | 581 | #endif |
| 582 | 582 | |
| 583 | 583 | limit = getInt(&zDelta, &lenDelta); |
| 584 | 584 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -316,11 +316,11 @@ | |
| 316 | ** |
| 317 | ** The last term is of the form |
| 318 | ** |
| 319 | ** NNN; |
| 320 | ** |
| 321 | ** In this case, NNN is a 32-bit bigendian checksum of the output file |
| 322 | ** that can be used to verify that the delta applied correctly. All |
| 323 | ** numbers are in base-64. |
| 324 | ** |
| 325 | ** Pure text files generate a pure text delta. Binary files generate a |
| 326 | ** delta that may contain some binary data. |
| @@ -572,12 +572,12 @@ | |
| 572 | int lenSrc, /* Length of the source file */ |
| 573 | const char *zDelta, /* Delta to apply to the pattern */ |
| 574 | int lenDelta, /* Length of the delta */ |
| 575 | char *zOut /* Write the output into this preallocated buffer */ |
| 576 | ){ |
| 577 | unsigned int limit; |
| 578 | unsigned int total = 0; |
| 579 | #ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST |
| 580 | char *zOrigOut = zOut; |
| 581 | #endif |
| 582 | |
| 583 | limit = getInt(&zDelta, &lenDelta); |
| 584 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -316,11 +316,11 @@ | |
| 316 | ** |
| 317 | ** The last term is of the form |
| 318 | ** |
| 319 | ** NNN; |
| 320 | ** |
| 321 | ** In this case, NNN is a 32-bit big endian checksum of the output file |
| 322 | ** that can be used to verify that the delta applied correctly. All |
| 323 | ** numbers are in base-64. |
| 324 | ** |
| 325 | ** Pure text files generate a pure text delta. Binary files generate a |
| 326 | ** delta that may contain some binary data. |
| @@ -572,12 +572,12 @@ | |
| 572 | int lenSrc, /* Length of the source file */ |
| 573 | const char *zDelta, /* Delta to apply to the pattern */ |
| 574 | int lenDelta, /* Length of the delta */ |
| 575 | char *zOut /* Write the output into this preallocated buffer */ |
| 576 | ){ |
| 577 | sqlite3_uint64 limit; |
| 578 | sqlite3_uint64 total = 0; |
| 579 | #ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST |
| 580 | char *zOrigOut = zOut; |
| 581 | #endif |
| 582 | |
| 583 | limit = getInt(&zDelta, &lenDelta); |
| 584 |
+1
-1
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -159,11 +159,11 @@ | ||
| 159 | 159 | } |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | 162 | /* |
| 163 | 163 | ** If RID refers to a check-in, return the mtime of that check-in - the |
| 164 | -** julian day number of when the check-in occurred. | |
| 164 | +** Julian day number of when the check-in occurred. | |
| 165 | 165 | */ |
| 166 | 166 | double mtime_of_rid(int rid, double mtime){ |
| 167 | 167 | static Stmt q; |
| 168 | 168 | db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid"); |
| 169 | 169 | db_bind_int(&q, ":rid", rid); |
| 170 | 170 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -159,11 +159,11 @@ | |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | ** If RID refers to a check-in, return the mtime of that check-in - the |
| 164 | ** julian day number of when the check-in occurred. |
| 165 | */ |
| 166 | double mtime_of_rid(int rid, double mtime){ |
| 167 | static Stmt q; |
| 168 | db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid"); |
| 169 | db_bind_int(&q, ":rid", rid); |
| 170 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -159,11 +159,11 @@ | |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | ** If RID refers to a check-in, return the mtime of that check-in - the |
| 164 | ** Julian day number of when the check-in occurred. |
| 165 | */ |
| 166 | double mtime_of_rid(int rid, double mtime){ |
| 167 | static Stmt q; |
| 168 | db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid"); |
| 169 | db_bind_int(&q, ":rid", rid); |
| 170 |
+22
-2
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -1045,11 +1045,11 @@ | ||
| 1045 | 1045 | |
| 1046 | 1046 | /************************* DiffBuilderDebug ********************************/ |
| 1047 | 1047 | /* This version of DiffBuilder is used for debugging the diff and diff |
| 1048 | 1048 | ** diff formatter logic. It is accessed using the (undocumented) --debug |
| 1049 | 1049 | ** option to the diff command. The output is human-readable text that |
| 1050 | -** describes the various method calls that are invoked agains the DiffBuilder | |
| 1050 | +** describes the various method calls that are invoked against the DiffBuilder | |
| 1051 | 1051 | ** object. |
| 1052 | 1052 | */ |
| 1053 | 1053 | static void dfdebugSkip(DiffBuilder *p, unsigned int n, int isFinal){ |
| 1054 | 1054 | blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)%s\n", |
| 1055 | 1055 | n, p->lnLeft+1, p->lnLeft+n, p->lnRight+1, p->lnRight+n, |
| @@ -3622,11 +3622,11 @@ | ||
| 3622 | 3622 | iLimit = 0; |
| 3623 | 3623 | mxTime = current_time_in_milliseconds()+1000; |
| 3624 | 3624 | } |
| 3625 | 3625 | db_begin_transaction(); |
| 3626 | 3626 | |
| 3627 | - /* Get the artifact ID for the check-in begin analyzed */ | |
| 3627 | + /* Get the artifact ID for the check-in being analyzed */ | |
| 3628 | 3628 | if( zRevision ){ |
| 3629 | 3629 | cid = name_to_typed_rid(zRevision, "ci"); |
| 3630 | 3630 | }else{ |
| 3631 | 3631 | db_must_be_within_tree(); |
| 3632 | 3632 | cid = db_lget_int("checkout", 0); |
| @@ -3939,10 +3939,15 @@ | ||
| 3939 | 3939 | ** (example: "-o trunk") then these commands show changes moving towards |
| 3940 | 3940 | ** that alternative origin. Thus using "-o trunk" on an historical version |
| 3941 | 3941 | ** of the file shows the first time each line in the file was changed or |
| 3942 | 3942 | ** removed by any subsequent check-in. |
| 3943 | 3943 | ** |
| 3944 | +** With -t or -T, the "blame" and "praise" commands show for each file the | |
| 3945 | +** latest (relative to the revision given by -r) check-in that modified it and | |
| 3946 | +** the check-in's author. If not given, the revision defaults to "current" for | |
| 3947 | +** a check-out. Option -T additionally shows a comment snippet for the check-in. | |
| 3948 | +** | |
| 3944 | 3949 | ** Options: |
| 3945 | 3950 | ** --filevers Show file version numbers rather than |
| 3946 | 3951 | ** check-in versions |
| 3947 | 3952 | ** -r|--revision VERSION The specific check-in containing the file |
| 3948 | 3953 | ** -l|--log List all versions analyzed |
| @@ -3954,10 +3959,13 @@ | ||
| 3954 | 3959 | ** root of the repository. Set to the name of |
| 3955 | 3960 | ** the main branch (usually "trunk") or |
| 3956 | 3961 | ** similar for a reverse annotation. |
| 3957 | 3962 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 3958 | 3963 | ** -Z|--ignore-trailing-space Ignore whitespace at line end |
| 3964 | +** -t Show latest check-in and its author for each | |
| 3965 | +** tracked file in the tree as of VERSION | |
| 3966 | +** -T Like -t, plus comment snippet | |
| 3959 | 3967 | ** |
| 3960 | 3968 | ** See also: [[info]], [[finfo]], [[timeline]] |
| 3961 | 3969 | */ |
| 3962 | 3970 | void annotate_cmd(void){ |
| 3963 | 3971 | const char *zRevision; /* Revision name, or NULL for current check-in */ |
| @@ -3967,16 +3975,28 @@ | ||
| 3967 | 3975 | const char *zOrig; /* The value for -o|--origin */ |
| 3968 | 3976 | int showLog; /* True to show the log */ |
| 3969 | 3977 | int fileVers; /* Show file version instead of check-in versions */ |
| 3970 | 3978 | u64 annFlags = 0; /* Flags to control annotation properties */ |
| 3971 | 3979 | int bBlame = 0; /* True for BLAME output. False for ANNOTATE. */ |
| 3980 | + int bTreeInfo = 0; /* Show for the entire tree: 1=checkin, 2=with comment */ | |
| 3972 | 3981 | int szHash; /* Display size of a version hash */ |
| 3973 | 3982 | Blob treename; /* Name of file to be annotated */ |
| 3974 | 3983 | char *zFilename; /* Name of file to be annotated */ |
| 3975 | 3984 | |
| 3976 | 3985 | bBlame = g.argv[1][0]!='a'; |
| 3986 | + if( find_option("t","t",0)!=0 ) bTreeInfo = 1; | |
| 3987 | + if( find_option("T","T",0)!=0 ) bTreeInfo = 2; | |
| 3977 | 3988 | zRevision = find_option("revision","r",1); |
| 3989 | + if( bBlame && bTreeInfo ){ | |
| 3990 | + if( find_repository_option()!=0 && zRevision==0 ){ | |
| 3991 | + fossil_fatal("the -r is required in addition to -R"); | |
| 3992 | + } | |
| 3993 | + db_find_and_open_repository(0, 0); | |
| 3994 | + if( zRevision==0 ) zRevision = "current"; | |
| 3995 | + ls_cmd_rev(zRevision,1,1,0,1,bTreeInfo,0,0); | |
| 3996 | + return; | |
| 3997 | + } | |
| 3978 | 3998 | zLimit = find_option("limit","n",1); |
| 3979 | 3999 | zOrig = find_option("origin","o",1); |
| 3980 | 4000 | showLog = find_option("log","l",0)!=0; |
| 3981 | 4001 | if( find_option("ignore-trailing-space","Z",0)!=0 ){ |
| 3982 | 4002 | annFlags = DIFF_IGNORE_EOLWS; |
| 3983 | 4003 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -1045,11 +1045,11 @@ | |
| 1045 | |
| 1046 | /************************* DiffBuilderDebug ********************************/ |
| 1047 | /* This version of DiffBuilder is used for debugging the diff and diff |
| 1048 | ** diff formatter logic. It is accessed using the (undocumented) --debug |
| 1049 | ** option to the diff command. The output is human-readable text that |
| 1050 | ** describes the various method calls that are invoked agains the DiffBuilder |
| 1051 | ** object. |
| 1052 | */ |
| 1053 | static void dfdebugSkip(DiffBuilder *p, unsigned int n, int isFinal){ |
| 1054 | blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)%s\n", |
| 1055 | n, p->lnLeft+1, p->lnLeft+n, p->lnRight+1, p->lnRight+n, |
| @@ -3622,11 +3622,11 @@ | |
| 3622 | iLimit = 0; |
| 3623 | mxTime = current_time_in_milliseconds()+1000; |
| 3624 | } |
| 3625 | db_begin_transaction(); |
| 3626 | |
| 3627 | /* Get the artifact ID for the check-in begin analyzed */ |
| 3628 | if( zRevision ){ |
| 3629 | cid = name_to_typed_rid(zRevision, "ci"); |
| 3630 | }else{ |
| 3631 | db_must_be_within_tree(); |
| 3632 | cid = db_lget_int("checkout", 0); |
| @@ -3939,10 +3939,15 @@ | |
| 3939 | ** (example: "-o trunk") then these commands show changes moving towards |
| 3940 | ** that alternative origin. Thus using "-o trunk" on an historical version |
| 3941 | ** of the file shows the first time each line in the file was changed or |
| 3942 | ** removed by any subsequent check-in. |
| 3943 | ** |
| 3944 | ** Options: |
| 3945 | ** --filevers Show file version numbers rather than |
| 3946 | ** check-in versions |
| 3947 | ** -r|--revision VERSION The specific check-in containing the file |
| 3948 | ** -l|--log List all versions analyzed |
| @@ -3954,10 +3959,13 @@ | |
| 3954 | ** root of the repository. Set to the name of |
| 3955 | ** the main branch (usually "trunk") or |
| 3956 | ** similar for a reverse annotation. |
| 3957 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 3958 | ** -Z|--ignore-trailing-space Ignore whitespace at line end |
| 3959 | ** |
| 3960 | ** See also: [[info]], [[finfo]], [[timeline]] |
| 3961 | */ |
| 3962 | void annotate_cmd(void){ |
| 3963 | const char *zRevision; /* Revision name, or NULL for current check-in */ |
| @@ -3967,16 +3975,28 @@ | |
| 3967 | const char *zOrig; /* The value for -o|--origin */ |
| 3968 | int showLog; /* True to show the log */ |
| 3969 | int fileVers; /* Show file version instead of check-in versions */ |
| 3970 | u64 annFlags = 0; /* Flags to control annotation properties */ |
| 3971 | int bBlame = 0; /* True for BLAME output. False for ANNOTATE. */ |
| 3972 | int szHash; /* Display size of a version hash */ |
| 3973 | Blob treename; /* Name of file to be annotated */ |
| 3974 | char *zFilename; /* Name of file to be annotated */ |
| 3975 | |
| 3976 | bBlame = g.argv[1][0]!='a'; |
| 3977 | zRevision = find_option("revision","r",1); |
| 3978 | zLimit = find_option("limit","n",1); |
| 3979 | zOrig = find_option("origin","o",1); |
| 3980 | showLog = find_option("log","l",0)!=0; |
| 3981 | if( find_option("ignore-trailing-space","Z",0)!=0 ){ |
| 3982 | annFlags = DIFF_IGNORE_EOLWS; |
| 3983 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -1045,11 +1045,11 @@ | |
| 1045 | |
| 1046 | /************************* DiffBuilderDebug ********************************/ |
| 1047 | /* This version of DiffBuilder is used for debugging the diff and diff |
| 1048 | ** diff formatter logic. It is accessed using the (undocumented) --debug |
| 1049 | ** option to the diff command. The output is human-readable text that |
| 1050 | ** describes the various method calls that are invoked against the DiffBuilder |
| 1051 | ** object. |
| 1052 | */ |
| 1053 | static void dfdebugSkip(DiffBuilder *p, unsigned int n, int isFinal){ |
| 1054 | blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)%s\n", |
| 1055 | n, p->lnLeft+1, p->lnLeft+n, p->lnRight+1, p->lnRight+n, |
| @@ -3622,11 +3622,11 @@ | |
| 3622 | iLimit = 0; |
| 3623 | mxTime = current_time_in_milliseconds()+1000; |
| 3624 | } |
| 3625 | db_begin_transaction(); |
| 3626 | |
| 3627 | /* Get the artifact ID for the check-in being analyzed */ |
| 3628 | if( zRevision ){ |
| 3629 | cid = name_to_typed_rid(zRevision, "ci"); |
| 3630 | }else{ |
| 3631 | db_must_be_within_tree(); |
| 3632 | cid = db_lget_int("checkout", 0); |
| @@ -3939,10 +3939,15 @@ | |
| 3939 | ** (example: "-o trunk") then these commands show changes moving towards |
| 3940 | ** that alternative origin. Thus using "-o trunk" on an historical version |
| 3941 | ** of the file shows the first time each line in the file was changed or |
| 3942 | ** removed by any subsequent check-in. |
| 3943 | ** |
| 3944 | ** With -t or -T, the "blame" and "praise" commands show for each file the |
| 3945 | ** latest (relative to the revision given by -r) check-in that modified it and |
| 3946 | ** the check-in's author. If not given, the revision defaults to "current" for |
| 3947 | ** a check-out. Option -T additionally shows a comment snippet for the check-in. |
| 3948 | ** |
| 3949 | ** Options: |
| 3950 | ** --filevers Show file version numbers rather than |
| 3951 | ** check-in versions |
| 3952 | ** -r|--revision VERSION The specific check-in containing the file |
| 3953 | ** -l|--log List all versions analyzed |
| @@ -3954,10 +3959,13 @@ | |
| 3959 | ** root of the repository. Set to the name of |
| 3960 | ** the main branch (usually "trunk") or |
| 3961 | ** similar for a reverse annotation. |
| 3962 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 3963 | ** -Z|--ignore-trailing-space Ignore whitespace at line end |
| 3964 | ** -t Show latest check-in and its author for each |
| 3965 | ** tracked file in the tree as of VERSION |
| 3966 | ** -T Like -t, plus comment snippet |
| 3967 | ** |
| 3968 | ** See also: [[info]], [[finfo]], [[timeline]] |
| 3969 | */ |
| 3970 | void annotate_cmd(void){ |
| 3971 | const char *zRevision; /* Revision name, or NULL for current check-in */ |
| @@ -3967,16 +3975,28 @@ | |
| 3975 | const char *zOrig; /* The value for -o|--origin */ |
| 3976 | int showLog; /* True to show the log */ |
| 3977 | int fileVers; /* Show file version instead of check-in versions */ |
| 3978 | u64 annFlags = 0; /* Flags to control annotation properties */ |
| 3979 | int bBlame = 0; /* True for BLAME output. False for ANNOTATE. */ |
| 3980 | int bTreeInfo = 0; /* Show for the entire tree: 1=checkin, 2=with comment */ |
| 3981 | int szHash; /* Display size of a version hash */ |
| 3982 | Blob treename; /* Name of file to be annotated */ |
| 3983 | char *zFilename; /* Name of file to be annotated */ |
| 3984 | |
| 3985 | bBlame = g.argv[1][0]!='a'; |
| 3986 | if( find_option("t","t",0)!=0 ) bTreeInfo = 1; |
| 3987 | if( find_option("T","T",0)!=0 ) bTreeInfo = 2; |
| 3988 | zRevision = find_option("revision","r",1); |
| 3989 | if( bBlame && bTreeInfo ){ |
| 3990 | if( find_repository_option()!=0 && zRevision==0 ){ |
| 3991 | fossil_fatal("the -r is required in addition to -R"); |
| 3992 | } |
| 3993 | db_find_and_open_repository(0, 0); |
| 3994 | if( zRevision==0 ) zRevision = "current"; |
| 3995 | ls_cmd_rev(zRevision,1,1,0,1,bTreeInfo,0,0); |
| 3996 | return; |
| 3997 | } |
| 3998 | zLimit = find_option("limit","n",1); |
| 3999 | zOrig = find_option("origin","o",1); |
| 4000 | showLog = find_option("log","l",0)!=0; |
| 4001 | if( find_option("ignore-trailing-space","Z",0)!=0 ){ |
| 4002 | annFlags = DIFF_IGNORE_EOLWS; |
| 4003 |
+5
-5
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -212,11 +212,11 @@ | ||
| 212 | 212 | blob_appendf(pOut, "%.*c %.*s %.*c versus %.*c %.*s %.*c\n", |
| 213 | 213 | (w-n1+10)/2, '=', n1, zLeft, (w-n1+1)/2, '=', |
| 214 | 214 | (w-n2)/2, '=', n2, zRight, (w-n2+1)/2, '='); |
| 215 | 215 | } |
| 216 | 216 | }else{ |
| 217 | - blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight); | |
| 217 | + blob_appendf(pOut, "--- %s\t\n+++ %s\t\n", zLeft, zRight); | |
| 218 | 218 | } |
| 219 | 219 | } |
| 220 | 220 | |
| 221 | 221 | |
| 222 | 222 | /* |
| @@ -813,11 +813,11 @@ | ||
| 813 | 813 | Blob *pOut /* Blob to output diff instead of stdout */ |
| 814 | 814 | ){ |
| 815 | 815 | int vid; |
| 816 | 816 | Blob sql; |
| 817 | 817 | Stmt q; |
| 818 | - int asNewFile; /* Treat non-existant files as empty files */ | |
| 818 | + int asNewFile; /* Treat non-existent files as empty files */ | |
| 819 | 819 | int isNumStat; /* True for --numstat */ |
| 820 | 820 | |
| 821 | 821 | asNewFile = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT|DIFF_HTML))!=0; |
| 822 | 822 | isNumStat = (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_TCL|DIFF_HTML))!=0; |
| 823 | 823 | vid = db_lget_int("checkout", 0); |
| @@ -1329,11 +1329,11 @@ | ||
| 1329 | 1329 | ** |
| 1330 | 1330 | ** The "--binary" option causes files matching the glob PATTERN to be treated |
| 1331 | 1331 | ** as binary when considering if they should be used with the external diff |
| 1332 | 1332 | ** program. This option overrides the "binary-glob" setting. |
| 1333 | 1333 | ** |
| 1334 | -** These command show differences between managed files. Use the "fossil xdiff" | |
| 1334 | +** These commands show differences between managed files. Use the "fossil xdiff" | |
| 1335 | 1335 | ** command to see differences in unmanaged files. |
| 1336 | 1336 | ** |
| 1337 | 1337 | ** Options: |
| 1338 | 1338 | ** --binary PATTERN Treat files that match the glob PATTERN |
| 1339 | 1339 | ** as binary |
| @@ -1357,12 +1357,12 @@ | ||
| 1357 | 1357 | ** --invert Invert the diff |
| 1358 | 1358 | ** --json Output formatted as JSON |
| 1359 | 1359 | ** -n|--linenum Show line numbers |
| 1360 | 1360 | ** -N|--new-file Alias for --verbose |
| 1361 | 1361 | ** -s|--numstat Show the number of added and deleted lines per |
| 1362 | -** file, omitting the diff. When combined with | |
| 1363 | -** --brief, show only the total row. | |
| 1362 | +** file, omitting the diff. When combined | |
| 1363 | +** with --brief, show only the total row. | |
| 1364 | 1364 | ** -y|--side-by-side Side-by-side diff |
| 1365 | 1365 | ** --strip-trailing-cr Strip trailing CR |
| 1366 | 1366 | ** --tcl Tcl-formatted output used internally by --tk |
| 1367 | 1367 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1368 | 1368 | ** --tk Launch a Tcl/Tk GUI for display |
| 1369 | 1369 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -212,11 +212,11 @@ | |
| 212 | blob_appendf(pOut, "%.*c %.*s %.*c versus %.*c %.*s %.*c\n", |
| 213 | (w-n1+10)/2, '=', n1, zLeft, (w-n1+1)/2, '=', |
| 214 | (w-n2)/2, '=', n2, zRight, (w-n2+1)/2, '='); |
| 215 | } |
| 216 | }else{ |
| 217 | blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | |
| 222 | /* |
| @@ -813,11 +813,11 @@ | |
| 813 | Blob *pOut /* Blob to output diff instead of stdout */ |
| 814 | ){ |
| 815 | int vid; |
| 816 | Blob sql; |
| 817 | Stmt q; |
| 818 | int asNewFile; /* Treat non-existant files as empty files */ |
| 819 | int isNumStat; /* True for --numstat */ |
| 820 | |
| 821 | asNewFile = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT|DIFF_HTML))!=0; |
| 822 | isNumStat = (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_TCL|DIFF_HTML))!=0; |
| 823 | vid = db_lget_int("checkout", 0); |
| @@ -1329,11 +1329,11 @@ | |
| 1329 | ** |
| 1330 | ** The "--binary" option causes files matching the glob PATTERN to be treated |
| 1331 | ** as binary when considering if they should be used with the external diff |
| 1332 | ** program. This option overrides the "binary-glob" setting. |
| 1333 | ** |
| 1334 | ** These command show differences between managed files. Use the "fossil xdiff" |
| 1335 | ** command to see differences in unmanaged files. |
| 1336 | ** |
| 1337 | ** Options: |
| 1338 | ** --binary PATTERN Treat files that match the glob PATTERN |
| 1339 | ** as binary |
| @@ -1357,12 +1357,12 @@ | |
| 1357 | ** --invert Invert the diff |
| 1358 | ** --json Output formatted as JSON |
| 1359 | ** -n|--linenum Show line numbers |
| 1360 | ** -N|--new-file Alias for --verbose |
| 1361 | ** -s|--numstat Show the number of added and deleted lines per |
| 1362 | ** file, omitting the diff. When combined with |
| 1363 | ** --brief, show only the total row. |
| 1364 | ** -y|--side-by-side Side-by-side diff |
| 1365 | ** --strip-trailing-cr Strip trailing CR |
| 1366 | ** --tcl Tcl-formatted output used internally by --tk |
| 1367 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1368 | ** --tk Launch a Tcl/Tk GUI for display |
| 1369 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -212,11 +212,11 @@ | |
| 212 | blob_appendf(pOut, "%.*c %.*s %.*c versus %.*c %.*s %.*c\n", |
| 213 | (w-n1+10)/2, '=', n1, zLeft, (w-n1+1)/2, '=', |
| 214 | (w-n2)/2, '=', n2, zRight, (w-n2+1)/2, '='); |
| 215 | } |
| 216 | }else{ |
| 217 | blob_appendf(pOut, "--- %s\t\n+++ %s\t\n", zLeft, zRight); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | |
| 222 | /* |
| @@ -813,11 +813,11 @@ | |
| 813 | Blob *pOut /* Blob to output diff instead of stdout */ |
| 814 | ){ |
| 815 | int vid; |
| 816 | Blob sql; |
| 817 | Stmt q; |
| 818 | int asNewFile; /* Treat non-existent files as empty files */ |
| 819 | int isNumStat; /* True for --numstat */ |
| 820 | |
| 821 | asNewFile = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT|DIFF_HTML))!=0; |
| 822 | isNumStat = (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_TCL|DIFF_HTML))!=0; |
| 823 | vid = db_lget_int("checkout", 0); |
| @@ -1329,11 +1329,11 @@ | |
| 1329 | ** |
| 1330 | ** The "--binary" option causes files matching the glob PATTERN to be treated |
| 1331 | ** as binary when considering if they should be used with the external diff |
| 1332 | ** program. This option overrides the "binary-glob" setting. |
| 1333 | ** |
| 1334 | ** These commands show differences between managed files. Use the "fossil xdiff" |
| 1335 | ** command to see differences in unmanaged files. |
| 1336 | ** |
| 1337 | ** Options: |
| 1338 | ** --binary PATTERN Treat files that match the glob PATTERN |
| 1339 | ** as binary |
| @@ -1357,12 +1357,12 @@ | |
| 1357 | ** --invert Invert the diff |
| 1358 | ** --json Output formatted as JSON |
| 1359 | ** -n|--linenum Show line numbers |
| 1360 | ** -N|--new-file Alias for --verbose |
| 1361 | ** -s|--numstat Show the number of added and deleted lines per |
| 1362 | ** file, omitting the diff. When combined |
| 1363 | ** with --brief, show only the total row. |
| 1364 | ** -y|--side-by-side Side-by-side diff |
| 1365 | ** --strip-trailing-cr Strip trailing CR |
| 1366 | ** --tcl Tcl-formatted output used internally by --tk |
| 1367 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1368 | ** --tk Launch a Tcl/Tk GUI for display |
| 1369 |
+115
-73
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -25,11 +25,11 @@ | ||
| 25 | 25 | #include "dispatch.h" |
| 26 | 26 | |
| 27 | 27 | #if INTERFACE |
| 28 | 28 | /* |
| 29 | 29 | ** An instance of this object defines everything we need to know about an |
| 30 | -** individual command, webpage, or setting. | |
| 30 | +** individual command, webpage, setting, or help topic. | |
| 31 | 31 | */ |
| 32 | 32 | struct CmdOrPage { |
| 33 | 33 | const char *zName; /* Name. Webpages start with "/". Commands do not */ |
| 34 | 34 | void (*xFunc)(void); /* Implementation function, or NULL for settings */ |
| 35 | 35 | const char *zHelp; /* Raw help text */ |
| @@ -39,31 +39,32 @@ | ||
| 39 | 39 | |
| 40 | 40 | /*************************************************************************** |
| 41 | 41 | ** These macros must match similar macros in mkindex.c |
| 42 | 42 | ** Allowed values for CmdOrPage.eCmdFlags. |
| 43 | 43 | */ |
| 44 | -#define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ | |
| 45 | -#define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ | |
| 46 | -#define CMDFLAG_TEST 0x0004 /* Commands for testing only */ | |
| 47 | -#define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ | |
| 48 | -#define CMDFLAG_COMMAND 0x0010 /* A command */ | |
| 49 | -#define CMDFLAG_SETTING 0x0020 /* A setting */ | |
| 50 | -#define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ | |
| 51 | -#define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ | |
| 52 | -#define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ | |
| 53 | -#define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret POST content */ | |
| 54 | -/* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ | |
| 55 | -#define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ | |
| 56 | -#define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ | |
| 57 | -#define CMDFLAG_ALIAS 0x2000 /* Command aliases */ | |
| 58 | -#define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ | |
| 59 | -#define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */ | |
| 44 | +#define CMDFLAG_1ST_TIER 0x000001 /* Most important commands */ | |
| 45 | +#define CMDFLAG_2ND_TIER 0x000002 /* Obscure and seldom used commands */ | |
| 46 | +#define CMDFLAG_TEST 0x000004 /* Commands for testing only */ | |
| 47 | +#define CMDFLAG_WEBPAGE 0x000008 /* Web pages */ | |
| 48 | +#define CMDFLAG_COMMAND 0x000010 /* A command */ | |
| 49 | +#define CMDFLAG_SETTING 0x000020 /* A setting */ | |
| 50 | +#define CMDFLAG_VERSIONABLE 0x000040 /* A versionable setting */ | |
| 51 | +#define CMDFLAG_BLOCKTEXT 0x000080 /* Multi-line text setting */ | |
| 52 | +#define CMDFLAG_BOOLEAN 0x000100 /* A boolean setting */ | |
| 53 | +#define CMDFLAG_RAWCONTENT 0x000200 /* Do not interpret POST content */ | |
| 54 | +/* NOTE: 0x000400 = CMDFLAG_SENSITIVE in mkindex.c! */ | |
| 55 | +#define CMDFLAG_HIDDEN 0x000800 /* Elide from most listings */ | |
| 56 | +#define CMDFLAG_LDAVG_EXEMPT 0x001000 /* Exempt from load_control() */ | |
| 57 | +#define CMDFLAG_ALIAS 0x002000 /* Command aliases */ | |
| 58 | +#define CMDFLAG_KEEPEMPTY 0x004000 /* Do not unset empty settings */ | |
| 59 | +#define CMDFLAG_ABBREVSUBCMD 0x008000 /* Help text abbreviates subcommands */ | |
| 60 | +#define CMDFLAG_TOPIC 0x010000 /* A help topic */ | |
| 60 | 61 | /**************************************************************************/ |
| 61 | 62 | |
| 62 | 63 | /* Values for the 2nd parameter to dispatch_name_search() */ |
| 63 | -#define CMDFLAG_ANY 0x0038 /* Match anything */ | |
| 64 | -#define CMDFLAG_PREFIX 0x0200 /* Prefix match is OK */ | |
| 64 | +#define CMDFLAG_ANY 0x010038 /* Match anything */ | |
| 65 | +#define CMDFLAG_PREFIX 0x000200 /* Prefix match is OK */ | |
| 65 | 66 | |
| 66 | 67 | #endif /* INTERFACE */ |
| 67 | 68 | |
| 68 | 69 | /* |
| 69 | 70 | ** The page_index.h file contains the definition for aCommand[] - an array |
| @@ -461,11 +462,15 @@ | ||
| 461 | 462 | || hasGap(zHelp+nIndent,i-nIndent) ){ |
| 462 | 463 | iLevel++; |
| 463 | 464 | aIndent[iLevel] = nIndent; |
| 464 | 465 | azEnd[iLevel] = zEndDL; |
| 465 | 466 | wantP = 0; |
| 466 | - blob_append(pHtml, "<blockquote><dl>\n", -1); | |
| 467 | + if( isDT ){ | |
| 468 | + blob_append(pHtml, "<blockquote><dl>\n", -1); | |
| 469 | + }else{ | |
| 470 | + blob_append(pHtml, "<blockquote><dl class=\"helpOptions\">\n", -1); | |
| 471 | + } | |
| 467 | 472 | }else if( azEnd[iLevel]==zEndDL ){ |
| 468 | 473 | iLevel++; |
| 469 | 474 | aIndent[iLevel] = nIndent; |
| 470 | 475 | azEnd[iLevel] = zEndDD; |
| 471 | 476 | if( wantP ){ |
| @@ -578,10 +583,11 @@ | ||
| 578 | 583 | if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n"); |
| 579 | 584 | if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n"); |
| 580 | 585 | if( mask & CMDFLAG_TEST ) fossil_print(" * Test commands\n"); |
| 581 | 586 | if( mask & CMDFLAG_WEBPAGE ) fossil_print(" * Web pages\n"); |
| 582 | 587 | if( mask & CMDFLAG_SETTING ) fossil_print(" * Settings\n"); |
| 588 | + if( mask & CMDFLAG_TOPIC ) fossil_print(" * Help Topic\n"); | |
| 583 | 589 | if( useHtml ){ |
| 584 | 590 | fossil_print("-->\n"); |
| 585 | 591 | fossil_print("<!-- start_all_help -->\n"); |
| 586 | 592 | }else{ |
| 587 | 593 | fossil_print("---\n"); |
| @@ -644,10 +650,11 @@ | ||
| 644 | 650 | ** Defaults to just the CLI commands. Specify --www to see only the |
| 645 | 651 | ** web pages, or --everything to see both commands and pages. |
| 646 | 652 | ** |
| 647 | 653 | ** Options: |
| 648 | 654 | ** -a|--aliases Show aliases |
| 655 | +** -c|--topics Show help topics | |
| 649 | 656 | ** -e|--everything Show all commands and pages. Omit aliases to |
| 650 | 657 | ** avoid duplicates. |
| 651 | 658 | ** -h|--html Transform output to HTML |
| 652 | 659 | ** -o|--options Show global options |
| 653 | 660 | ** -r|--raw No output formatting |
| @@ -663,20 +670,23 @@ | ||
| 663 | 670 | if( find_option("www","w",0) ){ |
| 664 | 671 | mask = CMDFLAG_WEBPAGE; |
| 665 | 672 | } |
| 666 | 673 | if( find_option("everything","e",0) ){ |
| 667 | 674 | mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 668 | - CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST; | |
| 675 | + CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST | CMDFLAG_TOPIC; | |
| 669 | 676 | } |
| 670 | 677 | if( find_option("settings","s",0) ){ |
| 671 | 678 | mask = CMDFLAG_SETTING; |
| 672 | 679 | } |
| 673 | 680 | if( find_option("aliases","a",0) ){ |
| 674 | 681 | mask = CMDFLAG_ALIAS; |
| 675 | 682 | } |
| 676 | 683 | if( find_option("test","t",0) ){ |
| 677 | 684 | mask |= CMDFLAG_TEST; |
| 685 | + } | |
| 686 | + if( find_option("topics","c",0) ){ | |
| 687 | + mask |= CMDFLAG_TOPIC; | |
| 678 | 688 | } |
| 679 | 689 | display_all_help(mask, useHtml, rawOut); |
| 680 | 690 | } |
| 681 | 691 | |
| 682 | 692 | /* |
| @@ -710,10 +720,12 @@ | ||
| 710 | 720 | countCmds( CMDFLAG_TEST )); |
| 711 | 721 | fossil_print("web-pages: %4d\n", |
| 712 | 722 | countCmds( CMDFLAG_WEBPAGE )); |
| 713 | 723 | fossil_print("settings: %4d\n", |
| 714 | 724 | countCmds( CMDFLAG_SETTING )); |
| 725 | + fossil_print("help-topics: %4d\n", | |
| 726 | + countCmds( CMDFLAG_TOPIC )); | |
| 715 | 727 | fossil_print("total entries: %4d\n", MX_COMMAND); |
| 716 | 728 | } |
| 717 | 729 | |
| 718 | 730 | /* |
| 719 | 731 | ** Compute an estimate of the edit-distance between to input strings. |
| @@ -892,10 +904,12 @@ | ||
| 892 | 904 | /* Some of the webpages require query parameters in order to work. |
| 893 | 905 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 894 | 906 | @ <h1>The "%h(pCmd->zName)" page:</h1> |
| 895 | 907 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| 896 | 908 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 909 | + }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_TOPIC)!=0 ){ | |
| 910 | + @ <h1>The "%h(pCmd->zName)" help topic:</h1> | |
| 897 | 911 | }else{ |
| 898 | 912 | @ <h1>The "%h(pCmd->zName)" command:</h1> |
| 899 | 913 | } |
| 900 | 914 | if( rc==1 || (rc==2 && zCmd[0]=='/') ){ |
| 901 | 915 | if( zCmd && help_is_platform_command(zCmd) ){ |
| @@ -933,11 +947,11 @@ | ||
| 933 | 947 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ |
| 934 | 948 | style_header("Help"); |
| 935 | 949 | search_screen(SRCH_HELP, 0x02); |
| 936 | 950 | |
| 937 | 951 | @ <a name='commands'></a> |
| 938 | - @ <h1>Available commands:</h1> | |
| 952 | + @ <h1>Commands:</h1> | |
| 939 | 953 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 940 | 954 | @ <ul> |
| 941 | 955 | /* Fill in help string buckets */ |
| 942 | 956 | for(i=0; i<MX_COMMAND; i++){ |
| 943 | 957 | if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; |
| @@ -946,11 +960,13 @@ | ||
| 946 | 960 | for(i=0; i<MX_COMMAND; i++){ |
| 947 | 961 | const char *z = aCommand[i].zName; |
| 948 | 962 | const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :""; |
| 949 | 963 | const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":""; |
| 950 | 964 | if( '/'==*z || strncmp(z,"test",4)==0 ) continue; |
| 951 | - if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue; | |
| 965 | + if( (aCommand[i].eCmdFlags & (CMDFLAG_SETTING|CMDFLAG_TOPIC))!=0 ){ | |
| 966 | + continue; | |
| 967 | + } | |
| 952 | 968 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 953 | 969 | else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue; |
| 954 | 970 | @ <li><a href="%R/help/%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a> |
| 955 | 971 | /* Output aliases */ |
| 956 | 972 | if( occHelp[aCommand[i].iHelp] > 1 ){ |
| @@ -978,11 +994,11 @@ | ||
| 978 | 994 | } |
| 979 | 995 | |
| 980 | 996 | @ </ul></div> |
| 981 | 997 | |
| 982 | 998 | @ <a name='webpages'></a> |
| 983 | - @ <h1>Available web UI pages:</h1> | |
| 999 | + @ <h1>Web pages:</h1> | |
| 984 | 1000 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 985 | 1001 | @ <ul> |
| 986 | 1002 | for(i=0; i<MX_COMMAND; i++){ |
| 987 | 1003 | const char *z = aCommand[i].zName; |
| 988 | 1004 | if( '/'!=*z ) continue; |
| @@ -993,33 +1009,48 @@ | ||
| 993 | 1009 | @ <li>%s(z+1)</li> |
| 994 | 1010 | } |
| 995 | 1011 | } |
| 996 | 1012 | @ </ul></div> |
| 997 | 1013 | |
| 998 | - @ <a name='unsupported'></a> | |
| 999 | - @ <h1>Unsupported commands:</h1> | |
| 1014 | + @ <a name='settings'></a> | |
| 1015 | + @ <h1>Settings:</h1> | |
| 1000 | 1016 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1001 | 1017 | @ <ul> |
| 1002 | 1018 | for(i=0; i<MX_COMMAND; i++){ |
| 1003 | 1019 | const char *z = aCommand[i].zName; |
| 1004 | - if( strncmp(z,"test",4)!=0 ) continue; | |
| 1020 | + if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue; | |
| 1005 | 1021 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 1006 | 1022 | if( aCommand[i].zHelp[0] ){ |
| 1007 | 1023 | @ <li><a href="%R/help/%s(z)">%s(z)</a></li> |
| 1008 | 1024 | }else{ |
| 1009 | 1025 | @ <li>%s(z)</li> |
| 1010 | 1026 | } |
| 1011 | 1027 | } |
| 1012 | 1028 | @ </ul></div> |
| 1013 | 1029 | |
| 1014 | - @ <a name='settings'></a> | |
| 1015 | - @ <h1>Settings:</h1> | |
| 1030 | + @ <a name='topics'></a> | |
| 1031 | + @ <h1>Other Miscellaneous Help Topics:</h1> | |
| 1032 | + @ <div class="columns" style="column-width: %s(zWidth);"> | |
| 1033 | + @ <ul> | |
| 1034 | + for(i=0; i<MX_COMMAND; i++){ | |
| 1035 | + const char *z = aCommand[i].zName; | |
| 1036 | + if( (aCommand[i].eCmdFlags & CMDFLAG_TOPIC)==0 ) continue; | |
| 1037 | + if( aCommand[i].zHelp[0] ){ | |
| 1038 | + @ <li><a href="%R/help/%s(z)">%s(z)</a></li> | |
| 1039 | + }else{ | |
| 1040 | + @ <li>%s(z)</li> | |
| 1041 | + } | |
| 1042 | + } | |
| 1043 | + @ </ul></div> | |
| 1044 | + | |
| 1045 | + @ <a name='unsupported'></a> | |
| 1046 | + @ <h1>Unsupported and Testing Commands:</h1> | |
| 1016 | 1047 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1017 | 1048 | @ <ul> |
| 1018 | 1049 | for(i=0; i<MX_COMMAND; i++){ |
| 1019 | 1050 | const char *z = aCommand[i].zName; |
| 1020 | - if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue; | |
| 1051 | + if( strncmp(z,"test",4)!=0 ) continue; | |
| 1021 | 1052 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 1022 | 1053 | if( aCommand[i].zHelp[0] ){ |
| 1023 | 1054 | @ <li><a href="%R/help/%s(z)">%s(z)</a></li> |
| 1024 | 1055 | }else{ |
| 1025 | 1056 | @ <li>%s(z)</li> |
| @@ -1059,10 +1090,12 @@ | ||
| 1059 | 1090 | zDesc = "2nd tier command"; |
| 1060 | 1091 | }else if( e & CMDFLAG_ALIAS ){ |
| 1061 | 1092 | zDesc = "alias"; |
| 1062 | 1093 | }else if( e & CMDFLAG_TEST ){ |
| 1063 | 1094 | zDesc = "test command"; |
| 1095 | + }else if( e & CMDFLAG_TOPIC ){ | |
| 1096 | + zDesc = "help-topic"; | |
| 1064 | 1097 | }else if( e & CMDFLAG_WEBPAGE ){ |
| 1065 | 1098 | if( e & CMDFLAG_RAWCONTENT ){ |
| 1066 | 1099 | zDesc = "raw-content web page"; |
| 1067 | 1100 | }else{ |
| 1068 | 1101 | zDesc = "web page"; |
| @@ -1155,11 +1188,11 @@ | ||
| 1155 | 1188 | Blob *pOut, /* Write simplified help text here */ |
| 1156 | 1189 | const char *zTopic, /* TOPIC */ |
| 1157 | 1190 | const char *zSubtopic, /* SUBTOPIC */ |
| 1158 | 1191 | int bAbbrevSubcmd /* True if z[] contains abbreviated subcommands */ |
| 1159 | 1192 | ){ |
| 1160 | - Blob in, line; //, subsection; | |
| 1193 | + Blob in, line;/*, subsection;*/ | |
| 1161 | 1194 | int n = 0; |
| 1162 | 1195 | char *zQTop = re_quote(zTopic); |
| 1163 | 1196 | char *zQSub = re_quote(zSubtopic); |
| 1164 | 1197 | char *zPattern; |
| 1165 | 1198 | ReCompiled *pRe = 0; |
| @@ -1392,46 +1425,44 @@ | ||
| 1392 | 1425 | multi_column_list(aCmd, nCmd); |
| 1393 | 1426 | } |
| 1394 | 1427 | } |
| 1395 | 1428 | |
| 1396 | 1429 | /* |
| 1397 | -** Documentation on universal command-line options. | |
| 1430 | +** TOPIC: options | |
| 1431 | +** | |
| 1432 | +** Command-line options common to all commands: | |
| 1433 | +** | |
| 1434 | +** --args FILENAME Read additional arguments and options from FILENAME | |
| 1435 | +** --case-sensitive BOOL Set case sensitivity for file names | |
| 1436 | +** --cgitrace Active CGI tracing | |
| 1437 | +** --chdir PATH Change to PATH before performing any operations | |
| 1438 | +** --errorlog FILENAME Log errors to FILENAME | |
| 1439 | +** -?|--help Show help on the command rather than running it | |
| 1440 | +** --httptrace Trace outbound HTTP requests | |
| 1441 | +** --localtime Display times using the local timezone | |
| 1442 | +** --nocgi Do not act as CGI | |
| 1443 | +** --no-th-hook Do not run TH1 hooks | |
| 1444 | +** -q|--quiet Reduce the amount of output | |
| 1445 | +** --sqlstats Show SQL usage statistics when done | |
| 1446 | +** --sqltrace Trace all SQL commands | |
| 1447 | +** --sshtrace Trace SSH activity | |
| 1448 | +** --ssl-identity NAME Set the SSL identity to NAME | |
| 1449 | +** --systemtrace Trace calls to system() | |
| 1450 | +** -U|--user USER Make the default user be USER | |
| 1451 | +** --utc Display times using UTC | |
| 1452 | +** --vfs NAME Cause SQLite to use the NAME VFS | |
| 1453 | +** | |
| 1454 | +** Additional options available on most commands that use network I/O: | |
| 1455 | +** | |
| 1456 | +** --accept-any-cert Disable server SSL cdert validation. Accept any SSL | |
| 1457 | +** cert that the server provides. WARNING: Unsafe! | |
| 1458 | +** Testing and debugging use only! | |
| 1459 | +** --ipv4 Use only IPv4. Disable IPv6 support. | |
| 1460 | +** --ipv6 Use only IPv6. Disable IPv4 support. | |
| 1461 | +** --nosync Disable autosync for the current command. | |
| 1462 | +** --proxy URL Specify the HTTP proxy to use. URL can be "off". | |
| 1398 | 1463 | */ |
| 1399 | -/* @-comment: # */ | |
| 1400 | -static const char zOptions[] = | |
| 1401 | -@ Command-line options common to all commands: | |
| 1402 | -@ | |
| 1403 | -@ --args FILENAME Read additional arguments and options from FILENAME | |
| 1404 | -@ --case-sensitive BOOL Set case sensitivity for file names | |
| 1405 | -@ --cgitrace Active CGI tracing | |
| 1406 | -@ --chdir PATH Change to PATH before performing any operations | |
| 1407 | -@ --errorlog FILENAME Log errors to FILENAME | |
| 1408 | -@ -?|--help Show help on the command rather than running it | |
| 1409 | -@ --httptrace Trace outbound HTTP requests | |
| 1410 | -@ --localtime Display times using the local timezone | |
| 1411 | -@ --nocgi Do not act as CGI | |
| 1412 | -@ --no-th-hook Do not run TH1 hooks | |
| 1413 | -@ --quiet Reduce the amount of output | |
| 1414 | -@ --sqlstats Show SQL usage statistics when done | |
| 1415 | -@ --sqltrace Trace all SQL commands | |
| 1416 | -@ --sshtrace Trace SSH activity | |
| 1417 | -@ --ssl-identity NAME Set the SSL identity to NAME | |
| 1418 | -@ --systemtrace Trace calls to system() | |
| 1419 | -@ -U|--user USER Make the default user be USER | |
| 1420 | -@ --utc Display times using UTC | |
| 1421 | -@ --vfs NAME Cause SQLite to use the NAME VFS | |
| 1422 | -@ | |
| 1423 | -@ Additional options available on most commands that use network I/O: | |
| 1424 | -@ | |
| 1425 | -@ --accept-any-cert Disable server SSL cdert validation. Accept any SSL | |
| 1426 | -@ cert that the server provides. WARNING: Unsafe! | |
| 1427 | -@ Testing and debugging use only! | |
| 1428 | -@ --ipv4 Use only IPv4. Disable IPv6 support. | |
| 1429 | -@ --ipv6 Use only IPv6. Disable IPv4 support. | |
| 1430 | -@ --nosync Disable autosync for the current command. | |
| 1431 | -@ --proxy URL Specify the HTTP proxy to use. URL can be "off". | |
| 1432 | -; | |
| 1433 | 1464 | |
| 1434 | 1465 | /* |
| 1435 | 1466 | ** COMMAND: help |
| 1436 | 1467 | ** |
| 1437 | 1468 | ** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND] |
| @@ -1508,21 +1539,26 @@ | ||
| 1508 | 1539 | return; |
| 1509 | 1540 | } |
| 1510 | 1541 | else if( find_option("setting","s",0) ){ |
| 1511 | 1542 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1512 | 1543 | return; |
| 1544 | + } | |
| 1545 | + else if( find_option("topic","c",0) ){ | |
| 1546 | + command_list(CMDFLAG_TOPIC, verboseFlag, useHtml); | |
| 1547 | + return; | |
| 1513 | 1548 | } |
| 1514 | 1549 | else if( find_option("full","f",0) ){ |
| 1515 | 1550 | fossil_print("fossil commands:\n\n"); |
| 1516 | 1551 | command_list(CMDFLAG_1ST_TIER, verboseFlag, useHtml); |
| 1517 | 1552 | fossil_print("\nfossil auxiliary commands:\n\n"); |
| 1518 | 1553 | command_list(CMDFLAG_2ND_TIER, verboseFlag, useHtml); |
| 1519 | - fossil_print("\n%s", zOptions); | |
| 1520 | 1554 | fossil_print("\nfossil settings:\n\n"); |
| 1521 | 1555 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1522 | 1556 | fossil_print("\nfossil web pages:\n\n"); |
| 1523 | 1557 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| 1558 | + fossil_print("\nfossil miscellaneous help topics:\n\n"); | |
| 1559 | + command_list(CMDFLAG_TOPIC, verboseFlag, useHtml); | |
| 1524 | 1560 | fossil_print("\nfossil test commands (unsupported):\n\n"); |
| 1525 | 1561 | command_list(CMDFLAG_TEST, verboseFlag, useHtml); |
| 1526 | 1562 | if ( !verboseFlag ) { |
| 1527 | 1563 | fossil_print("\n"); |
| 1528 | 1564 | version_cmd(); |
| @@ -1529,18 +1565,23 @@ | ||
| 1529 | 1565 | } |
| 1530 | 1566 | return; |
| 1531 | 1567 | } |
| 1532 | 1568 | else if( find_option("everything","e",0) ){ |
| 1533 | 1569 | display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 1534 | - CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0); | |
| 1570 | + CMDFLAG_SETTING | CMDFLAG_TEST | CMDFLAG_TOPIC, | |
| 1571 | + useHtml, 0); | |
| 1535 | 1572 | return; |
| 1536 | 1573 | } |
| 1537 | 1574 | verify_all_options(); |
| 1575 | + zCmdOrPage = "help topic"; | |
| 1538 | 1576 | if( g.argc<3 ){ |
| 1539 | 1577 | if( bOptions ){ |
| 1540 | - fossil_print("%s", zOptions); | |
| 1541 | - return; | |
| 1578 | + zTopic = "options"; | |
| 1579 | + zSubtopic = 0; | |
| 1580 | + mask = CMDFLAG_TOPIC; | |
| 1581 | + bOptions = 0; | |
| 1582 | + goto find_and_show_help; | |
| 1542 | 1583 | } |
| 1543 | 1584 | z = g.argv[0]; |
| 1544 | 1585 | fossil_print( |
| 1545 | 1586 | "Usage: %s help TOPIC\n" |
| 1546 | 1587 | "Things to try:\n\n" |
| @@ -1560,14 +1601,13 @@ | ||
| 1560 | 1601 | if( isPage ){ |
| 1561 | 1602 | zCmdOrPage = "page"; |
| 1562 | 1603 | }else if( commandsFlag ){ |
| 1563 | 1604 | mask = CMDFLAG_COMMAND; |
| 1564 | 1605 | zCmdOrPage = "command"; |
| 1565 | - }else{ | |
| 1566 | - zCmdOrPage = "command or setting"; | |
| 1567 | 1606 | } |
| 1568 | - rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); | |
| 1607 | +find_and_show_help: | |
| 1608 | + rc = dispatch_name_search(zTopic, mask|CMDFLAG_PREFIX, &pCmd); | |
| 1569 | 1609 | if( rc ){ |
| 1570 | 1610 | int i, n; |
| 1571 | 1611 | const char *az[5]; |
| 1572 | 1612 | if( rc==1 ){ |
| 1573 | 1613 | if( help_is_platform_command(g.argv[2]) ){ |
| @@ -1805,10 +1845,12 @@ | ||
| 1805 | 1845 | zType = "command"; |
| 1806 | 1846 | }else if( pPage->eCmdFlags & CMDFLAG_WEBPAGE ){ |
| 1807 | 1847 | zType = "webpage"; |
| 1808 | 1848 | }else if( pPage->eCmdFlags & CMDFLAG_SETTING ){ |
| 1809 | 1849 | zType = "setting"; |
| 1850 | + }else if( pPage->eCmdFlags & CMDFLAG_TOPIC ){ | |
| 1851 | + zType = "help-topic"; | |
| 1810 | 1852 | } |
| 1811 | 1853 | sqlite3_result_text(ctx, zType, -1, SQLITE_STATIC); |
| 1812 | 1854 | break; |
| 1813 | 1855 | } |
| 1814 | 1856 | case 2: /* flags */ |
| 1815 | 1857 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -25,11 +25,11 @@ | |
| 25 | #include "dispatch.h" |
| 26 | |
| 27 | #if INTERFACE |
| 28 | /* |
| 29 | ** An instance of this object defines everything we need to know about an |
| 30 | ** individual command, webpage, or setting. |
| 31 | */ |
| 32 | struct CmdOrPage { |
| 33 | const char *zName; /* Name. Webpages start with "/". Commands do not */ |
| 34 | void (*xFunc)(void); /* Implementation function, or NULL for settings */ |
| 35 | const char *zHelp; /* Raw help text */ |
| @@ -39,31 +39,32 @@ | |
| 39 | |
| 40 | /*************************************************************************** |
| 41 | ** These macros must match similar macros in mkindex.c |
| 42 | ** Allowed values for CmdOrPage.eCmdFlags. |
| 43 | */ |
| 44 | #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ |
| 45 | #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ |
| 46 | #define CMDFLAG_TEST 0x0004 /* Commands for testing only */ |
| 47 | #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ |
| 48 | #define CMDFLAG_COMMAND 0x0010 /* A command */ |
| 49 | #define CMDFLAG_SETTING 0x0020 /* A setting */ |
| 50 | #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ |
| 51 | #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ |
| 52 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ |
| 53 | #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret POST content */ |
| 54 | /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ |
| 55 | #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ |
| 56 | #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ |
| 57 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ |
| 58 | #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ |
| 59 | #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */ |
| 60 | /**************************************************************************/ |
| 61 | |
| 62 | /* Values for the 2nd parameter to dispatch_name_search() */ |
| 63 | #define CMDFLAG_ANY 0x0038 /* Match anything */ |
| 64 | #define CMDFLAG_PREFIX 0x0200 /* Prefix match is OK */ |
| 65 | |
| 66 | #endif /* INTERFACE */ |
| 67 | |
| 68 | /* |
| 69 | ** The page_index.h file contains the definition for aCommand[] - an array |
| @@ -461,11 +462,15 @@ | |
| 461 | || hasGap(zHelp+nIndent,i-nIndent) ){ |
| 462 | iLevel++; |
| 463 | aIndent[iLevel] = nIndent; |
| 464 | azEnd[iLevel] = zEndDL; |
| 465 | wantP = 0; |
| 466 | blob_append(pHtml, "<blockquote><dl>\n", -1); |
| 467 | }else if( azEnd[iLevel]==zEndDL ){ |
| 468 | iLevel++; |
| 469 | aIndent[iLevel] = nIndent; |
| 470 | azEnd[iLevel] = zEndDD; |
| 471 | if( wantP ){ |
| @@ -578,10 +583,11 @@ | |
| 578 | if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n"); |
| 579 | if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n"); |
| 580 | if( mask & CMDFLAG_TEST ) fossil_print(" * Test commands\n"); |
| 581 | if( mask & CMDFLAG_WEBPAGE ) fossil_print(" * Web pages\n"); |
| 582 | if( mask & CMDFLAG_SETTING ) fossil_print(" * Settings\n"); |
| 583 | if( useHtml ){ |
| 584 | fossil_print("-->\n"); |
| 585 | fossil_print("<!-- start_all_help -->\n"); |
| 586 | }else{ |
| 587 | fossil_print("---\n"); |
| @@ -644,10 +650,11 @@ | |
| 644 | ** Defaults to just the CLI commands. Specify --www to see only the |
| 645 | ** web pages, or --everything to see both commands and pages. |
| 646 | ** |
| 647 | ** Options: |
| 648 | ** -a|--aliases Show aliases |
| 649 | ** -e|--everything Show all commands and pages. Omit aliases to |
| 650 | ** avoid duplicates. |
| 651 | ** -h|--html Transform output to HTML |
| 652 | ** -o|--options Show global options |
| 653 | ** -r|--raw No output formatting |
| @@ -663,20 +670,23 @@ | |
| 663 | if( find_option("www","w",0) ){ |
| 664 | mask = CMDFLAG_WEBPAGE; |
| 665 | } |
| 666 | if( find_option("everything","e",0) ){ |
| 667 | mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 668 | CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST; |
| 669 | } |
| 670 | if( find_option("settings","s",0) ){ |
| 671 | mask = CMDFLAG_SETTING; |
| 672 | } |
| 673 | if( find_option("aliases","a",0) ){ |
| 674 | mask = CMDFLAG_ALIAS; |
| 675 | } |
| 676 | if( find_option("test","t",0) ){ |
| 677 | mask |= CMDFLAG_TEST; |
| 678 | } |
| 679 | display_all_help(mask, useHtml, rawOut); |
| 680 | } |
| 681 | |
| 682 | /* |
| @@ -710,10 +720,12 @@ | |
| 710 | countCmds( CMDFLAG_TEST )); |
| 711 | fossil_print("web-pages: %4d\n", |
| 712 | countCmds( CMDFLAG_WEBPAGE )); |
| 713 | fossil_print("settings: %4d\n", |
| 714 | countCmds( CMDFLAG_SETTING )); |
| 715 | fossil_print("total entries: %4d\n", MX_COMMAND); |
| 716 | } |
| 717 | |
| 718 | /* |
| 719 | ** Compute an estimate of the edit-distance between to input strings. |
| @@ -892,10 +904,12 @@ | |
| 892 | /* Some of the webpages require query parameters in order to work. |
| 893 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 894 | @ <h1>The "%h(pCmd->zName)" page:</h1> |
| 895 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| 896 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 897 | }else{ |
| 898 | @ <h1>The "%h(pCmd->zName)" command:</h1> |
| 899 | } |
| 900 | if( rc==1 || (rc==2 && zCmd[0]=='/') ){ |
| 901 | if( zCmd && help_is_platform_command(zCmd) ){ |
| @@ -933,11 +947,11 @@ | |
| 933 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ |
| 934 | style_header("Help"); |
| 935 | search_screen(SRCH_HELP, 0x02); |
| 936 | |
| 937 | @ <a name='commands'></a> |
| 938 | @ <h1>Available commands:</h1> |
| 939 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 940 | @ <ul> |
| 941 | /* Fill in help string buckets */ |
| 942 | for(i=0; i<MX_COMMAND; i++){ |
| 943 | if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; |
| @@ -946,11 +960,13 @@ | |
| 946 | for(i=0; i<MX_COMMAND; i++){ |
| 947 | const char *z = aCommand[i].zName; |
| 948 | const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :""; |
| 949 | const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":""; |
| 950 | if( '/'==*z || strncmp(z,"test",4)==0 ) continue; |
| 951 | if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue; |
| 952 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 953 | else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue; |
| 954 | @ <li><a href="%R/help/%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a> |
| 955 | /* Output aliases */ |
| 956 | if( occHelp[aCommand[i].iHelp] > 1 ){ |
| @@ -978,11 +994,11 @@ | |
| 978 | } |
| 979 | |
| 980 | @ </ul></div> |
| 981 | |
| 982 | @ <a name='webpages'></a> |
| 983 | @ <h1>Available web UI pages:</h1> |
| 984 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 985 | @ <ul> |
| 986 | for(i=0; i<MX_COMMAND; i++){ |
| 987 | const char *z = aCommand[i].zName; |
| 988 | if( '/'!=*z ) continue; |
| @@ -993,33 +1009,48 @@ | |
| 993 | @ <li>%s(z+1)</li> |
| 994 | } |
| 995 | } |
| 996 | @ </ul></div> |
| 997 | |
| 998 | @ <a name='unsupported'></a> |
| 999 | @ <h1>Unsupported commands:</h1> |
| 1000 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1001 | @ <ul> |
| 1002 | for(i=0; i<MX_COMMAND; i++){ |
| 1003 | const char *z = aCommand[i].zName; |
| 1004 | if( strncmp(z,"test",4)!=0 ) continue; |
| 1005 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 1006 | if( aCommand[i].zHelp[0] ){ |
| 1007 | @ <li><a href="%R/help/%s(z)">%s(z)</a></li> |
| 1008 | }else{ |
| 1009 | @ <li>%s(z)</li> |
| 1010 | } |
| 1011 | } |
| 1012 | @ </ul></div> |
| 1013 | |
| 1014 | @ <a name='settings'></a> |
| 1015 | @ <h1>Settings:</h1> |
| 1016 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1017 | @ <ul> |
| 1018 | for(i=0; i<MX_COMMAND; i++){ |
| 1019 | const char *z = aCommand[i].zName; |
| 1020 | if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue; |
| 1021 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 1022 | if( aCommand[i].zHelp[0] ){ |
| 1023 | @ <li><a href="%R/help/%s(z)">%s(z)</a></li> |
| 1024 | }else{ |
| 1025 | @ <li>%s(z)</li> |
| @@ -1059,10 +1090,12 @@ | |
| 1059 | zDesc = "2nd tier command"; |
| 1060 | }else if( e & CMDFLAG_ALIAS ){ |
| 1061 | zDesc = "alias"; |
| 1062 | }else if( e & CMDFLAG_TEST ){ |
| 1063 | zDesc = "test command"; |
| 1064 | }else if( e & CMDFLAG_WEBPAGE ){ |
| 1065 | if( e & CMDFLAG_RAWCONTENT ){ |
| 1066 | zDesc = "raw-content web page"; |
| 1067 | }else{ |
| 1068 | zDesc = "web page"; |
| @@ -1155,11 +1188,11 @@ | |
| 1155 | Blob *pOut, /* Write simplified help text here */ |
| 1156 | const char *zTopic, /* TOPIC */ |
| 1157 | const char *zSubtopic, /* SUBTOPIC */ |
| 1158 | int bAbbrevSubcmd /* True if z[] contains abbreviated subcommands */ |
| 1159 | ){ |
| 1160 | Blob in, line; //, subsection; |
| 1161 | int n = 0; |
| 1162 | char *zQTop = re_quote(zTopic); |
| 1163 | char *zQSub = re_quote(zSubtopic); |
| 1164 | char *zPattern; |
| 1165 | ReCompiled *pRe = 0; |
| @@ -1392,46 +1425,44 @@ | |
| 1392 | multi_column_list(aCmd, nCmd); |
| 1393 | } |
| 1394 | } |
| 1395 | |
| 1396 | /* |
| 1397 | ** Documentation on universal command-line options. |
| 1398 | */ |
| 1399 | /* @-comment: # */ |
| 1400 | static const char zOptions[] = |
| 1401 | @ Command-line options common to all commands: |
| 1402 | @ |
| 1403 | @ --args FILENAME Read additional arguments and options from FILENAME |
| 1404 | @ --case-sensitive BOOL Set case sensitivity for file names |
| 1405 | @ --cgitrace Active CGI tracing |
| 1406 | @ --chdir PATH Change to PATH before performing any operations |
| 1407 | @ --errorlog FILENAME Log errors to FILENAME |
| 1408 | @ -?|--help Show help on the command rather than running it |
| 1409 | @ --httptrace Trace outbound HTTP requests |
| 1410 | @ --localtime Display times using the local timezone |
| 1411 | @ --nocgi Do not act as CGI |
| 1412 | @ --no-th-hook Do not run TH1 hooks |
| 1413 | @ --quiet Reduce the amount of output |
| 1414 | @ --sqlstats Show SQL usage statistics when done |
| 1415 | @ --sqltrace Trace all SQL commands |
| 1416 | @ --sshtrace Trace SSH activity |
| 1417 | @ --ssl-identity NAME Set the SSL identity to NAME |
| 1418 | @ --systemtrace Trace calls to system() |
| 1419 | @ -U|--user USER Make the default user be USER |
| 1420 | @ --utc Display times using UTC |
| 1421 | @ --vfs NAME Cause SQLite to use the NAME VFS |
| 1422 | @ |
| 1423 | @ Additional options available on most commands that use network I/O: |
| 1424 | @ |
| 1425 | @ --accept-any-cert Disable server SSL cdert validation. Accept any SSL |
| 1426 | @ cert that the server provides. WARNING: Unsafe! |
| 1427 | @ Testing and debugging use only! |
| 1428 | @ --ipv4 Use only IPv4. Disable IPv6 support. |
| 1429 | @ --ipv6 Use only IPv6. Disable IPv4 support. |
| 1430 | @ --nosync Disable autosync for the current command. |
| 1431 | @ --proxy URL Specify the HTTP proxy to use. URL can be "off". |
| 1432 | ; |
| 1433 | |
| 1434 | /* |
| 1435 | ** COMMAND: help |
| 1436 | ** |
| 1437 | ** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND] |
| @@ -1508,21 +1539,26 @@ | |
| 1508 | return; |
| 1509 | } |
| 1510 | else if( find_option("setting","s",0) ){ |
| 1511 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1512 | return; |
| 1513 | } |
| 1514 | else if( find_option("full","f",0) ){ |
| 1515 | fossil_print("fossil commands:\n\n"); |
| 1516 | command_list(CMDFLAG_1ST_TIER, verboseFlag, useHtml); |
| 1517 | fossil_print("\nfossil auxiliary commands:\n\n"); |
| 1518 | command_list(CMDFLAG_2ND_TIER, verboseFlag, useHtml); |
| 1519 | fossil_print("\n%s", zOptions); |
| 1520 | fossil_print("\nfossil settings:\n\n"); |
| 1521 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1522 | fossil_print("\nfossil web pages:\n\n"); |
| 1523 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| 1524 | fossil_print("\nfossil test commands (unsupported):\n\n"); |
| 1525 | command_list(CMDFLAG_TEST, verboseFlag, useHtml); |
| 1526 | if ( !verboseFlag ) { |
| 1527 | fossil_print("\n"); |
| 1528 | version_cmd(); |
| @@ -1529,18 +1565,23 @@ | |
| 1529 | } |
| 1530 | return; |
| 1531 | } |
| 1532 | else if( find_option("everything","e",0) ){ |
| 1533 | display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 1534 | CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0); |
| 1535 | return; |
| 1536 | } |
| 1537 | verify_all_options(); |
| 1538 | if( g.argc<3 ){ |
| 1539 | if( bOptions ){ |
| 1540 | fossil_print("%s", zOptions); |
| 1541 | return; |
| 1542 | } |
| 1543 | z = g.argv[0]; |
| 1544 | fossil_print( |
| 1545 | "Usage: %s help TOPIC\n" |
| 1546 | "Things to try:\n\n" |
| @@ -1560,14 +1601,13 @@ | |
| 1560 | if( isPage ){ |
| 1561 | zCmdOrPage = "page"; |
| 1562 | }else if( commandsFlag ){ |
| 1563 | mask = CMDFLAG_COMMAND; |
| 1564 | zCmdOrPage = "command"; |
| 1565 | }else{ |
| 1566 | zCmdOrPage = "command or setting"; |
| 1567 | } |
| 1568 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1569 | if( rc ){ |
| 1570 | int i, n; |
| 1571 | const char *az[5]; |
| 1572 | if( rc==1 ){ |
| 1573 | if( help_is_platform_command(g.argv[2]) ){ |
| @@ -1805,10 +1845,12 @@ | |
| 1805 | zType = "command"; |
| 1806 | }else if( pPage->eCmdFlags & CMDFLAG_WEBPAGE ){ |
| 1807 | zType = "webpage"; |
| 1808 | }else if( pPage->eCmdFlags & CMDFLAG_SETTING ){ |
| 1809 | zType = "setting"; |
| 1810 | } |
| 1811 | sqlite3_result_text(ctx, zType, -1, SQLITE_STATIC); |
| 1812 | break; |
| 1813 | } |
| 1814 | case 2: /* flags */ |
| 1815 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -25,11 +25,11 @@ | |
| 25 | #include "dispatch.h" |
| 26 | |
| 27 | #if INTERFACE |
| 28 | /* |
| 29 | ** An instance of this object defines everything we need to know about an |
| 30 | ** individual command, webpage, setting, or help topic. |
| 31 | */ |
| 32 | struct CmdOrPage { |
| 33 | const char *zName; /* Name. Webpages start with "/". Commands do not */ |
| 34 | void (*xFunc)(void); /* Implementation function, or NULL for settings */ |
| 35 | const char *zHelp; /* Raw help text */ |
| @@ -39,31 +39,32 @@ | |
| 39 | |
| 40 | /*************************************************************************** |
| 41 | ** These macros must match similar macros in mkindex.c |
| 42 | ** Allowed values for CmdOrPage.eCmdFlags. |
| 43 | */ |
| 44 | #define CMDFLAG_1ST_TIER 0x000001 /* Most important commands */ |
| 45 | #define CMDFLAG_2ND_TIER 0x000002 /* Obscure and seldom used commands */ |
| 46 | #define CMDFLAG_TEST 0x000004 /* Commands for testing only */ |
| 47 | #define CMDFLAG_WEBPAGE 0x000008 /* Web pages */ |
| 48 | #define CMDFLAG_COMMAND 0x000010 /* A command */ |
| 49 | #define CMDFLAG_SETTING 0x000020 /* A setting */ |
| 50 | #define CMDFLAG_VERSIONABLE 0x000040 /* A versionable setting */ |
| 51 | #define CMDFLAG_BLOCKTEXT 0x000080 /* Multi-line text setting */ |
| 52 | #define CMDFLAG_BOOLEAN 0x000100 /* A boolean setting */ |
| 53 | #define CMDFLAG_RAWCONTENT 0x000200 /* Do not interpret POST content */ |
| 54 | /* NOTE: 0x000400 = CMDFLAG_SENSITIVE in mkindex.c! */ |
| 55 | #define CMDFLAG_HIDDEN 0x000800 /* Elide from most listings */ |
| 56 | #define CMDFLAG_LDAVG_EXEMPT 0x001000 /* Exempt from load_control() */ |
| 57 | #define CMDFLAG_ALIAS 0x002000 /* Command aliases */ |
| 58 | #define CMDFLAG_KEEPEMPTY 0x004000 /* Do not unset empty settings */ |
| 59 | #define CMDFLAG_ABBREVSUBCMD 0x008000 /* Help text abbreviates subcommands */ |
| 60 | #define CMDFLAG_TOPIC 0x010000 /* A help topic */ |
| 61 | /**************************************************************************/ |
| 62 | |
| 63 | /* Values for the 2nd parameter to dispatch_name_search() */ |
| 64 | #define CMDFLAG_ANY 0x010038 /* Match anything */ |
| 65 | #define CMDFLAG_PREFIX 0x000200 /* Prefix match is OK */ |
| 66 | |
| 67 | #endif /* INTERFACE */ |
| 68 | |
| 69 | /* |
| 70 | ** The page_index.h file contains the definition for aCommand[] - an array |
| @@ -461,11 +462,15 @@ | |
| 462 | || hasGap(zHelp+nIndent,i-nIndent) ){ |
| 463 | iLevel++; |
| 464 | aIndent[iLevel] = nIndent; |
| 465 | azEnd[iLevel] = zEndDL; |
| 466 | wantP = 0; |
| 467 | if( isDT ){ |
| 468 | blob_append(pHtml, "<blockquote><dl>\n", -1); |
| 469 | }else{ |
| 470 | blob_append(pHtml, "<blockquote><dl class=\"helpOptions\">\n", -1); |
| 471 | } |
| 472 | }else if( azEnd[iLevel]==zEndDL ){ |
| 473 | iLevel++; |
| 474 | aIndent[iLevel] = nIndent; |
| 475 | azEnd[iLevel] = zEndDD; |
| 476 | if( wantP ){ |
| @@ -578,10 +583,11 @@ | |
| 583 | if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n"); |
| 584 | if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n"); |
| 585 | if( mask & CMDFLAG_TEST ) fossil_print(" * Test commands\n"); |
| 586 | if( mask & CMDFLAG_WEBPAGE ) fossil_print(" * Web pages\n"); |
| 587 | if( mask & CMDFLAG_SETTING ) fossil_print(" * Settings\n"); |
| 588 | if( mask & CMDFLAG_TOPIC ) fossil_print(" * Help Topic\n"); |
| 589 | if( useHtml ){ |
| 590 | fossil_print("-->\n"); |
| 591 | fossil_print("<!-- start_all_help -->\n"); |
| 592 | }else{ |
| 593 | fossil_print("---\n"); |
| @@ -644,10 +650,11 @@ | |
| 650 | ** Defaults to just the CLI commands. Specify --www to see only the |
| 651 | ** web pages, or --everything to see both commands and pages. |
| 652 | ** |
| 653 | ** Options: |
| 654 | ** -a|--aliases Show aliases |
| 655 | ** -c|--topics Show help topics |
| 656 | ** -e|--everything Show all commands and pages. Omit aliases to |
| 657 | ** avoid duplicates. |
| 658 | ** -h|--html Transform output to HTML |
| 659 | ** -o|--options Show global options |
| 660 | ** -r|--raw No output formatting |
| @@ -663,20 +670,23 @@ | |
| 670 | if( find_option("www","w",0) ){ |
| 671 | mask = CMDFLAG_WEBPAGE; |
| 672 | } |
| 673 | if( find_option("everything","e",0) ){ |
| 674 | mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 675 | CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST | CMDFLAG_TOPIC; |
| 676 | } |
| 677 | if( find_option("settings","s",0) ){ |
| 678 | mask = CMDFLAG_SETTING; |
| 679 | } |
| 680 | if( find_option("aliases","a",0) ){ |
| 681 | mask = CMDFLAG_ALIAS; |
| 682 | } |
| 683 | if( find_option("test","t",0) ){ |
| 684 | mask |= CMDFLAG_TEST; |
| 685 | } |
| 686 | if( find_option("topics","c",0) ){ |
| 687 | mask |= CMDFLAG_TOPIC; |
| 688 | } |
| 689 | display_all_help(mask, useHtml, rawOut); |
| 690 | } |
| 691 | |
| 692 | /* |
| @@ -710,10 +720,12 @@ | |
| 720 | countCmds( CMDFLAG_TEST )); |
| 721 | fossil_print("web-pages: %4d\n", |
| 722 | countCmds( CMDFLAG_WEBPAGE )); |
| 723 | fossil_print("settings: %4d\n", |
| 724 | countCmds( CMDFLAG_SETTING )); |
| 725 | fossil_print("help-topics: %4d\n", |
| 726 | countCmds( CMDFLAG_TOPIC )); |
| 727 | fossil_print("total entries: %4d\n", MX_COMMAND); |
| 728 | } |
| 729 | |
| 730 | /* |
| 731 | ** Compute an estimate of the edit-distance between to input strings. |
| @@ -892,10 +904,12 @@ | |
| 904 | /* Some of the webpages require query parameters in order to work. |
| 905 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 906 | @ <h1>The "%h(pCmd->zName)" page:</h1> |
| 907 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| 908 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 909 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_TOPIC)!=0 ){ |
| 910 | @ <h1>The "%h(pCmd->zName)" help topic:</h1> |
| 911 | }else{ |
| 912 | @ <h1>The "%h(pCmd->zName)" command:</h1> |
| 913 | } |
| 914 | if( rc==1 || (rc==2 && zCmd[0]=='/') ){ |
| 915 | if( zCmd && help_is_platform_command(zCmd) ){ |
| @@ -933,11 +947,11 @@ | |
| 947 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ |
| 948 | style_header("Help"); |
| 949 | search_screen(SRCH_HELP, 0x02); |
| 950 | |
| 951 | @ <a name='commands'></a> |
| 952 | @ <h1>Commands:</h1> |
| 953 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 954 | @ <ul> |
| 955 | /* Fill in help string buckets */ |
| 956 | for(i=0; i<MX_COMMAND; i++){ |
| 957 | if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; |
| @@ -946,11 +960,13 @@ | |
| 960 | for(i=0; i<MX_COMMAND; i++){ |
| 961 | const char *z = aCommand[i].zName; |
| 962 | const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :""; |
| 963 | const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":""; |
| 964 | if( '/'==*z || strncmp(z,"test",4)==0 ) continue; |
| 965 | if( (aCommand[i].eCmdFlags & (CMDFLAG_SETTING|CMDFLAG_TOPIC))!=0 ){ |
| 966 | continue; |
| 967 | } |
| 968 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 969 | else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue; |
| 970 | @ <li><a href="%R/help/%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a> |
| 971 | /* Output aliases */ |
| 972 | if( occHelp[aCommand[i].iHelp] > 1 ){ |
| @@ -978,11 +994,11 @@ | |
| 994 | } |
| 995 | |
| 996 | @ </ul></div> |
| 997 | |
| 998 | @ <a name='webpages'></a> |
| 999 | @ <h1>Web pages:</h1> |
| 1000 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1001 | @ <ul> |
| 1002 | for(i=0; i<MX_COMMAND; i++){ |
| 1003 | const char *z = aCommand[i].zName; |
| 1004 | if( '/'!=*z ) continue; |
| @@ -993,33 +1009,48 @@ | |
| 1009 | @ <li>%s(z+1)</li> |
| 1010 | } |
| 1011 | } |
| 1012 | @ </ul></div> |
| 1013 | |
| 1014 | @ <a name='settings'></a> |
| 1015 | @ <h1>Settings:</h1> |
| 1016 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1017 | @ <ul> |
| 1018 | for(i=0; i<MX_COMMAND; i++){ |
| 1019 | const char *z = aCommand[i].zName; |
| 1020 | if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue; |
| 1021 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 1022 | if( aCommand[i].zHelp[0] ){ |
| 1023 | @ <li><a href="%R/help/%s(z)">%s(z)</a></li> |
| 1024 | }else{ |
| 1025 | @ <li>%s(z)</li> |
| 1026 | } |
| 1027 | } |
| 1028 | @ </ul></div> |
| 1029 | |
| 1030 | @ <a name='topics'></a> |
| 1031 | @ <h1>Other Miscellaneous Help Topics:</h1> |
| 1032 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1033 | @ <ul> |
| 1034 | for(i=0; i<MX_COMMAND; i++){ |
| 1035 | const char *z = aCommand[i].zName; |
| 1036 | if( (aCommand[i].eCmdFlags & CMDFLAG_TOPIC)==0 ) continue; |
| 1037 | if( aCommand[i].zHelp[0] ){ |
| 1038 | @ <li><a href="%R/help/%s(z)">%s(z)</a></li> |
| 1039 | }else{ |
| 1040 | @ <li>%s(z)</li> |
| 1041 | } |
| 1042 | } |
| 1043 | @ </ul></div> |
| 1044 | |
| 1045 | @ <a name='unsupported'></a> |
| 1046 | @ <h1>Unsupported and Testing Commands:</h1> |
| 1047 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 1048 | @ <ul> |
| 1049 | for(i=0; i<MX_COMMAND; i++){ |
| 1050 | const char *z = aCommand[i].zName; |
| 1051 | if( strncmp(z,"test",4)!=0 ) continue; |
| 1052 | else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; |
| 1053 | if( aCommand[i].zHelp[0] ){ |
| 1054 | @ <li><a href="%R/help/%s(z)">%s(z)</a></li> |
| 1055 | }else{ |
| 1056 | @ <li>%s(z)</li> |
| @@ -1059,10 +1090,12 @@ | |
| 1090 | zDesc = "2nd tier command"; |
| 1091 | }else if( e & CMDFLAG_ALIAS ){ |
| 1092 | zDesc = "alias"; |
| 1093 | }else if( e & CMDFLAG_TEST ){ |
| 1094 | zDesc = "test command"; |
| 1095 | }else if( e & CMDFLAG_TOPIC ){ |
| 1096 | zDesc = "help-topic"; |
| 1097 | }else if( e & CMDFLAG_WEBPAGE ){ |
| 1098 | if( e & CMDFLAG_RAWCONTENT ){ |
| 1099 | zDesc = "raw-content web page"; |
| 1100 | }else{ |
| 1101 | zDesc = "web page"; |
| @@ -1155,11 +1188,11 @@ | |
| 1188 | Blob *pOut, /* Write simplified help text here */ |
| 1189 | const char *zTopic, /* TOPIC */ |
| 1190 | const char *zSubtopic, /* SUBTOPIC */ |
| 1191 | int bAbbrevSubcmd /* True if z[] contains abbreviated subcommands */ |
| 1192 | ){ |
| 1193 | Blob in, line;/*, subsection;*/ |
| 1194 | int n = 0; |
| 1195 | char *zQTop = re_quote(zTopic); |
| 1196 | char *zQSub = re_quote(zSubtopic); |
| 1197 | char *zPattern; |
| 1198 | ReCompiled *pRe = 0; |
| @@ -1392,46 +1425,44 @@ | |
| 1425 | multi_column_list(aCmd, nCmd); |
| 1426 | } |
| 1427 | } |
| 1428 | |
| 1429 | /* |
| 1430 | ** TOPIC: options |
| 1431 | ** |
| 1432 | ** Command-line options common to all commands: |
| 1433 | ** |
| 1434 | ** --args FILENAME Read additional arguments and options from FILENAME |
| 1435 | ** --case-sensitive BOOL Set case sensitivity for file names |
| 1436 | ** --cgitrace Active CGI tracing |
| 1437 | ** --chdir PATH Change to PATH before performing any operations |
| 1438 | ** --errorlog FILENAME Log errors to FILENAME |
| 1439 | ** -?|--help Show help on the command rather than running it |
| 1440 | ** --httptrace Trace outbound HTTP requests |
| 1441 | ** --localtime Display times using the local timezone |
| 1442 | ** --nocgi Do not act as CGI |
| 1443 | ** --no-th-hook Do not run TH1 hooks |
| 1444 | ** -q|--quiet Reduce the amount of output |
| 1445 | ** --sqlstats Show SQL usage statistics when done |
| 1446 | ** --sqltrace Trace all SQL commands |
| 1447 | ** --sshtrace Trace SSH activity |
| 1448 | ** --ssl-identity NAME Set the SSL identity to NAME |
| 1449 | ** --systemtrace Trace calls to system() |
| 1450 | ** -U|--user USER Make the default user be USER |
| 1451 | ** --utc Display times using UTC |
| 1452 | ** --vfs NAME Cause SQLite to use the NAME VFS |
| 1453 | ** |
| 1454 | ** Additional options available on most commands that use network I/O: |
| 1455 | ** |
| 1456 | ** --accept-any-cert Disable server SSL cdert validation. Accept any SSL |
| 1457 | ** cert that the server provides. WARNING: Unsafe! |
| 1458 | ** Testing and debugging use only! |
| 1459 | ** --ipv4 Use only IPv4. Disable IPv6 support. |
| 1460 | ** --ipv6 Use only IPv6. Disable IPv4 support. |
| 1461 | ** --nosync Disable autosync for the current command. |
| 1462 | ** --proxy URL Specify the HTTP proxy to use. URL can be "off". |
| 1463 | */ |
| 1464 | |
| 1465 | /* |
| 1466 | ** COMMAND: help |
| 1467 | ** |
| 1468 | ** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND] |
| @@ -1508,21 +1539,26 @@ | |
| 1539 | return; |
| 1540 | } |
| 1541 | else if( find_option("setting","s",0) ){ |
| 1542 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1543 | return; |
| 1544 | } |
| 1545 | else if( find_option("topic","c",0) ){ |
| 1546 | command_list(CMDFLAG_TOPIC, verboseFlag, useHtml); |
| 1547 | return; |
| 1548 | } |
| 1549 | else if( find_option("full","f",0) ){ |
| 1550 | fossil_print("fossil commands:\n\n"); |
| 1551 | command_list(CMDFLAG_1ST_TIER, verboseFlag, useHtml); |
| 1552 | fossil_print("\nfossil auxiliary commands:\n\n"); |
| 1553 | command_list(CMDFLAG_2ND_TIER, verboseFlag, useHtml); |
| 1554 | fossil_print("\nfossil settings:\n\n"); |
| 1555 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1556 | fossil_print("\nfossil web pages:\n\n"); |
| 1557 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| 1558 | fossil_print("\nfossil miscellaneous help topics:\n\n"); |
| 1559 | command_list(CMDFLAG_TOPIC, verboseFlag, useHtml); |
| 1560 | fossil_print("\nfossil test commands (unsupported):\n\n"); |
| 1561 | command_list(CMDFLAG_TEST, verboseFlag, useHtml); |
| 1562 | if ( !verboseFlag ) { |
| 1563 | fossil_print("\n"); |
| 1564 | version_cmd(); |
| @@ -1529,18 +1565,23 @@ | |
| 1565 | } |
| 1566 | return; |
| 1567 | } |
| 1568 | else if( find_option("everything","e",0) ){ |
| 1569 | display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 1570 | CMDFLAG_SETTING | CMDFLAG_TEST | CMDFLAG_TOPIC, |
| 1571 | useHtml, 0); |
| 1572 | return; |
| 1573 | } |
| 1574 | verify_all_options(); |
| 1575 | zCmdOrPage = "help topic"; |
| 1576 | if( g.argc<3 ){ |
| 1577 | if( bOptions ){ |
| 1578 | zTopic = "options"; |
| 1579 | zSubtopic = 0; |
| 1580 | mask = CMDFLAG_TOPIC; |
| 1581 | bOptions = 0; |
| 1582 | goto find_and_show_help; |
| 1583 | } |
| 1584 | z = g.argv[0]; |
| 1585 | fossil_print( |
| 1586 | "Usage: %s help TOPIC\n" |
| 1587 | "Things to try:\n\n" |
| @@ -1560,14 +1601,13 @@ | |
| 1601 | if( isPage ){ |
| 1602 | zCmdOrPage = "page"; |
| 1603 | }else if( commandsFlag ){ |
| 1604 | mask = CMDFLAG_COMMAND; |
| 1605 | zCmdOrPage = "command"; |
| 1606 | } |
| 1607 | find_and_show_help: |
| 1608 | rc = dispatch_name_search(zTopic, mask|CMDFLAG_PREFIX, &pCmd); |
| 1609 | if( rc ){ |
| 1610 | int i, n; |
| 1611 | const char *az[5]; |
| 1612 | if( rc==1 ){ |
| 1613 | if( help_is_platform_command(g.argv[2]) ){ |
| @@ -1805,10 +1845,12 @@ | |
| 1845 | zType = "command"; |
| 1846 | }else if( pPage->eCmdFlags & CMDFLAG_WEBPAGE ){ |
| 1847 | zType = "webpage"; |
| 1848 | }else if( pPage->eCmdFlags & CMDFLAG_SETTING ){ |
| 1849 | zType = "setting"; |
| 1850 | }else if( pPage->eCmdFlags & CMDFLAG_TOPIC ){ |
| 1851 | zType = "help-topic"; |
| 1852 | } |
| 1853 | sqlite3_result_text(ctx, zType, -1, SQLITE_STATIC); |
| 1854 | break; |
| 1855 | } |
| 1856 | case 2: /* flags */ |
| 1857 |
+1
-1
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -425,11 +425,11 @@ | ||
| 425 | 425 | if( rid ){ |
| 426 | 426 | zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid); |
| 427 | 427 | if( zClr && zClr[0] ){ |
| 428 | 428 | const char * zRequestMethod = P("REQUEST_METHOD"); |
| 429 | 429 | if(zRequestMethod && 'G'==zRequestMethod[0]){ |
| 430 | - /* Apply saved color by defaut for GET requests | |
| 430 | + /* Apply saved color by default for GET requests | |
| 431 | 431 | ** (e.g., an Edit menu link). |
| 432 | 432 | */ |
| 433 | 433 | zClrFlag = " checked"; |
| 434 | 434 | } |
| 435 | 435 | } |
| 436 | 436 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -425,11 +425,11 @@ | |
| 425 | if( rid ){ |
| 426 | zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid); |
| 427 | if( zClr && zClr[0] ){ |
| 428 | const char * zRequestMethod = P("REQUEST_METHOD"); |
| 429 | if(zRequestMethod && 'G'==zRequestMethod[0]){ |
| 430 | /* Apply saved color by defaut for GET requests |
| 431 | ** (e.g., an Edit menu link). |
| 432 | */ |
| 433 | zClrFlag = " checked"; |
| 434 | } |
| 435 | } |
| 436 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -425,11 +425,11 @@ | |
| 425 | if( rid ){ |
| 426 | zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid); |
| 427 | if( zClr && zClr[0] ){ |
| 428 | const char * zRequestMethod = P("REQUEST_METHOD"); |
| 429 | if(zRequestMethod && 'G'==zRequestMethod[0]){ |
| 430 | /* Apply saved color by default for GET requests |
| 431 | ** (e.g., an Edit menu link). |
| 432 | */ |
| 433 | zClrFlag = " checked"; |
| 434 | } |
| 435 | } |
| 436 |
+4
-3
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -28,11 +28,11 @@ | ||
| 28 | 28 | const char *zTrunkName; /* Name of trunk branch */ |
| 29 | 29 | } gexport; |
| 30 | 30 | |
| 31 | 31 | #if INTERFACE |
| 32 | 32 | /* |
| 33 | -** Each line in a git-fast-export "marK" file is an instance of | |
| 33 | +** Each line in a git-fast-export "mark" file is an instance of | |
| 34 | 34 | ** this object. |
| 35 | 35 | */ |
| 36 | 36 | struct mark_t { |
| 37 | 37 | char *name; /* Name of the mark. Also starts with ":" */ |
| 38 | 38 | int rid; /* Corresponding object in the BLOB table */ |
| @@ -759,11 +759,11 @@ | ||
| 759 | 759 | ** |
| 760 | 760 | ** This table contains all check-ins of the repository in topological |
| 761 | 761 | ** order. "Topological order" means that every parent check-in comes |
| 762 | 762 | ** before all of its children. Topological order is *almost* the same |
| 763 | 763 | ** thing as "ORDER BY event.mtime". Differences only arise when there |
| 764 | -** are timewarps. In as much as Git hates timewarps, we have to compute | |
| 764 | +** are timewarps. Inasmuch as Git hates timewarps, we have to compute | |
| 765 | 765 | ** a correct topological order when doing an export. |
| 766 | 766 | ** |
| 767 | 767 | ** Since mtime is a usually already nearly in topological order, the |
| 768 | 768 | ** algorithm is to start with mtime, then make adjustments as necessary |
| 769 | 769 | ** for timewarps. This is not a great algorithm for the general case, |
| @@ -1413,10 +1413,11 @@ | ||
| 1413 | 1413 | zAutoPush = find_option("autopush",0,1); |
| 1414 | 1414 | zMainBr = (char*)find_option("mainbranch",0,1); |
| 1415 | 1415 | bForce = find_option("force","f",0)!=0; |
| 1416 | 1416 | bIfExists = find_option("if-mirrored",0,0)!=0; |
| 1417 | 1417 | gitmirror_verbosity = VERB_NORMAL; |
| 1418 | + if( g.fQuiet ){ gitmirror_verbosity--; } /* Global option not repeatable. */ | |
| 1418 | 1419 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1419 | 1420 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1420 | 1421 | verify_all_options(); |
| 1421 | 1422 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| 1422 | 1423 | if( g.argc==4 ){ |
| @@ -1752,11 +1753,11 @@ | ||
| 1752 | 1753 | int bQuiet = 0; |
| 1753 | 1754 | int bByAll = 0; /* Undocumented option meaning this command was invoked |
| 1754 | 1755 | ** from "fossil all" and should modify output accordingly */ |
| 1755 | 1756 | |
| 1756 | 1757 | db_find_and_open_repository(0, 0); |
| 1757 | - bQuiet = find_option("quiet","q",0)!=0; | |
| 1758 | + bQuiet = g.fQuiet; | |
| 1758 | 1759 | bByAll = find_option("by-all",0,0)!=0; |
| 1759 | 1760 | verify_all_options(); |
| 1760 | 1761 | zMirror = db_get("last-git-export-repo", 0); |
| 1761 | 1762 | if( zMirror==0 ){ |
| 1762 | 1763 | if( bQuiet ) return; |
| 1763 | 1764 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -28,11 +28,11 @@ | |
| 28 | const char *zTrunkName; /* Name of trunk branch */ |
| 29 | } gexport; |
| 30 | |
| 31 | #if INTERFACE |
| 32 | /* |
| 33 | ** Each line in a git-fast-export "marK" file is an instance of |
| 34 | ** this object. |
| 35 | */ |
| 36 | struct mark_t { |
| 37 | char *name; /* Name of the mark. Also starts with ":" */ |
| 38 | int rid; /* Corresponding object in the BLOB table */ |
| @@ -759,11 +759,11 @@ | |
| 759 | ** |
| 760 | ** This table contains all check-ins of the repository in topological |
| 761 | ** order. "Topological order" means that every parent check-in comes |
| 762 | ** before all of its children. Topological order is *almost* the same |
| 763 | ** thing as "ORDER BY event.mtime". Differences only arise when there |
| 764 | ** are timewarps. In as much as Git hates timewarps, we have to compute |
| 765 | ** a correct topological order when doing an export. |
| 766 | ** |
| 767 | ** Since mtime is a usually already nearly in topological order, the |
| 768 | ** algorithm is to start with mtime, then make adjustments as necessary |
| 769 | ** for timewarps. This is not a great algorithm for the general case, |
| @@ -1413,10 +1413,11 @@ | |
| 1413 | zAutoPush = find_option("autopush",0,1); |
| 1414 | zMainBr = (char*)find_option("mainbranch",0,1); |
| 1415 | bForce = find_option("force","f",0)!=0; |
| 1416 | bIfExists = find_option("if-mirrored",0,0)!=0; |
| 1417 | gitmirror_verbosity = VERB_NORMAL; |
| 1418 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1419 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1420 | verify_all_options(); |
| 1421 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| 1422 | if( g.argc==4 ){ |
| @@ -1752,11 +1753,11 @@ | |
| 1752 | int bQuiet = 0; |
| 1753 | int bByAll = 0; /* Undocumented option meaning this command was invoked |
| 1754 | ** from "fossil all" and should modify output accordingly */ |
| 1755 | |
| 1756 | db_find_and_open_repository(0, 0); |
| 1757 | bQuiet = find_option("quiet","q",0)!=0; |
| 1758 | bByAll = find_option("by-all",0,0)!=0; |
| 1759 | verify_all_options(); |
| 1760 | zMirror = db_get("last-git-export-repo", 0); |
| 1761 | if( zMirror==0 ){ |
| 1762 | if( bQuiet ) return; |
| 1763 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -28,11 +28,11 @@ | |
| 28 | const char *zTrunkName; /* Name of trunk branch */ |
| 29 | } gexport; |
| 30 | |
| 31 | #if INTERFACE |
| 32 | /* |
| 33 | ** Each line in a git-fast-export "mark" file is an instance of |
| 34 | ** this object. |
| 35 | */ |
| 36 | struct mark_t { |
| 37 | char *name; /* Name of the mark. Also starts with ":" */ |
| 38 | int rid; /* Corresponding object in the BLOB table */ |
| @@ -759,11 +759,11 @@ | |
| 759 | ** |
| 760 | ** This table contains all check-ins of the repository in topological |
| 761 | ** order. "Topological order" means that every parent check-in comes |
| 762 | ** before all of its children. Topological order is *almost* the same |
| 763 | ** thing as "ORDER BY event.mtime". Differences only arise when there |
| 764 | ** are timewarps. Inasmuch as Git hates timewarps, we have to compute |
| 765 | ** a correct topological order when doing an export. |
| 766 | ** |
| 767 | ** Since mtime is a usually already nearly in topological order, the |
| 768 | ** algorithm is to start with mtime, then make adjustments as necessary |
| 769 | ** for timewarps. This is not a great algorithm for the general case, |
| @@ -1413,10 +1413,11 @@ | |
| 1413 | zAutoPush = find_option("autopush",0,1); |
| 1414 | zMainBr = (char*)find_option("mainbranch",0,1); |
| 1415 | bForce = find_option("force","f",0)!=0; |
| 1416 | bIfExists = find_option("if-mirrored",0,0)!=0; |
| 1417 | gitmirror_verbosity = VERB_NORMAL; |
| 1418 | if( g.fQuiet ){ gitmirror_verbosity--; } /* Global option not repeatable. */ |
| 1419 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1420 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1421 | verify_all_options(); |
| 1422 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| 1423 | if( g.argc==4 ){ |
| @@ -1752,11 +1753,11 @@ | |
| 1753 | int bQuiet = 0; |
| 1754 | int bByAll = 0; /* Undocumented option meaning this command was invoked |
| 1755 | ** from "fossil all" and should modify output accordingly */ |
| 1756 | |
| 1757 | db_find_and_open_repository(0, 0); |
| 1758 | bQuiet = g.fQuiet; |
| 1759 | bByAll = find_option("by-all",0,0)!=0; |
| 1760 | verify_all_options(); |
| 1761 | zMirror = db_get("last-git-export-repo", 0); |
| 1762 | if( zMirror==0 ){ |
| 1763 | if( bQuiet ) return; |
| 1764 |
+2
-2
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -51,11 +51,11 @@ | ||
| 51 | 51 | ** a few special cases such as the "fossil test-tarball" command when we never |
| 52 | 52 | ** want to follow symlinks. |
| 53 | 53 | ** |
| 54 | 54 | ** ExtFILE Symbolic links always refer to the object to which the |
| 55 | 55 | ** link points. Symlinks are never recognized as symlinks but |
| 56 | -** instead always appear to the the target object. | |
| 56 | +** instead always appear to be the target object. | |
| 57 | 57 | ** |
| 58 | 58 | ** SymFILE Symbolic links always appear to be files whose name is |
| 59 | 59 | ** the target pathname of the symbolic link. |
| 60 | 60 | ** |
| 61 | 61 | ** RepoFILE Like SymFILE if allow-symlinks is true, or like |
| @@ -2818,11 +2818,11 @@ | ||
| 2818 | 2818 | i64 nowTime = 0; /* Timestamp of --now or --checkout */ |
| 2819 | 2819 | Stmt q; |
| 2820 | 2820 | Blob absBuffer = empty_blob; /* Absolute filename buffer */ |
| 2821 | 2821 | |
| 2822 | 2822 | verboseFlag = find_option("verbose","v",0)!=0; |
| 2823 | - quietFlag = find_option("quiet","q",0)!=0 || g.fQuiet; | |
| 2823 | + quietFlag = g.fQuiet; | |
| 2824 | 2824 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2825 | 2825 | zGlobList = find_option("glob", "g",1); |
| 2826 | 2826 | zGlobFile = find_option("globfile", "G",1); |
| 2827 | 2827 | |
| 2828 | 2828 | if(zGlobList && zGlobFile){ |
| 2829 | 2829 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | ** a few special cases such as the "fossil test-tarball" command when we never |
| 52 | ** want to follow symlinks. |
| 53 | ** |
| 54 | ** ExtFILE Symbolic links always refer to the object to which the |
| 55 | ** link points. Symlinks are never recognized as symlinks but |
| 56 | ** instead always appear to the the target object. |
| 57 | ** |
| 58 | ** SymFILE Symbolic links always appear to be files whose name is |
| 59 | ** the target pathname of the symbolic link. |
| 60 | ** |
| 61 | ** RepoFILE Like SymFILE if allow-symlinks is true, or like |
| @@ -2818,11 +2818,11 @@ | |
| 2818 | i64 nowTime = 0; /* Timestamp of --now or --checkout */ |
| 2819 | Stmt q; |
| 2820 | Blob absBuffer = empty_blob; /* Absolute filename buffer */ |
| 2821 | |
| 2822 | verboseFlag = find_option("verbose","v",0)!=0; |
| 2823 | quietFlag = find_option("quiet","q",0)!=0 || g.fQuiet; |
| 2824 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2825 | zGlobList = find_option("glob", "g",1); |
| 2826 | zGlobFile = find_option("globfile", "G",1); |
| 2827 | |
| 2828 | if(zGlobList && zGlobFile){ |
| 2829 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | ** a few special cases such as the "fossil test-tarball" command when we never |
| 52 | ** want to follow symlinks. |
| 53 | ** |
| 54 | ** ExtFILE Symbolic links always refer to the object to which the |
| 55 | ** link points. Symlinks are never recognized as symlinks but |
| 56 | ** instead always appear to be the target object. |
| 57 | ** |
| 58 | ** SymFILE Symbolic links always appear to be files whose name is |
| 59 | ** the target pathname of the symbolic link. |
| 60 | ** |
| 61 | ** RepoFILE Like SymFILE if allow-symlinks is true, or like |
| @@ -2818,11 +2818,11 @@ | |
| 2818 | i64 nowTime = 0; /* Timestamp of --now or --checkout */ |
| 2819 | Stmt q; |
| 2820 | Blob absBuffer = empty_blob; /* Absolute filename buffer */ |
| 2821 | |
| 2822 | verboseFlag = find_option("verbose","v",0)!=0; |
| 2823 | quietFlag = g.fQuiet; |
| 2824 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2825 | zGlobList = find_option("glob", "g",1); |
| 2826 | zGlobFile = find_option("globfile", "G",1); |
| 2827 | |
| 2828 | if(zGlobList && zGlobFile){ |
| 2829 |
+3
-3
| --- src/fileedit.c | ||
| +++ src/fileedit.c | ||
| @@ -48,11 +48,11 @@ | ||
| 48 | 48 | Blob comment; /* Check-in comment text */ |
| 49 | 49 | char *zCommentMimetype; /* Mimetype of comment. May be NULL */ |
| 50 | 50 | char *zUser; /* User name */ |
| 51 | 51 | char *zDate; /* Optionally force this date string (anything |
| 52 | 52 | supported by date_in_standard_format()). |
| 53 | - Maybe be NULL. */ | |
| 53 | + May be NULL. */ | |
| 54 | 54 | Blob *pMfOut; /* If not NULL, checkin_mini() will write a |
| 55 | 55 | copy of the generated manifest here. This |
| 56 | 56 | memory is NOT owned by CheckinMiniInfo. */ |
| 57 | 57 | int filePerm; /* Permissions (via file_perm()) of the input |
| 58 | 58 | file. We need to store this before calling |
| @@ -123,11 +123,11 @@ | ||
| 123 | 123 | */ |
| 124 | 124 | CIMINI_PREFER_DELTA = 1<<8, |
| 125 | 125 | /* |
| 126 | 126 | ** A "stronger hint" to checkin_mini() to prefer creation of a delta |
| 127 | 127 | ** manifest if it at all can. It will decide not to only if creation |
| 128 | -** of a delta is not a realistic option or if it's forbitted by the | |
| 128 | +** of a delta is not a realistic option or if it's forbidden by the | |
| 129 | 129 | ** forbid-delta-manifests repo config option. For this to work, it |
| 130 | 130 | ** must be set together with the CIMINI_PREFER_DELTA flag, but the two |
| 131 | 131 | ** cannot be combined in this enum. |
| 132 | 132 | ** |
| 133 | 133 | ** This option is ONLY INTENDED FOR TESTING, used in bypassing |
| @@ -400,11 +400,11 @@ | ||
| 400 | 400 | ** This routine uses the state from the given fully-populated pCI |
| 401 | 401 | ** argument to add pCI->fileContent to the database, and create and |
| 402 | 402 | ** save a manifest for that change. Ownership of pCI and its contents |
| 403 | 403 | ** are unchanged. |
| 404 | 404 | ** |
| 405 | -** This function may may modify pCI as follows: | |
| 405 | +** This function may modify pCI as follows: | |
| 406 | 406 | ** |
| 407 | 407 | ** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL, |
| 408 | 408 | ** then the other will be assigned based on its counterpart. Both |
| 409 | 409 | ** may not be NULL. |
| 410 | 410 | ** |
| 411 | 411 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -48,11 +48,11 @@ | |
| 48 | Blob comment; /* Check-in comment text */ |
| 49 | char *zCommentMimetype; /* Mimetype of comment. May be NULL */ |
| 50 | char *zUser; /* User name */ |
| 51 | char *zDate; /* Optionally force this date string (anything |
| 52 | supported by date_in_standard_format()). |
| 53 | Maybe be NULL. */ |
| 54 | Blob *pMfOut; /* If not NULL, checkin_mini() will write a |
| 55 | copy of the generated manifest here. This |
| 56 | memory is NOT owned by CheckinMiniInfo. */ |
| 57 | int filePerm; /* Permissions (via file_perm()) of the input |
| 58 | file. We need to store this before calling |
| @@ -123,11 +123,11 @@ | |
| 123 | */ |
| 124 | CIMINI_PREFER_DELTA = 1<<8, |
| 125 | /* |
| 126 | ** A "stronger hint" to checkin_mini() to prefer creation of a delta |
| 127 | ** manifest if it at all can. It will decide not to only if creation |
| 128 | ** of a delta is not a realistic option or if it's forbitted by the |
| 129 | ** forbid-delta-manifests repo config option. For this to work, it |
| 130 | ** must be set together with the CIMINI_PREFER_DELTA flag, but the two |
| 131 | ** cannot be combined in this enum. |
| 132 | ** |
| 133 | ** This option is ONLY INTENDED FOR TESTING, used in bypassing |
| @@ -400,11 +400,11 @@ | |
| 400 | ** This routine uses the state from the given fully-populated pCI |
| 401 | ** argument to add pCI->fileContent to the database, and create and |
| 402 | ** save a manifest for that change. Ownership of pCI and its contents |
| 403 | ** are unchanged. |
| 404 | ** |
| 405 | ** This function may may modify pCI as follows: |
| 406 | ** |
| 407 | ** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL, |
| 408 | ** then the other will be assigned based on its counterpart. Both |
| 409 | ** may not be NULL. |
| 410 | ** |
| 411 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -48,11 +48,11 @@ | |
| 48 | Blob comment; /* Check-in comment text */ |
| 49 | char *zCommentMimetype; /* Mimetype of comment. May be NULL */ |
| 50 | char *zUser; /* User name */ |
| 51 | char *zDate; /* Optionally force this date string (anything |
| 52 | supported by date_in_standard_format()). |
| 53 | May be NULL. */ |
| 54 | Blob *pMfOut; /* If not NULL, checkin_mini() will write a |
| 55 | copy of the generated manifest here. This |
| 56 | memory is NOT owned by CheckinMiniInfo. */ |
| 57 | int filePerm; /* Permissions (via file_perm()) of the input |
| 58 | file. We need to store this before calling |
| @@ -123,11 +123,11 @@ | |
| 123 | */ |
| 124 | CIMINI_PREFER_DELTA = 1<<8, |
| 125 | /* |
| 126 | ** A "stronger hint" to checkin_mini() to prefer creation of a delta |
| 127 | ** manifest if it at all can. It will decide not to only if creation |
| 128 | ** of a delta is not a realistic option or if it's forbidden by the |
| 129 | ** forbid-delta-manifests repo config option. For this to work, it |
| 130 | ** must be set together with the CIMINI_PREFER_DELTA flag, but the two |
| 131 | ** cannot be combined in this enum. |
| 132 | ** |
| 133 | ** This option is ONLY INTENDED FOR TESTING, used in bypassing |
| @@ -400,11 +400,11 @@ | |
| 400 | ** This routine uses the state from the given fully-populated pCI |
| 401 | ** argument to add pCI->fileContent to the database, and create and |
| 402 | ** save a manifest for that change. Ownership of pCI and its contents |
| 403 | ** are unchanged. |
| 404 | ** |
| 405 | ** This function may modify pCI as follows: |
| 406 | ** |
| 407 | ** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL, |
| 408 | ** then the other will be assigned based on its counterpart. Both |
| 409 | ** may not be NULL. |
| 410 | ** |
| 411 |
+1
-1
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -133,11 +133,11 @@ | ||
| 133 | 133 | ** (recursively), else they are not. When checking in-response-to |
| 134 | 134 | ** posts, the first one which is closed ends the search. |
| 135 | 135 | ** |
| 136 | 136 | ** Note that this function checks _exactly_ the given rid, whereas |
| 137 | 137 | ** forum post closure/re-opening is always applied to the head of an |
| 138 | -** edit chain so that we get consistent implied locking beheavior for | |
| 138 | +** edit chain so that we get consistent implied locking behavior for | |
| 139 | 139 | ** later versions and responses to arbitrary versions in the |
| 140 | 140 | ** chain. Even so, the "closed" tag is applied as a propagating tag |
| 141 | 141 | ** so will apply to all edits in a given chain. |
| 142 | 142 | ** |
| 143 | 143 | ** The return value is one of: |
| 144 | 144 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -133,11 +133,11 @@ | |
| 133 | ** (recursively), else they are not. When checking in-response-to |
| 134 | ** posts, the first one which is closed ends the search. |
| 135 | ** |
| 136 | ** Note that this function checks _exactly_ the given rid, whereas |
| 137 | ** forum post closure/re-opening is always applied to the head of an |
| 138 | ** edit chain so that we get consistent implied locking beheavior for |
| 139 | ** later versions and responses to arbitrary versions in the |
| 140 | ** chain. Even so, the "closed" tag is applied as a propagating tag |
| 141 | ** so will apply to all edits in a given chain. |
| 142 | ** |
| 143 | ** The return value is one of: |
| 144 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -133,11 +133,11 @@ | |
| 133 | ** (recursively), else they are not. When checking in-response-to |
| 134 | ** posts, the first one which is closed ends the search. |
| 135 | ** |
| 136 | ** Note that this function checks _exactly_ the given rid, whereas |
| 137 | ** forum post closure/re-opening is always applied to the head of an |
| 138 | ** edit chain so that we get consistent implied locking behavior for |
| 139 | ** later versions and responses to arbitrary versions in the |
| 140 | ** chain. Even so, the "closed" tag is applied as a propagating tag |
| 141 | ** so will apply to all edits in a given chain. |
| 142 | ** |
| 143 | ** The return value is one of: |
| 144 |
+1
-1
| --- src/fossil.confirmer.js | ||
| +++ src/fossil.confirmer.js | ||
| @@ -168,11 +168,11 @@ | ||
| 168 | 168 | childs.forEach((e)=>target.appendChild(e)); |
| 169 | 169 | } |
| 170 | 170 | } |
| 171 | 171 | const formatCountdown = (txt, number) => txt + " ["+number+"]"; |
| 172 | 172 | if(opt.pinSize && opt.confirmText){ |
| 173 | - /* Try to pin the element's width the the greater of its | |
| 173 | + /* Try to pin the element's width to the greater of its | |
| 174 | 174 | current width or its waiting-on-confirmation width |
| 175 | 175 | to avoid layout reflow when it's activated. */ |
| 176 | 176 | const digits = (''+(opt.timeout/1000 || opt.ticks)).length; |
| 177 | 177 | const lblLong = formatCountdown(opt.confirmText, "00000000".substr(0,digits+1)); |
| 178 | 178 | const w1 = parseInt(target.getBoundingClientRect().width); |
| 179 | 179 |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -168,11 +168,11 @@ | |
| 168 | childs.forEach((e)=>target.appendChild(e)); |
| 169 | } |
| 170 | } |
| 171 | const formatCountdown = (txt, number) => txt + " ["+number+"]"; |
| 172 | if(opt.pinSize && opt.confirmText){ |
| 173 | /* Try to pin the element's width the the greater of its |
| 174 | current width or its waiting-on-confirmation width |
| 175 | to avoid layout reflow when it's activated. */ |
| 176 | const digits = (''+(opt.timeout/1000 || opt.ticks)).length; |
| 177 | const lblLong = formatCountdown(opt.confirmText, "00000000".substr(0,digits+1)); |
| 178 | const w1 = parseInt(target.getBoundingClientRect().width); |
| 179 |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -168,11 +168,11 @@ | |
| 168 | childs.forEach((e)=>target.appendChild(e)); |
| 169 | } |
| 170 | } |
| 171 | const formatCountdown = (txt, number) => txt + " ["+number+"]"; |
| 172 | if(opt.pinSize && opt.confirmText){ |
| 173 | /* Try to pin the element's width to the greater of its |
| 174 | current width or its waiting-on-confirmation width |
| 175 | to avoid layout reflow when it's activated. */ |
| 176 | const digits = (''+(opt.timeout/1000 || opt.ticks)).length; |
| 177 | const lblLong = formatCountdown(opt.confirmText, "00000000".substr(0,digits+1)); |
| 178 | const w1 = parseInt(target.getBoundingClientRect().width); |
| 179 |
+4
-4
| --- src/fossil.diff.js | ||
| +++ src/fossil.diff.js | ||
| @@ -35,11 +35,11 @@ | ||
| 35 | 35 | } |
| 36 | 36 | const D = window.fossil.dom; |
| 37 | 37 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 38 | 38 | let checkedCount = |
| 39 | 39 | 0 /* When showing more than one diff, keep track of how many |
| 40 | - "show/hide" checkboxes are are checked so we can update the | |
| 40 | + "show/hide" checkboxes are checked so we can update the | |
| 41 | 41 | "show/hide all" label dynamically. */; |
| 42 | 42 | let btnAll /* UI control to show/hide all diffs */; |
| 43 | 43 | /* Install a diff-toggle button for the given diff table element. */ |
| 44 | 44 | const addToggle = function(diffElem){ |
| 45 | 45 | const sib = diffElem.previousElementSibling, |
| @@ -106,11 +106,11 @@ | ||
| 106 | 106 | chunkLoadLines: ( |
| 107 | 107 | F.config.diffContextLines * 3 |
| 108 | 108 | /*per /chat discussion*/ |
| 109 | 109 | ) || 20, |
| 110 | 110 | chunkFetch: { |
| 111 | - /* Default callack handlers for Diff.fetchArtifactChunk(), | |
| 111 | + /* Default callback handlers for Diff.fetchArtifactChunk(), | |
| 112 | 112 | unless overridden by options passeed to that function. */ |
| 113 | 113 | beforesend: function(){}, |
| 114 | 114 | aftersend: function(){}, |
| 115 | 115 | onerror: function(e){ |
| 116 | 116 | console.error("XHR error: ",e); |
| @@ -368,11 +368,11 @@ | ||
| 368 | 368 | } |
| 369 | 369 | return this; |
| 370 | 370 | }, |
| 371 | 371 | |
| 372 | 372 | /** |
| 373 | - Callack for /jchunk responses. | |
| 373 | + Callback for /jchunk responses. | |
| 374 | 374 | */ |
| 375 | 375 | injectResponse: function f(fetchType/*as for fetchChunk()*/, |
| 376 | 376 | urlParam/*from fetchChunk()*/, |
| 377 | 377 | lines/*response lines*/){ |
| 378 | 378 | if(!lines.length){ |
| @@ -718,11 +718,11 @@ | ||
| 718 | 718 | : undefined; |
| 719 | 719 | const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */; |
| 720 | 720 | if( eToggleParent ){ |
| 721 | 721 | /* Add a checkbox to toggle sbs scroll sync. Remember that in |
| 722 | 722 | order to be UI-consistent in the /vdiff page we have to ensure |
| 723 | - that the checkbox is to the LEFT of of its label. We store the | |
| 723 | + that the checkbox is to the LEFT of its label. We store the | |
| 724 | 724 | sync-scroll preference in F.storage (not a cookie) so that it |
| 725 | 725 | persists across page loads and different apps. */ |
| 726 | 726 | cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true)); |
| 727 | 727 | D.append(eToggleParent, D.append( |
| 728 | 728 | D.addClass(D.create('span'), 'input-with-label'), |
| 729 | 729 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -35,11 +35,11 @@ | |
| 35 | } |
| 36 | const D = window.fossil.dom; |
| 37 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 38 | let checkedCount = |
| 39 | 0 /* When showing more than one diff, keep track of how many |
| 40 | "show/hide" checkboxes are are checked so we can update the |
| 41 | "show/hide all" label dynamically. */; |
| 42 | let btnAll /* UI control to show/hide all diffs */; |
| 43 | /* Install a diff-toggle button for the given diff table element. */ |
| 44 | const addToggle = function(diffElem){ |
| 45 | const sib = diffElem.previousElementSibling, |
| @@ -106,11 +106,11 @@ | |
| 106 | chunkLoadLines: ( |
| 107 | F.config.diffContextLines * 3 |
| 108 | /*per /chat discussion*/ |
| 109 | ) || 20, |
| 110 | chunkFetch: { |
| 111 | /* Default callack handlers for Diff.fetchArtifactChunk(), |
| 112 | unless overridden by options passeed to that function. */ |
| 113 | beforesend: function(){}, |
| 114 | aftersend: function(){}, |
| 115 | onerror: function(e){ |
| 116 | console.error("XHR error: ",e); |
| @@ -368,11 +368,11 @@ | |
| 368 | } |
| 369 | return this; |
| 370 | }, |
| 371 | |
| 372 | /** |
| 373 | Callack for /jchunk responses. |
| 374 | */ |
| 375 | injectResponse: function f(fetchType/*as for fetchChunk()*/, |
| 376 | urlParam/*from fetchChunk()*/, |
| 377 | lines/*response lines*/){ |
| 378 | if(!lines.length){ |
| @@ -718,11 +718,11 @@ | |
| 718 | : undefined; |
| 719 | const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */; |
| 720 | if( eToggleParent ){ |
| 721 | /* Add a checkbox to toggle sbs scroll sync. Remember that in |
| 722 | order to be UI-consistent in the /vdiff page we have to ensure |
| 723 | that the checkbox is to the LEFT of of its label. We store the |
| 724 | sync-scroll preference in F.storage (not a cookie) so that it |
| 725 | persists across page loads and different apps. */ |
| 726 | cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true)); |
| 727 | D.append(eToggleParent, D.append( |
| 728 | D.addClass(D.create('span'), 'input-with-label'), |
| 729 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -35,11 +35,11 @@ | |
| 35 | } |
| 36 | const D = window.fossil.dom; |
| 37 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 38 | let checkedCount = |
| 39 | 0 /* When showing more than one diff, keep track of how many |
| 40 | "show/hide" checkboxes are checked so we can update the |
| 41 | "show/hide all" label dynamically. */; |
| 42 | let btnAll /* UI control to show/hide all diffs */; |
| 43 | /* Install a diff-toggle button for the given diff table element. */ |
| 44 | const addToggle = function(diffElem){ |
| 45 | const sib = diffElem.previousElementSibling, |
| @@ -106,11 +106,11 @@ | |
| 106 | chunkLoadLines: ( |
| 107 | F.config.diffContextLines * 3 |
| 108 | /*per /chat discussion*/ |
| 109 | ) || 20, |
| 110 | chunkFetch: { |
| 111 | /* Default callback handlers for Diff.fetchArtifactChunk(), |
| 112 | unless overridden by options passeed to that function. */ |
| 113 | beforesend: function(){}, |
| 114 | aftersend: function(){}, |
| 115 | onerror: function(e){ |
| 116 | console.error("XHR error: ",e); |
| @@ -368,11 +368,11 @@ | |
| 368 | } |
| 369 | return this; |
| 370 | }, |
| 371 | |
| 372 | /** |
| 373 | Callback for /jchunk responses. |
| 374 | */ |
| 375 | injectResponse: function f(fetchType/*as for fetchChunk()*/, |
| 376 | urlParam/*from fetchChunk()*/, |
| 377 | lines/*response lines*/){ |
| 378 | if(!lines.length){ |
| @@ -718,11 +718,11 @@ | |
| 718 | : undefined; |
| 719 | const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */; |
| 720 | if( eToggleParent ){ |
| 721 | /* Add a checkbox to toggle sbs scroll sync. Remember that in |
| 722 | order to be UI-consistent in the /vdiff page we have to ensure |
| 723 | that the checkbox is to the LEFT of its label. We store the |
| 724 | sync-scroll preference in F.storage (not a cookie) so that it |
| 725 | persists across page loads and different apps. */ |
| 726 | cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true)); |
| 727 | D.append(eToggleParent, D.append( |
| 728 | D.addClass(D.create('span'), 'input-with-label'), |
| 729 |
+1
-1
| --- src/fossil.dom.js | ||
| +++ src/fossil.dom.js | ||
| @@ -259,11 +259,11 @@ | ||
| 259 | 259 | dom.td = dom.createElemFactoryWithOptionalParent('td'); |
| 260 | 260 | dom.th = dom.createElemFactoryWithOptionalParent('th'); |
| 261 | 261 | |
| 262 | 262 | /** |
| 263 | 263 | Creates and returns a FIELDSET element, optionaly with a LEGEND |
| 264 | - element added to it. If legendText is an HTMLElement then is is | |
| 264 | + element added to it. If legendText is an HTMLElement then it is | |
| 265 | 265 | assumed to be a LEGEND and is appended as-is, else it is assumed |
| 266 | 266 | (if truthy) to be a value suitable for passing to |
| 267 | 267 | dom.append(aLegendElement,...). |
| 268 | 268 | */ |
| 269 | 269 | dom.fieldset = function(legendText){ |
| 270 | 270 |
| --- src/fossil.dom.js | |
| +++ src/fossil.dom.js | |
| @@ -259,11 +259,11 @@ | |
| 259 | dom.td = dom.createElemFactoryWithOptionalParent('td'); |
| 260 | dom.th = dom.createElemFactoryWithOptionalParent('th'); |
| 261 | |
| 262 | /** |
| 263 | Creates and returns a FIELDSET element, optionaly with a LEGEND |
| 264 | element added to it. If legendText is an HTMLElement then is is |
| 265 | assumed to be a LEGEND and is appended as-is, else it is assumed |
| 266 | (if truthy) to be a value suitable for passing to |
| 267 | dom.append(aLegendElement,...). |
| 268 | */ |
| 269 | dom.fieldset = function(legendText){ |
| 270 |
| --- src/fossil.dom.js | |
| +++ src/fossil.dom.js | |
| @@ -259,11 +259,11 @@ | |
| 259 | dom.td = dom.createElemFactoryWithOptionalParent('td'); |
| 260 | dom.th = dom.createElemFactoryWithOptionalParent('th'); |
| 261 | |
| 262 | /** |
| 263 | Creates and returns a FIELDSET element, optionaly with a LEGEND |
| 264 | element added to it. If legendText is an HTMLElement then it is |
| 265 | assumed to be a LEGEND and is appended as-is, else it is assumed |
| 266 | (if truthy) to be a value suitable for passing to |
| 267 | dom.append(aLegendElement,...). |
| 268 | */ |
| 269 | dom.fieldset = function(legendText){ |
| 270 |
+1
-1
| --- src/fossil.fetch.js | ||
| +++ src/fossil.fetch.js | ||
| @@ -61,11 +61,11 @@ | ||
| 61 | 61 | reported here, else they are reported through onerror(). |
| 62 | 62 | Unfortunately, XHR fires two events for a timeout: an |
| 63 | 63 | onreadystatechange() and an ontimeout(), in that order. From the |
| 64 | 64 | former, however, we cannot unambiguously identify the error as |
| 65 | 65 | having been caused by a timeout, so clients which set ontimeout() |
| 66 | - will get _two_ callback calls: one with with an HTTP error response | |
| 66 | + will get _two_ callback calls: one with an HTTP error response | |
| 67 | 67 | followed immediately by an ontimeout() response. Error objects |
| 68 | 68 | passed to this will have (.name='timeout', .status=xhr.HttpStatus). |
| 69 | 69 | In the context of the callback, the options object is "this", Like |
| 70 | 70 | onerror(), any exceptions thrown by the ontimeout() handler are |
| 71 | 71 | suppressed, but may generate a console error message. The onerror() |
| 72 | 72 |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -61,11 +61,11 @@ | |
| 61 | reported here, else they are reported through onerror(). |
| 62 | Unfortunately, XHR fires two events for a timeout: an |
| 63 | onreadystatechange() and an ontimeout(), in that order. From the |
| 64 | former, however, we cannot unambiguously identify the error as |
| 65 | having been caused by a timeout, so clients which set ontimeout() |
| 66 | will get _two_ callback calls: one with with an HTTP error response |
| 67 | followed immediately by an ontimeout() response. Error objects |
| 68 | passed to this will have (.name='timeout', .status=xhr.HttpStatus). |
| 69 | In the context of the callback, the options object is "this", Like |
| 70 | onerror(), any exceptions thrown by the ontimeout() handler are |
| 71 | suppressed, but may generate a console error message. The onerror() |
| 72 |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -61,11 +61,11 @@ | |
| 61 | reported here, else they are reported through onerror(). |
| 62 | Unfortunately, XHR fires two events for a timeout: an |
| 63 | onreadystatechange() and an ontimeout(), in that order. From the |
| 64 | former, however, we cannot unambiguously identify the error as |
| 65 | having been caused by a timeout, so clients which set ontimeout() |
| 66 | will get _two_ callback calls: one with an HTTP error response |
| 67 | followed immediately by an ontimeout() response. Error objects |
| 68 | passed to this will have (.name='timeout', .status=xhr.HttpStatus). |
| 69 | In the context of the callback, the options object is "this", Like |
| 70 | onerror(), any exceptions thrown by the ontimeout() handler are |
| 71 | suppressed, but may generate a console error message. The onerror() |
| 72 |
+3
-3
| --- src/fossil.page.chat.js | ||
| +++ src/fossil.page.chat.js | ||
| @@ -357,11 +357,11 @@ | ||
| 357 | 357 | /** Either scrolls .message-widget element eMsg into view |
| 358 | 358 | immediately or, if it represents an inlined image, delays |
| 359 | 359 | the scroll until the image is loaded, at which point it will |
| 360 | 360 | scroll to either the newest message, if one is set or to |
| 361 | 361 | eMsg (the liklihood is good, at least on initial page load, |
| 362 | - that the the image won't be loaded until other messages have | |
| 362 | + that the image won't be loaded until other messages have | |
| 363 | 363 | been injected). */ |
| 364 | 364 | scheduleScrollOfMsg: function(eMsg){ |
| 365 | 365 | if(1===+eMsg.dataset.hasImage){ |
| 366 | 366 | eMsg.querySelector('img').addEventListener( |
| 367 | 367 | 'load', ()=>(this.e.newestMessage || eMsg).scrollIntoView(false) |
| @@ -1050,11 +1050,11 @@ | ||
| 1050 | 1050 | if(eUser==this || !eUser) return false; |
| 1051 | 1051 | const uname = eUser.dataset.uname; |
| 1052 | 1052 | let eLast; |
| 1053 | 1053 | cs.setCurrentView(cs.e.viewMessages); |
| 1054 | 1054 | if(eUser.classList.contains('selected')){ |
| 1055 | - /* If curently selected, toggle filter off */ | |
| 1055 | + /* If currently selected, toggle filter off */ | |
| 1056 | 1056 | eUser.classList.remove('selected'); |
| 1057 | 1057 | cs.setUserFilter(false); |
| 1058 | 1058 | delete f.$eSelected; |
| 1059 | 1059 | }else{ |
| 1060 | 1060 | if(f.$eSelected) f.$eSelected.classList.remove('selected'); |
| @@ -2856,11 +2856,11 @@ | ||
| 2856 | 2856 | beforesend: chatPollBeforeSend, |
| 2857 | 2857 | aftersend: function(){ |
| 2858 | 2858 | poll.running = false; |
| 2859 | 2859 | }, |
| 2860 | 2860 | ontimeout: function(err){ |
| 2861 | - f.pendingOnError = undefined /*strip preceeding non-timeout error, if any*/; | |
| 2861 | + f.pendingOnError = undefined /*strip preceding non-timeout error, if any*/; | |
| 2862 | 2862 | afterPollFetch(err); |
| 2863 | 2863 | }, |
| 2864 | 2864 | onerror:function(err){ |
| 2865 | 2865 | Chat._isBatchLoading = false; |
| 2866 | 2866 | if(Chat.beVerbose){ |
| 2867 | 2867 |
| --- src/fossil.page.chat.js | |
| +++ src/fossil.page.chat.js | |
| @@ -357,11 +357,11 @@ | |
| 357 | /** Either scrolls .message-widget element eMsg into view |
| 358 | immediately or, if it represents an inlined image, delays |
| 359 | the scroll until the image is loaded, at which point it will |
| 360 | scroll to either the newest message, if one is set or to |
| 361 | eMsg (the liklihood is good, at least on initial page load, |
| 362 | that the the image won't be loaded until other messages have |
| 363 | been injected). */ |
| 364 | scheduleScrollOfMsg: function(eMsg){ |
| 365 | if(1===+eMsg.dataset.hasImage){ |
| 366 | eMsg.querySelector('img').addEventListener( |
| 367 | 'load', ()=>(this.e.newestMessage || eMsg).scrollIntoView(false) |
| @@ -1050,11 +1050,11 @@ | |
| 1050 | if(eUser==this || !eUser) return false; |
| 1051 | const uname = eUser.dataset.uname; |
| 1052 | let eLast; |
| 1053 | cs.setCurrentView(cs.e.viewMessages); |
| 1054 | if(eUser.classList.contains('selected')){ |
| 1055 | /* If curently selected, toggle filter off */ |
| 1056 | eUser.classList.remove('selected'); |
| 1057 | cs.setUserFilter(false); |
| 1058 | delete f.$eSelected; |
| 1059 | }else{ |
| 1060 | if(f.$eSelected) f.$eSelected.classList.remove('selected'); |
| @@ -2856,11 +2856,11 @@ | |
| 2856 | beforesend: chatPollBeforeSend, |
| 2857 | aftersend: function(){ |
| 2858 | poll.running = false; |
| 2859 | }, |
| 2860 | ontimeout: function(err){ |
| 2861 | f.pendingOnError = undefined /*strip preceeding non-timeout error, if any*/; |
| 2862 | afterPollFetch(err); |
| 2863 | }, |
| 2864 | onerror:function(err){ |
| 2865 | Chat._isBatchLoading = false; |
| 2866 | if(Chat.beVerbose){ |
| 2867 |
| --- src/fossil.page.chat.js | |
| +++ src/fossil.page.chat.js | |
| @@ -357,11 +357,11 @@ | |
| 357 | /** Either scrolls .message-widget element eMsg into view |
| 358 | immediately or, if it represents an inlined image, delays |
| 359 | the scroll until the image is loaded, at which point it will |
| 360 | scroll to either the newest message, if one is set or to |
| 361 | eMsg (the liklihood is good, at least on initial page load, |
| 362 | that the image won't be loaded until other messages have |
| 363 | been injected). */ |
| 364 | scheduleScrollOfMsg: function(eMsg){ |
| 365 | if(1===+eMsg.dataset.hasImage){ |
| 366 | eMsg.querySelector('img').addEventListener( |
| 367 | 'load', ()=>(this.e.newestMessage || eMsg).scrollIntoView(false) |
| @@ -1050,11 +1050,11 @@ | |
| 1050 | if(eUser==this || !eUser) return false; |
| 1051 | const uname = eUser.dataset.uname; |
| 1052 | let eLast; |
| 1053 | cs.setCurrentView(cs.e.viewMessages); |
| 1054 | if(eUser.classList.contains('selected')){ |
| 1055 | /* If currently selected, toggle filter off */ |
| 1056 | eUser.classList.remove('selected'); |
| 1057 | cs.setUserFilter(false); |
| 1058 | delete f.$eSelected; |
| 1059 | }else{ |
| 1060 | if(f.$eSelected) f.$eSelected.classList.remove('selected'); |
| @@ -2856,11 +2856,11 @@ | |
| 2856 | beforesend: chatPollBeforeSend, |
| 2857 | aftersend: function(){ |
| 2858 | poll.running = false; |
| 2859 | }, |
| 2860 | ontimeout: function(err){ |
| 2861 | f.pendingOnError = undefined /*strip preceding non-timeout error, if any*/; |
| 2862 | afterPollFetch(err); |
| 2863 | }, |
| 2864 | onerror:function(err){ |
| 2865 | Chat._isBatchLoading = false; |
| 2866 | if(Chat.beVerbose){ |
| 2867 |
+1
-1
| --- src/fossil.page.fileedit.js | ||
| +++ src/fossil.page.fileedit.js | ||
| @@ -1149,11 +1149,11 @@ | ||
| 1149 | 1149 | return this; |
| 1150 | 1150 | }; |
| 1151 | 1151 | |
| 1152 | 1152 | /** |
| 1153 | 1153 | Fetches the page preview based on the contents and settings of |
| 1154 | - this page's input fields, and updates the UI with with the | |
| 1154 | + this page's input fields, and updates the UI with the | |
| 1155 | 1155 | preview. |
| 1156 | 1156 | |
| 1157 | 1157 | Returns this object, noting that the operation is async. |
| 1158 | 1158 | */ |
| 1159 | 1159 | P.preview = function f(switchToTab){ |
| 1160 | 1160 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -1149,11 +1149,11 @@ | |
| 1149 | return this; |
| 1150 | }; |
| 1151 | |
| 1152 | /** |
| 1153 | Fetches the page preview based on the contents and settings of |
| 1154 | this page's input fields, and updates the UI with with the |
| 1155 | preview. |
| 1156 | |
| 1157 | Returns this object, noting that the operation is async. |
| 1158 | */ |
| 1159 | P.preview = function f(switchToTab){ |
| 1160 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -1149,11 +1149,11 @@ | |
| 1149 | return this; |
| 1150 | }; |
| 1151 | |
| 1152 | /** |
| 1153 | Fetches the page preview based on the contents and settings of |
| 1154 | this page's input fields, and updates the UI with the |
| 1155 | preview. |
| 1156 | |
| 1157 | Returns this object, noting that the operation is async. |
| 1158 | */ |
| 1159 | P.preview = function f(switchToTab){ |
| 1160 |
+1
-1
| --- src/fossil.page.wikiedit.js | ||
| +++ src/fossil.page.wikiedit.js | ||
| @@ -1457,11 +1457,11 @@ | ||
| 1457 | 1457 | return this; |
| 1458 | 1458 | }; |
| 1459 | 1459 | |
| 1460 | 1460 | /** |
| 1461 | 1461 | Fetches the page preview based on the contents and settings of |
| 1462 | - this page's input fields, and updates the UI with with the | |
| 1462 | + this page's input fields, and updates the UI with the | |
| 1463 | 1463 | preview. |
| 1464 | 1464 | |
| 1465 | 1465 | Returns this object, noting that the operation is async. |
| 1466 | 1466 | */ |
| 1467 | 1467 | P.preview = function f(switchToTab){ |
| 1468 | 1468 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -1457,11 +1457,11 @@ | |
| 1457 | return this; |
| 1458 | }; |
| 1459 | |
| 1460 | /** |
| 1461 | Fetches the page preview based on the contents and settings of |
| 1462 | this page's input fields, and updates the UI with with the |
| 1463 | preview. |
| 1464 | |
| 1465 | Returns this object, noting that the operation is async. |
| 1466 | */ |
| 1467 | P.preview = function f(switchToTab){ |
| 1468 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -1457,11 +1457,11 @@ | |
| 1457 | return this; |
| 1458 | }; |
| 1459 | |
| 1460 | /** |
| 1461 | Fetches the page preview based on the contents and settings of |
| 1462 | this page's input fields, and updates the UI with the |
| 1463 | preview. |
| 1464 | |
| 1465 | Returns this object, noting that the operation is async. |
| 1466 | */ |
| 1467 | P.preview = function f(switchToTab){ |
| 1468 |
+1
-1
| --- src/fossil.pikchr.js | ||
| +++ src/fossil.pikchr.js | ||
| @@ -54,11 +54,11 @@ | ||
| 54 | 54 | meta) is handled differently everywhere. Shift is used |
| 55 | 55 | by the browser, Ctrl doesn't work on an iMac, and Alt is |
| 56 | 56 | intercepted by most Linux window managers to control |
| 57 | 57 | window movement! So... we just listen for *any* of them |
| 58 | 58 | (except Shift) and the user will need to find one which |
| 59 | - works on on their environment. */ | |
| 59 | + works on their environment. */ | |
| 60 | 60 | || this.classList.contains('toggle')){ |
| 61 | 61 | this.classList.toggle('source'); |
| 62 | 62 | ev.stopPropagation(); |
| 63 | 63 | ev.preventDefault(); |
| 64 | 64 | } |
| 65 | 65 |
| --- src/fossil.pikchr.js | |
| +++ src/fossil.pikchr.js | |
| @@ -54,11 +54,11 @@ | |
| 54 | meta) is handled differently everywhere. Shift is used |
| 55 | by the browser, Ctrl doesn't work on an iMac, and Alt is |
| 56 | intercepted by most Linux window managers to control |
| 57 | window movement! So... we just listen for *any* of them |
| 58 | (except Shift) and the user will need to find one which |
| 59 | works on on their environment. */ |
| 60 | || this.classList.contains('toggle')){ |
| 61 | this.classList.toggle('source'); |
| 62 | ev.stopPropagation(); |
| 63 | ev.preventDefault(); |
| 64 | } |
| 65 |
| --- src/fossil.pikchr.js | |
| +++ src/fossil.pikchr.js | |
| @@ -54,11 +54,11 @@ | |
| 54 | meta) is handled differently everywhere. Shift is used |
| 55 | by the browser, Ctrl doesn't work on an iMac, and Alt is |
| 56 | intercepted by most Linux window managers to control |
| 57 | window movement! So... we just listen for *any* of them |
| 58 | (except Shift) and the user will need to find one which |
| 59 | works on their environment. */ |
| 60 | || this.classList.contains('toggle')){ |
| 61 | this.classList.toggle('source'); |
| 62 | ev.stopPropagation(); |
| 63 | ev.preventDefault(); |
| 64 | } |
| 65 |
+2
-2
| --- src/fossil.wikiedit-wysiwyg.js | ||
| +++ src/fossil.wikiedit-wysiwyg.js | ||
| @@ -449,21 +449,21 @@ | ||
| 449 | 449 | oDoc.focus(); |
| 450 | 450 | } |
| 451 | 451 | |
| 452 | 452 | //////////////////////////////////////////////////////////////////////// |
| 453 | 453 | // A hook which can be activated via a site skin to plug this editor |
| 454 | - // in to the wikiedit page. | |
| 454 | + // into the wikiedit page. | |
| 455 | 455 | F.page.wysiwyg = { |
| 456 | 456 | // only for debugging: oDoc: oDoc, |
| 457 | 457 | /* |
| 458 | 458 | Replaces wikiedit's default editor widget with this wysiwyg |
| 459 | 459 | editor. |
| 460 | 460 | |
| 461 | 461 | Must either be called via an onPageLoad handler via the site |
| 462 | 462 | skin's footer or else it can be called manually from the dev |
| 463 | 463 | tools console. Calling it too early (e.g. in the page footer |
| 464 | - outside of an an onPageLoad handler) will crash because wikiedit | |
| 464 | + outside of an onPageLoad handler) will crash because wikiedit | |
| 465 | 465 | has not been initialized. |
| 466 | 466 | */ |
| 467 | 467 | init: function(){ |
| 468 | 468 | initDoc(); |
| 469 | 469 | const content = F.page.wikiContent() || ''; |
| 470 | 470 |
| --- src/fossil.wikiedit-wysiwyg.js | |
| +++ src/fossil.wikiedit-wysiwyg.js | |
| @@ -449,21 +449,21 @@ | |
| 449 | oDoc.focus(); |
| 450 | } |
| 451 | |
| 452 | //////////////////////////////////////////////////////////////////////// |
| 453 | // A hook which can be activated via a site skin to plug this editor |
| 454 | // in to the wikiedit page. |
| 455 | F.page.wysiwyg = { |
| 456 | // only for debugging: oDoc: oDoc, |
| 457 | /* |
| 458 | Replaces wikiedit's default editor widget with this wysiwyg |
| 459 | editor. |
| 460 | |
| 461 | Must either be called via an onPageLoad handler via the site |
| 462 | skin's footer or else it can be called manually from the dev |
| 463 | tools console. Calling it too early (e.g. in the page footer |
| 464 | outside of an an onPageLoad handler) will crash because wikiedit |
| 465 | has not been initialized. |
| 466 | */ |
| 467 | init: function(){ |
| 468 | initDoc(); |
| 469 | const content = F.page.wikiContent() || ''; |
| 470 |
| --- src/fossil.wikiedit-wysiwyg.js | |
| +++ src/fossil.wikiedit-wysiwyg.js | |
| @@ -449,21 +449,21 @@ | |
| 449 | oDoc.focus(); |
| 450 | } |
| 451 | |
| 452 | //////////////////////////////////////////////////////////////////////// |
| 453 | // A hook which can be activated via a site skin to plug this editor |
| 454 | // into the wikiedit page. |
| 455 | F.page.wysiwyg = { |
| 456 | // only for debugging: oDoc: oDoc, |
| 457 | /* |
| 458 | Replaces wikiedit's default editor widget with this wysiwyg |
| 459 | editor. |
| 460 | |
| 461 | Must either be called via an onPageLoad handler via the site |
| 462 | skin's footer or else it can be called manually from the dev |
| 463 | tools console. Calling it too early (e.g. in the page footer |
| 464 | outside of an onPageLoad handler) will crash because wikiedit |
| 465 | has not been initialized. |
| 466 | */ |
| 467 | init: function(){ |
| 468 | initDoc(); |
| 469 | const content = F.page.wikiContent() || ''; |
| 470 |
+1
-1
| --- src/fshell.c | ||
| +++ src/fshell.c | ||
| @@ -21,11 +21,11 @@ | ||
| 21 | 21 | ** after the fashion of a standard Bourne shell and forks a child process |
| 22 | 22 | ** to run the corresponding Fossil command. This only works on Unix. |
| 23 | 23 | ** |
| 24 | 24 | ** The "fossil shell" command is intended for use with SEE-enabled fossil. |
| 25 | 25 | ** It allows multiple commands to be issued without having to reenter the |
| 26 | -** crypto phasephrase for each command. | |
| 26 | +** crypto passphrase for each command. | |
| 27 | 27 | */ |
| 28 | 28 | #include "config.h" |
| 29 | 29 | #include "fshell.h" |
| 30 | 30 | #include <ctype.h> |
| 31 | 31 | |
| 32 | 32 |
| --- src/fshell.c | |
| +++ src/fshell.c | |
| @@ -21,11 +21,11 @@ | |
| 21 | ** after the fashion of a standard Bourne shell and forks a child process |
| 22 | ** to run the corresponding Fossil command. This only works on Unix. |
| 23 | ** |
| 24 | ** The "fossil shell" command is intended for use with SEE-enabled fossil. |
| 25 | ** It allows multiple commands to be issued without having to reenter the |
| 26 | ** crypto phasephrase for each command. |
| 27 | */ |
| 28 | #include "config.h" |
| 29 | #include "fshell.h" |
| 30 | #include <ctype.h> |
| 31 | |
| 32 |
| --- src/fshell.c | |
| +++ src/fshell.c | |
| @@ -21,11 +21,11 @@ | |
| 21 | ** after the fashion of a standard Bourne shell and forks a child process |
| 22 | ** to run the corresponding Fossil command. This only works on Unix. |
| 23 | ** |
| 24 | ** The "fossil shell" command is intended for use with SEE-enabled fossil. |
| 25 | ** It allows multiple commands to be issued without having to reenter the |
| 26 | ** crypto passphrase for each command. |
| 27 | */ |
| 28 | #include "config.h" |
| 29 | #include "fshell.h" |
| 30 | #include <ctype.h> |
| 31 | |
| 32 |
+2
-2
| --- src/glob.c | ||
| +++ src/glob.c | ||
| @@ -29,11 +29,11 @@ | ||
| 29 | 29 | ** zVal: "x" |
| 30 | 30 | ** zGlobList: "*.o,*.obj" |
| 31 | 31 | ** |
| 32 | 32 | ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')" |
| 33 | 33 | ** |
| 34 | -** Commas and whitespace are considered to be element delimters. Each | |
| 34 | +** Commas and whitespace are considered to be element delimiters. Each | |
| 35 | 35 | ** element of the GLOB list may optionally be enclosed in either '...' or |
| 36 | 36 | ** "...". This allows commas and/or whitespace to be used in the elements |
| 37 | 37 | ** themselves. |
| 38 | 38 | ** |
| 39 | 39 | ** The returned string is owned by the caller, who must fossil_free() |
| @@ -57,11 +57,11 @@ | ||
| 57 | 57 | cTerm = zGlobList[0]; |
| 58 | 58 | zGlobList++; |
| 59 | 59 | }else{ |
| 60 | 60 | cTerm = ','; |
| 61 | 61 | } |
| 62 | - /* Find the next delimter (or the end of the string). */ | |
| 62 | + /* Find the next delimiter (or the end of the string). */ | |
| 63 | 63 | for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){ |
| 64 | 64 | if( cTerm!=',' ) continue; /* If quoted, keep going. */ |
| 65 | 65 | if( fossil_isspace(zGlobList[i]) ) break; /* If space, stop. */ |
| 66 | 66 | } |
| 67 | 67 | blob_appendf(&expr, "%s%s GLOB '%#q'", zSep, zVal, i, zGlobList); |
| 68 | 68 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -29,11 +29,11 @@ | |
| 29 | ** zVal: "x" |
| 30 | ** zGlobList: "*.o,*.obj" |
| 31 | ** |
| 32 | ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')" |
| 33 | ** |
| 34 | ** Commas and whitespace are considered to be element delimters. Each |
| 35 | ** element of the GLOB list may optionally be enclosed in either '...' or |
| 36 | ** "...". This allows commas and/or whitespace to be used in the elements |
| 37 | ** themselves. |
| 38 | ** |
| 39 | ** The returned string is owned by the caller, who must fossil_free() |
| @@ -57,11 +57,11 @@ | |
| 57 | cTerm = zGlobList[0]; |
| 58 | zGlobList++; |
| 59 | }else{ |
| 60 | cTerm = ','; |
| 61 | } |
| 62 | /* Find the next delimter (or the end of the string). */ |
| 63 | for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){ |
| 64 | if( cTerm!=',' ) continue; /* If quoted, keep going. */ |
| 65 | if( fossil_isspace(zGlobList[i]) ) break; /* If space, stop. */ |
| 66 | } |
| 67 | blob_appendf(&expr, "%s%s GLOB '%#q'", zSep, zVal, i, zGlobList); |
| 68 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -29,11 +29,11 @@ | |
| 29 | ** zVal: "x" |
| 30 | ** zGlobList: "*.o,*.obj" |
| 31 | ** |
| 32 | ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')" |
| 33 | ** |
| 34 | ** Commas and whitespace are considered to be element delimiters. Each |
| 35 | ** element of the GLOB list may optionally be enclosed in either '...' or |
| 36 | ** "...". This allows commas and/or whitespace to be used in the elements |
| 37 | ** themselves. |
| 38 | ** |
| 39 | ** The returned string is owned by the caller, who must fossil_free() |
| @@ -57,11 +57,11 @@ | |
| 57 | cTerm = zGlobList[0]; |
| 58 | zGlobList++; |
| 59 | }else{ |
| 60 | cTerm = ','; |
| 61 | } |
| 62 | /* Find the next delimiter (or the end of the string). */ |
| 63 | for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){ |
| 64 | if( cTerm!=',' ) continue; /* If quoted, keep going. */ |
| 65 | if( fossil_isspace(zGlobList[i]) ) break; /* If space, stop. */ |
| 66 | } |
| 67 | blob_appendf(&expr, "%s%s GLOB '%#q'", zSep, zVal, i, zGlobList); |
| 68 |
+1
-1
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -51,11 +51,11 @@ | ||
| 51 | 51 | |
| 52 | 52 | /* |
| 53 | 53 | ** The type of integer identifiers for rows of the graph. |
| 54 | 54 | ** |
| 55 | 55 | ** For a normal /timeline graph, the identifiers are never that big |
| 56 | -** an an ordinary 32-bit int will work fine. But for the /finfo page, | |
| 56 | +** an ordinary 32-bit int will work fine. But for the /finfo page, | |
| 57 | 57 | ** the identifier is a combination of the BLOB.RID and the FILENAME.FNID |
| 58 | 58 | ** values, and so it can become quite large for repos that have both many |
| 59 | 59 | ** check-ins and many files. For this reason, we make the identifier |
| 60 | 60 | ** a 64-bit integer, to dramatically reduce the risk of an overflow. |
| 61 | 61 | */ |
| 62 | 62 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | |
| 52 | /* |
| 53 | ** The type of integer identifiers for rows of the graph. |
| 54 | ** |
| 55 | ** For a normal /timeline graph, the identifiers are never that big |
| 56 | ** an an ordinary 32-bit int will work fine. But for the /finfo page, |
| 57 | ** the identifier is a combination of the BLOB.RID and the FILENAME.FNID |
| 58 | ** values, and so it can become quite large for repos that have both many |
| 59 | ** check-ins and many files. For this reason, we make the identifier |
| 60 | ** a 64-bit integer, to dramatically reduce the risk of an overflow. |
| 61 | */ |
| 62 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | |
| 52 | /* |
| 53 | ** The type of integer identifiers for rows of the graph. |
| 54 | ** |
| 55 | ** For a normal /timeline graph, the identifiers are never that big |
| 56 | ** an ordinary 32-bit int will work fine. But for the /finfo page, |
| 57 | ** the identifier is a combination of the BLOB.RID and the FILENAME.FNID |
| 58 | ** values, and so it can become quite large for repos that have both many |
| 59 | ** check-ins and many files. For this reason, we make the identifier |
| 60 | ** a 64-bit integer, to dramatically reduce the risk of an overflow. |
| 61 | */ |
| 62 |
+1
-1
| --- src/hname.c | ||
| +++ src/hname.c | ||
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | |
| 200 | 200 | /* |
| 201 | 201 | ** Return the default hash policy for repositories that do not currently |
| 202 | 202 | ** have an assigned hash policy. |
| 203 | 203 | ** |
| 204 | -** Make the default HPOLICY_AUTO if there are SHA1 artficates but no SHA3 | |
| 204 | +** Make the default HPOLICY_AUTO if there are SHA1 artifacts but no SHA3 | |
| 205 | 205 | ** artifacts in the repository. Make the default HPOLICY_SHA3 if there |
| 206 | 206 | ** are one or more SHA3 artifacts or if the repository is initially empty. |
| 207 | 207 | */ |
| 208 | 208 | int hname_default_policy(void){ |
| 209 | 209 | if( db_exists("SELECT 1 FROM blob WHERE length(uuid)>40") |
| 210 | 210 |
| --- src/hname.c | |
| +++ src/hname.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | |
| 200 | /* |
| 201 | ** Return the default hash policy for repositories that do not currently |
| 202 | ** have an assigned hash policy. |
| 203 | ** |
| 204 | ** Make the default HPOLICY_AUTO if there are SHA1 artficates but no SHA3 |
| 205 | ** artifacts in the repository. Make the default HPOLICY_SHA3 if there |
| 206 | ** are one or more SHA3 artifacts or if the repository is initially empty. |
| 207 | */ |
| 208 | int hname_default_policy(void){ |
| 209 | if( db_exists("SELECT 1 FROM blob WHERE length(uuid)>40") |
| 210 |
| --- src/hname.c | |
| +++ src/hname.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | |
| 200 | /* |
| 201 | ** Return the default hash policy for repositories that do not currently |
| 202 | ** have an assigned hash policy. |
| 203 | ** |
| 204 | ** Make the default HPOLICY_AUTO if there are SHA1 artifacts but no SHA3 |
| 205 | ** artifacts in the repository. Make the default HPOLICY_SHA3 if there |
| 206 | ** are one or more SHA3 artifacts or if the repository is initially empty. |
| 207 | */ |
| 208 | int hname_default_policy(void){ |
| 209 | if( db_exists("SELECT 1 FROM blob WHERE length(uuid)>40") |
| 210 |
+1
-1
| --- src/hook.c | ||
| +++ src/hook.c | ||
| @@ -29,11 +29,11 @@ | ||
| 29 | 29 | ** } |
| 30 | 30 | ** |
| 31 | 31 | ** hook-last-rcvid The last rcvid for which post-receive hooks were |
| 32 | 32 | ** run. |
| 33 | 33 | ** |
| 34 | -** hook-embargo Do not run hooks again before this julianday. | |
| 34 | +** hook-embargo Do not run hooks again before this Julian day. | |
| 35 | 35 | ** |
| 36 | 36 | ** For "after-receive" hooks, a list of the received artifacts is sent |
| 37 | 37 | ** into the command via standard input. Each line of input begins with |
| 38 | 38 | ** the hash of the artifact and continues with a description of the |
| 39 | 39 | ** interpretation of the artifact. |
| 40 | 40 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -29,11 +29,11 @@ | |
| 29 | ** } |
| 30 | ** |
| 31 | ** hook-last-rcvid The last rcvid for which post-receive hooks were |
| 32 | ** run. |
| 33 | ** |
| 34 | ** hook-embargo Do not run hooks again before this julianday. |
| 35 | ** |
| 36 | ** For "after-receive" hooks, a list of the received artifacts is sent |
| 37 | ** into the command via standard input. Each line of input begins with |
| 38 | ** the hash of the artifact and continues with a description of the |
| 39 | ** interpretation of the artifact. |
| 40 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -29,11 +29,11 @@ | |
| 29 | ** } |
| 30 | ** |
| 31 | ** hook-last-rcvid The last rcvid for which post-receive hooks were |
| 32 | ** run. |
| 33 | ** |
| 34 | ** hook-embargo Do not run hooks again before this Julian day. |
| 35 | ** |
| 36 | ** For "after-receive" hooks, a list of the received artifacts is sent |
| 37 | ** into the command via standard input. Each line of input begins with |
| 38 | ** the hash of the artifact and continues with a description of the |
| 39 | ** interpretation of the artifact. |
| 40 |
+33
-14
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -246,11 +246,11 @@ | ||
| 246 | 246 | } |
| 247 | 247 | return zHttpAuth; |
| 248 | 248 | } |
| 249 | 249 | |
| 250 | 250 | /* |
| 251 | -** Send content pSend to the the server identified by g.url using the | |
| 251 | +** Send content pSend to the server identified by g.url using the | |
| 252 | 252 | ** external program given by g.zHttpCmd. Capture the reply from that |
| 253 | 253 | ** program and load it into pReply. |
| 254 | 254 | ** |
| 255 | 255 | ** This routine implements the --transport-command option for "fossil sync". |
| 256 | 256 | */ |
| @@ -353,11 +353,11 @@ | ||
| 353 | 353 | db_finalize(&s); |
| 354 | 354 | db_swap_connections(); |
| 355 | 355 | } |
| 356 | 356 | } |
| 357 | 357 | |
| 358 | -/* Add an approprate PATH= argument to the SSH command under construction | |
| 358 | +/* Add an appropriate PATH= argument to the SSH command under construction | |
| 359 | 359 | ** in pCmd. |
| 360 | 360 | ** |
| 361 | 361 | ** About This Feature |
| 362 | 362 | ** ================== |
| 363 | 363 | ** |
| @@ -581,11 +581,13 @@ | ||
| 581 | 581 | } |
| 582 | 582 | if( rc!=200 && rc!=301 && rc!=302 && rc!=307 && rc!=308 ){ |
| 583 | 583 | int ii; |
| 584 | 584 | for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} |
| 585 | 585 | while( zLine[ii]==' ' ) ii++; |
| 586 | - fossil_warning("server says: %s", &zLine[ii]); | |
| 586 | + if( (mHttpFlags & HTTP_QUIET)==0 ){ | |
| 587 | + fossil_warning("server says: %s", &zLine[ii]); | |
| 588 | + } | |
| 587 | 589 | goto write_err; |
| 588 | 590 | } |
| 589 | 591 | if( iHttpVersion==0 ){ |
| 590 | 592 | closeConnection = 1; |
| 591 | 593 | }else{ |
| @@ -616,11 +618,13 @@ | ||
| 616 | 618 | int i, j; |
| 617 | 619 | int wasHttps; |
| 618 | 620 | int priorUrlFlags; |
| 619 | 621 | |
| 620 | 622 | if ( --maxRedirect == 0){ |
| 621 | - fossil_warning("redirect limit exceeded"); | |
| 623 | + if( (mHttpFlags & HTTP_QUIET)==0 ){ | |
| 624 | + fossil_warning("redirect limit exceeded"); | |
| 625 | + } | |
| 622 | 626 | goto write_err; |
| 623 | 627 | } |
| 624 | 628 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 625 | 629 | if( zLine[i]==0 ){ |
| 626 | 630 | fossil_warning("malformed redirect: %s", zLine); |
| @@ -633,23 +637,29 @@ | ||
| 633 | 637 | } |
| 634 | 638 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 635 | 639 | fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); |
| 636 | 640 | } |
| 637 | 641 | if( g.url.isFile || g.url.isSsh ){ |
| 638 | - fossil_warning("cannot redirect from %s to %s", g.url.canonical, | |
| 639 | - &zLine[i]); | |
| 642 | + if( (mHttpFlags & HTTP_QUIET)==0 ){ | |
| 643 | + fossil_warning("cannot redirect from %s to %s", g.url.canonical, | |
| 644 | + &zLine[i]); | |
| 645 | + } | |
| 640 | 646 | goto write_err; |
| 641 | 647 | } |
| 642 | 648 | wasHttps = g.url.isHttps; |
| 643 | 649 | priorUrlFlags = g.url.flags; |
| 644 | 650 | url_parse(&zLine[i], 0); |
| 645 | 651 | if( wasHttps && !g.url.isHttps ){ |
| 646 | - fossil_warning("cannot redirect from HTTPS to HTTP"); | |
| 652 | + if( (mHttpFlags & HTTP_QUIET)==0 ){ | |
| 653 | + fossil_warning("cannot redirect from HTTPS to HTTP"); | |
| 654 | + } | |
| 647 | 655 | goto write_err; |
| 648 | 656 | } |
| 649 | 657 | if( g.url.isSsh || g.url.isFile ){ |
| 650 | - fossil_warning("cannot redirect to %s", &zLine[i]); | |
| 658 | + if( (mHttpFlags & HTTP_QUIET)==0 ){ | |
| 659 | + fossil_warning("cannot redirect to %s", &zLine[i]); | |
| 660 | + } | |
| 651 | 661 | goto write_err; |
| 652 | 662 | } |
| 653 | 663 | transport_close(&g.url); |
| 654 | 664 | transport_global_shutdown(&g.url); |
| 655 | 665 | fSeenHttpAuth = 0; |
| @@ -704,20 +714,23 @@ | ||
| 704 | 714 | } |
| 705 | 715 | return rc; |
| 706 | 716 | }else{ |
| 707 | 717 | /* The problem could not be corrected by retrying. Report the |
| 708 | 718 | ** the error. */ |
| 709 | - if( g.url.isSsh && !g.fSshTrace ){ | |
| 719 | + if( mHttpFlags & HTTP_QUIET ){ | |
| 720 | + /* no-op */ | |
| 721 | + }else if( g.url.isSsh && !g.fSshTrace ){ | |
| 710 | 722 | fossil_warning("server did not reply: " |
| 711 | 723 | " rerun with --sshtrace for diagnostics"); |
| 712 | 724 | }else{ |
| 713 | 725 | fossil_warning("server did not reply"); |
| 714 | 726 | } |
| 715 | 727 | goto write_err; |
| 716 | 728 | } |
| 717 | 729 | } |
| 718 | 730 | if( rc!=200 ){ |
| 731 | + if( mHttpFlags & HTTP_QUIET ) goto write_err; | |
| 719 | 732 | fossil_warning("\"location:\" missing from %d redirect reply", rc); |
| 720 | 733 | goto write_err; |
| 721 | 734 | } |
| 722 | 735 | |
| 723 | 736 | /* |
| @@ -733,19 +746,21 @@ | ||
| 733 | 746 | iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength); |
| 734 | 747 | if( mHttpFlags & HTTP_VERBOSE ){ |
| 735 | 748 | fossil_print("Reply received: %d of %d bytes\n", iRecvLen, iLength); |
| 736 | 749 | } |
| 737 | 750 | if( iRecvLen != iLength ){ |
| 751 | + if( mHttpFlags & HTTP_QUIET ) goto write_err; | |
| 738 | 752 | fossil_warning("response truncated: got %d bytes of %d", |
| 739 | 753 | iRecvLen, iLength); |
| 740 | 754 | goto write_err; |
| 741 | 755 | } |
| 742 | - }else if( closeConnection ){ | |
| 756 | + }else{ | |
| 743 | 757 | /* Read content until end-of-file */ |
| 744 | 758 | int iRecvLen; /* Received length of the reply payload */ |
| 745 | 759 | unsigned int nReq = 1000; |
| 746 | 760 | unsigned int nPrior = 0; |
| 761 | + closeConnection = 1; | |
| 747 | 762 | do{ |
| 748 | 763 | nReq *= 2; |
| 749 | 764 | blob_resize(pReply, nPrior+nReq); |
| 750 | 765 | iRecvLen = transport_receive(&g.url, &pReply->aData[nPrior], (int)nReq); |
| 751 | 766 | nPrior += iRecvLen; |
| @@ -752,13 +767,10 @@ | ||
| 752 | 767 | pReply->nUsed = nPrior; |
| 753 | 768 | }while( iRecvLen==nReq && nReq<0x20000000 ); |
| 754 | 769 | if( mHttpFlags & HTTP_VERBOSE ){ |
| 755 | 770 | fossil_print("Reply received: %u bytes (w/o content-length)\n", nPrior); |
| 756 | 771 | } |
| 757 | - }else{ | |
| 758 | - assert( iLength<0 && !closeConnection ); | |
| 759 | - fossil_warning("\"content-length\" missing from %d keep-alive reply", rc); | |
| 760 | 772 | } |
| 761 | 773 | if( isError ){ |
| 762 | 774 | char *z; |
| 763 | 775 | int i, j; |
| 764 | 776 | z = blob_str(pReply); |
| @@ -768,11 +780,17 @@ | ||
| 768 | 780 | if( z[i]==0 ) break; |
| 769 | 781 | } |
| 770 | 782 | z[j] = z[i]; |
| 771 | 783 | } |
| 772 | 784 | z[j] = 0; |
| 773 | - fossil_warning("server sends error: %s", z); | |
| 785 | + if( mHttpFlags & HTTP_QUIET ){ | |
| 786 | + /* no-op */ | |
| 787 | + }else if( mHttpFlags & HTTP_VERBOSE ){ | |
| 788 | + fossil_warning("server sends error: %s", z); | |
| 789 | + }else{ | |
| 790 | + fossil_warning("server sends error"); | |
| 791 | + } | |
| 774 | 792 | goto write_err; |
| 775 | 793 | } |
| 776 | 794 | if( isCompressed ) blob_uncompress(pReply, pReply); |
| 777 | 795 | |
| 778 | 796 | /* |
| @@ -794,10 +812,11 @@ | ||
| 794 | 812 | |
| 795 | 813 | /* |
| 796 | 814 | ** Jump to here if an error is seen. |
| 797 | 815 | */ |
| 798 | 816 | write_err: |
| 817 | + g.iResultCode = 1; | |
| 799 | 818 | transport_close(&g.url); |
| 800 | 819 | return 1; |
| 801 | 820 | } |
| 802 | 821 | |
| 803 | 822 | /* |
| 804 | 823 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -246,11 +246,11 @@ | |
| 246 | } |
| 247 | return zHttpAuth; |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | ** Send content pSend to the the server identified by g.url using the |
| 252 | ** external program given by g.zHttpCmd. Capture the reply from that |
| 253 | ** program and load it into pReply. |
| 254 | ** |
| 255 | ** This routine implements the --transport-command option for "fossil sync". |
| 256 | */ |
| @@ -353,11 +353,11 @@ | |
| 353 | db_finalize(&s); |
| 354 | db_swap_connections(); |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | /* Add an approprate PATH= argument to the SSH command under construction |
| 359 | ** in pCmd. |
| 360 | ** |
| 361 | ** About This Feature |
| 362 | ** ================== |
| 363 | ** |
| @@ -581,11 +581,13 @@ | |
| 581 | } |
| 582 | if( rc!=200 && rc!=301 && rc!=302 && rc!=307 && rc!=308 ){ |
| 583 | int ii; |
| 584 | for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} |
| 585 | while( zLine[ii]==' ' ) ii++; |
| 586 | fossil_warning("server says: %s", &zLine[ii]); |
| 587 | goto write_err; |
| 588 | } |
| 589 | if( iHttpVersion==0 ){ |
| 590 | closeConnection = 1; |
| 591 | }else{ |
| @@ -616,11 +618,13 @@ | |
| 616 | int i, j; |
| 617 | int wasHttps; |
| 618 | int priorUrlFlags; |
| 619 | |
| 620 | if ( --maxRedirect == 0){ |
| 621 | fossil_warning("redirect limit exceeded"); |
| 622 | goto write_err; |
| 623 | } |
| 624 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 625 | if( zLine[i]==0 ){ |
| 626 | fossil_warning("malformed redirect: %s", zLine); |
| @@ -633,23 +637,29 @@ | |
| 633 | } |
| 634 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 635 | fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); |
| 636 | } |
| 637 | if( g.url.isFile || g.url.isSsh ){ |
| 638 | fossil_warning("cannot redirect from %s to %s", g.url.canonical, |
| 639 | &zLine[i]); |
| 640 | goto write_err; |
| 641 | } |
| 642 | wasHttps = g.url.isHttps; |
| 643 | priorUrlFlags = g.url.flags; |
| 644 | url_parse(&zLine[i], 0); |
| 645 | if( wasHttps && !g.url.isHttps ){ |
| 646 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 647 | goto write_err; |
| 648 | } |
| 649 | if( g.url.isSsh || g.url.isFile ){ |
| 650 | fossil_warning("cannot redirect to %s", &zLine[i]); |
| 651 | goto write_err; |
| 652 | } |
| 653 | transport_close(&g.url); |
| 654 | transport_global_shutdown(&g.url); |
| 655 | fSeenHttpAuth = 0; |
| @@ -704,20 +714,23 @@ | |
| 704 | } |
| 705 | return rc; |
| 706 | }else{ |
| 707 | /* The problem could not be corrected by retrying. Report the |
| 708 | ** the error. */ |
| 709 | if( g.url.isSsh && !g.fSshTrace ){ |
| 710 | fossil_warning("server did not reply: " |
| 711 | " rerun with --sshtrace for diagnostics"); |
| 712 | }else{ |
| 713 | fossil_warning("server did not reply"); |
| 714 | } |
| 715 | goto write_err; |
| 716 | } |
| 717 | } |
| 718 | if( rc!=200 ){ |
| 719 | fossil_warning("\"location:\" missing from %d redirect reply", rc); |
| 720 | goto write_err; |
| 721 | } |
| 722 | |
| 723 | /* |
| @@ -733,19 +746,21 @@ | |
| 733 | iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength); |
| 734 | if( mHttpFlags & HTTP_VERBOSE ){ |
| 735 | fossil_print("Reply received: %d of %d bytes\n", iRecvLen, iLength); |
| 736 | } |
| 737 | if( iRecvLen != iLength ){ |
| 738 | fossil_warning("response truncated: got %d bytes of %d", |
| 739 | iRecvLen, iLength); |
| 740 | goto write_err; |
| 741 | } |
| 742 | }else if( closeConnection ){ |
| 743 | /* Read content until end-of-file */ |
| 744 | int iRecvLen; /* Received length of the reply payload */ |
| 745 | unsigned int nReq = 1000; |
| 746 | unsigned int nPrior = 0; |
| 747 | do{ |
| 748 | nReq *= 2; |
| 749 | blob_resize(pReply, nPrior+nReq); |
| 750 | iRecvLen = transport_receive(&g.url, &pReply->aData[nPrior], (int)nReq); |
| 751 | nPrior += iRecvLen; |
| @@ -752,13 +767,10 @@ | |
| 752 | pReply->nUsed = nPrior; |
| 753 | }while( iRecvLen==nReq && nReq<0x20000000 ); |
| 754 | if( mHttpFlags & HTTP_VERBOSE ){ |
| 755 | fossil_print("Reply received: %u bytes (w/o content-length)\n", nPrior); |
| 756 | } |
| 757 | }else{ |
| 758 | assert( iLength<0 && !closeConnection ); |
| 759 | fossil_warning("\"content-length\" missing from %d keep-alive reply", rc); |
| 760 | } |
| 761 | if( isError ){ |
| 762 | char *z; |
| 763 | int i, j; |
| 764 | z = blob_str(pReply); |
| @@ -768,11 +780,17 @@ | |
| 768 | if( z[i]==0 ) break; |
| 769 | } |
| 770 | z[j] = z[i]; |
| 771 | } |
| 772 | z[j] = 0; |
| 773 | fossil_warning("server sends error: %s", z); |
| 774 | goto write_err; |
| 775 | } |
| 776 | if( isCompressed ) blob_uncompress(pReply, pReply); |
| 777 | |
| 778 | /* |
| @@ -794,10 +812,11 @@ | |
| 794 | |
| 795 | /* |
| 796 | ** Jump to here if an error is seen. |
| 797 | */ |
| 798 | write_err: |
| 799 | transport_close(&g.url); |
| 800 | return 1; |
| 801 | } |
| 802 | |
| 803 | /* |
| 804 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -246,11 +246,11 @@ | |
| 246 | } |
| 247 | return zHttpAuth; |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | ** Send content pSend to the server identified by g.url using the |
| 252 | ** external program given by g.zHttpCmd. Capture the reply from that |
| 253 | ** program and load it into pReply. |
| 254 | ** |
| 255 | ** This routine implements the --transport-command option for "fossil sync". |
| 256 | */ |
| @@ -353,11 +353,11 @@ | |
| 353 | db_finalize(&s); |
| 354 | db_swap_connections(); |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | /* Add an appropriate PATH= argument to the SSH command under construction |
| 359 | ** in pCmd. |
| 360 | ** |
| 361 | ** About This Feature |
| 362 | ** ================== |
| 363 | ** |
| @@ -581,11 +581,13 @@ | |
| 581 | } |
| 582 | if( rc!=200 && rc!=301 && rc!=302 && rc!=307 && rc!=308 ){ |
| 583 | int ii; |
| 584 | for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} |
| 585 | while( zLine[ii]==' ' ) ii++; |
| 586 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 587 | fossil_warning("server says: %s", &zLine[ii]); |
| 588 | } |
| 589 | goto write_err; |
| 590 | } |
| 591 | if( iHttpVersion==0 ){ |
| 592 | closeConnection = 1; |
| 593 | }else{ |
| @@ -616,11 +618,13 @@ | |
| 618 | int i, j; |
| 619 | int wasHttps; |
| 620 | int priorUrlFlags; |
| 621 | |
| 622 | if ( --maxRedirect == 0){ |
| 623 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 624 | fossil_warning("redirect limit exceeded"); |
| 625 | } |
| 626 | goto write_err; |
| 627 | } |
| 628 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 629 | if( zLine[i]==0 ){ |
| 630 | fossil_warning("malformed redirect: %s", zLine); |
| @@ -633,23 +637,29 @@ | |
| 637 | } |
| 638 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 639 | fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); |
| 640 | } |
| 641 | if( g.url.isFile || g.url.isSsh ){ |
| 642 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 643 | fossil_warning("cannot redirect from %s to %s", g.url.canonical, |
| 644 | &zLine[i]); |
| 645 | } |
| 646 | goto write_err; |
| 647 | } |
| 648 | wasHttps = g.url.isHttps; |
| 649 | priorUrlFlags = g.url.flags; |
| 650 | url_parse(&zLine[i], 0); |
| 651 | if( wasHttps && !g.url.isHttps ){ |
| 652 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 653 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 654 | } |
| 655 | goto write_err; |
| 656 | } |
| 657 | if( g.url.isSsh || g.url.isFile ){ |
| 658 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 659 | fossil_warning("cannot redirect to %s", &zLine[i]); |
| 660 | } |
| 661 | goto write_err; |
| 662 | } |
| 663 | transport_close(&g.url); |
| 664 | transport_global_shutdown(&g.url); |
| 665 | fSeenHttpAuth = 0; |
| @@ -704,20 +714,23 @@ | |
| 714 | } |
| 715 | return rc; |
| 716 | }else{ |
| 717 | /* The problem could not be corrected by retrying. Report the |
| 718 | ** the error. */ |
| 719 | if( mHttpFlags & HTTP_QUIET ){ |
| 720 | /* no-op */ |
| 721 | }else if( g.url.isSsh && !g.fSshTrace ){ |
| 722 | fossil_warning("server did not reply: " |
| 723 | " rerun with --sshtrace for diagnostics"); |
| 724 | }else{ |
| 725 | fossil_warning("server did not reply"); |
| 726 | } |
| 727 | goto write_err; |
| 728 | } |
| 729 | } |
| 730 | if( rc!=200 ){ |
| 731 | if( mHttpFlags & HTTP_QUIET ) goto write_err; |
| 732 | fossil_warning("\"location:\" missing from %d redirect reply", rc); |
| 733 | goto write_err; |
| 734 | } |
| 735 | |
| 736 | /* |
| @@ -733,19 +746,21 @@ | |
| 746 | iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength); |
| 747 | if( mHttpFlags & HTTP_VERBOSE ){ |
| 748 | fossil_print("Reply received: %d of %d bytes\n", iRecvLen, iLength); |
| 749 | } |
| 750 | if( iRecvLen != iLength ){ |
| 751 | if( mHttpFlags & HTTP_QUIET ) goto write_err; |
| 752 | fossil_warning("response truncated: got %d bytes of %d", |
| 753 | iRecvLen, iLength); |
| 754 | goto write_err; |
| 755 | } |
| 756 | }else{ |
| 757 | /* Read content until end-of-file */ |
| 758 | int iRecvLen; /* Received length of the reply payload */ |
| 759 | unsigned int nReq = 1000; |
| 760 | unsigned int nPrior = 0; |
| 761 | closeConnection = 1; |
| 762 | do{ |
| 763 | nReq *= 2; |
| 764 | blob_resize(pReply, nPrior+nReq); |
| 765 | iRecvLen = transport_receive(&g.url, &pReply->aData[nPrior], (int)nReq); |
| 766 | nPrior += iRecvLen; |
| @@ -752,13 +767,10 @@ | |
| 767 | pReply->nUsed = nPrior; |
| 768 | }while( iRecvLen==nReq && nReq<0x20000000 ); |
| 769 | if( mHttpFlags & HTTP_VERBOSE ){ |
| 770 | fossil_print("Reply received: %u bytes (w/o content-length)\n", nPrior); |
| 771 | } |
| 772 | } |
| 773 | if( isError ){ |
| 774 | char *z; |
| 775 | int i, j; |
| 776 | z = blob_str(pReply); |
| @@ -768,11 +780,17 @@ | |
| 780 | if( z[i]==0 ) break; |
| 781 | } |
| 782 | z[j] = z[i]; |
| 783 | } |
| 784 | z[j] = 0; |
| 785 | if( mHttpFlags & HTTP_QUIET ){ |
| 786 | /* no-op */ |
| 787 | }else if( mHttpFlags & HTTP_VERBOSE ){ |
| 788 | fossil_warning("server sends error: %s", z); |
| 789 | }else{ |
| 790 | fossil_warning("server sends error"); |
| 791 | } |
| 792 | goto write_err; |
| 793 | } |
| 794 | if( isCompressed ) blob_uncompress(pReply, pReply); |
| 795 | |
| 796 | /* |
| @@ -794,10 +812,11 @@ | |
| 812 | |
| 813 | /* |
| 814 | ** Jump to here if an error is seen. |
| 815 | */ |
| 816 | write_err: |
| 817 | g.iResultCode = 1; |
| 818 | transport_close(&g.url); |
| 819 | return 1; |
| 820 | } |
| 821 | |
| 822 | /* |
| 823 |
+7
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -87,10 +87,17 @@ | ||
| 87 | 87 | char *socket_errmsg(void){ |
| 88 | 88 | char *zResult = socketErrMsg; |
| 89 | 89 | socketErrMsg = 0; |
| 90 | 90 | return zResult; |
| 91 | 91 | } |
| 92 | + | |
| 93 | +/* | |
| 94 | +** Return the file descriptor for the open socket. | |
| 95 | +*/ | |
| 96 | +int socket_get_fd(){ | |
| 97 | + return iSocket; | |
| 98 | +} | |
| 92 | 99 | |
| 93 | 100 | /* |
| 94 | 101 | ** Call this routine once before any other use of the socket interface. |
| 95 | 102 | ** This routine does initial configuration of the socket module. |
| 96 | 103 | */ |
| 97 | 104 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -87,10 +87,17 @@ | |
| 87 | char *socket_errmsg(void){ |
| 88 | char *zResult = socketErrMsg; |
| 89 | socketErrMsg = 0; |
| 90 | return zResult; |
| 91 | } |
| 92 | |
| 93 | /* |
| 94 | ** Call this routine once before any other use of the socket interface. |
| 95 | ** This routine does initial configuration of the socket module. |
| 96 | */ |
| 97 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -87,10 +87,17 @@ | |
| 87 | char *socket_errmsg(void){ |
| 88 | char *zResult = socketErrMsg; |
| 89 | socketErrMsg = 0; |
| 90 | return zResult; |
| 91 | } |
| 92 | |
| 93 | /* |
| 94 | ** Return the file descriptor for the open socket. |
| 95 | */ |
| 96 | int socket_get_fd(){ |
| 97 | return iSocket; |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 | ** Call this routine once before any other use of the socket interface. |
| 102 | ** This routine does initial configuration of the socket module. |
| 103 | */ |
| 104 |
+21
-100
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -270,11 +270,11 @@ | ||
| 270 | 270 | |
| 271 | 271 | /* Find the trust store */ |
| 272 | 272 | zFile = 0; |
| 273 | 273 | for(i=0; zFile==0 && i<5; i++){ |
| 274 | 274 | switch( i ){ |
| 275 | - case 0: /* First priority is environmentn variables */ | |
| 275 | + case 0: /* First priority is environment variables */ | |
| 276 | 276 | zFile = fossil_getenv(X509_get_default_cert_file_env()); |
| 277 | 277 | break; |
| 278 | 278 | case 1: |
| 279 | 279 | zFile = fossil_getenv(X509_get_default_cert_dir_env()); |
| 280 | 280 | break; |
| @@ -363,10 +363,11 @@ | ||
| 363 | 363 | if( sslIsInit ){ |
| 364 | 364 | SSL_CTX_free(sslCtx); |
| 365 | 365 | ssl_clear_errmsg(); |
| 366 | 366 | sslIsInit = 0; |
| 367 | 367 | } |
| 368 | + socket_global_shutdown(); | |
| 368 | 369 | } |
| 369 | 370 | |
| 370 | 371 | /* |
| 371 | 372 | ** Close the currently open client SSL connection. If no connection is open, |
| 372 | 373 | ** this routine is a no-op. |
| @@ -375,10 +376,11 @@ | ||
| 375 | 376 | if( iBio!=NULL ){ |
| 376 | 377 | (void)BIO_reset(iBio); |
| 377 | 378 | BIO_free_all(iBio); |
| 378 | 379 | iBio = NULL; |
| 379 | 380 | } |
| 381 | + socket_close(); | |
| 380 | 382 | } |
| 381 | 383 | |
| 382 | 384 | /* See RFC2817 for details */ |
| 383 | 385 | static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){ |
| 384 | 386 | int rc, httpVerMin; |
| @@ -450,63 +452,35 @@ | ||
| 450 | 452 | ** Return the number of errors. |
| 451 | 453 | */ |
| 452 | 454 | int ssl_open_client(UrlData *pUrlData){ |
| 453 | 455 | X509 *cert; |
| 454 | 456 | const char *zRemoteHost; |
| 457 | + BIO *sBio; | |
| 455 | 458 | |
| 456 | 459 | ssl_global_init_client(); |
| 460 | + if( socket_open(pUrlData) ){ | |
| 461 | + ssl_set_errmsg("SSL: cannot open socket (%s)", socket_errmsg()); | |
| 462 | + return 1; | |
| 463 | + } | |
| 464 | + sBio = BIO_new_socket(socket_get_fd(), 0); | |
| 457 | 465 | if( pUrlData->useProxy ){ |
| 458 | - int rc; | |
| 459 | - char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); | |
| 460 | - BIO *sBio = BIO_new_connect(connStr); | |
| 461 | - switch( g.eIPvers ){ | |
| 462 | - default: /* Any available protocol */ | |
| 463 | - break; | |
| 464 | - case 1: /* IPv4 only */ | |
| 465 | -#ifdef BIO_FAMILY_IPV4 | |
| 466 | - BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4); | |
| 467 | -#else | |
| 468 | - fossil_warning("The --ipv4 option is not supported in this build\n"); | |
| 469 | -#endif | |
| 470 | - break; | |
| 471 | - case 2: /* IPv6 only */ | |
| 472 | -#ifdef BIO_FAMILY_IPV6 | |
| 473 | - BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV6); | |
| 474 | -#else | |
| 475 | - fossil_warning("The --ipv6 option is not supported in this build\n"); | |
| 476 | -#endif | |
| 477 | - break; | |
| 478 | - } | |
| 479 | - fossil_free(connStr); | |
| 480 | - if( BIO_do_connect(sBio)<=0 ){ | |
| 481 | - ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", | |
| 482 | - pUrlData->name, pUrlData->port, | |
| 483 | - ERR_reason_error_string(ERR_get_error())); | |
| 484 | - ssl_close_client(); | |
| 485 | - return 1; | |
| 486 | - } | |
| 487 | - rc = establish_proxy_tunnel(pUrlData, sBio); | |
| 466 | + int rc = establish_proxy_tunnel(pUrlData, sBio); | |
| 488 | 467 | if( rc<200||rc>299 ){ |
| 489 | 468 | ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc); |
| 469 | + ssl_close_client(); | |
| 490 | 470 | return 1; |
| 491 | 471 | } |
| 492 | 472 | |
| 493 | 473 | pUrlData->path = pUrlData->proxyUrlPath; |
| 494 | - | |
| 495 | - iBio = BIO_new_ssl(sslCtx, 1); | |
| 496 | - BIO_push(iBio, sBio); | |
| 497 | - zRemoteHost = pUrlData->hostname; | |
| 498 | - }else{ | |
| 499 | - iBio = BIO_new_ssl_connect(sslCtx); | |
| 500 | - zRemoteHost = pUrlData->name; | |
| 501 | - } | |
| 502 | - if( iBio==NULL ) { | |
| 503 | - ssl_set_errmsg("SSL: cannot open SSL (%s)", | |
| 504 | - ERR_reason_error_string(ERR_get_error())); | |
| 505 | - return 1; | |
| 506 | - } | |
| 474 | + } | |
| 475 | + iBio = BIO_new_ssl(sslCtx, 1); | |
| 476 | + BIO_push(iBio, sBio); | |
| 477 | + BIO_set_ssl(sBio, ssl, BIO_NOCLOSE); | |
| 478 | + BIO_set_ssl_mode(iBio, 1); | |
| 507 | 479 | BIO_get_ssl(iBio, &ssl); |
| 480 | + | |
| 481 | + zRemoteHost = pUrlData->useProxy ? pUrlData->hostname : pUrlData->name; | |
| 508 | 482 | |
| 509 | 483 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 510 | 484 | if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){ |
| 511 | 485 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 512 | 486 | "continuing without it.\n"); |
| @@ -523,45 +497,14 @@ | ||
| 523 | 497 | } |
| 524 | 498 | /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */ |
| 525 | 499 | } |
| 526 | 500 | #endif |
| 527 | 501 | |
| 528 | - if( !pUrlData->useProxy ){ | |
| 529 | - char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); | |
| 530 | - BIO_set_conn_hostname(iBio, connStr); | |
| 531 | - fossil_free(connStr); | |
| 532 | - switch( g.eIPvers ){ | |
| 533 | - default: /* Any available protocol */ | |
| 534 | - break; | |
| 535 | - case 1: /* IPv4 only */ | |
| 536 | -#ifdef BIO_FAMILY_IPV4 | |
| 537 | - BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4); | |
| 538 | -#else | |
| 539 | - fossil_warning("The --ipv4 option is not supported in this build\n"); | |
| 540 | -#endif | |
| 541 | - break; | |
| 542 | - case 2: /* IPv6 only */ | |
| 543 | -#ifdef BIO_FAMILY_IPV6 | |
| 544 | - BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV6); | |
| 545 | -#else | |
| 546 | - fossil_warning("The --ipv6 option is not supported in this build\n"); | |
| 547 | -#endif | |
| 548 | - break; | |
| 549 | - } | |
| 550 | - if( BIO_do_connect(iBio)<=0 ){ | |
| 551 | - ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", | |
| 552 | - pUrlData->name, pUrlData->port, | |
| 553 | - ERR_reason_error_string(ERR_get_error())); | |
| 554 | - ssl_close_client(); | |
| 555 | - return 1; | |
| 556 | - } | |
| 557 | - } | |
| 558 | - | |
| 559 | - if( BIO_do_handshake(iBio)<=0 ) { | |
| 502 | + if( BIO_do_handshake(iBio)<=0 ){ | |
| 560 | 503 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 561 | - pUrlData->useProxy?pUrlData->hostname:pUrlData->name, | |
| 562 | - pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port, | |
| 504 | + zRemoteHost, | |
| 505 | + pUrlData->useProxy ? pUrlData->proxyOrigPort : pUrlData->port, | |
| 563 | 506 | ERR_reason_error_string(ERR_get_error())); |
| 564 | 507 | ssl_close_client(); |
| 565 | 508 | return 1; |
| 566 | 509 | } |
| 567 | 510 | /* Check if certificate is valid */ |
| @@ -651,32 +594,10 @@ | ||
| 651 | 594 | } |
| 652 | 595 | blob_reset(&ans); |
| 653 | 596 | } |
| 654 | 597 | } |
| 655 | 598 | |
| 656 | - /* Set the Global.zIpAddr variable to the server we are talking to. | |
| 657 | - ** This is used to populate the ipaddr column of the rcvfrom table, | |
| 658 | - ** if any files are received from the server. | |
| 659 | - */ | |
| 660 | - { | |
| 661 | - /* As soon as libressl implements | |
| 662 | - ** BIO_ADDR_hostname_string/BIO_get_conn_address. | |
| 663 | - ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable | |
| 664 | - */ | |
| 665 | -#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ | |
| 666 | - && !defined(LIBRESSL_VERSION_NUMBER) | |
| 667 | - char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1); | |
| 668 | - g.zIpAddr = fossil_strdup(ip); | |
| 669 | - OPENSSL_free(ip); | |
| 670 | -#else | |
| 671 | - /* IPv4 only code */ | |
| 672 | - const unsigned char *ip; | |
| 673 | - ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2); | |
| 674 | - g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); | |
| 675 | -#endif | |
| 676 | - } | |
| 677 | - | |
| 678 | 599 | X509_free(cert); |
| 679 | 600 | return 0; |
| 680 | 601 | } |
| 681 | 602 | |
| 682 | 603 | /* |
| 683 | 604 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -270,11 +270,11 @@ | |
| 270 | |
| 271 | /* Find the trust store */ |
| 272 | zFile = 0; |
| 273 | for(i=0; zFile==0 && i<5; i++){ |
| 274 | switch( i ){ |
| 275 | case 0: /* First priority is environmentn variables */ |
| 276 | zFile = fossil_getenv(X509_get_default_cert_file_env()); |
| 277 | break; |
| 278 | case 1: |
| 279 | zFile = fossil_getenv(X509_get_default_cert_dir_env()); |
| 280 | break; |
| @@ -363,10 +363,11 @@ | |
| 363 | if( sslIsInit ){ |
| 364 | SSL_CTX_free(sslCtx); |
| 365 | ssl_clear_errmsg(); |
| 366 | sslIsInit = 0; |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | /* |
| 371 | ** Close the currently open client SSL connection. If no connection is open, |
| 372 | ** this routine is a no-op. |
| @@ -375,10 +376,11 @@ | |
| 375 | if( iBio!=NULL ){ |
| 376 | (void)BIO_reset(iBio); |
| 377 | BIO_free_all(iBio); |
| 378 | iBio = NULL; |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | /* See RFC2817 for details */ |
| 383 | static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){ |
| 384 | int rc, httpVerMin; |
| @@ -450,63 +452,35 @@ | |
| 450 | ** Return the number of errors. |
| 451 | */ |
| 452 | int ssl_open_client(UrlData *pUrlData){ |
| 453 | X509 *cert; |
| 454 | const char *zRemoteHost; |
| 455 | |
| 456 | ssl_global_init_client(); |
| 457 | if( pUrlData->useProxy ){ |
| 458 | int rc; |
| 459 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| 460 | BIO *sBio = BIO_new_connect(connStr); |
| 461 | switch( g.eIPvers ){ |
| 462 | default: /* Any available protocol */ |
| 463 | break; |
| 464 | case 1: /* IPv4 only */ |
| 465 | #ifdef BIO_FAMILY_IPV4 |
| 466 | BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4); |
| 467 | #else |
| 468 | fossil_warning("The --ipv4 option is not supported in this build\n"); |
| 469 | #endif |
| 470 | break; |
| 471 | case 2: /* IPv6 only */ |
| 472 | #ifdef BIO_FAMILY_IPV6 |
| 473 | BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV6); |
| 474 | #else |
| 475 | fossil_warning("The --ipv6 option is not supported in this build\n"); |
| 476 | #endif |
| 477 | break; |
| 478 | } |
| 479 | fossil_free(connStr); |
| 480 | if( BIO_do_connect(sBio)<=0 ){ |
| 481 | ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", |
| 482 | pUrlData->name, pUrlData->port, |
| 483 | ERR_reason_error_string(ERR_get_error())); |
| 484 | ssl_close_client(); |
| 485 | return 1; |
| 486 | } |
| 487 | rc = establish_proxy_tunnel(pUrlData, sBio); |
| 488 | if( rc<200||rc>299 ){ |
| 489 | ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc); |
| 490 | return 1; |
| 491 | } |
| 492 | |
| 493 | pUrlData->path = pUrlData->proxyUrlPath; |
| 494 | |
| 495 | iBio = BIO_new_ssl(sslCtx, 1); |
| 496 | BIO_push(iBio, sBio); |
| 497 | zRemoteHost = pUrlData->hostname; |
| 498 | }else{ |
| 499 | iBio = BIO_new_ssl_connect(sslCtx); |
| 500 | zRemoteHost = pUrlData->name; |
| 501 | } |
| 502 | if( iBio==NULL ) { |
| 503 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 504 | ERR_reason_error_string(ERR_get_error())); |
| 505 | return 1; |
| 506 | } |
| 507 | BIO_get_ssl(iBio, &ssl); |
| 508 | |
| 509 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 510 | if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){ |
| 511 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 512 | "continuing without it.\n"); |
| @@ -523,45 +497,14 @@ | |
| 523 | } |
| 524 | /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */ |
| 525 | } |
| 526 | #endif |
| 527 | |
| 528 | if( !pUrlData->useProxy ){ |
| 529 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 530 | BIO_set_conn_hostname(iBio, connStr); |
| 531 | fossil_free(connStr); |
| 532 | switch( g.eIPvers ){ |
| 533 | default: /* Any available protocol */ |
| 534 | break; |
| 535 | case 1: /* IPv4 only */ |
| 536 | #ifdef BIO_FAMILY_IPV4 |
| 537 | BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4); |
| 538 | #else |
| 539 | fossil_warning("The --ipv4 option is not supported in this build\n"); |
| 540 | #endif |
| 541 | break; |
| 542 | case 2: /* IPv6 only */ |
| 543 | #ifdef BIO_FAMILY_IPV6 |
| 544 | BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV6); |
| 545 | #else |
| 546 | fossil_warning("The --ipv6 option is not supported in this build\n"); |
| 547 | #endif |
| 548 | break; |
| 549 | } |
| 550 | if( BIO_do_connect(iBio)<=0 ){ |
| 551 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 552 | pUrlData->name, pUrlData->port, |
| 553 | ERR_reason_error_string(ERR_get_error())); |
| 554 | ssl_close_client(); |
| 555 | return 1; |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | if( BIO_do_handshake(iBio)<=0 ) { |
| 560 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 561 | pUrlData->useProxy?pUrlData->hostname:pUrlData->name, |
| 562 | pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port, |
| 563 | ERR_reason_error_string(ERR_get_error())); |
| 564 | ssl_close_client(); |
| 565 | return 1; |
| 566 | } |
| 567 | /* Check if certificate is valid */ |
| @@ -651,32 +594,10 @@ | |
| 651 | } |
| 652 | blob_reset(&ans); |
| 653 | } |
| 654 | } |
| 655 | |
| 656 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 657 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| 658 | ** if any files are received from the server. |
| 659 | */ |
| 660 | { |
| 661 | /* As soon as libressl implements |
| 662 | ** BIO_ADDR_hostname_string/BIO_get_conn_address. |
| 663 | ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable |
| 664 | */ |
| 665 | #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ |
| 666 | && !defined(LIBRESSL_VERSION_NUMBER) |
| 667 | char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1); |
| 668 | g.zIpAddr = fossil_strdup(ip); |
| 669 | OPENSSL_free(ip); |
| 670 | #else |
| 671 | /* IPv4 only code */ |
| 672 | const unsigned char *ip; |
| 673 | ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2); |
| 674 | g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); |
| 675 | #endif |
| 676 | } |
| 677 | |
| 678 | X509_free(cert); |
| 679 | return 0; |
| 680 | } |
| 681 | |
| 682 | /* |
| 683 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -270,11 +270,11 @@ | |
| 270 | |
| 271 | /* Find the trust store */ |
| 272 | zFile = 0; |
| 273 | for(i=0; zFile==0 && i<5; i++){ |
| 274 | switch( i ){ |
| 275 | case 0: /* First priority is environment variables */ |
| 276 | zFile = fossil_getenv(X509_get_default_cert_file_env()); |
| 277 | break; |
| 278 | case 1: |
| 279 | zFile = fossil_getenv(X509_get_default_cert_dir_env()); |
| 280 | break; |
| @@ -363,10 +363,11 @@ | |
| 363 | if( sslIsInit ){ |
| 364 | SSL_CTX_free(sslCtx); |
| 365 | ssl_clear_errmsg(); |
| 366 | sslIsInit = 0; |
| 367 | } |
| 368 | socket_global_shutdown(); |
| 369 | } |
| 370 | |
| 371 | /* |
| 372 | ** Close the currently open client SSL connection. If no connection is open, |
| 373 | ** this routine is a no-op. |
| @@ -375,10 +376,11 @@ | |
| 376 | if( iBio!=NULL ){ |
| 377 | (void)BIO_reset(iBio); |
| 378 | BIO_free_all(iBio); |
| 379 | iBio = NULL; |
| 380 | } |
| 381 | socket_close(); |
| 382 | } |
| 383 | |
| 384 | /* See RFC2817 for details */ |
| 385 | static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){ |
| 386 | int rc, httpVerMin; |
| @@ -450,63 +452,35 @@ | |
| 452 | ** Return the number of errors. |
| 453 | */ |
| 454 | int ssl_open_client(UrlData *pUrlData){ |
| 455 | X509 *cert; |
| 456 | const char *zRemoteHost; |
| 457 | BIO *sBio; |
| 458 | |
| 459 | ssl_global_init_client(); |
| 460 | if( socket_open(pUrlData) ){ |
| 461 | ssl_set_errmsg("SSL: cannot open socket (%s)", socket_errmsg()); |
| 462 | return 1; |
| 463 | } |
| 464 | sBio = BIO_new_socket(socket_get_fd(), 0); |
| 465 | if( pUrlData->useProxy ){ |
| 466 | int rc = establish_proxy_tunnel(pUrlData, sBio); |
| 467 | if( rc<200||rc>299 ){ |
| 468 | ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc); |
| 469 | ssl_close_client(); |
| 470 | return 1; |
| 471 | } |
| 472 | |
| 473 | pUrlData->path = pUrlData->proxyUrlPath; |
| 474 | } |
| 475 | iBio = BIO_new_ssl(sslCtx, 1); |
| 476 | BIO_push(iBio, sBio); |
| 477 | BIO_set_ssl(sBio, ssl, BIO_NOCLOSE); |
| 478 | BIO_set_ssl_mode(iBio, 1); |
| 479 | BIO_get_ssl(iBio, &ssl); |
| 480 | |
| 481 | zRemoteHost = pUrlData->useProxy ? pUrlData->hostname : pUrlData->name; |
| 482 | |
| 483 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 484 | if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){ |
| 485 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 486 | "continuing without it.\n"); |
| @@ -523,45 +497,14 @@ | |
| 497 | } |
| 498 | /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */ |
| 499 | } |
| 500 | #endif |
| 501 | |
| 502 | if( BIO_do_handshake(iBio)<=0 ){ |
| 503 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 504 | zRemoteHost, |
| 505 | pUrlData->useProxy ? pUrlData->proxyOrigPort : pUrlData->port, |
| 506 | ERR_reason_error_string(ERR_get_error())); |
| 507 | ssl_close_client(); |
| 508 | return 1; |
| 509 | } |
| 510 | /* Check if certificate is valid */ |
| @@ -651,32 +594,10 @@ | |
| 594 | } |
| 595 | blob_reset(&ans); |
| 596 | } |
| 597 | } |
| 598 | |
| 599 | X509_free(cert); |
| 600 | return 0; |
| 601 | } |
| 602 | |
| 603 | /* |
| 604 |
+4
-4
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -240,11 +240,11 @@ | ||
| 240 | 240 | ** - g.json.gc.a is NULL |
| 241 | 241 | ** - key is NULL or empty. |
| 242 | 242 | ** |
| 243 | 243 | ** Returns 0 on success. |
| 244 | 244 | ** |
| 245 | -** Ownership of v is transfered to (or shared with) g.json.gc, and v | |
| 245 | +** Ownership of v is transferred to (or shared with) g.json.gc, and v | |
| 246 | 246 | ** will be valid until that object is cleaned up or some internal code |
| 247 | 247 | ** incorrectly removes it from the gc (which we never do). If this |
| 248 | 248 | ** function fails, it is fatal to the app (as it indicates an |
| 249 | 249 | ** allocation error (more likely than not) or a serious internal error |
| 250 | 250 | ** such as numeric overflow). |
| @@ -271,11 +271,11 @@ | ||
| 271 | 271 | |
| 272 | 272 | |
| 273 | 273 | /* |
| 274 | 274 | ** Returns the value of json_rc_cstr(code) as a new JSON |
| 275 | 275 | ** string, which is owned by the caller and must eventually |
| 276 | -** be cson_value_free()d or transfered to a JSON container. | |
| 276 | +** be cson_value_free()d or transferred to a JSON container. | |
| 277 | 277 | */ |
| 278 | 278 | cson_value * json_rc_string( int code ){ |
| 279 | 279 | return cson_value_new_string( json_rc_cstr(code), 11 ); |
| 280 | 280 | } |
| 281 | 281 | |
| @@ -864,11 +864,11 @@ | ||
| 864 | 864 | /* |
| 865 | 865 | ** Splits zStr (which must not be NULL) into tokens separated by the |
| 866 | 866 | ** given separator character. If doDeHttp is true then each element |
| 867 | 867 | ** will be passed through dehttpize(), otherwise they are used |
| 868 | 868 | ** as-is. Note that tokenization happens before dehttpize(), |
| 869 | -** which is significant if the ENcoded tokens might contain the | |
| 869 | +** which is significant if the encoded tokens might contain the | |
| 870 | 870 | ** separator character. |
| 871 | 871 | ** |
| 872 | 872 | ** Each new element is appended to the given target array object, |
| 873 | 873 | ** which must not be NULL and ownership of it is not changed by this |
| 874 | 874 | ** call. |
| @@ -1437,11 +1437,11 @@ | ||
| 1437 | 1437 | ** If payload is not NULL and resultCode is 0 then it is set as the |
| 1438 | 1438 | ** "payload" property of the returned object. If resultCode is 0 then |
| 1439 | 1439 | ** it defaults to g.json.resultCode. If resultCode is (or defaults to) |
| 1440 | 1440 | ** non-zero and payload is not NULL then this function calls |
| 1441 | 1441 | ** cson_value_free(payload) and does not insert the payload into the |
| 1442 | -** response. In either case, ownership of payload is transfered to (or | |
| 1442 | +** response. In either case, ownership of payload is transferred to (or | |
| 1443 | 1443 | ** shared with, if the caller holds a reference) this function. |
| 1444 | 1444 | ** |
| 1445 | 1445 | ** pMsg is an optional message string property (resultText) of the |
| 1446 | 1446 | ** response. If resultCode is non-0 and pMsg is NULL then |
| 1447 | 1447 | ** json_err_cstr() is used to get the error string. The caller may |
| 1448 | 1448 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -240,11 +240,11 @@ | |
| 240 | ** - g.json.gc.a is NULL |
| 241 | ** - key is NULL or empty. |
| 242 | ** |
| 243 | ** Returns 0 on success. |
| 244 | ** |
| 245 | ** Ownership of v is transfered to (or shared with) g.json.gc, and v |
| 246 | ** will be valid until that object is cleaned up or some internal code |
| 247 | ** incorrectly removes it from the gc (which we never do). If this |
| 248 | ** function fails, it is fatal to the app (as it indicates an |
| 249 | ** allocation error (more likely than not) or a serious internal error |
| 250 | ** such as numeric overflow). |
| @@ -271,11 +271,11 @@ | |
| 271 | |
| 272 | |
| 273 | /* |
| 274 | ** Returns the value of json_rc_cstr(code) as a new JSON |
| 275 | ** string, which is owned by the caller and must eventually |
| 276 | ** be cson_value_free()d or transfered to a JSON container. |
| 277 | */ |
| 278 | cson_value * json_rc_string( int code ){ |
| 279 | return cson_value_new_string( json_rc_cstr(code), 11 ); |
| 280 | } |
| 281 | |
| @@ -864,11 +864,11 @@ | |
| 864 | /* |
| 865 | ** Splits zStr (which must not be NULL) into tokens separated by the |
| 866 | ** given separator character. If doDeHttp is true then each element |
| 867 | ** will be passed through dehttpize(), otherwise they are used |
| 868 | ** as-is. Note that tokenization happens before dehttpize(), |
| 869 | ** which is significant if the ENcoded tokens might contain the |
| 870 | ** separator character. |
| 871 | ** |
| 872 | ** Each new element is appended to the given target array object, |
| 873 | ** which must not be NULL and ownership of it is not changed by this |
| 874 | ** call. |
| @@ -1437,11 +1437,11 @@ | |
| 1437 | ** If payload is not NULL and resultCode is 0 then it is set as the |
| 1438 | ** "payload" property of the returned object. If resultCode is 0 then |
| 1439 | ** it defaults to g.json.resultCode. If resultCode is (or defaults to) |
| 1440 | ** non-zero and payload is not NULL then this function calls |
| 1441 | ** cson_value_free(payload) and does not insert the payload into the |
| 1442 | ** response. In either case, ownership of payload is transfered to (or |
| 1443 | ** shared with, if the caller holds a reference) this function. |
| 1444 | ** |
| 1445 | ** pMsg is an optional message string property (resultText) of the |
| 1446 | ** response. If resultCode is non-0 and pMsg is NULL then |
| 1447 | ** json_err_cstr() is used to get the error string. The caller may |
| 1448 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -240,11 +240,11 @@ | |
| 240 | ** - g.json.gc.a is NULL |
| 241 | ** - key is NULL or empty. |
| 242 | ** |
| 243 | ** Returns 0 on success. |
| 244 | ** |
| 245 | ** Ownership of v is transferred to (or shared with) g.json.gc, and v |
| 246 | ** will be valid until that object is cleaned up or some internal code |
| 247 | ** incorrectly removes it from the gc (which we never do). If this |
| 248 | ** function fails, it is fatal to the app (as it indicates an |
| 249 | ** allocation error (more likely than not) or a serious internal error |
| 250 | ** such as numeric overflow). |
| @@ -271,11 +271,11 @@ | |
| 271 | |
| 272 | |
| 273 | /* |
| 274 | ** Returns the value of json_rc_cstr(code) as a new JSON |
| 275 | ** string, which is owned by the caller and must eventually |
| 276 | ** be cson_value_free()d or transferred to a JSON container. |
| 277 | */ |
| 278 | cson_value * json_rc_string( int code ){ |
| 279 | return cson_value_new_string( json_rc_cstr(code), 11 ); |
| 280 | } |
| 281 | |
| @@ -864,11 +864,11 @@ | |
| 864 | /* |
| 865 | ** Splits zStr (which must not be NULL) into tokens separated by the |
| 866 | ** given separator character. If doDeHttp is true then each element |
| 867 | ** will be passed through dehttpize(), otherwise they are used |
| 868 | ** as-is. Note that tokenization happens before dehttpize(), |
| 869 | ** which is significant if the encoded tokens might contain the |
| 870 | ** separator character. |
| 871 | ** |
| 872 | ** Each new element is appended to the given target array object, |
| 873 | ** which must not be NULL and ownership of it is not changed by this |
| 874 | ** call. |
| @@ -1437,11 +1437,11 @@ | |
| 1437 | ** If payload is not NULL and resultCode is 0 then it is set as the |
| 1438 | ** "payload" property of the returned object. If resultCode is 0 then |
| 1439 | ** it defaults to g.json.resultCode. If resultCode is (or defaults to) |
| 1440 | ** non-zero and payload is not NULL then this function calls |
| 1441 | ** cson_value_free(payload) and does not insert the payload into the |
| 1442 | ** response. In either case, ownership of payload is transferred to (or |
| 1443 | ** shared with, if the caller holds a reference) this function. |
| 1444 | ** |
| 1445 | ** pMsg is an optional message string property (resultText) of the |
| 1446 | ** response. If resultCode is non-0 and pMsg is NULL then |
| 1447 | ** json_err_cstr() is used to get the error string. The caller may |
| 1448 |
+1
-1
| --- src/json_report.c | ||
| +++ src/json_report.c | ||
| @@ -155,11 +155,11 @@ | ||
| 155 | 155 | ** |
| 156 | 156 | ** report=int (CLI: -report # or -r #) is the report number to run. |
| 157 | 157 | ** |
| 158 | 158 | ** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands. |
| 159 | 159 | ** |
| 160 | -** format=a|o Specifies result format: a=each row is an arry, o=each | |
| 160 | +** format=a|o Specifies result format: a=each row is an array, o=each | |
| 161 | 161 | ** row is an object. Default=o. |
| 162 | 162 | */ |
| 163 | 163 | static cson_value * json_report_run(void){ |
| 164 | 164 | int nReport; |
| 165 | 165 | Stmt q = empty_Stmt; |
| 166 | 166 |
| --- src/json_report.c | |
| +++ src/json_report.c | |
| @@ -155,11 +155,11 @@ | |
| 155 | ** |
| 156 | ** report=int (CLI: -report # or -r #) is the report number to run. |
| 157 | ** |
| 158 | ** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands. |
| 159 | ** |
| 160 | ** format=a|o Specifies result format: a=each row is an arry, o=each |
| 161 | ** row is an object. Default=o. |
| 162 | */ |
| 163 | static cson_value * json_report_run(void){ |
| 164 | int nReport; |
| 165 | Stmt q = empty_Stmt; |
| 166 |
| --- src/json_report.c | |
| +++ src/json_report.c | |
| @@ -155,11 +155,11 @@ | |
| 155 | ** |
| 156 | ** report=int (CLI: -report # or -r #) is the report number to run. |
| 157 | ** |
| 158 | ** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands. |
| 159 | ** |
| 160 | ** format=a|o Specifies result format: a=each row is an array, o=each |
| 161 | ** row is an object. Default=o. |
| 162 | */ |
| 163 | static cson_value * json_report_run(void){ |
| 164 | int nReport; |
| 165 | Stmt q = empty_Stmt; |
| 166 |
+1
-1
| --- src/json_status.c | ||
| +++ src/json_status.c | ||
| @@ -126,11 +126,11 @@ | ||
| 126 | 126 | zStatus = "missing"; |
| 127 | 127 | ++nErr; |
| 128 | 128 | } |
| 129 | 129 | }else if( isChnged ){ |
| 130 | 130 | switch( isChnged ){ |
| 131 | - /* These numbers from from checkin.c: status_report() */ | |
| 131 | + /* These numbers from checkin.c: status_report() */ | |
| 132 | 132 | case 1: |
| 133 | 133 | if( file_contains_merge_marker(zFullName) ){ |
| 134 | 134 | zStatus = "conflict"; |
| 135 | 135 | }else{ |
| 136 | 136 | zStatus = "edited"; |
| 137 | 137 |
| --- src/json_status.c | |
| +++ src/json_status.c | |
| @@ -126,11 +126,11 @@ | |
| 126 | zStatus = "missing"; |
| 127 | ++nErr; |
| 128 | } |
| 129 | }else if( isChnged ){ |
| 130 | switch( isChnged ){ |
| 131 | /* These numbers from from checkin.c: status_report() */ |
| 132 | case 1: |
| 133 | if( file_contains_merge_marker(zFullName) ){ |
| 134 | zStatus = "conflict"; |
| 135 | }else{ |
| 136 | zStatus = "edited"; |
| 137 |
| --- src/json_status.c | |
| +++ src/json_status.c | |
| @@ -126,11 +126,11 @@ | |
| 126 | zStatus = "missing"; |
| 127 | ++nErr; |
| 128 | } |
| 129 | }else if( isChnged ){ |
| 130 | switch( isChnged ){ |
| 131 | /* These numbers from checkin.c: status_report() */ |
| 132 | case 1: |
| 133 | if( file_contains_merge_marker(zFullName) ){ |
| 134 | zStatus = "conflict"; |
| 135 | }else{ |
| 136 | zStatus = "edited"; |
| 137 |
+15
-9
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -293,11 +293,11 @@ | ||
| 293 | 293 | ** |
| 294 | 294 | ** This function also updates the user.cookie, user.ipaddr, |
| 295 | 295 | ** and user.cexpire fields for the given user. |
| 296 | 296 | ** |
| 297 | 297 | ** If zDest is not NULL then the generated cookie is copied to |
| 298 | -** *zDdest and ownership is transfered to the caller (who should | |
| 298 | +** *zDdest and ownership is transferred to the caller (who should | |
| 299 | 299 | ** eventually pass it to free()). |
| 300 | 300 | ** |
| 301 | 301 | ** If bSessionCookie is true, the cookie will be a session cookie, |
| 302 | 302 | ** else a persistent cookie. If it's a session cookie, the |
| 303 | 303 | ** [user].[cexpire] and [user].[cookie] entries will be modified as if |
| @@ -379,11 +379,11 @@ | ||
| 379 | 379 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 380 | 380 | ** |
| 381 | 381 | ** Search for tag-20250817a to find the code that recognizes this cookie. |
| 382 | 382 | */ |
| 383 | 383 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 384 | - char *zNow; /* Current time (julian day number) */ | |
| 384 | + char *zNow; /* Current time (Julian day number) */ | |
| 385 | 385 | char *zCookie; /* The login cookie */ |
| 386 | 386 | const char *zUserAgent; /* The user agent */ |
| 387 | 387 | const char *zCookieName; /* Name of the login cookie */ |
| 388 | 388 | Blob b; /* Blob used during cookie construction */ |
| 389 | 389 | int expires = bSessionCookie ? 0 : anon_cookie_lifespan(); |
| @@ -1371,10 +1371,11 @@ | ||
| 1371 | 1371 | ** g.zLogin Database USER.LOGIN value. NULL for user "nobody" |
| 1372 | 1372 | ** g.perm Permissions granted to this user |
| 1373 | 1373 | ** g.anon Permissions that would be available to anonymous |
| 1374 | 1374 | ** g.isRobot True if the client is known to be a spider or robot |
| 1375 | 1375 | ** g.perm Populated based on user account's capabilities |
| 1376 | +** g.eAuthMethod The mechanism used for authentication | |
| 1376 | 1377 | ** |
| 1377 | 1378 | */ |
| 1378 | 1379 | void login_check_credentials(void){ |
| 1379 | 1380 | int uid = 0; /* User id */ |
| 1380 | 1381 | const char *zCookie; /* Text of the login cookie */ |
| @@ -1411,10 +1412,11 @@ | ||
| 1411 | 1412 | } |
| 1412 | 1413 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 1413 | 1414 | zCap = "sxy"; |
| 1414 | 1415 | g.noPswd = 1; |
| 1415 | 1416 | g.isRobot = 0; |
| 1417 | + g.eAuthMethod = AUTH_LOCAL; | |
| 1416 | 1418 | zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)" |
| 1417 | 1419 | " FROM user WHERE uid=%d", uid); |
| 1418 | 1420 | login_create_csrf_secret(zSeed); |
| 1419 | 1421 | fossil_free(zSeed); |
| 1420 | 1422 | } |
| @@ -1490,10 +1492,11 @@ | ||
| 1490 | 1492 | " AND octet_length(cap)>0" |
| 1491 | 1493 | " AND octet_length(pw)>0"); |
| 1492 | 1494 | } |
| 1493 | 1495 | } |
| 1494 | 1496 | } |
| 1497 | + if( uid ) g.eAuthMethod = AUTH_COOKIE; | |
| 1495 | 1498 | login_create_csrf_secret(zHash); |
| 1496 | 1499 | } |
| 1497 | 1500 | |
| 1498 | 1501 | /* If no user found and the REMOTE_USER environment variable is set, |
| 1499 | 1502 | ** then accept the value of REMOTE_USER as the user. |
| @@ -1502,19 +1505,21 @@ | ||
| 1502 | 1505 | const char *zRemoteUser = P("REMOTE_USER"); |
| 1503 | 1506 | if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){ |
| 1504 | 1507 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q" |
| 1505 | 1508 | " AND octet_length(cap)>0 AND octet_length(pw)>0", |
| 1506 | 1509 | zRemoteUser); |
| 1510 | + if( uid ) g.eAuthMethod = AUTH_ENV; | |
| 1507 | 1511 | } |
| 1508 | 1512 | } |
| 1509 | 1513 | |
| 1510 | 1514 | /* If the request didn't provide a login cookie or the login cookie didn't |
| 1511 | 1515 | ** match a known valid user, check the HTTP "Authorization" header and |
| 1512 | 1516 | ** see if those credentials are valid for a known user. |
| 1513 | 1517 | */ |
| 1514 | 1518 | if( uid==0 && db_get_boolean("http_authentication_ok",0) ){ |
| 1515 | 1519 | uid = login_basic_authentication(zIpAddr); |
| 1520 | + if( uid ) g.eAuthMethod = AUTH_HTTP; | |
| 1516 | 1521 | } |
| 1517 | 1522 | |
| 1518 | 1523 | /* Check for magic query parameters "resid" (for the username) and |
| 1519 | 1524 | ** "token" for the password. Both values (if they exist) will be |
| 1520 | 1525 | ** obfuscated. |
| @@ -1529,10 +1534,11 @@ | ||
| 1529 | 1534 | " WHERE login=%Q" |
| 1530 | 1535 | " AND (constant_time_cmp(pw,%Q)=0" |
| 1531 | 1536 | " OR constant_time_cmp(pw,%Q)=0)", |
| 1532 | 1537 | zUsr, zSha1Pw, zPW); |
| 1533 | 1538 | fossil_free(zSha1Pw); |
| 1539 | + if( uid ) g.eAuthMethod = AUTH_PW; | |
| 1534 | 1540 | } |
| 1535 | 1541 | } |
| 1536 | 1542 | |
| 1537 | 1543 | /* If no user found yet, try to log in as "nobody" */ |
| 1538 | 1544 | if( uid==0 ){ |
| @@ -1925,11 +1931,11 @@ | ||
| 1925 | 1931 | } |
| 1926 | 1932 | } |
| 1927 | 1933 | |
| 1928 | 1934 | /* |
| 1929 | 1935 | ** Call this routine if the user lacks g.perm.Hyperlink permission. If |
| 1930 | -** the anonymous user has Hyperlink permission, then paint a mesage | |
| 1936 | +** the anonymous user has Hyperlink permission, then paint a message | |
| 1931 | 1937 | ** to inform the user that much more information is available by |
| 1932 | 1938 | ** logging in as anonymous. |
| 1933 | 1939 | */ |
| 1934 | 1940 | void login_anonymous_available(void){ |
| 1935 | 1941 | if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){ |
| @@ -1949,13 +1955,13 @@ | ||
| 1949 | 1955 | } |
| 1950 | 1956 | |
| 1951 | 1957 | /* |
| 1952 | 1958 | ** Check to see if the candidate username zUserID is already used. |
| 1953 | 1959 | ** Return 1 if it is already in use. Return 0 if the name is |
| 1954 | -** available for a self-registeration. | |
| 1960 | +** available for a self-registration. | |
| 1955 | 1961 | */ |
| 1956 | -static int login_self_choosen_userid_already_exists(const char *zUserID){ | |
| 1962 | +static int login_self_chosen_userid_already_exists(const char *zUserID){ | |
| 1957 | 1963 | int rc = db_exists( |
| 1958 | 1964 | "SELECT 1 FROM user WHERE login=%Q " |
| 1959 | 1965 | "UNION ALL " |
| 1960 | 1966 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1961 | 1967 | zUserID, zUserID, zUserID |
| @@ -2140,11 +2146,11 @@ | ||
| 2140 | 2146 | iErrLine = 5; |
| 2141 | 2147 | zErr = "Passwords do not match"; |
| 2142 | 2148 | }else if( (uid = email_address_in_use(zEAddr))!=0 ){ |
| 2143 | 2149 | iErrLine = 3; |
| 2144 | 2150 | zErr = "This email address is already associated with a user"; |
| 2145 | - }else if( login_self_choosen_userid_already_exists(zUserID) ){ | |
| 2151 | + }else if( login_self_chosen_userid_already_exists(zUserID) ){ | |
| 2146 | 2152 | iErrLine = 1; |
| 2147 | 2153 | zErr = "This User ID is already taken. Choose something different."; |
| 2148 | 2154 | }else{ |
| 2149 | 2155 | /* If all of the tests above have passed, that means that the submitted |
| 2150 | 2156 | ** form contains valid data and we can proceed to create the new login */ |
| @@ -2151,11 +2157,11 @@ | ||
| 2151 | 2157 | Blob sql; |
| 2152 | 2158 | int uid; |
| 2153 | 2159 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 2154 | 2160 | const char *zStartPerms = zPerms; |
| 2155 | 2161 | if( db_get_boolean("selfreg-verify",0) ){ |
| 2156 | - /* If email verification is required for self-registration, initalize | |
| 2162 | + /* If email verification is required for self-registration, initialize | |
| 2157 | 2163 | ** the new user capabilities to just "7" (Sign up for email). The |
| 2158 | 2164 | ** full "default-perms" permissions will be added when they click |
| 2159 | 2165 | ** the verification link on the email they are sent. */ |
| 2160 | 2166 | zStartPerms = "7"; |
| 2161 | 2167 | } |
| @@ -2206,11 +2212,11 @@ | ||
| 2206 | 2212 | ); |
| 2207 | 2213 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q" |
| 2208 | 2214 | " AND sverified", zEAddr) ){ |
| 2209 | 2215 | /* This the case where the user was formerly a verified subscriber |
| 2210 | 2216 | ** and here they have also registered as a user as well. It is |
| 2211 | - ** not necessary to repeat the verfication step */ | |
| 2217 | + ** not necessary to repeat the verification step */ | |
| 2212 | 2218 | login_redirect_to_g(); |
| 2213 | 2219 | } |
| 2214 | 2220 | /* A verification email */ |
| 2215 | 2221 | pSender = alert_sender_new(0,0); |
| 2216 | 2222 | blob_init(&hdr,0,0); |
| @@ -2377,11 +2383,11 @@ | ||
| 2377 | 2383 | style_finish_page(); |
| 2378 | 2384 | return; |
| 2379 | 2385 | } |
| 2380 | 2386 | zEAddr = PDT("ea",""); |
| 2381 | 2387 | |
| 2382 | - /* Verify user imputs */ | |
| 2388 | + /* Verify user inputs */ | |
| 2383 | 2389 | if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){ |
| 2384 | 2390 | /* This is the initial display of the form. No processing or error |
| 2385 | 2391 | ** checking is to be done. Fall through into the form display |
| 2386 | 2392 | ** |
| 2387 | 2393 | ** cgi_csrf_safe(): Nothing interesting happens on this page without |
| 2388 | 2394 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -293,11 +293,11 @@ | |
| 293 | ** |
| 294 | ** This function also updates the user.cookie, user.ipaddr, |
| 295 | ** and user.cexpire fields for the given user. |
| 296 | ** |
| 297 | ** If zDest is not NULL then the generated cookie is copied to |
| 298 | ** *zDdest and ownership is transfered to the caller (who should |
| 299 | ** eventually pass it to free()). |
| 300 | ** |
| 301 | ** If bSessionCookie is true, the cookie will be a session cookie, |
| 302 | ** else a persistent cookie. If it's a session cookie, the |
| 303 | ** [user].[cexpire] and [user].[cookie] entries will be modified as if |
| @@ -379,11 +379,11 @@ | |
| 379 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 380 | ** |
| 381 | ** Search for tag-20250817a to find the code that recognizes this cookie. |
| 382 | */ |
| 383 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 384 | char *zNow; /* Current time (julian day number) */ |
| 385 | char *zCookie; /* The login cookie */ |
| 386 | const char *zUserAgent; /* The user agent */ |
| 387 | const char *zCookieName; /* Name of the login cookie */ |
| 388 | Blob b; /* Blob used during cookie construction */ |
| 389 | int expires = bSessionCookie ? 0 : anon_cookie_lifespan(); |
| @@ -1371,10 +1371,11 @@ | |
| 1371 | ** g.zLogin Database USER.LOGIN value. NULL for user "nobody" |
| 1372 | ** g.perm Permissions granted to this user |
| 1373 | ** g.anon Permissions that would be available to anonymous |
| 1374 | ** g.isRobot True if the client is known to be a spider or robot |
| 1375 | ** g.perm Populated based on user account's capabilities |
| 1376 | ** |
| 1377 | */ |
| 1378 | void login_check_credentials(void){ |
| 1379 | int uid = 0; /* User id */ |
| 1380 | const char *zCookie; /* Text of the login cookie */ |
| @@ -1411,10 +1412,11 @@ | |
| 1411 | } |
| 1412 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 1413 | zCap = "sxy"; |
| 1414 | g.noPswd = 1; |
| 1415 | g.isRobot = 0; |
| 1416 | zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)" |
| 1417 | " FROM user WHERE uid=%d", uid); |
| 1418 | login_create_csrf_secret(zSeed); |
| 1419 | fossil_free(zSeed); |
| 1420 | } |
| @@ -1490,10 +1492,11 @@ | |
| 1490 | " AND octet_length(cap)>0" |
| 1491 | " AND octet_length(pw)>0"); |
| 1492 | } |
| 1493 | } |
| 1494 | } |
| 1495 | login_create_csrf_secret(zHash); |
| 1496 | } |
| 1497 | |
| 1498 | /* If no user found and the REMOTE_USER environment variable is set, |
| 1499 | ** then accept the value of REMOTE_USER as the user. |
| @@ -1502,19 +1505,21 @@ | |
| 1502 | const char *zRemoteUser = P("REMOTE_USER"); |
| 1503 | if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){ |
| 1504 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q" |
| 1505 | " AND octet_length(cap)>0 AND octet_length(pw)>0", |
| 1506 | zRemoteUser); |
| 1507 | } |
| 1508 | } |
| 1509 | |
| 1510 | /* If the request didn't provide a login cookie or the login cookie didn't |
| 1511 | ** match a known valid user, check the HTTP "Authorization" header and |
| 1512 | ** see if those credentials are valid for a known user. |
| 1513 | */ |
| 1514 | if( uid==0 && db_get_boolean("http_authentication_ok",0) ){ |
| 1515 | uid = login_basic_authentication(zIpAddr); |
| 1516 | } |
| 1517 | |
| 1518 | /* Check for magic query parameters "resid" (for the username) and |
| 1519 | ** "token" for the password. Both values (if they exist) will be |
| 1520 | ** obfuscated. |
| @@ -1529,10 +1534,11 @@ | |
| 1529 | " WHERE login=%Q" |
| 1530 | " AND (constant_time_cmp(pw,%Q)=0" |
| 1531 | " OR constant_time_cmp(pw,%Q)=0)", |
| 1532 | zUsr, zSha1Pw, zPW); |
| 1533 | fossil_free(zSha1Pw); |
| 1534 | } |
| 1535 | } |
| 1536 | |
| 1537 | /* If no user found yet, try to log in as "nobody" */ |
| 1538 | if( uid==0 ){ |
| @@ -1925,11 +1931,11 @@ | |
| 1925 | } |
| 1926 | } |
| 1927 | |
| 1928 | /* |
| 1929 | ** Call this routine if the user lacks g.perm.Hyperlink permission. If |
| 1930 | ** the anonymous user has Hyperlink permission, then paint a mesage |
| 1931 | ** to inform the user that much more information is available by |
| 1932 | ** logging in as anonymous. |
| 1933 | */ |
| 1934 | void login_anonymous_available(void){ |
| 1935 | if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){ |
| @@ -1949,13 +1955,13 @@ | |
| 1949 | } |
| 1950 | |
| 1951 | /* |
| 1952 | ** Check to see if the candidate username zUserID is already used. |
| 1953 | ** Return 1 if it is already in use. Return 0 if the name is |
| 1954 | ** available for a self-registeration. |
| 1955 | */ |
| 1956 | static int login_self_choosen_userid_already_exists(const char *zUserID){ |
| 1957 | int rc = db_exists( |
| 1958 | "SELECT 1 FROM user WHERE login=%Q " |
| 1959 | "UNION ALL " |
| 1960 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1961 | zUserID, zUserID, zUserID |
| @@ -2140,11 +2146,11 @@ | |
| 2140 | iErrLine = 5; |
| 2141 | zErr = "Passwords do not match"; |
| 2142 | }else if( (uid = email_address_in_use(zEAddr))!=0 ){ |
| 2143 | iErrLine = 3; |
| 2144 | zErr = "This email address is already associated with a user"; |
| 2145 | }else if( login_self_choosen_userid_already_exists(zUserID) ){ |
| 2146 | iErrLine = 1; |
| 2147 | zErr = "This User ID is already taken. Choose something different."; |
| 2148 | }else{ |
| 2149 | /* If all of the tests above have passed, that means that the submitted |
| 2150 | ** form contains valid data and we can proceed to create the new login */ |
| @@ -2151,11 +2157,11 @@ | |
| 2151 | Blob sql; |
| 2152 | int uid; |
| 2153 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 2154 | const char *zStartPerms = zPerms; |
| 2155 | if( db_get_boolean("selfreg-verify",0) ){ |
| 2156 | /* If email verification is required for self-registration, initalize |
| 2157 | ** the new user capabilities to just "7" (Sign up for email). The |
| 2158 | ** full "default-perms" permissions will be added when they click |
| 2159 | ** the verification link on the email they are sent. */ |
| 2160 | zStartPerms = "7"; |
| 2161 | } |
| @@ -2206,11 +2212,11 @@ | |
| 2206 | ); |
| 2207 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q" |
| 2208 | " AND sverified", zEAddr) ){ |
| 2209 | /* This the case where the user was formerly a verified subscriber |
| 2210 | ** and here they have also registered as a user as well. It is |
| 2211 | ** not necessary to repeat the verfication step */ |
| 2212 | login_redirect_to_g(); |
| 2213 | } |
| 2214 | /* A verification email */ |
| 2215 | pSender = alert_sender_new(0,0); |
| 2216 | blob_init(&hdr,0,0); |
| @@ -2377,11 +2383,11 @@ | |
| 2377 | style_finish_page(); |
| 2378 | return; |
| 2379 | } |
| 2380 | zEAddr = PDT("ea",""); |
| 2381 | |
| 2382 | /* Verify user imputs */ |
| 2383 | if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){ |
| 2384 | /* This is the initial display of the form. No processing or error |
| 2385 | ** checking is to be done. Fall through into the form display |
| 2386 | ** |
| 2387 | ** cgi_csrf_safe(): Nothing interesting happens on this page without |
| 2388 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -293,11 +293,11 @@ | |
| 293 | ** |
| 294 | ** This function also updates the user.cookie, user.ipaddr, |
| 295 | ** and user.cexpire fields for the given user. |
| 296 | ** |
| 297 | ** If zDest is not NULL then the generated cookie is copied to |
| 298 | ** *zDdest and ownership is transferred to the caller (who should |
| 299 | ** eventually pass it to free()). |
| 300 | ** |
| 301 | ** If bSessionCookie is true, the cookie will be a session cookie, |
| 302 | ** else a persistent cookie. If it's a session cookie, the |
| 303 | ** [user].[cexpire] and [user].[cookie] entries will be modified as if |
| @@ -379,11 +379,11 @@ | |
| 379 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 380 | ** |
| 381 | ** Search for tag-20250817a to find the code that recognizes this cookie. |
| 382 | */ |
| 383 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 384 | char *zNow; /* Current time (Julian day number) */ |
| 385 | char *zCookie; /* The login cookie */ |
| 386 | const char *zUserAgent; /* The user agent */ |
| 387 | const char *zCookieName; /* Name of the login cookie */ |
| 388 | Blob b; /* Blob used during cookie construction */ |
| 389 | int expires = bSessionCookie ? 0 : anon_cookie_lifespan(); |
| @@ -1371,10 +1371,11 @@ | |
| 1371 | ** g.zLogin Database USER.LOGIN value. NULL for user "nobody" |
| 1372 | ** g.perm Permissions granted to this user |
| 1373 | ** g.anon Permissions that would be available to anonymous |
| 1374 | ** g.isRobot True if the client is known to be a spider or robot |
| 1375 | ** g.perm Populated based on user account's capabilities |
| 1376 | ** g.eAuthMethod The mechanism used for authentication |
| 1377 | ** |
| 1378 | */ |
| 1379 | void login_check_credentials(void){ |
| 1380 | int uid = 0; /* User id */ |
| 1381 | const char *zCookie; /* Text of the login cookie */ |
| @@ -1411,10 +1412,11 @@ | |
| 1412 | } |
| 1413 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 1414 | zCap = "sxy"; |
| 1415 | g.noPswd = 1; |
| 1416 | g.isRobot = 0; |
| 1417 | g.eAuthMethod = AUTH_LOCAL; |
| 1418 | zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)" |
| 1419 | " FROM user WHERE uid=%d", uid); |
| 1420 | login_create_csrf_secret(zSeed); |
| 1421 | fossil_free(zSeed); |
| 1422 | } |
| @@ -1490,10 +1492,11 @@ | |
| 1492 | " AND octet_length(cap)>0" |
| 1493 | " AND octet_length(pw)>0"); |
| 1494 | } |
| 1495 | } |
| 1496 | } |
| 1497 | if( uid ) g.eAuthMethod = AUTH_COOKIE; |
| 1498 | login_create_csrf_secret(zHash); |
| 1499 | } |
| 1500 | |
| 1501 | /* If no user found and the REMOTE_USER environment variable is set, |
| 1502 | ** then accept the value of REMOTE_USER as the user. |
| @@ -1502,19 +1505,21 @@ | |
| 1505 | const char *zRemoteUser = P("REMOTE_USER"); |
| 1506 | if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){ |
| 1507 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q" |
| 1508 | " AND octet_length(cap)>0 AND octet_length(pw)>0", |
| 1509 | zRemoteUser); |
| 1510 | if( uid ) g.eAuthMethod = AUTH_ENV; |
| 1511 | } |
| 1512 | } |
| 1513 | |
| 1514 | /* If the request didn't provide a login cookie or the login cookie didn't |
| 1515 | ** match a known valid user, check the HTTP "Authorization" header and |
| 1516 | ** see if those credentials are valid for a known user. |
| 1517 | */ |
| 1518 | if( uid==0 && db_get_boolean("http_authentication_ok",0) ){ |
| 1519 | uid = login_basic_authentication(zIpAddr); |
| 1520 | if( uid ) g.eAuthMethod = AUTH_HTTP; |
| 1521 | } |
| 1522 | |
| 1523 | /* Check for magic query parameters "resid" (for the username) and |
| 1524 | ** "token" for the password. Both values (if they exist) will be |
| 1525 | ** obfuscated. |
| @@ -1529,10 +1534,11 @@ | |
| 1534 | " WHERE login=%Q" |
| 1535 | " AND (constant_time_cmp(pw,%Q)=0" |
| 1536 | " OR constant_time_cmp(pw,%Q)=0)", |
| 1537 | zUsr, zSha1Pw, zPW); |
| 1538 | fossil_free(zSha1Pw); |
| 1539 | if( uid ) g.eAuthMethod = AUTH_PW; |
| 1540 | } |
| 1541 | } |
| 1542 | |
| 1543 | /* If no user found yet, try to log in as "nobody" */ |
| 1544 | if( uid==0 ){ |
| @@ -1925,11 +1931,11 @@ | |
| 1931 | } |
| 1932 | } |
| 1933 | |
| 1934 | /* |
| 1935 | ** Call this routine if the user lacks g.perm.Hyperlink permission. If |
| 1936 | ** the anonymous user has Hyperlink permission, then paint a message |
| 1937 | ** to inform the user that much more information is available by |
| 1938 | ** logging in as anonymous. |
| 1939 | */ |
| 1940 | void login_anonymous_available(void){ |
| 1941 | if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){ |
| @@ -1949,13 +1955,13 @@ | |
| 1955 | } |
| 1956 | |
| 1957 | /* |
| 1958 | ** Check to see if the candidate username zUserID is already used. |
| 1959 | ** Return 1 if it is already in use. Return 0 if the name is |
| 1960 | ** available for a self-registration. |
| 1961 | */ |
| 1962 | static int login_self_chosen_userid_already_exists(const char *zUserID){ |
| 1963 | int rc = db_exists( |
| 1964 | "SELECT 1 FROM user WHERE login=%Q " |
| 1965 | "UNION ALL " |
| 1966 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1967 | zUserID, zUserID, zUserID |
| @@ -2140,11 +2146,11 @@ | |
| 2146 | iErrLine = 5; |
| 2147 | zErr = "Passwords do not match"; |
| 2148 | }else if( (uid = email_address_in_use(zEAddr))!=0 ){ |
| 2149 | iErrLine = 3; |
| 2150 | zErr = "This email address is already associated with a user"; |
| 2151 | }else if( login_self_chosen_userid_already_exists(zUserID) ){ |
| 2152 | iErrLine = 1; |
| 2153 | zErr = "This User ID is already taken. Choose something different."; |
| 2154 | }else{ |
| 2155 | /* If all of the tests above have passed, that means that the submitted |
| 2156 | ** form contains valid data and we can proceed to create the new login */ |
| @@ -2151,11 +2157,11 @@ | |
| 2157 | Blob sql; |
| 2158 | int uid; |
| 2159 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 2160 | const char *zStartPerms = zPerms; |
| 2161 | if( db_get_boolean("selfreg-verify",0) ){ |
| 2162 | /* If email verification is required for self-registration, initialize |
| 2163 | ** the new user capabilities to just "7" (Sign up for email). The |
| 2164 | ** full "default-perms" permissions will be added when they click |
| 2165 | ** the verification link on the email they are sent. */ |
| 2166 | zStartPerms = "7"; |
| 2167 | } |
| @@ -2206,11 +2212,11 @@ | |
| 2212 | ); |
| 2213 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q" |
| 2214 | " AND sverified", zEAddr) ){ |
| 2215 | /* This the case where the user was formerly a verified subscriber |
| 2216 | ** and here they have also registered as a user as well. It is |
| 2217 | ** not necessary to repeat the verification step */ |
| 2218 | login_redirect_to_g(); |
| 2219 | } |
| 2220 | /* A verification email */ |
| 2221 | pSender = alert_sender_new(0,0); |
| 2222 | blob_init(&hdr,0,0); |
| @@ -2377,11 +2383,11 @@ | |
| 2383 | style_finish_page(); |
| 2384 | return; |
| 2385 | } |
| 2386 | zEAddr = PDT("ea",""); |
| 2387 | |
| 2388 | /* Verify user inputs */ |
| 2389 | if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){ |
| 2390 | /* This is the initial display of the form. No processing or error |
| 2391 | ** checking is to be done. Fall through into the form display |
| 2392 | ** |
| 2393 | ** cgi_csrf_safe(): Nothing interesting happens on this page without |
| 2394 |
+16
-8
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -63,11 +63,11 @@ | ||
| 63 | 63 | #ifdef HAVE_BACKTRACE |
| 64 | 64 | # include <execinfo.h> |
| 65 | 65 | #endif |
| 66 | 66 | |
| 67 | 67 | /* |
| 68 | -** Default length of a timeout for serving an HTTP request. Changable | |
| 68 | +** Default length of a timeout for serving an HTTP request. Changeable | |
| 69 | 69 | ** using the "--timeout N" command-line option or via "timeout: N" in the |
| 70 | 70 | ** CGI script. |
| 71 | 71 | */ |
| 72 | 72 | #ifndef FOSSIL_DEFAULT_TIMEOUT |
| 73 | 73 | # define FOSSIL_DEFAULT_TIMEOUT 600 /* 10 minutes */ |
| @@ -153,10 +153,11 @@ | ||
| 153 | 153 | char *nameOfExe; /* Full path of executable. */ |
| 154 | 154 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 155 | 155 | const char *zPhase; /* Phase of operation, for use by the error log |
| 156 | 156 | ** and for deriving $canonical_page TH1 variable */ |
| 157 | 157 | int isConst; /* True if the output is unchanging & cacheable */ |
| 158 | + int iResultCode; /* Process reply code for commands */ | |
| 158 | 159 | const char *zVfsName; /* The VFS to use for database connections */ |
| 159 | 160 | sqlite3 *db; /* The connection to the databases */ |
| 160 | 161 | sqlite3 *dbConfig; /* Separate connection for global_config table */ |
| 161 | 162 | char *zAuxSchema; /* Main repository aux-schema */ |
| 162 | 163 | int dbIgnoreErrors; /* Ignore database errors if true */ |
| @@ -236,10 +237,17 @@ | ||
| 236 | 237 | * applicable when using SEE on Windows or Linux. */ |
| 237 | 238 | #endif |
| 238 | 239 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 239 | 240 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 240 | 241 | int userUid; /* Integer user id */ |
| 242 | + int eAuthMethod; /* How the user authenticated to us */ | |
| 243 | +# define AUTH_NONE 0 /* Not authenticated */ | |
| 244 | +# define AUTH_COOKIE 1 /* Authentication by cookie */ | |
| 245 | +# define AUTH_LOCAL 2 /* Uses loopback */ | |
| 246 | +# define AUTH_PW 3 /* Authentication by password */ | |
| 247 | +# define AUTH_ENV 4 /* Authenticated by REMOTE_USER environment var */ | |
| 248 | +# define AUTH_HTTP 5 /* HTTP Basic Authentication */ | |
| 241 | 249 | int isRobot; /* True if the client is definitely a robot. False |
| 242 | 250 | ** negatives are common for this flag */ |
| 243 | 251 | int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be |
| 244 | 252 | ** accessed through get_comment_format(). */ |
| 245 | 253 | const char *zSockName; /* Name of the unix-domain socket file */ |
| @@ -823,11 +831,11 @@ | ||
| 823 | 831 | fossil_exit(1); |
| 824 | 832 | }else{ |
| 825 | 833 | const char *zChdir = find_option("chdir",0,1); |
| 826 | 834 | g.isHTTP = 0; |
| 827 | 835 | g.rcvid = 0; |
| 828 | - g.fQuiet = find_option("quiet", 0, 0)!=0; | |
| 836 | + g.fQuiet = find_option("quiet", "q", 0)!=0; | |
| 829 | 837 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 830 | 838 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 831 | 839 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 832 | 840 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 833 | 841 | g.fCgiTrace = find_option("cgitrace", 0, 0)!=0; |
| @@ -1009,11 +1017,11 @@ | ||
| 1009 | 1017 | if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ |
| 1010 | 1018 | Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags); |
| 1011 | 1019 | } |
| 1012 | 1020 | } |
| 1013 | 1021 | #endif |
| 1014 | - fossil_exit(0); | |
| 1022 | + fossil_exit(g.iResultCode); | |
| 1015 | 1023 | /*NOT_REACHED*/ |
| 1016 | 1024 | return 0; |
| 1017 | 1025 | } |
| 1018 | 1026 | |
| 1019 | 1027 | /* |
| @@ -1183,11 +1191,11 @@ | ||
| 1183 | 1191 | ** list and this function immediately returns. The effect is to treat |
| 1184 | 1192 | ** all arguments after "--" as non-flags (conventionally used to |
| 1185 | 1193 | ** enable passing-in of filenames which start with a dash). |
| 1186 | 1194 | ** |
| 1187 | 1195 | ** This function must normally only be called one time per app |
| 1188 | -** invokation. The exception is commands which process their | |
| 1196 | +** invocation. The exception is commands which process their | |
| 1189 | 1197 | ** arguments, call this to confirm that there are no extraneous flags, |
| 1190 | 1198 | ** then modify the arguments list for forwarding to another |
| 1191 | 1199 | ** (sub)command (which itself will call this to confirm its own |
| 1192 | 1200 | ** arguments). |
| 1193 | 1201 | */ |
| @@ -1395,11 +1403,11 @@ | ||
| 1395 | 1403 | style_finish_page(); |
| 1396 | 1404 | } |
| 1397 | 1405 | |
| 1398 | 1406 | |
| 1399 | 1407 | /* |
| 1400 | -** Set the g.zBaseURL value to the full URL for the toplevel of | |
| 1408 | +** Set the g.zBaseURL value to the full URL for the top level of | |
| 1401 | 1409 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 1402 | 1410 | ** leading "http://" and the host and port. |
| 1403 | 1411 | ** |
| 1404 | 1412 | ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME |
| 1405 | 1413 | ** environment variables. However, if zAltBase is not NULL then it |
| @@ -2869,11 +2877,11 @@ | ||
| 2869 | 2877 | ** then the server redirects (HTTP code 302) to the URL of --notfound. |
| 2870 | 2878 | ** When REPOSITORY is a directory, the pathname must contain only |
| 2871 | 2879 | ** alphanumerics, "_", "/", "-" and "." and no "-" may occur after a "/" |
| 2872 | 2880 | ** and every "." must be surrounded on both sides by alphanumerics or else |
| 2873 | 2881 | ** a 404 error is returned. Static content files in the directory are |
| 2874 | -** returned if they match comma-separate GLOB pattern specified by --files | |
| 2882 | +** returned if they match comma-separated GLOB pattern specified by --files | |
| 2875 | 2883 | ** and do not match "*.fossil*" and have a well-known suffix. |
| 2876 | 2884 | ** |
| 2877 | 2885 | ** Options: |
| 2878 | 2886 | ** --acme Deliver files from the ".well-known" subdirectory |
| 2879 | 2887 | ** --baseurl URL Base URL (useful with reverse proxies) |
| @@ -2881,11 +2889,11 @@ | ||
| 2881 | 2889 | ** fullchain.pem) taken from FILE. |
| 2882 | 2890 | ** --chroot DIR Use directory for chroot instead of repository path. |
| 2883 | 2891 | ** --ckout-alias N Treat URIs of the form /doc/N/... as if they were |
| 2884 | 2892 | ** /doc/ckout/... |
| 2885 | 2893 | ** --extroot DIR Document root for the /ext extension mechanism |
| 2886 | -** --files GLOB Comma-separate glob patterns for static file to serve | |
| 2894 | +** --files GLOB Comma-separated glob patterns for static files to serve | |
| 2887 | 2895 | ** --host NAME DNS Hostname of the server |
| 2888 | 2896 | ** --https The HTTP request originated from https but has already |
| 2889 | 2897 | ** been decoded by a reverse proxy. Hence, URLs created |
| 2890 | 2898 | ** by Fossil should use "https:" rather than "http:". |
| 2891 | 2899 | ** --in FILE Take input from FILE instead of standard input |
| @@ -3102,11 +3110,11 @@ | ||
| 3102 | 3110 | ** using this command interactively over SSH. A better solution would be |
| 3103 | 3111 | ** to use a different command for "ssh" sync, but we cannot do that without |
| 3104 | 3112 | ** breaking legacy. |
| 3105 | 3113 | ** |
| 3106 | 3114 | ** Options: |
| 3107 | -** --csrf-safe N Set cgi_csrf_safe() to to return N | |
| 3115 | +** --csrf-safe N Set cgi_csrf_safe() to return N | |
| 3108 | 3116 | ** --nobody Pretend to be user "nobody" |
| 3109 | 3117 | ** --ssh-sim Pretend to be over an SSH connection |
| 3110 | 3118 | ** --test Do not do special "sync" processing when operating |
| 3111 | 3119 | ** over an SSH link |
| 3112 | 3120 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3113 | 3121 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -63,11 +63,11 @@ | |
| 63 | #ifdef HAVE_BACKTRACE |
| 64 | # include <execinfo.h> |
| 65 | #endif |
| 66 | |
| 67 | /* |
| 68 | ** Default length of a timeout for serving an HTTP request. Changable |
| 69 | ** using the "--timeout N" command-line option or via "timeout: N" in the |
| 70 | ** CGI script. |
| 71 | */ |
| 72 | #ifndef FOSSIL_DEFAULT_TIMEOUT |
| 73 | # define FOSSIL_DEFAULT_TIMEOUT 600 /* 10 minutes */ |
| @@ -153,10 +153,11 @@ | |
| 153 | char *nameOfExe; /* Full path of executable. */ |
| 154 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 155 | const char *zPhase; /* Phase of operation, for use by the error log |
| 156 | ** and for deriving $canonical_page TH1 variable */ |
| 157 | int isConst; /* True if the output is unchanging & cacheable */ |
| 158 | const char *zVfsName; /* The VFS to use for database connections */ |
| 159 | sqlite3 *db; /* The connection to the databases */ |
| 160 | sqlite3 *dbConfig; /* Separate connection for global_config table */ |
| 161 | char *zAuxSchema; /* Main repository aux-schema */ |
| 162 | int dbIgnoreErrors; /* Ignore database errors if true */ |
| @@ -236,10 +237,17 @@ | |
| 236 | * applicable when using SEE on Windows or Linux. */ |
| 237 | #endif |
| 238 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 239 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 240 | int userUid; /* Integer user id */ |
| 241 | int isRobot; /* True if the client is definitely a robot. False |
| 242 | ** negatives are common for this flag */ |
| 243 | int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be |
| 244 | ** accessed through get_comment_format(). */ |
| 245 | const char *zSockName; /* Name of the unix-domain socket file */ |
| @@ -823,11 +831,11 @@ | |
| 823 | fossil_exit(1); |
| 824 | }else{ |
| 825 | const char *zChdir = find_option("chdir",0,1); |
| 826 | g.isHTTP = 0; |
| 827 | g.rcvid = 0; |
| 828 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 829 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 830 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 831 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 832 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 833 | g.fCgiTrace = find_option("cgitrace", 0, 0)!=0; |
| @@ -1009,11 +1017,11 @@ | |
| 1009 | if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ |
| 1010 | Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags); |
| 1011 | } |
| 1012 | } |
| 1013 | #endif |
| 1014 | fossil_exit(0); |
| 1015 | /*NOT_REACHED*/ |
| 1016 | return 0; |
| 1017 | } |
| 1018 | |
| 1019 | /* |
| @@ -1183,11 +1191,11 @@ | |
| 1183 | ** list and this function immediately returns. The effect is to treat |
| 1184 | ** all arguments after "--" as non-flags (conventionally used to |
| 1185 | ** enable passing-in of filenames which start with a dash). |
| 1186 | ** |
| 1187 | ** This function must normally only be called one time per app |
| 1188 | ** invokation. The exception is commands which process their |
| 1189 | ** arguments, call this to confirm that there are no extraneous flags, |
| 1190 | ** then modify the arguments list for forwarding to another |
| 1191 | ** (sub)command (which itself will call this to confirm its own |
| 1192 | ** arguments). |
| 1193 | */ |
| @@ -1395,11 +1403,11 @@ | |
| 1395 | style_finish_page(); |
| 1396 | } |
| 1397 | |
| 1398 | |
| 1399 | /* |
| 1400 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 1401 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 1402 | ** leading "http://" and the host and port. |
| 1403 | ** |
| 1404 | ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME |
| 1405 | ** environment variables. However, if zAltBase is not NULL then it |
| @@ -2869,11 +2877,11 @@ | |
| 2869 | ** then the server redirects (HTTP code 302) to the URL of --notfound. |
| 2870 | ** When REPOSITORY is a directory, the pathname must contain only |
| 2871 | ** alphanumerics, "_", "/", "-" and "." and no "-" may occur after a "/" |
| 2872 | ** and every "." must be surrounded on both sides by alphanumerics or else |
| 2873 | ** a 404 error is returned. Static content files in the directory are |
| 2874 | ** returned if they match comma-separate GLOB pattern specified by --files |
| 2875 | ** and do not match "*.fossil*" and have a well-known suffix. |
| 2876 | ** |
| 2877 | ** Options: |
| 2878 | ** --acme Deliver files from the ".well-known" subdirectory |
| 2879 | ** --baseurl URL Base URL (useful with reverse proxies) |
| @@ -2881,11 +2889,11 @@ | |
| 2881 | ** fullchain.pem) taken from FILE. |
| 2882 | ** --chroot DIR Use directory for chroot instead of repository path. |
| 2883 | ** --ckout-alias N Treat URIs of the form /doc/N/... as if they were |
| 2884 | ** /doc/ckout/... |
| 2885 | ** --extroot DIR Document root for the /ext extension mechanism |
| 2886 | ** --files GLOB Comma-separate glob patterns for static file to serve |
| 2887 | ** --host NAME DNS Hostname of the server |
| 2888 | ** --https The HTTP request originated from https but has already |
| 2889 | ** been decoded by a reverse proxy. Hence, URLs created |
| 2890 | ** by Fossil should use "https:" rather than "http:". |
| 2891 | ** --in FILE Take input from FILE instead of standard input |
| @@ -3102,11 +3110,11 @@ | |
| 3102 | ** using this command interactively over SSH. A better solution would be |
| 3103 | ** to use a different command for "ssh" sync, but we cannot do that without |
| 3104 | ** breaking legacy. |
| 3105 | ** |
| 3106 | ** Options: |
| 3107 | ** --csrf-safe N Set cgi_csrf_safe() to to return N |
| 3108 | ** --nobody Pretend to be user "nobody" |
| 3109 | ** --ssh-sim Pretend to be over an SSH connection |
| 3110 | ** --test Do not do special "sync" processing when operating |
| 3111 | ** over an SSH link |
| 3112 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3113 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -63,11 +63,11 @@ | |
| 63 | #ifdef HAVE_BACKTRACE |
| 64 | # include <execinfo.h> |
| 65 | #endif |
| 66 | |
| 67 | /* |
| 68 | ** Default length of a timeout for serving an HTTP request. Changeable |
| 69 | ** using the "--timeout N" command-line option or via "timeout: N" in the |
| 70 | ** CGI script. |
| 71 | */ |
| 72 | #ifndef FOSSIL_DEFAULT_TIMEOUT |
| 73 | # define FOSSIL_DEFAULT_TIMEOUT 600 /* 10 minutes */ |
| @@ -153,10 +153,11 @@ | |
| 153 | char *nameOfExe; /* Full path of executable. */ |
| 154 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 155 | const char *zPhase; /* Phase of operation, for use by the error log |
| 156 | ** and for deriving $canonical_page TH1 variable */ |
| 157 | int isConst; /* True if the output is unchanging & cacheable */ |
| 158 | int iResultCode; /* Process reply code for commands */ |
| 159 | const char *zVfsName; /* The VFS to use for database connections */ |
| 160 | sqlite3 *db; /* The connection to the databases */ |
| 161 | sqlite3 *dbConfig; /* Separate connection for global_config table */ |
| 162 | char *zAuxSchema; /* Main repository aux-schema */ |
| 163 | int dbIgnoreErrors; /* Ignore database errors if true */ |
| @@ -236,10 +237,17 @@ | |
| 237 | * applicable when using SEE on Windows or Linux. */ |
| 238 | #endif |
| 239 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 240 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 241 | int userUid; /* Integer user id */ |
| 242 | int eAuthMethod; /* How the user authenticated to us */ |
| 243 | # define AUTH_NONE 0 /* Not authenticated */ |
| 244 | # define AUTH_COOKIE 1 /* Authentication by cookie */ |
| 245 | # define AUTH_LOCAL 2 /* Uses loopback */ |
| 246 | # define AUTH_PW 3 /* Authentication by password */ |
| 247 | # define AUTH_ENV 4 /* Authenticated by REMOTE_USER environment var */ |
| 248 | # define AUTH_HTTP 5 /* HTTP Basic Authentication */ |
| 249 | int isRobot; /* True if the client is definitely a robot. False |
| 250 | ** negatives are common for this flag */ |
| 251 | int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be |
| 252 | ** accessed through get_comment_format(). */ |
| 253 | const char *zSockName; /* Name of the unix-domain socket file */ |
| @@ -823,11 +831,11 @@ | |
| 831 | fossil_exit(1); |
| 832 | }else{ |
| 833 | const char *zChdir = find_option("chdir",0,1); |
| 834 | g.isHTTP = 0; |
| 835 | g.rcvid = 0; |
| 836 | g.fQuiet = find_option("quiet", "q", 0)!=0; |
| 837 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 838 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 839 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 840 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 841 | g.fCgiTrace = find_option("cgitrace", 0, 0)!=0; |
| @@ -1009,11 +1017,11 @@ | |
| 1017 | if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ |
| 1018 | Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags); |
| 1019 | } |
| 1020 | } |
| 1021 | #endif |
| 1022 | fossil_exit(g.iResultCode); |
| 1023 | /*NOT_REACHED*/ |
| 1024 | return 0; |
| 1025 | } |
| 1026 | |
| 1027 | /* |
| @@ -1183,11 +1191,11 @@ | |
| 1191 | ** list and this function immediately returns. The effect is to treat |
| 1192 | ** all arguments after "--" as non-flags (conventionally used to |
| 1193 | ** enable passing-in of filenames which start with a dash). |
| 1194 | ** |
| 1195 | ** This function must normally only be called one time per app |
| 1196 | ** invocation. The exception is commands which process their |
| 1197 | ** arguments, call this to confirm that there are no extraneous flags, |
| 1198 | ** then modify the arguments list for forwarding to another |
| 1199 | ** (sub)command (which itself will call this to confirm its own |
| 1200 | ** arguments). |
| 1201 | */ |
| @@ -1395,11 +1403,11 @@ | |
| 1403 | style_finish_page(); |
| 1404 | } |
| 1405 | |
| 1406 | |
| 1407 | /* |
| 1408 | ** Set the g.zBaseURL value to the full URL for the top level of |
| 1409 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 1410 | ** leading "http://" and the host and port. |
| 1411 | ** |
| 1412 | ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME |
| 1413 | ** environment variables. However, if zAltBase is not NULL then it |
| @@ -2869,11 +2877,11 @@ | |
| 2877 | ** then the server redirects (HTTP code 302) to the URL of --notfound. |
| 2878 | ** When REPOSITORY is a directory, the pathname must contain only |
| 2879 | ** alphanumerics, "_", "/", "-" and "." and no "-" may occur after a "/" |
| 2880 | ** and every "." must be surrounded on both sides by alphanumerics or else |
| 2881 | ** a 404 error is returned. Static content files in the directory are |
| 2882 | ** returned if they match comma-separated GLOB pattern specified by --files |
| 2883 | ** and do not match "*.fossil*" and have a well-known suffix. |
| 2884 | ** |
| 2885 | ** Options: |
| 2886 | ** --acme Deliver files from the ".well-known" subdirectory |
| 2887 | ** --baseurl URL Base URL (useful with reverse proxies) |
| @@ -2881,11 +2889,11 @@ | |
| 2889 | ** fullchain.pem) taken from FILE. |
| 2890 | ** --chroot DIR Use directory for chroot instead of repository path. |
| 2891 | ** --ckout-alias N Treat URIs of the form /doc/N/... as if they were |
| 2892 | ** /doc/ckout/... |
| 2893 | ** --extroot DIR Document root for the /ext extension mechanism |
| 2894 | ** --files GLOB Comma-separated glob patterns for static files to serve |
| 2895 | ** --host NAME DNS Hostname of the server |
| 2896 | ** --https The HTTP request originated from https but has already |
| 2897 | ** been decoded by a reverse proxy. Hence, URLs created |
| 2898 | ** by Fossil should use "https:" rather than "http:". |
| 2899 | ** --in FILE Take input from FILE instead of standard input |
| @@ -3102,11 +3110,11 @@ | |
| 3110 | ** using this command interactively over SSH. A better solution would be |
| 3111 | ** to use a different command for "ssh" sync, but we cannot do that without |
| 3112 | ** breaking legacy. |
| 3113 | ** |
| 3114 | ** Options: |
| 3115 | ** --csrf-safe N Set cgi_csrf_safe() to return N |
| 3116 | ** --nobody Pretend to be user "nobody" |
| 3117 | ** --ssh-sim Pretend to be over an SSH connection |
| 3118 | ** --test Do not do special "sync" processing when operating |
| 3119 | ** over an SSH link |
| 3120 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3121 |
+3
-4
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -946,12 +946,11 @@ | ||
| 946 | 946 | ** T (+|*|-)<tagname> <uuid> ?<value>? |
| 947 | 947 | ** |
| 948 | 948 | ** Create or cancel a tag or property. The tagname is fossil-encoded. |
| 949 | 949 | ** The first character of the name must be either "+" to create a |
| 950 | 950 | ** singleton tag, "*" to create a propagating tag, or "-" to create |
| 951 | - ** anti-tag that undoes a prior "+" or blocks propagation of of | |
| 952 | - ** a "*". | |
| 951 | + ** anti-tag that undoes a prior "+" or blocks propagation of a "*". | |
| 953 | 952 | ** |
| 954 | 953 | ** The tag is applied to <uuid>. If <uuid> is "*" then the tag is |
| 955 | 954 | ** applied to the current manifest. If <value> is provided then |
| 956 | 955 | ** the tag is really a property with the given value. |
| 957 | 956 | ** |
| @@ -2994,11 +2993,11 @@ | ||
| 2994 | 2993 | KVP_STR(1, uuid, pF->zUuid); |
| 2995 | 2994 | KVP_STR(1, perm, pF->zPerm); |
| 2996 | 2995 | KVP_STR(1, rename, pF->zPrior); |
| 2997 | 2996 | blob_append_char(b, '}'); |
| 2998 | 2997 | } |
| 2999 | - /* Special case: model checkins with no F-card as having an empty | |
| 2998 | + /* Special case: model check-ins with no F-card as having an empty | |
| 3000 | 2999 | ** array, rather than no F-cards, to hypothetically simplify |
| 3001 | 3000 | ** handling in JSON queries. */ |
| 3002 | 3001 | blob_append_char(b, ']'); |
| 3003 | 3002 | } |
| 3004 | 3003 | CARD_STR2(G, p->zThreadRoot); |
| @@ -3038,11 +3037,11 @@ | ||
| 3038 | 3037 | blob_append_char(b, '['); |
| 3039 | 3038 | for( i = 0; i < p->nParent; ++i ){ |
| 3040 | 3039 | if( i>0 ) blob_append_char(b, ','); |
| 3041 | 3040 | blob_appendf(b, "%!j", p->azParent[i]); |
| 3042 | 3041 | } |
| 3043 | - /* Special case: model checkins with no P-card as having an empty | |
| 3042 | + /* Special case: model check-ins with no P-card as having an empty | |
| 3044 | 3043 | ** array, as per F-cards. */ |
| 3045 | 3044 | blob_append_char(b, ']'); |
| 3046 | 3045 | } |
| 3047 | 3046 | if( p->nCherrypick ){ |
| 3048 | 3047 | CARD_LETTER(Q); |
| 3049 | 3048 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -946,12 +946,11 @@ | |
| 946 | ** T (+|*|-)<tagname> <uuid> ?<value>? |
| 947 | ** |
| 948 | ** Create or cancel a tag or property. The tagname is fossil-encoded. |
| 949 | ** The first character of the name must be either "+" to create a |
| 950 | ** singleton tag, "*" to create a propagating tag, or "-" to create |
| 951 | ** anti-tag that undoes a prior "+" or blocks propagation of of |
| 952 | ** a "*". |
| 953 | ** |
| 954 | ** The tag is applied to <uuid>. If <uuid> is "*" then the tag is |
| 955 | ** applied to the current manifest. If <value> is provided then |
| 956 | ** the tag is really a property with the given value. |
| 957 | ** |
| @@ -2994,11 +2993,11 @@ | |
| 2994 | KVP_STR(1, uuid, pF->zUuid); |
| 2995 | KVP_STR(1, perm, pF->zPerm); |
| 2996 | KVP_STR(1, rename, pF->zPrior); |
| 2997 | blob_append_char(b, '}'); |
| 2998 | } |
| 2999 | /* Special case: model checkins with no F-card as having an empty |
| 3000 | ** array, rather than no F-cards, to hypothetically simplify |
| 3001 | ** handling in JSON queries. */ |
| 3002 | blob_append_char(b, ']'); |
| 3003 | } |
| 3004 | CARD_STR2(G, p->zThreadRoot); |
| @@ -3038,11 +3037,11 @@ | |
| 3038 | blob_append_char(b, '['); |
| 3039 | for( i = 0; i < p->nParent; ++i ){ |
| 3040 | if( i>0 ) blob_append_char(b, ','); |
| 3041 | blob_appendf(b, "%!j", p->azParent[i]); |
| 3042 | } |
| 3043 | /* Special case: model checkins with no P-card as having an empty |
| 3044 | ** array, as per F-cards. */ |
| 3045 | blob_append_char(b, ']'); |
| 3046 | } |
| 3047 | if( p->nCherrypick ){ |
| 3048 | CARD_LETTER(Q); |
| 3049 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -946,12 +946,11 @@ | |
| 946 | ** T (+|*|-)<tagname> <uuid> ?<value>? |
| 947 | ** |
| 948 | ** Create or cancel a tag or property. The tagname is fossil-encoded. |
| 949 | ** The first character of the name must be either "+" to create a |
| 950 | ** singleton tag, "*" to create a propagating tag, or "-" to create |
| 951 | ** anti-tag that undoes a prior "+" or blocks propagation of a "*". |
| 952 | ** |
| 953 | ** The tag is applied to <uuid>. If <uuid> is "*" then the tag is |
| 954 | ** applied to the current manifest. If <value> is provided then |
| 955 | ** the tag is really a property with the given value. |
| 956 | ** |
| @@ -2994,11 +2993,11 @@ | |
| 2993 | KVP_STR(1, uuid, pF->zUuid); |
| 2994 | KVP_STR(1, perm, pF->zPerm); |
| 2995 | KVP_STR(1, rename, pF->zPrior); |
| 2996 | blob_append_char(b, '}'); |
| 2997 | } |
| 2998 | /* Special case: model check-ins with no F-card as having an empty |
| 2999 | ** array, rather than no F-cards, to hypothetically simplify |
| 3000 | ** handling in JSON queries. */ |
| 3001 | blob_append_char(b, ']'); |
| 3002 | } |
| 3003 | CARD_STR2(G, p->zThreadRoot); |
| @@ -3038,11 +3037,11 @@ | |
| 3037 | blob_append_char(b, '['); |
| 3038 | for( i = 0; i < p->nParent; ++i ){ |
| 3039 | if( i>0 ) blob_append_char(b, ','); |
| 3040 | blob_appendf(b, "%!j", p->azParent[i]); |
| 3041 | } |
| 3042 | /* Special case: model check-ins with no P-card as having an empty |
| 3043 | ** array, as per F-cards. */ |
| 3044 | blob_append_char(b, ']'); |
| 3045 | } |
| 3046 | if( p->nCherrypick ){ |
| 3047 | CARD_LETTER(Q); |
| 3048 |
+4
-4
| --- src/markdown.c | ||
| +++ src/markdown.c | ||
| @@ -530,12 +530,12 @@ | ||
| 530 | 530 | } |
| 531 | 531 | } |
| 532 | 532 | |
| 533 | 533 | /* |
| 534 | 534 | ** data[*pI] should be a "`" character that introduces a code-span. |
| 535 | -** The code-span boundry mark can be any number of one or more "`" | |
| 536 | -** characters. We do not know the size of the boundry marker, only | |
| 535 | +** The code-span boundary mark can be any number of one or more "`" | |
| 536 | +** characters. We do not know the size of the boundary marker, only | |
| 537 | 537 | ** that there is at least one "`" at data[*pI]. |
| 538 | 538 | ** |
| 539 | 539 | ** This routine increases *pI to move it past the code-span, including |
| 540 | 540 | ** the closing boundary mark. Or, if the code-span is unterminated, |
| 541 | 541 | ** this routine moves *pI past the opening boundary mark only. |
| @@ -634,11 +634,11 @@ | ||
| 634 | 634 | ** punct alnum (*x yes no |
| 635 | 635 | ** alnum space a* no yes |
| 636 | 636 | ** alnum punct a*( no yes |
| 637 | 637 | ** alnum alnum a*x yes yes |
| 638 | 638 | ** |
| 639 | -** The following routines determine whether a delimitor is left | |
| 639 | +** The following routines determine whether a delimiter is left | |
| 640 | 640 | ** or right flanking. |
| 641 | 641 | */ |
| 642 | 642 | static int left_flanking(char before, char after){ |
| 643 | 643 | if( fossil_isspace(after) ) return 0; |
| 644 | 644 | if( fossil_isalnum(after) ) return 1; |
| @@ -2533,11 +2533,11 @@ | ||
| 2533 | 2533 | |
| 2534 | 2534 | /* failfast if data is too short */ |
| 2535 | 2535 | if( beg+5>=end ) return 0; |
| 2536 | 2536 | i = beg; |
| 2537 | 2537 | |
| 2538 | - /* footnote definition must start at the begining of a line */ | |
| 2538 | + /* footnote definition must start at the beginning of a line */ | |
| 2539 | 2539 | if( data[i]!='[' ) return 0; |
| 2540 | 2540 | i++; |
| 2541 | 2541 | if( data[i]!='^' ) return 0; |
| 2542 | 2542 | id_offset = ++i; |
| 2543 | 2543 | |
| 2544 | 2544 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -530,12 +530,12 @@ | |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | /* |
| 534 | ** data[*pI] should be a "`" character that introduces a code-span. |
| 535 | ** The code-span boundry mark can be any number of one or more "`" |
| 536 | ** characters. We do not know the size of the boundry marker, only |
| 537 | ** that there is at least one "`" at data[*pI]. |
| 538 | ** |
| 539 | ** This routine increases *pI to move it past the code-span, including |
| 540 | ** the closing boundary mark. Or, if the code-span is unterminated, |
| 541 | ** this routine moves *pI past the opening boundary mark only. |
| @@ -634,11 +634,11 @@ | |
| 634 | ** punct alnum (*x yes no |
| 635 | ** alnum space a* no yes |
| 636 | ** alnum punct a*( no yes |
| 637 | ** alnum alnum a*x yes yes |
| 638 | ** |
| 639 | ** The following routines determine whether a delimitor is left |
| 640 | ** or right flanking. |
| 641 | */ |
| 642 | static int left_flanking(char before, char after){ |
| 643 | if( fossil_isspace(after) ) return 0; |
| 644 | if( fossil_isalnum(after) ) return 1; |
| @@ -2533,11 +2533,11 @@ | |
| 2533 | |
| 2534 | /* failfast if data is too short */ |
| 2535 | if( beg+5>=end ) return 0; |
| 2536 | i = beg; |
| 2537 | |
| 2538 | /* footnote definition must start at the begining of a line */ |
| 2539 | if( data[i]!='[' ) return 0; |
| 2540 | i++; |
| 2541 | if( data[i]!='^' ) return 0; |
| 2542 | id_offset = ++i; |
| 2543 | |
| 2544 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -530,12 +530,12 @@ | |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | /* |
| 534 | ** data[*pI] should be a "`" character that introduces a code-span. |
| 535 | ** The code-span boundary mark can be any number of one or more "`" |
| 536 | ** characters. We do not know the size of the boundary marker, only |
| 537 | ** that there is at least one "`" at data[*pI]. |
| 538 | ** |
| 539 | ** This routine increases *pI to move it past the code-span, including |
| 540 | ** the closing boundary mark. Or, if the code-span is unterminated, |
| 541 | ** this routine moves *pI past the opening boundary mark only. |
| @@ -634,11 +634,11 @@ | |
| 634 | ** punct alnum (*x yes no |
| 635 | ** alnum space a* no yes |
| 636 | ** alnum punct a*( no yes |
| 637 | ** alnum alnum a*x yes yes |
| 638 | ** |
| 639 | ** The following routines determine whether a delimiter is left |
| 640 | ** or right flanking. |
| 641 | */ |
| 642 | static int left_flanking(char before, char after){ |
| 643 | if( fossil_isspace(after) ) return 0; |
| 644 | if( fossil_isalnum(after) ) return 1; |
| @@ -2533,11 +2533,11 @@ | |
| 2533 | |
| 2534 | /* failfast if data is too short */ |
| 2535 | if( beg+5>=end ) return 0; |
| 2536 | i = beg; |
| 2537 | |
| 2538 | /* footnote definition must start at the beginning of a line */ |
| 2539 | if( data[i]!='[' ) return 0; |
| 2540 | i++; |
| 2541 | if( data[i]!='^' ) return 0; |
| 2542 | id_offset = ++i; |
| 2543 | |
| 2544 |
+42
-1
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -216,20 +216,61 @@ | ||
| 216 | 216 | struct Blob *text, |
| 217 | 217 | int level, |
| 218 | 218 | void *opaque |
| 219 | 219 | ){ |
| 220 | 220 | struct Blob *title = ((MarkdownToHtml*)opaque)->output_title; |
| 221 | + char *z = 0; | |
| 222 | + int i,j; | |
| 221 | 223 | /* The first header at the beginning of a text is considered as |
| 222 | 224 | * a title and not output. */ |
| 223 | 225 | if( blob_size(ob)<=PROLOG_SIZE && title!=0 && blob_size(title)==0 ){ |
| 224 | 226 | blob_appendb(title, text); |
| 225 | 227 | return; |
| 226 | 228 | } |
| 227 | 229 | INTER_BLOCK(ob); |
| 228 | - blob_appendf(ob, "<h%d>", level); | |
| 230 | + z = fossil_strdup(blob_buffer(text)); | |
| 231 | + if( z==0 ){ | |
| 232 | + j = 0; | |
| 233 | + }else{ | |
| 234 | + /* | |
| 235 | + ** The GitHub "slugify" algorithm converts the text of a markdown header | |
| 236 | + ** into a ID for that header. The algorithm is: | |
| 237 | + ** | |
| 238 | + ** 1. ASCII alphanumerics -> convert to lower case | |
| 239 | + ** 2. Spaces, hyphens, underscores -> convert to '-' | |
| 240 | + ** 3. Non-ASCII -> preserve as-is | |
| 241 | + ** 4. Other punctuation -> remove | |
| 242 | + ** 5. Multiple consecutive dashes -> collapse to one | |
| 243 | + ** 6. Leading and trailing dashes -> remove | |
| 244 | + ** 7. Markup <...> and &...; -> remove | |
| 245 | + ** | |
| 246 | + ** This implementation does the conversion in-place. | |
| 247 | + */ | |
| 248 | + for(i=j=0; z[i]; i++){ | |
| 249 | + if( fossil_isalnum(z[i]) ){ | |
| 250 | + z[j++] = fossil_tolower(z[i]); | |
| 251 | + }else if( fossil_isspace(z[i]) || z[i]=='-' || z[i]=='_' ){ | |
| 252 | + if( j>0 && z[j-1]!='-' ) z[j++] = '-'; | |
| 253 | + }else if( z[i]=='<' ){ | |
| 254 | + do{ i++; }while( z[i]!=0 && z[i]!='>' ); | |
| 255 | + }else if( z[i]=='&' ){ | |
| 256 | + do{ i++; }while( z[i]!=0 && z[i]!=';' ); | |
| 257 | + }else if( (z[i]&0x80)!=0 ){ | |
| 258 | + z[j++] = z[i]; | |
| 259 | + } | |
| 260 | + } | |
| 261 | + if( j>0 && z[j-1]=='-' ) j--; | |
| 262 | + z[j] = 0; | |
| 263 | + } | |
| 264 | + if( j>0 ){ | |
| 265 | + blob_appendf(ob, "<h%d id=\"%s\">", level, z); | |
| 266 | + }else{ | |
| 267 | + blob_appendf(ob, "<h%d>", level); | |
| 268 | + } | |
| 229 | 269 | blob_appendb(ob, text); |
| 230 | 270 | blob_appendf(ob, "</h%d>", level); |
| 271 | + fossil_free(z); | |
| 231 | 272 | } |
| 232 | 273 | |
| 233 | 274 | static void html_hrule(struct Blob *ob, void *opaque){ |
| 234 | 275 | INTER_BLOCK(ob); |
| 235 | 276 | blob_append_literal(ob, "<hr>\n"); |
| 236 | 277 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -216,20 +216,61 @@ | |
| 216 | struct Blob *text, |
| 217 | int level, |
| 218 | void *opaque |
| 219 | ){ |
| 220 | struct Blob *title = ((MarkdownToHtml*)opaque)->output_title; |
| 221 | /* The first header at the beginning of a text is considered as |
| 222 | * a title and not output. */ |
| 223 | if( blob_size(ob)<=PROLOG_SIZE && title!=0 && blob_size(title)==0 ){ |
| 224 | blob_appendb(title, text); |
| 225 | return; |
| 226 | } |
| 227 | INTER_BLOCK(ob); |
| 228 | blob_appendf(ob, "<h%d>", level); |
| 229 | blob_appendb(ob, text); |
| 230 | blob_appendf(ob, "</h%d>", level); |
| 231 | } |
| 232 | |
| 233 | static void html_hrule(struct Blob *ob, void *opaque){ |
| 234 | INTER_BLOCK(ob); |
| 235 | blob_append_literal(ob, "<hr>\n"); |
| 236 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -216,20 +216,61 @@ | |
| 216 | struct Blob *text, |
| 217 | int level, |
| 218 | void *opaque |
| 219 | ){ |
| 220 | struct Blob *title = ((MarkdownToHtml*)opaque)->output_title; |
| 221 | char *z = 0; |
| 222 | int i,j; |
| 223 | /* The first header at the beginning of a text is considered as |
| 224 | * a title and not output. */ |
| 225 | if( blob_size(ob)<=PROLOG_SIZE && title!=0 && blob_size(title)==0 ){ |
| 226 | blob_appendb(title, text); |
| 227 | return; |
| 228 | } |
| 229 | INTER_BLOCK(ob); |
| 230 | z = fossil_strdup(blob_buffer(text)); |
| 231 | if( z==0 ){ |
| 232 | j = 0; |
| 233 | }else{ |
| 234 | /* |
| 235 | ** The GitHub "slugify" algorithm converts the text of a markdown header |
| 236 | ** into a ID for that header. The algorithm is: |
| 237 | ** |
| 238 | ** 1. ASCII alphanumerics -> convert to lower case |
| 239 | ** 2. Spaces, hyphens, underscores -> convert to '-' |
| 240 | ** 3. Non-ASCII -> preserve as-is |
| 241 | ** 4. Other punctuation -> remove |
| 242 | ** 5. Multiple consecutive dashes -> collapse to one |
| 243 | ** 6. Leading and trailing dashes -> remove |
| 244 | ** 7. Markup <...> and &...; -> remove |
| 245 | ** |
| 246 | ** This implementation does the conversion in-place. |
| 247 | */ |
| 248 | for(i=j=0; z[i]; i++){ |
| 249 | if( fossil_isalnum(z[i]) ){ |
| 250 | z[j++] = fossil_tolower(z[i]); |
| 251 | }else if( fossil_isspace(z[i]) || z[i]=='-' || z[i]=='_' ){ |
| 252 | if( j>0 && z[j-1]!='-' ) z[j++] = '-'; |
| 253 | }else if( z[i]=='<' ){ |
| 254 | do{ i++; }while( z[i]!=0 && z[i]!='>' ); |
| 255 | }else if( z[i]=='&' ){ |
| 256 | do{ i++; }while( z[i]!=0 && z[i]!=';' ); |
| 257 | }else if( (z[i]&0x80)!=0 ){ |
| 258 | z[j++] = z[i]; |
| 259 | } |
| 260 | } |
| 261 | if( j>0 && z[j-1]=='-' ) j--; |
| 262 | z[j] = 0; |
| 263 | } |
| 264 | if( j>0 ){ |
| 265 | blob_appendf(ob, "<h%d id=\"%s\">", level, z); |
| 266 | }else{ |
| 267 | blob_appendf(ob, "<h%d>", level); |
| 268 | } |
| 269 | blob_appendb(ob, text); |
| 270 | blob_appendf(ob, "</h%d>", level); |
| 271 | fossil_free(z); |
| 272 | } |
| 273 | |
| 274 | static void html_hrule(struct Blob *ob, void *opaque){ |
| 275 | INTER_BLOCK(ob); |
| 276 | blob_append_literal(ob, "<hr>\n"); |
| 277 |
+1
-1
| --- src/match.c | ||
| +++ src/match.c | ||
| @@ -265,11 +265,11 @@ | ||
| 265 | 265 | ** checks for integer match against the tag ID which is looked up directly by |
| 266 | 266 | ** this function. For the other modes, the returned SQL expression performs |
| 267 | 267 | ** string comparisons against the tag names, so it is necessary to join against |
| 268 | 268 | ** the tag table to access the "tagname" column. |
| 269 | 269 | ** |
| 270 | -** Each pattern is adjusted to to start with "sym-" and be anchored at end. | |
| 270 | +** Each pattern is adjusted to start with "sym-" and be anchored at end. | |
| 271 | 271 | ** |
| 272 | 272 | ** In MS_REGEXP mode, backslash can be used to protect delimiter characters. |
| 273 | 273 | ** The backslashes are not removed from the regular expression. |
| 274 | 274 | ** |
| 275 | 275 | ** In addition to assembling and returning an SQL expression, this function |
| 276 | 276 |
| --- src/match.c | |
| +++ src/match.c | |
| @@ -265,11 +265,11 @@ | |
| 265 | ** checks for integer match against the tag ID which is looked up directly by |
| 266 | ** this function. For the other modes, the returned SQL expression performs |
| 267 | ** string comparisons against the tag names, so it is necessary to join against |
| 268 | ** the tag table to access the "tagname" column. |
| 269 | ** |
| 270 | ** Each pattern is adjusted to to start with "sym-" and be anchored at end. |
| 271 | ** |
| 272 | ** In MS_REGEXP mode, backslash can be used to protect delimiter characters. |
| 273 | ** The backslashes are not removed from the regular expression. |
| 274 | ** |
| 275 | ** In addition to assembling and returning an SQL expression, this function |
| 276 |
| --- src/match.c | |
| +++ src/match.c | |
| @@ -265,11 +265,11 @@ | |
| 265 | ** checks for integer match against the tag ID which is looked up directly by |
| 266 | ** this function. For the other modes, the returned SQL expression performs |
| 267 | ** string comparisons against the tag names, so it is necessary to join against |
| 268 | ** the tag table to access the "tagname" column. |
| 269 | ** |
| 270 | ** Each pattern is adjusted to start with "sym-" and be anchored at end. |
| 271 | ** |
| 272 | ** In MS_REGEXP mode, backslash can be used to protect delimiter characters. |
| 273 | ** The backslashes are not removed from the regular expression. |
| 274 | ** |
| 275 | ** In addition to assembling and returning an SQL expression, this function |
| 276 |
+3
-3
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -794,11 +794,11 @@ | ||
| 794 | 794 | char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ |
| 795 | 795 | const char *zVersion; /* The VERSION argument */ |
| 796 | 796 | int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ |
| 797 | 797 | int nMerge = 0; /* Number of prior merges processed */ |
| 798 | 798 | int useUndo = 1; /* True to record changes in the undo log */ |
| 799 | - Stmt q; /* SQL statment used for merge processing */ | |
| 799 | + Stmt q; /* SQL statement used for merge processing */ | |
| 800 | 800 | |
| 801 | 801 | |
| 802 | 802 | /* Notation: |
| 803 | 803 | ** |
| 804 | 804 | ** V The current check-out |
| @@ -1044,11 +1044,11 @@ | ||
| 1044 | 1044 | } |
| 1045 | 1045 | if( showVfileFlag ) debug_show_vfile(); |
| 1046 | 1046 | |
| 1047 | 1047 | /* |
| 1048 | 1048 | ** The vfile.pathname field is used to match files against each other. The |
| 1049 | - ** FV table contains one row for each each unique filename in | |
| 1049 | + ** FV table contains one row for each unique filename in | |
| 1050 | 1050 | ** in the current check-out, the pivot, and the version being merged. |
| 1051 | 1051 | */ |
| 1052 | 1052 | db_multi_exec( |
| 1053 | 1053 | "DROP TABLE IF EXISTS fv;" |
| 1054 | 1054 | "CREATE TEMP TABLE fv(\n" |
| @@ -1564,11 +1564,11 @@ | ||
| 1564 | 1564 | zFullName = mprintf("%s%s", g.zLocalRoot, zName); |
| 1565 | 1565 | if( file_isfile_or_link(zFullName) |
| 1566 | 1566 | && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){ |
| 1567 | 1567 | /* Name of backup file with Original content */ |
| 1568 | 1568 | char *zOrig = file_newname(zFullName, "original", 1); |
| 1569 | - /* Backup previously unanaged file before to be overwritten */ | |
| 1569 | + /* Backup previously unmanaged file before being overwritten */ | |
| 1570 | 1570 | file_copy(zFullName, zOrig); |
| 1571 | 1571 | fossil_free(zOrig); |
| 1572 | 1572 | fossil_print("ADDED %s (overwrites an unmanaged file)", zName); |
| 1573 | 1573 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 1574 | 1574 | fossil_print("\n"); |
| 1575 | 1575 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -794,11 +794,11 @@ | |
| 794 | char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ |
| 795 | const char *zVersion; /* The VERSION argument */ |
| 796 | int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ |
| 797 | int nMerge = 0; /* Number of prior merges processed */ |
| 798 | int useUndo = 1; /* True to record changes in the undo log */ |
| 799 | Stmt q; /* SQL statment used for merge processing */ |
| 800 | |
| 801 | |
| 802 | /* Notation: |
| 803 | ** |
| 804 | ** V The current check-out |
| @@ -1044,11 +1044,11 @@ | |
| 1044 | } |
| 1045 | if( showVfileFlag ) debug_show_vfile(); |
| 1046 | |
| 1047 | /* |
| 1048 | ** The vfile.pathname field is used to match files against each other. The |
| 1049 | ** FV table contains one row for each each unique filename in |
| 1050 | ** in the current check-out, the pivot, and the version being merged. |
| 1051 | */ |
| 1052 | db_multi_exec( |
| 1053 | "DROP TABLE IF EXISTS fv;" |
| 1054 | "CREATE TEMP TABLE fv(\n" |
| @@ -1564,11 +1564,11 @@ | |
| 1564 | zFullName = mprintf("%s%s", g.zLocalRoot, zName); |
| 1565 | if( file_isfile_or_link(zFullName) |
| 1566 | && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){ |
| 1567 | /* Name of backup file with Original content */ |
| 1568 | char *zOrig = file_newname(zFullName, "original", 1); |
| 1569 | /* Backup previously unanaged file before to be overwritten */ |
| 1570 | file_copy(zFullName, zOrig); |
| 1571 | fossil_free(zOrig); |
| 1572 | fossil_print("ADDED %s (overwrites an unmanaged file)", zName); |
| 1573 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 1574 | fossil_print("\n"); |
| 1575 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -794,11 +794,11 @@ | |
| 794 | char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ |
| 795 | const char *zVersion; /* The VERSION argument */ |
| 796 | int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ |
| 797 | int nMerge = 0; /* Number of prior merges processed */ |
| 798 | int useUndo = 1; /* True to record changes in the undo log */ |
| 799 | Stmt q; /* SQL statement used for merge processing */ |
| 800 | |
| 801 | |
| 802 | /* Notation: |
| 803 | ** |
| 804 | ** V The current check-out |
| @@ -1044,11 +1044,11 @@ | |
| 1044 | } |
| 1045 | if( showVfileFlag ) debug_show_vfile(); |
| 1046 | |
| 1047 | /* |
| 1048 | ** The vfile.pathname field is used to match files against each other. The |
| 1049 | ** FV table contains one row for each unique filename in |
| 1050 | ** in the current check-out, the pivot, and the version being merged. |
| 1051 | */ |
| 1052 | db_multi_exec( |
| 1053 | "DROP TABLE IF EXISTS fv;" |
| 1054 | "CREATE TEMP TABLE fv(\n" |
| @@ -1564,11 +1564,11 @@ | |
| 1564 | zFullName = mprintf("%s%s", g.zLocalRoot, zName); |
| 1565 | if( file_isfile_or_link(zFullName) |
| 1566 | && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){ |
| 1567 | /* Name of backup file with Original content */ |
| 1568 | char *zOrig = file_newname(zFullName, "original", 1); |
| 1569 | /* Backup previously unmanaged file before being overwritten */ |
| 1570 | file_copy(zFullName, zOrig); |
| 1571 | fossil_free(zOrig); |
| 1572 | fossil_print("ADDED %s (overwrites an unmanaged file)", zName); |
| 1573 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 1574 | fossil_print("\n"); |
| 1575 |
+2
-2
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -175,11 +175,11 @@ | ||
| 175 | 175 | /* These are generic methods for MergeBuilder. They just output debugging |
| 176 | 176 | ** information. But some of them are useful as base methods for other useful |
| 177 | 177 | ** implementations of MergeBuilder. |
| 178 | 178 | */ |
| 179 | 179 | |
| 180 | -/* xStart() and xEnd() are called to generate header and fotter information | |
| 180 | +/* xStart() and xEnd() are called to generate header and footer information | |
| 181 | 181 | ** in the output. This is a no-op in the generic implementation. |
| 182 | 182 | */ |
| 183 | 183 | static void dbgStartEnd(MergeBuilder *p){ (void)p; } |
| 184 | 184 | |
| 185 | 185 | /* The next N lines of PIVOT are unchanged in both V1 and V2 |
| @@ -716,11 +716,11 @@ | ||
| 716 | 716 | */ |
| 717 | 717 | static int skip_conflict( |
| 718 | 718 | int *aC, /* Array of integer triples describing the edit */ |
| 719 | 719 | int i, /* Index in aC[] of current location */ |
| 720 | 720 | int sz, /* Lines of A that have been skipped */ |
| 721 | - unsigned int *pLn /* OUT: Lines of B to skip to keep aligment with A */ | |
| 721 | + unsigned int *pLn /* OUT: Lines of B to skip to keep alignment with A */ | |
| 722 | 722 | ){ |
| 723 | 723 | *pLn = 0; |
| 724 | 724 | while( sz>0 ){ |
| 725 | 725 | if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; |
| 726 | 726 | if( aC[i]>=sz ){ |
| 727 | 727 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -175,11 +175,11 @@ | |
| 175 | /* These are generic methods for MergeBuilder. They just output debugging |
| 176 | ** information. But some of them are useful as base methods for other useful |
| 177 | ** implementations of MergeBuilder. |
| 178 | */ |
| 179 | |
| 180 | /* xStart() and xEnd() are called to generate header and fotter information |
| 181 | ** in the output. This is a no-op in the generic implementation. |
| 182 | */ |
| 183 | static void dbgStartEnd(MergeBuilder *p){ (void)p; } |
| 184 | |
| 185 | /* The next N lines of PIVOT are unchanged in both V1 and V2 |
| @@ -716,11 +716,11 @@ | |
| 716 | */ |
| 717 | static int skip_conflict( |
| 718 | int *aC, /* Array of integer triples describing the edit */ |
| 719 | int i, /* Index in aC[] of current location */ |
| 720 | int sz, /* Lines of A that have been skipped */ |
| 721 | unsigned int *pLn /* OUT: Lines of B to skip to keep aligment with A */ |
| 722 | ){ |
| 723 | *pLn = 0; |
| 724 | while( sz>0 ){ |
| 725 | if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; |
| 726 | if( aC[i]>=sz ){ |
| 727 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -175,11 +175,11 @@ | |
| 175 | /* These are generic methods for MergeBuilder. They just output debugging |
| 176 | ** information. But some of them are useful as base methods for other useful |
| 177 | ** implementations of MergeBuilder. |
| 178 | */ |
| 179 | |
| 180 | /* xStart() and xEnd() are called to generate header and footer information |
| 181 | ** in the output. This is a no-op in the generic implementation. |
| 182 | */ |
| 183 | static void dbgStartEnd(MergeBuilder *p){ (void)p; } |
| 184 | |
| 185 | /* The next N lines of PIVOT are unchanged in both V1 and V2 |
| @@ -716,11 +716,11 @@ | |
| 716 | */ |
| 717 | static int skip_conflict( |
| 718 | int *aC, /* Array of integer triples describing the edit */ |
| 719 | int i, /* Index in aC[] of current location */ |
| 720 | int sz, /* Lines of A that have been skipped */ |
| 721 | unsigned int *pLn /* OUT: Lines of B to skip to keep alignment with A */ |
| 722 | ){ |
| 723 | *pLn = 0; |
| 724 | while( sz>0 ){ |
| 725 | if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; |
| 726 | if( aC[i]>=sz ){ |
| 727 |
+17
-5
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -1131,10 +1131,11 @@ | ||
| 1131 | 1131 | #if INTERFACE |
| 1132 | 1132 | #define WHATIS_VERBOSE 0x01 /* Extra output */ |
| 1133 | 1133 | #define WHATIS_BRIEF 0x02 /* Omit unnecessary output */ |
| 1134 | 1134 | #define WHATIS_REPO 0x04 /* Show repository name */ |
| 1135 | 1135 | #define WHATIS_OMIT_UNK 0x08 /* Do not show "unknown" lines */ |
| 1136 | +#define WHATIS_HASHONLY 0x10 /* Show only the hash */ | |
| 1136 | 1137 | #endif |
| 1137 | 1138 | |
| 1138 | 1139 | /* |
| 1139 | 1140 | ** Generate a description of artifact "rid" |
| 1140 | 1141 | */ |
| @@ -1148,11 +1149,13 @@ | ||
| 1148 | 1149 | " FROM blob, rcvfrom" |
| 1149 | 1150 | " WHERE rid=%d" |
| 1150 | 1151 | " AND rcvfrom.rcvid=blob.rcvid", |
| 1151 | 1152 | rid); |
| 1152 | 1153 | if( db_step(&q)==SQLITE_ROW ){ |
| 1153 | - if( flags & WHATIS_VERBOSE ){ | |
| 1154 | + if( flags & WHATIS_HASHONLY ){ | |
| 1155 | + fossil_print("%s\n", db_column_text(&q,0)); | |
| 1156 | + }else if( flags & WHATIS_VERBOSE ){ | |
| 1154 | 1157 | fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); |
| 1155 | 1158 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 1156 | 1159 | fossil_print("received: %s from %s\n", |
| 1157 | 1160 | db_column_text(&q, 2), |
| 1158 | 1161 | db_column_text(&q, 3)); |
| @@ -1160,10 +1163,11 @@ | ||
| 1160 | 1163 | fossil_print("artifact: %s\n", db_column_text(&q,0)); |
| 1161 | 1164 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 1162 | 1165 | } |
| 1163 | 1166 | } |
| 1164 | 1167 | db_finalize(&q); |
| 1168 | + if( flags & WHATIS_HASHONLY ) return; | |
| 1165 | 1169 | |
| 1166 | 1170 | /* Report any symbolic tags on this artifact */ |
| 1167 | 1171 | db_prepare(&q, |
| 1168 | 1172 | "SELECT substr(tagname,5)" |
| 1169 | 1173 | " FROM tag JOIN tagxref ON tag.tagid=tagxref.tagid" |
| @@ -1330,11 +1334,11 @@ | ||
| 1330 | 1334 | if( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt); |
| 1331 | 1335 | whatis_rid(db_column_int(&q, 0), mFlags); |
| 1332 | 1336 | } |
| 1333 | 1337 | db_finalize(&q); |
| 1334 | 1338 | }else if( rid==0 ){ |
| 1335 | - if( (mFlags & WHATIS_OMIT_UNK)==0 ){ | |
| 1339 | + if( (mFlags & (WHATIS_OMIT_UNK|WHATIS_HASHONLY))==0 ){ | |
| 1336 | 1340 | /* 0123456789 12 */ |
| 1337 | 1341 | if( zFileName ){ |
| 1338 | 1342 | fossil_print("%-12s%s\n", "name:", zFileName); |
| 1339 | 1343 | } |
| 1340 | 1344 | fossil_print("unknown: %s\n", zName); |
| @@ -1344,11 +1348,13 @@ | ||
| 1344 | 1348 | fossil_print("\nrepository: %s\n", g.zRepositoryName); |
| 1345 | 1349 | } |
| 1346 | 1350 | if( zFileName ){ |
| 1347 | 1351 | zName = zFileName; |
| 1348 | 1352 | } |
| 1349 | - fossil_print("%-12s%s\n", "name:", zName); | |
| 1353 | + if( (mFlags & WHATIS_HASHONLY)==0 ){ | |
| 1354 | + fossil_print("%-12s%s\n", "name:", zName); | |
| 1355 | + } | |
| 1350 | 1356 | whatis_rid(rid, mFlags); |
| 1351 | 1357 | } |
| 1352 | 1358 | } |
| 1353 | 1359 | |
| 1354 | 1360 | /* |
| @@ -1361,11 +1367,14 @@ | ||
| 1361 | 1367 | ** plays. |
| 1362 | 1368 | ** |
| 1363 | 1369 | ** Options: |
| 1364 | 1370 | ** -f|--file Find artifacts with the same hash as file NAME. |
| 1365 | 1371 | ** If NAME is "-", read content from standard input. |
| 1372 | +** -h|--hash Show only the hash of matching artifacts. | |
| 1366 | 1373 | ** -q|--quiet Show nothing if NAME is not found |
| 1374 | +** -R REPO_FILE Specifies the repository db to use. Default is | |
| 1375 | +** the current check-out's repository. | |
| 1367 | 1376 | ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't', |
| 1368 | 1377 | ** 'w', 'g', or 'e') |
| 1369 | 1378 | ** -v|--verbose Provide extra information (such as the RID) |
| 1370 | 1379 | */ |
| 1371 | 1380 | void whatis_cmd(void){ |
| @@ -1375,11 +1384,14 @@ | ||
| 1375 | 1384 | const char *zType = 0; |
| 1376 | 1385 | db_find_and_open_repository(0,0); |
| 1377 | 1386 | if( find_option("verbose","v",0)!=0 ){ |
| 1378 | 1387 | mFlags |= WHATIS_VERBOSE; |
| 1379 | 1388 | } |
| 1380 | - if( find_option("quiet","q",0)!=0 ){ | |
| 1389 | + if( find_option("hash","h",0)!=0 ){ | |
| 1390 | + mFlags |= WHATIS_HASHONLY; | |
| 1391 | + } | |
| 1392 | + if( g.fQuiet ){ | |
| 1381 | 1393 | mFlags |= WHATIS_OMIT_UNK | WHATIS_REPO; |
| 1382 | 1394 | } |
| 1383 | 1395 | fileFlag = find_option("file","f",0)!=0; |
| 1384 | 1396 | zType = find_option("type",0,1); |
| 1385 | 1397 | |
| @@ -2215,11 +2227,11 @@ | ||
| 2215 | 2227 | ** --delta Show all delta-phantoms. A delta-phantom is a |
| 2216 | 2228 | ** artifact for which there is a delta but the delta |
| 2217 | 2229 | ** source is a phantom. |
| 2218 | 2230 | ** --list Just list the phantoms. Do not try to describe them. |
| 2219 | 2231 | */ |
| 2220 | -void test_phatoms_cmd(void){ | |
| 2232 | +void test_phantoms_cmd(void){ | |
| 2221 | 2233 | int bDelta; |
| 2222 | 2234 | int bList; |
| 2223 | 2235 | int bCount; |
| 2224 | 2236 | unsigned nPhantom = 0; |
| 2225 | 2237 | unsigned nDeltaPhantom = 0; |
| 2226 | 2238 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -1131,10 +1131,11 @@ | |
| 1131 | #if INTERFACE |
| 1132 | #define WHATIS_VERBOSE 0x01 /* Extra output */ |
| 1133 | #define WHATIS_BRIEF 0x02 /* Omit unnecessary output */ |
| 1134 | #define WHATIS_REPO 0x04 /* Show repository name */ |
| 1135 | #define WHATIS_OMIT_UNK 0x08 /* Do not show "unknown" lines */ |
| 1136 | #endif |
| 1137 | |
| 1138 | /* |
| 1139 | ** Generate a description of artifact "rid" |
| 1140 | */ |
| @@ -1148,11 +1149,13 @@ | |
| 1148 | " FROM blob, rcvfrom" |
| 1149 | " WHERE rid=%d" |
| 1150 | " AND rcvfrom.rcvid=blob.rcvid", |
| 1151 | rid); |
| 1152 | if( db_step(&q)==SQLITE_ROW ){ |
| 1153 | if( flags & WHATIS_VERBOSE ){ |
| 1154 | fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); |
| 1155 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 1156 | fossil_print("received: %s from %s\n", |
| 1157 | db_column_text(&q, 2), |
| 1158 | db_column_text(&q, 3)); |
| @@ -1160,10 +1163,11 @@ | |
| 1160 | fossil_print("artifact: %s\n", db_column_text(&q,0)); |
| 1161 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 1162 | } |
| 1163 | } |
| 1164 | db_finalize(&q); |
| 1165 | |
| 1166 | /* Report any symbolic tags on this artifact */ |
| 1167 | db_prepare(&q, |
| 1168 | "SELECT substr(tagname,5)" |
| 1169 | " FROM tag JOIN tagxref ON tag.tagid=tagxref.tagid" |
| @@ -1330,11 +1334,11 @@ | |
| 1330 | if( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt); |
| 1331 | whatis_rid(db_column_int(&q, 0), mFlags); |
| 1332 | } |
| 1333 | db_finalize(&q); |
| 1334 | }else if( rid==0 ){ |
| 1335 | if( (mFlags & WHATIS_OMIT_UNK)==0 ){ |
| 1336 | /* 0123456789 12 */ |
| 1337 | if( zFileName ){ |
| 1338 | fossil_print("%-12s%s\n", "name:", zFileName); |
| 1339 | } |
| 1340 | fossil_print("unknown: %s\n", zName); |
| @@ -1344,11 +1348,13 @@ | |
| 1344 | fossil_print("\nrepository: %s\n", g.zRepositoryName); |
| 1345 | } |
| 1346 | if( zFileName ){ |
| 1347 | zName = zFileName; |
| 1348 | } |
| 1349 | fossil_print("%-12s%s\n", "name:", zName); |
| 1350 | whatis_rid(rid, mFlags); |
| 1351 | } |
| 1352 | } |
| 1353 | |
| 1354 | /* |
| @@ -1361,11 +1367,14 @@ | |
| 1361 | ** plays. |
| 1362 | ** |
| 1363 | ** Options: |
| 1364 | ** -f|--file Find artifacts with the same hash as file NAME. |
| 1365 | ** If NAME is "-", read content from standard input. |
| 1366 | ** -q|--quiet Show nothing if NAME is not found |
| 1367 | ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't', |
| 1368 | ** 'w', 'g', or 'e') |
| 1369 | ** -v|--verbose Provide extra information (such as the RID) |
| 1370 | */ |
| 1371 | void whatis_cmd(void){ |
| @@ -1375,11 +1384,14 @@ | |
| 1375 | const char *zType = 0; |
| 1376 | db_find_and_open_repository(0,0); |
| 1377 | if( find_option("verbose","v",0)!=0 ){ |
| 1378 | mFlags |= WHATIS_VERBOSE; |
| 1379 | } |
| 1380 | if( find_option("quiet","q",0)!=0 ){ |
| 1381 | mFlags |= WHATIS_OMIT_UNK | WHATIS_REPO; |
| 1382 | } |
| 1383 | fileFlag = find_option("file","f",0)!=0; |
| 1384 | zType = find_option("type",0,1); |
| 1385 | |
| @@ -2215,11 +2227,11 @@ | |
| 2215 | ** --delta Show all delta-phantoms. A delta-phantom is a |
| 2216 | ** artifact for which there is a delta but the delta |
| 2217 | ** source is a phantom. |
| 2218 | ** --list Just list the phantoms. Do not try to describe them. |
| 2219 | */ |
| 2220 | void test_phatoms_cmd(void){ |
| 2221 | int bDelta; |
| 2222 | int bList; |
| 2223 | int bCount; |
| 2224 | unsigned nPhantom = 0; |
| 2225 | unsigned nDeltaPhantom = 0; |
| 2226 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -1131,10 +1131,11 @@ | |
| 1131 | #if INTERFACE |
| 1132 | #define WHATIS_VERBOSE 0x01 /* Extra output */ |
| 1133 | #define WHATIS_BRIEF 0x02 /* Omit unnecessary output */ |
| 1134 | #define WHATIS_REPO 0x04 /* Show repository name */ |
| 1135 | #define WHATIS_OMIT_UNK 0x08 /* Do not show "unknown" lines */ |
| 1136 | #define WHATIS_HASHONLY 0x10 /* Show only the hash */ |
| 1137 | #endif |
| 1138 | |
| 1139 | /* |
| 1140 | ** Generate a description of artifact "rid" |
| 1141 | */ |
| @@ -1148,11 +1149,13 @@ | |
| 1149 | " FROM blob, rcvfrom" |
| 1150 | " WHERE rid=%d" |
| 1151 | " AND rcvfrom.rcvid=blob.rcvid", |
| 1152 | rid); |
| 1153 | if( db_step(&q)==SQLITE_ROW ){ |
| 1154 | if( flags & WHATIS_HASHONLY ){ |
| 1155 | fossil_print("%s\n", db_column_text(&q,0)); |
| 1156 | }else if( flags & WHATIS_VERBOSE ){ |
| 1157 | fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); |
| 1158 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 1159 | fossil_print("received: %s from %s\n", |
| 1160 | db_column_text(&q, 2), |
| 1161 | db_column_text(&q, 3)); |
| @@ -1160,10 +1163,11 @@ | |
| 1163 | fossil_print("artifact: %s\n", db_column_text(&q,0)); |
| 1164 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 1165 | } |
| 1166 | } |
| 1167 | db_finalize(&q); |
| 1168 | if( flags & WHATIS_HASHONLY ) return; |
| 1169 | |
| 1170 | /* Report any symbolic tags on this artifact */ |
| 1171 | db_prepare(&q, |
| 1172 | "SELECT substr(tagname,5)" |
| 1173 | " FROM tag JOIN tagxref ON tag.tagid=tagxref.tagid" |
| @@ -1330,11 +1334,11 @@ | |
| 1334 | if( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt); |
| 1335 | whatis_rid(db_column_int(&q, 0), mFlags); |
| 1336 | } |
| 1337 | db_finalize(&q); |
| 1338 | }else if( rid==0 ){ |
| 1339 | if( (mFlags & (WHATIS_OMIT_UNK|WHATIS_HASHONLY))==0 ){ |
| 1340 | /* 0123456789 12 */ |
| 1341 | if( zFileName ){ |
| 1342 | fossil_print("%-12s%s\n", "name:", zFileName); |
| 1343 | } |
| 1344 | fossil_print("unknown: %s\n", zName); |
| @@ -1344,11 +1348,13 @@ | |
| 1348 | fossil_print("\nrepository: %s\n", g.zRepositoryName); |
| 1349 | } |
| 1350 | if( zFileName ){ |
| 1351 | zName = zFileName; |
| 1352 | } |
| 1353 | if( (mFlags & WHATIS_HASHONLY)==0 ){ |
| 1354 | fossil_print("%-12s%s\n", "name:", zName); |
| 1355 | } |
| 1356 | whatis_rid(rid, mFlags); |
| 1357 | } |
| 1358 | } |
| 1359 | |
| 1360 | /* |
| @@ -1361,11 +1367,14 @@ | |
| 1367 | ** plays. |
| 1368 | ** |
| 1369 | ** Options: |
| 1370 | ** -f|--file Find artifacts with the same hash as file NAME. |
| 1371 | ** If NAME is "-", read content from standard input. |
| 1372 | ** -h|--hash Show only the hash of matching artifacts. |
| 1373 | ** -q|--quiet Show nothing if NAME is not found |
| 1374 | ** -R REPO_FILE Specifies the repository db to use. Default is |
| 1375 | ** the current check-out's repository. |
| 1376 | ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't', |
| 1377 | ** 'w', 'g', or 'e') |
| 1378 | ** -v|--verbose Provide extra information (such as the RID) |
| 1379 | */ |
| 1380 | void whatis_cmd(void){ |
| @@ -1375,11 +1384,14 @@ | |
| 1384 | const char *zType = 0; |
| 1385 | db_find_and_open_repository(0,0); |
| 1386 | if( find_option("verbose","v",0)!=0 ){ |
| 1387 | mFlags |= WHATIS_VERBOSE; |
| 1388 | } |
| 1389 | if( find_option("hash","h",0)!=0 ){ |
| 1390 | mFlags |= WHATIS_HASHONLY; |
| 1391 | } |
| 1392 | if( g.fQuiet ){ |
| 1393 | mFlags |= WHATIS_OMIT_UNK | WHATIS_REPO; |
| 1394 | } |
| 1395 | fileFlag = find_option("file","f",0)!=0; |
| 1396 | zType = find_option("type",0,1); |
| 1397 | |
| @@ -2215,11 +2227,11 @@ | |
| 2227 | ** --delta Show all delta-phantoms. A delta-phantom is a |
| 2228 | ** artifact for which there is a delta but the delta |
| 2229 | ** source is a phantom. |
| 2230 | ** --list Just list the phantoms. Do not try to describe them. |
| 2231 | */ |
| 2232 | void test_phantoms_cmd(void){ |
| 2233 | int bDelta; |
| 2234 | int bList; |
| 2235 | int bCount; |
| 2236 | unsigned nPhantom = 0; |
| 2237 | unsigned nDeltaPhantom = 0; |
| 2238 |
+1
-1
| --- src/piechart.c | ||
| +++ src/piechart.c | ||
| @@ -26,11 +26,11 @@ | ||
| 26 | 26 | # define M_PI 3.1415926535897932385 |
| 27 | 27 | #endif |
| 28 | 28 | |
| 29 | 29 | /* |
| 30 | 30 | ** Return an RGB color name given HSV values. The HSV values |
| 31 | -** must each be between between 0 and 255. The string | |
| 31 | +** must each be between 0 and 255. The string | |
| 32 | 32 | ** returned is held in a static buffer and is overwritten |
| 33 | 33 | ** on each call. |
| 34 | 34 | */ |
| 35 | 35 | const char *rgbName(unsigned char h, unsigned char s, unsigned char v){ |
| 36 | 36 | static char zColor[8]; |
| 37 | 37 |
| --- src/piechart.c | |
| +++ src/piechart.c | |
| @@ -26,11 +26,11 @@ | |
| 26 | # define M_PI 3.1415926535897932385 |
| 27 | #endif |
| 28 | |
| 29 | /* |
| 30 | ** Return an RGB color name given HSV values. The HSV values |
| 31 | ** must each be between between 0 and 255. The string |
| 32 | ** returned is held in a static buffer and is overwritten |
| 33 | ** on each call. |
| 34 | */ |
| 35 | const char *rgbName(unsigned char h, unsigned char s, unsigned char v){ |
| 36 | static char zColor[8]; |
| 37 |
| --- src/piechart.c | |
| +++ src/piechart.c | |
| @@ -26,11 +26,11 @@ | |
| 26 | # define M_PI 3.1415926535897932385 |
| 27 | #endif |
| 28 | |
| 29 | /* |
| 30 | ** Return an RGB color name given HSV values. The HSV values |
| 31 | ** must each be between 0 and 255. The string |
| 32 | ** returned is held in a static buffer and is overwritten |
| 33 | ** on each call. |
| 34 | */ |
| 35 | const char *rgbName(unsigned char h, unsigned char s, unsigned char v){ |
| 36 | static char zColor[8]; |
| 37 |
+7
-7
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -13,11 +13,11 @@ | ||
| 13 | 13 | ** [email protected] |
| 14 | 14 | ** http://www.hwaci.com/drh/ |
| 15 | 15 | ** |
| 16 | 16 | ******************************************************************************* |
| 17 | 17 | ** |
| 18 | -** This file contains implementions of routines for formatting output | |
| 18 | +** This file contains implementations of routines for formatting output | |
| 19 | 19 | ** (ex: mprintf()) and for output to the console. |
| 20 | 20 | */ |
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "printf.h" |
| 23 | 23 | #if defined(_WIN32) |
| @@ -89,11 +89,11 @@ | ||
| 89 | 89 | #define etBLOB 11 /* Blob objects. %b */ |
| 90 | 90 | #define etBLOBSQL 12 /* Blob objects quoted for SQL. %B */ |
| 91 | 91 | #define etSQLESCAPE 13 /* Strings with '\'' doubled. %q */ |
| 92 | 92 | #define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '', |
| 93 | 93 | NULL pointers replaced by SQL NULL. %Q */ |
| 94 | -#define etSQLESCAPE3 15 /* Double '"' characters within an indentifier. %w */ | |
| 94 | +#define etSQLESCAPE3 15 /* Double '"' characters within an identifier. %w */ | |
| 95 | 95 | #define etPOINTER 16 /* The %p conversion */ |
| 96 | 96 | #define etHTMLIZE 17 /* Make text safe for HTML */ |
| 97 | 97 | #define etHTTPIZE 18 /* Make text safe for HTTP. "/" encoded as %2f */ |
| 98 | 98 | #define etURLIZE 19 /* Make text safe for HTTP. "/" not encoded */ |
| 99 | 99 | #define etFOSSILIZE 20 /* The fossil header encoding format. */ |
| @@ -265,12 +265,12 @@ | ||
| 265 | 265 | ** configuration parameters. |
| 266 | 266 | ** |
| 267 | 267 | ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 |
| 268 | 268 | ** flags) and is false for plain "%W". The ! flag indicates that the |
| 269 | 269 | ** formatting is for display of a check-in comment on the timeline. Such |
| 270 | -** comments used to be renderedd differently, but ever since 2020, they | |
| 271 | -** have been rendered identially, so the ! flag does not make any different | |
| 270 | +** comments used to be rendered differently, but ever since 2020, they | |
| 271 | +** have been rendered identically, so the ! flag does not make any different | |
| 272 | 272 | ** in the output any more. |
| 273 | 273 | */ |
| 274 | 274 | int wiki_convert_flags(int altForm2){ |
| 275 | 275 | static int wikiFlags = 0; |
| 276 | 276 | (void)altForm2; |
| @@ -456,11 +456,11 @@ | ||
| 456 | 456 | ** flag_leftjustify TRUE if a '-' is present or if the |
| 457 | 457 | ** field width was negative. |
| 458 | 458 | ** flag_zeropad TRUE if the width began with 0. |
| 459 | 459 | ** flag_long TRUE if the letter 'l' (ell) prefixed |
| 460 | 460 | ** the conversion character. |
| 461 | - ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed | |
| 461 | + ** flag_longlong TRUE if the letters 'll' (ell ell) prefixed | |
| 462 | 462 | ** the conversion character. |
| 463 | 463 | ** flag_blanksign TRUE if a ' ' is present. |
| 464 | 464 | ** width The specified field width. This is |
| 465 | 465 | ** always non-negative. Zero is the default. |
| 466 | 466 | ** precision The specified precision. The default |
| @@ -1188,13 +1188,13 @@ | ||
| 1188 | 1188 | ** |
| 1189 | 1189 | ** The main difference between fossil_fatal() and fossil_panic() is that |
| 1190 | 1190 | ** fossil_panic() makes an entry in the error log whereas fossil_fatal() |
| 1191 | 1191 | ** does not. On POSIX platforms, if there is not an error log, then both |
| 1192 | 1192 | ** routines work similarly with respect to user-visible effects. Hence, |
| 1193 | -** the routines are interchangable for commands and only act differently | |
| 1193 | +** the routines are interchangeable for commands and only act differently | |
| 1194 | 1194 | ** when processing web pages. On the Windows platform, fossil_panic() |
| 1195 | -** also displays a pop-up stating that an error has occured and allowing | |
| 1195 | +** also displays a pop-up stating that an error has occurred and allowing | |
| 1196 | 1196 | ** just-in-time debugging to commence. On all platforms, fossil_panic() |
| 1197 | 1197 | ** ends execution with a SIGABRT signal, bypassing atexit processing. |
| 1198 | 1198 | ** This signal can also produce a core dump on POSIX platforms. |
| 1199 | 1199 | ** |
| 1200 | 1200 | ** Use fossil_fatal() for malformed inputs that should be reported back |
| 1201 | 1201 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -13,11 +13,11 @@ | |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains implementions of routines for formatting output |
| 19 | ** (ex: mprintf()) and for output to the console. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "printf.h" |
| 23 | #if defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | #define etBLOB 11 /* Blob objects. %b */ |
| 90 | #define etBLOBSQL 12 /* Blob objects quoted for SQL. %B */ |
| 91 | #define etSQLESCAPE 13 /* Strings with '\'' doubled. %q */ |
| 92 | #define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '', |
| 93 | NULL pointers replaced by SQL NULL. %Q */ |
| 94 | #define etSQLESCAPE3 15 /* Double '"' characters within an indentifier. %w */ |
| 95 | #define etPOINTER 16 /* The %p conversion */ |
| 96 | #define etHTMLIZE 17 /* Make text safe for HTML */ |
| 97 | #define etHTTPIZE 18 /* Make text safe for HTTP. "/" encoded as %2f */ |
| 98 | #define etURLIZE 19 /* Make text safe for HTTP. "/" not encoded */ |
| 99 | #define etFOSSILIZE 20 /* The fossil header encoding format. */ |
| @@ -265,12 +265,12 @@ | |
| 265 | ** configuration parameters. |
| 266 | ** |
| 267 | ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 |
| 268 | ** flags) and is false for plain "%W". The ! flag indicates that the |
| 269 | ** formatting is for display of a check-in comment on the timeline. Such |
| 270 | ** comments used to be renderedd differently, but ever since 2020, they |
| 271 | ** have been rendered identially, so the ! flag does not make any different |
| 272 | ** in the output any more. |
| 273 | */ |
| 274 | int wiki_convert_flags(int altForm2){ |
| 275 | static int wikiFlags = 0; |
| 276 | (void)altForm2; |
| @@ -456,11 +456,11 @@ | |
| 456 | ** flag_leftjustify TRUE if a '-' is present or if the |
| 457 | ** field width was negative. |
| 458 | ** flag_zeropad TRUE if the width began with 0. |
| 459 | ** flag_long TRUE if the letter 'l' (ell) prefixed |
| 460 | ** the conversion character. |
| 461 | ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed |
| 462 | ** the conversion character. |
| 463 | ** flag_blanksign TRUE if a ' ' is present. |
| 464 | ** width The specified field width. This is |
| 465 | ** always non-negative. Zero is the default. |
| 466 | ** precision The specified precision. The default |
| @@ -1188,13 +1188,13 @@ | |
| 1188 | ** |
| 1189 | ** The main difference between fossil_fatal() and fossil_panic() is that |
| 1190 | ** fossil_panic() makes an entry in the error log whereas fossil_fatal() |
| 1191 | ** does not. On POSIX platforms, if there is not an error log, then both |
| 1192 | ** routines work similarly with respect to user-visible effects. Hence, |
| 1193 | ** the routines are interchangable for commands and only act differently |
| 1194 | ** when processing web pages. On the Windows platform, fossil_panic() |
| 1195 | ** also displays a pop-up stating that an error has occured and allowing |
| 1196 | ** just-in-time debugging to commence. On all platforms, fossil_panic() |
| 1197 | ** ends execution with a SIGABRT signal, bypassing atexit processing. |
| 1198 | ** This signal can also produce a core dump on POSIX platforms. |
| 1199 | ** |
| 1200 | ** Use fossil_fatal() for malformed inputs that should be reported back |
| 1201 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -13,11 +13,11 @@ | |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains implementations of routines for formatting output |
| 19 | ** (ex: mprintf()) and for output to the console. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "printf.h" |
| 23 | #if defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | #define etBLOB 11 /* Blob objects. %b */ |
| 90 | #define etBLOBSQL 12 /* Blob objects quoted for SQL. %B */ |
| 91 | #define etSQLESCAPE 13 /* Strings with '\'' doubled. %q */ |
| 92 | #define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '', |
| 93 | NULL pointers replaced by SQL NULL. %Q */ |
| 94 | #define etSQLESCAPE3 15 /* Double '"' characters within an identifier. %w */ |
| 95 | #define etPOINTER 16 /* The %p conversion */ |
| 96 | #define etHTMLIZE 17 /* Make text safe for HTML */ |
| 97 | #define etHTTPIZE 18 /* Make text safe for HTTP. "/" encoded as %2f */ |
| 98 | #define etURLIZE 19 /* Make text safe for HTTP. "/" not encoded */ |
| 99 | #define etFOSSILIZE 20 /* The fossil header encoding format. */ |
| @@ -265,12 +265,12 @@ | |
| 265 | ** configuration parameters. |
| 266 | ** |
| 267 | ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 |
| 268 | ** flags) and is false for plain "%W". The ! flag indicates that the |
| 269 | ** formatting is for display of a check-in comment on the timeline. Such |
| 270 | ** comments used to be rendered differently, but ever since 2020, they |
| 271 | ** have been rendered identically, so the ! flag does not make any different |
| 272 | ** in the output any more. |
| 273 | */ |
| 274 | int wiki_convert_flags(int altForm2){ |
| 275 | static int wikiFlags = 0; |
| 276 | (void)altForm2; |
| @@ -456,11 +456,11 @@ | |
| 456 | ** flag_leftjustify TRUE if a '-' is present or if the |
| 457 | ** field width was negative. |
| 458 | ** flag_zeropad TRUE if the width began with 0. |
| 459 | ** flag_long TRUE if the letter 'l' (ell) prefixed |
| 460 | ** the conversion character. |
| 461 | ** flag_longlong TRUE if the letters 'll' (ell ell) prefixed |
| 462 | ** the conversion character. |
| 463 | ** flag_blanksign TRUE if a ' ' is present. |
| 464 | ** width The specified field width. This is |
| 465 | ** always non-negative. Zero is the default. |
| 466 | ** precision The specified precision. The default |
| @@ -1188,13 +1188,13 @@ | |
| 1188 | ** |
| 1189 | ** The main difference between fossil_fatal() and fossil_panic() is that |
| 1190 | ** fossil_panic() makes an entry in the error log whereas fossil_fatal() |
| 1191 | ** does not. On POSIX platforms, if there is not an error log, then both |
| 1192 | ** routines work similarly with respect to user-visible effects. Hence, |
| 1193 | ** the routines are interchangeable for commands and only act differently |
| 1194 | ** when processing web pages. On the Windows platform, fossil_panic() |
| 1195 | ** also displays a pop-up stating that an error has occurred and allowing |
| 1196 | ** just-in-time debugging to commence. On all platforms, fossil_panic() |
| 1197 | ** ends execution with a SIGABRT signal, bypassing atexit processing. |
| 1198 | ** This signal can also produce a core dump on POSIX platforms. |
| 1199 | ** |
| 1200 | ** Use fossil_fatal() for malformed inputs that should be reported back |
| 1201 |
+3
-3
| --- src/purge.c | ||
| +++ src/purge.c | ||
| @@ -62,11 +62,11 @@ | ||
| 62 | 62 | #define PURGE_EXPLAIN_ONLY 0x0002 /* Show what would have happened */ |
| 63 | 63 | #define PURGE_PRINT_SUMMARY 0x0004 /* Print a summary report at end */ |
| 64 | 64 | #endif |
| 65 | 65 | |
| 66 | 66 | /* |
| 67 | -** This routine purges multiple artifacts from the repository, transfering | |
| 67 | +** This routine purges multiple artifacts from the repository, transferring | |
| 68 | 68 | ** those artifacts into the PURGEITEM table. |
| 69 | 69 | ** |
| 70 | 70 | ** Prior to invoking this routine, the caller must create a (TEMP) table |
| 71 | 71 | ** named zTab that contains the RID of every artifact to be purged. |
| 72 | 72 | ** |
| @@ -120,11 +120,11 @@ | ||
| 120 | 120 | return 0; |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | 123 | /* Make sure we are not removing a manifest that is the baseline of some |
| 124 | 124 | ** manifest that is being left behind. This step is not strictly necessary. |
| 125 | - ** is is just a safety check. */ | |
| 125 | + ** It is just a safety check. */ | |
| 126 | 126 | if( purge_baseline_out_from_under_delta(zTab) ){ |
| 127 | 127 | fossil_panic("attempt to purge a baseline manifest without also purging " |
| 128 | 128 | "all of its deltas"); |
| 129 | 129 | } |
| 130 | 130 | |
| @@ -256,11 +256,11 @@ | ||
| 256 | 256 | ** When bExclusive is false, then all artifacts used by the check-ins |
| 257 | 257 | ** are added even if those artifacts are also used by other check-ins |
| 258 | 258 | ** not in the set. |
| 259 | 259 | ** |
| 260 | 260 | ** The "fossil publish" command with the (undocumented) --test and |
| 261 | -** --exclusive options can be used for interactiving testing of this | |
| 261 | +** --exclusive options can be used for interactive testing of this | |
| 262 | 262 | ** function. |
| 263 | 263 | */ |
| 264 | 264 | void find_checkin_associates(const char *zTab, int bExclusive){ |
| 265 | 265 | db_begin_transaction(); |
| 266 | 266 | |
| 267 | 267 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -62,11 +62,11 @@ | |
| 62 | #define PURGE_EXPLAIN_ONLY 0x0002 /* Show what would have happened */ |
| 63 | #define PURGE_PRINT_SUMMARY 0x0004 /* Print a summary report at end */ |
| 64 | #endif |
| 65 | |
| 66 | /* |
| 67 | ** This routine purges multiple artifacts from the repository, transfering |
| 68 | ** those artifacts into the PURGEITEM table. |
| 69 | ** |
| 70 | ** Prior to invoking this routine, the caller must create a (TEMP) table |
| 71 | ** named zTab that contains the RID of every artifact to be purged. |
| 72 | ** |
| @@ -120,11 +120,11 @@ | |
| 120 | return 0; |
| 121 | } |
| 122 | |
| 123 | /* Make sure we are not removing a manifest that is the baseline of some |
| 124 | ** manifest that is being left behind. This step is not strictly necessary. |
| 125 | ** is is just a safety check. */ |
| 126 | if( purge_baseline_out_from_under_delta(zTab) ){ |
| 127 | fossil_panic("attempt to purge a baseline manifest without also purging " |
| 128 | "all of its deltas"); |
| 129 | } |
| 130 | |
| @@ -256,11 +256,11 @@ | |
| 256 | ** When bExclusive is false, then all artifacts used by the check-ins |
| 257 | ** are added even if those artifacts are also used by other check-ins |
| 258 | ** not in the set. |
| 259 | ** |
| 260 | ** The "fossil publish" command with the (undocumented) --test and |
| 261 | ** --exclusive options can be used for interactiving testing of this |
| 262 | ** function. |
| 263 | */ |
| 264 | void find_checkin_associates(const char *zTab, int bExclusive){ |
| 265 | db_begin_transaction(); |
| 266 | |
| 267 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -62,11 +62,11 @@ | |
| 62 | #define PURGE_EXPLAIN_ONLY 0x0002 /* Show what would have happened */ |
| 63 | #define PURGE_PRINT_SUMMARY 0x0004 /* Print a summary report at end */ |
| 64 | #endif |
| 65 | |
| 66 | /* |
| 67 | ** This routine purges multiple artifacts from the repository, transferring |
| 68 | ** those artifacts into the PURGEITEM table. |
| 69 | ** |
| 70 | ** Prior to invoking this routine, the caller must create a (TEMP) table |
| 71 | ** named zTab that contains the RID of every artifact to be purged. |
| 72 | ** |
| @@ -120,11 +120,11 @@ | |
| 120 | return 0; |
| 121 | } |
| 122 | |
| 123 | /* Make sure we are not removing a manifest that is the baseline of some |
| 124 | ** manifest that is being left behind. This step is not strictly necessary. |
| 125 | ** It is just a safety check. */ |
| 126 | if( purge_baseline_out_from_under_delta(zTab) ){ |
| 127 | fossil_panic("attempt to purge a baseline manifest without also purging " |
| 128 | "all of its deltas"); |
| 129 | } |
| 130 | |
| @@ -256,11 +256,11 @@ | |
| 256 | ** When bExclusive is false, then all artifacts used by the check-ins |
| 257 | ** are added even if those artifacts are also used by other check-ins |
| 258 | ** not in the set. |
| 259 | ** |
| 260 | ** The "fossil publish" command with the (undocumented) --test and |
| 261 | ** --exclusive options can be used for interactive testing of this |
| 262 | ** function. |
| 263 | */ |
| 264 | void find_checkin_associates(const char *zTab, int bExclusive){ |
| 265 | db_begin_transaction(); |
| 266 | |
| 267 |
+1
-1
| --- src/regexp.c | ||
| +++ src/regexp.c | ||
| @@ -993,11 +993,11 @@ | ||
| 993 | 993 | |
| 994 | 994 | |
| 995 | 995 | if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1; |
| 996 | 996 | if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS; |
| 997 | 997 | if( find_option("verbose",0,0)!=0 ) bVerbose = 1; |
| 998 | - if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS; | |
| 998 | + if( g.fQuiet ) flags |= GREP_QUIET|GREP_EXISTS; | |
| 999 | 999 | bNoMsg = find_option("no-messages","s",0)!=0; |
| 1000 | 1000 | bOnce = find_option("once",0,0)!=0; |
| 1001 | 1001 | bInvert = find_option("invert-match","v",0)!=0; |
| 1002 | 1002 | if( bInvert ){ |
| 1003 | 1003 | flags |= GREP_QUIET|GREP_EXISTS; |
| 1004 | 1004 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -993,11 +993,11 @@ | |
| 993 | |
| 994 | |
| 995 | if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1; |
| 996 | if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS; |
| 997 | if( find_option("verbose",0,0)!=0 ) bVerbose = 1; |
| 998 | if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS; |
| 999 | bNoMsg = find_option("no-messages","s",0)!=0; |
| 1000 | bOnce = find_option("once",0,0)!=0; |
| 1001 | bInvert = find_option("invert-match","v",0)!=0; |
| 1002 | if( bInvert ){ |
| 1003 | flags |= GREP_QUIET|GREP_EXISTS; |
| 1004 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -993,11 +993,11 @@ | |
| 993 | |
| 994 | |
| 995 | if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1; |
| 996 | if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS; |
| 997 | if( find_option("verbose",0,0)!=0 ) bVerbose = 1; |
| 998 | if( g.fQuiet ) flags |= GREP_QUIET|GREP_EXISTS; |
| 999 | bNoMsg = find_option("no-messages","s",0)!=0; |
| 1000 | bOnce = find_option("once",0,0)!=0; |
| 1001 | bInvert = find_option("invert-match","v",0)!=0; |
| 1002 | if( bInvert ){ |
| 1003 | flags |= GREP_QUIET|GREP_EXISTS; |
| 1004 |
+1
-1
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -447,11 +447,11 @@ | ||
| 447 | 447 | int rn; |
| 448 | 448 | const char *zTitle; /* Title of the report */ |
| 449 | 449 | const char *z; |
| 450 | 450 | const char *zOwner; /* Owner of the report */ |
| 451 | 451 | const char *zClrKey; /* Color key - used to add colors to lines */ |
| 452 | - char *zSQL; /* The SQL text that gnerates the report */ | |
| 452 | + char *zSQL; /* The SQL text that generates the report */ | |
| 453 | 453 | char *zErr = 0; /* An error message */ |
| 454 | 454 | const char *zDesc; /* Extra descriptive text about the report */ |
| 455 | 455 | const char *zMimetype; /* Mimetype for zDesc */ |
| 456 | 456 | const char *zTag; /* Symbolic name for this report */ |
| 457 | 457 | int dflt = P("dflt") ? 1 : 0; |
| 458 | 458 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -447,11 +447,11 @@ | |
| 447 | int rn; |
| 448 | const char *zTitle; /* Title of the report */ |
| 449 | const char *z; |
| 450 | const char *zOwner; /* Owner of the report */ |
| 451 | const char *zClrKey; /* Color key - used to add colors to lines */ |
| 452 | char *zSQL; /* The SQL text that gnerates the report */ |
| 453 | char *zErr = 0; /* An error message */ |
| 454 | const char *zDesc; /* Extra descriptive text about the report */ |
| 455 | const char *zMimetype; /* Mimetype for zDesc */ |
| 456 | const char *zTag; /* Symbolic name for this report */ |
| 457 | int dflt = P("dflt") ? 1 : 0; |
| 458 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -447,11 +447,11 @@ | |
| 447 | int rn; |
| 448 | const char *zTitle; /* Title of the report */ |
| 449 | const char *z; |
| 450 | const char *zOwner; /* Owner of the report */ |
| 451 | const char *zClrKey; /* Color key - used to add colors to lines */ |
| 452 | char *zSQL; /* The SQL text that generates the report */ |
| 453 | char *zErr = 0; /* An error message */ |
| 454 | const char *zDesc; /* Extra descriptive text about the report */ |
| 455 | const char *zMimetype; /* Mimetype for zDesc */ |
| 456 | const char *zTag; /* Symbolic name for this report */ |
| 457 | int dflt = P("dflt") ? 1 : 0; |
| 458 |
+3
-1
| --- src/robot.c | ||
| +++ src/robot.c | ||
| @@ -263,11 +263,11 @@ | ||
| 263 | 263 | ** The VALUE of this setting is a list of GLOB patterns that match |
| 264 | 264 | ** pages for which complex HTTP requests from unauthenticated clients |
| 265 | 265 | ** should be disallowed. "Unauthenticated" means the user is "nobody". |
| 266 | 266 | ** The recommended value for this setting is: |
| 267 | 267 | ** |
| 268 | -** timelineX,diff,annotate,fileage,file,finfo,reports,tree,download,hexdump | |
| 268 | +** timelineX,diff,annotate,fileage,file,finfo,reports,tree,hexdump,download | |
| 269 | 269 | ** |
| 270 | 270 | ** Usually the tag should exactly match the page name. The "diff" tag |
| 271 | 271 | ** covers all diffing pages such as /vdiff, /fdiff, and /vpatch. The |
| 272 | 272 | ** "annotate" tag also covers /blame and /praise. "zip" also covers |
| 273 | 273 | ** /tarball and /sqlar. If a tag has an "X" character appended then it |
| @@ -320,10 +320,12 @@ | ||
| 320 | 320 | |
| 321 | 321 | /* |
| 322 | 322 | ** Return the default restriction GLOB |
| 323 | 323 | */ |
| 324 | 324 | const char *robot_restrict_default(void){ |
| 325 | + /* NOTE: The default value is also mentioned in the online help screen of | |
| 326 | + ** the "robot-restrict" setting, and in the www/antibot.wiki document. */ | |
| 325 | 327 | return "timelineX,diff,annotate,fileage,file,finfo,reports," |
| 326 | 328 | "tree,hexdump,download"; |
| 327 | 329 | } |
| 328 | 330 | |
| 329 | 331 | /* |
| 330 | 332 |
| --- src/robot.c | |
| +++ src/robot.c | |
| @@ -263,11 +263,11 @@ | |
| 263 | ** The VALUE of this setting is a list of GLOB patterns that match |
| 264 | ** pages for which complex HTTP requests from unauthenticated clients |
| 265 | ** should be disallowed. "Unauthenticated" means the user is "nobody". |
| 266 | ** The recommended value for this setting is: |
| 267 | ** |
| 268 | ** timelineX,diff,annotate,fileage,file,finfo,reports,tree,download,hexdump |
| 269 | ** |
| 270 | ** Usually the tag should exactly match the page name. The "diff" tag |
| 271 | ** covers all diffing pages such as /vdiff, /fdiff, and /vpatch. The |
| 272 | ** "annotate" tag also covers /blame and /praise. "zip" also covers |
| 273 | ** /tarball and /sqlar. If a tag has an "X" character appended then it |
| @@ -320,10 +320,12 @@ | |
| 320 | |
| 321 | /* |
| 322 | ** Return the default restriction GLOB |
| 323 | */ |
| 324 | const char *robot_restrict_default(void){ |
| 325 | return "timelineX,diff,annotate,fileage,file,finfo,reports," |
| 326 | "tree,hexdump,download"; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 |
| --- src/robot.c | |
| +++ src/robot.c | |
| @@ -263,11 +263,11 @@ | |
| 263 | ** The VALUE of this setting is a list of GLOB patterns that match |
| 264 | ** pages for which complex HTTP requests from unauthenticated clients |
| 265 | ** should be disallowed. "Unauthenticated" means the user is "nobody". |
| 266 | ** The recommended value for this setting is: |
| 267 | ** |
| 268 | ** timelineX,diff,annotate,fileage,file,finfo,reports,tree,hexdump,download |
| 269 | ** |
| 270 | ** Usually the tag should exactly match the page name. The "diff" tag |
| 271 | ** covers all diffing pages such as /vdiff, /fdiff, and /vpatch. The |
| 272 | ** "annotate" tag also covers /blame and /praise. "zip" also covers |
| 273 | ** /tarball and /sqlar. If a tag has an "X" character appended then it |
| @@ -320,10 +320,12 @@ | |
| 320 | |
| 321 | /* |
| 322 | ** Return the default restriction GLOB |
| 323 | */ |
| 324 | const char *robot_restrict_default(void){ |
| 325 | /* NOTE: The default value is also mentioned in the online help screen of |
| 326 | ** the "robot-restrict" setting, and in the www/antibot.wiki document. */ |
| 327 | return "timelineX,diff,annotate,fileage,file,finfo,reports," |
| 328 | "tree,hexdump,download"; |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 |
+3
-3
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -259,11 +259,11 @@ | ||
| 259 | 259 | @ -- pfnid = Parent File Name ID. |
| 260 | 260 | @ -- isaux = pmid IS AUXiliary parent, not primary parent |
| 261 | 261 | @ -- |
| 262 | 262 | @ -- pid==0 if the file is added by check-in mid. |
| 263 | 263 | @ -- pid==(-1) if the file exists in a merge parents but not in the primary |
| 264 | -@ -- parent. In other words, if the file file was added by merge. | |
| 264 | +@ -- parent. In other words, if the file was added by merge. | |
| 265 | 265 | @ -- fid==0 if the file is removed by check-in mid. |
| 266 | 266 | @ -- |
| 267 | 267 | @ CREATE TABLE mlink( |
| 268 | 268 | @ mid INTEGER, -- Check-in that contains fid |
| 269 | 269 | @ fid INTEGER, -- New file content. 0 if deleted |
| @@ -365,11 +365,11 @@ | ||
| 365 | 365 | @ -- Each artifact can have one or more tags. A tag |
| 366 | 366 | @ -- is defined by a row in the next table. |
| 367 | 367 | @ -- |
| 368 | 368 | @ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of |
| 369 | 369 | @ -- the wiki page. Tickets changes are tagged with "ticket-HASH" where |
| 370 | -@ -- HASH is the indentifier of the ticket. Tags used to assign symbolic | |
| 370 | +@ -- HASH is the identifier of the ticket. Tags used to assign symbolic | |
| 371 | 371 | @ -- names to baselines are branches are of the form "sym-NAME" where |
| 372 | 372 | @ -- NAME is the symbolic name. |
| 373 | 373 | @ -- |
| 374 | 374 | @ CREATE TABLE tag( |
| 375 | 375 | @ tagid INTEGER PRIMARY KEY, -- Numeric tag ID |
| @@ -500,11 +500,11 @@ | ||
| 500 | 500 | ** Allowed values for MIMEtype codes |
| 501 | 501 | */ |
| 502 | 502 | #if INTERFACE |
| 503 | 503 | # define MT_NONE 0 /* unspecified */ |
| 504 | 504 | # define MT_WIKI 1 /* Wiki */ |
| 505 | -# define MT_MARKDOWN 2 /* Markdonw */ | |
| 505 | +# define MT_MARKDOWN 2 /* Markdown */ | |
| 506 | 506 | # define MT_UNKNOWN 3 /* unknown */ |
| 507 | 507 | # define ValidMTC(X) ((X)>=0 && (X)<=3) /* True if MIMEtype code is valid */ |
| 508 | 508 | #endif |
| 509 | 509 | |
| 510 | 510 | /* |
| 511 | 511 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -259,11 +259,11 @@ | |
| 259 | @ -- pfnid = Parent File Name ID. |
| 260 | @ -- isaux = pmid IS AUXiliary parent, not primary parent |
| 261 | @ -- |
| 262 | @ -- pid==0 if the file is added by check-in mid. |
| 263 | @ -- pid==(-1) if the file exists in a merge parents but not in the primary |
| 264 | @ -- parent. In other words, if the file file was added by merge. |
| 265 | @ -- fid==0 if the file is removed by check-in mid. |
| 266 | @ -- |
| 267 | @ CREATE TABLE mlink( |
| 268 | @ mid INTEGER, -- Check-in that contains fid |
| 269 | @ fid INTEGER, -- New file content. 0 if deleted |
| @@ -365,11 +365,11 @@ | |
| 365 | @ -- Each artifact can have one or more tags. A tag |
| 366 | @ -- is defined by a row in the next table. |
| 367 | @ -- |
| 368 | @ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of |
| 369 | @ -- the wiki page. Tickets changes are tagged with "ticket-HASH" where |
| 370 | @ -- HASH is the indentifier of the ticket. Tags used to assign symbolic |
| 371 | @ -- names to baselines are branches are of the form "sym-NAME" where |
| 372 | @ -- NAME is the symbolic name. |
| 373 | @ -- |
| 374 | @ CREATE TABLE tag( |
| 375 | @ tagid INTEGER PRIMARY KEY, -- Numeric tag ID |
| @@ -500,11 +500,11 @@ | |
| 500 | ** Allowed values for MIMEtype codes |
| 501 | */ |
| 502 | #if INTERFACE |
| 503 | # define MT_NONE 0 /* unspecified */ |
| 504 | # define MT_WIKI 1 /* Wiki */ |
| 505 | # define MT_MARKDOWN 2 /* Markdonw */ |
| 506 | # define MT_UNKNOWN 3 /* unknown */ |
| 507 | # define ValidMTC(X) ((X)>=0 && (X)<=3) /* True if MIMEtype code is valid */ |
| 508 | #endif |
| 509 | |
| 510 | /* |
| 511 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -259,11 +259,11 @@ | |
| 259 | @ -- pfnid = Parent File Name ID. |
| 260 | @ -- isaux = pmid IS AUXiliary parent, not primary parent |
| 261 | @ -- |
| 262 | @ -- pid==0 if the file is added by check-in mid. |
| 263 | @ -- pid==(-1) if the file exists in a merge parents but not in the primary |
| 264 | @ -- parent. In other words, if the file was added by merge. |
| 265 | @ -- fid==0 if the file is removed by check-in mid. |
| 266 | @ -- |
| 267 | @ CREATE TABLE mlink( |
| 268 | @ mid INTEGER, -- Check-in that contains fid |
| 269 | @ fid INTEGER, -- New file content. 0 if deleted |
| @@ -365,11 +365,11 @@ | |
| 365 | @ -- Each artifact can have one or more tags. A tag |
| 366 | @ -- is defined by a row in the next table. |
| 367 | @ -- |
| 368 | @ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of |
| 369 | @ -- the wiki page. Tickets changes are tagged with "ticket-HASH" where |
| 370 | @ -- HASH is the identifier of the ticket. Tags used to assign symbolic |
| 371 | @ -- names to baselines are branches are of the form "sym-NAME" where |
| 372 | @ -- NAME is the symbolic name. |
| 373 | @ -- |
| 374 | @ CREATE TABLE tag( |
| 375 | @ tagid INTEGER PRIMARY KEY, -- Numeric tag ID |
| @@ -500,11 +500,11 @@ | |
| 500 | ** Allowed values for MIMEtype codes |
| 501 | */ |
| 502 | #if INTERFACE |
| 503 | # define MT_NONE 0 /* unspecified */ |
| 504 | # define MT_WIKI 1 /* Wiki */ |
| 505 | # define MT_MARKDOWN 2 /* Markdown */ |
| 506 | # define MT_UNKNOWN 3 /* unknown */ |
| 507 | # define ValidMTC(X) ((X)>=0 && (X)<=3) /* True if MIMEtype code is valid */ |
| 508 | #endif |
| 509 | |
| 510 | /* |
| 511 |
+2
-2
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -589,11 +589,11 @@ | ||
| 589 | 589 | ** or use "--highlight 91" to force it on. Change the argument to --highlight |
| 590 | 590 | ** to change the color. |
| 591 | 591 | ** |
| 592 | 592 | ** Options: |
| 593 | 593 | ** -a|--all Search everything |
| 594 | -** -c|--checkins Search checkin comments | |
| 594 | +** -c|--checkins Search check-in comments | |
| 595 | 595 | ** --docs Search embedded documentation |
| 596 | 596 | ** --forum Search forum posts |
| 597 | 597 | ** -h|--bi-help Search built-in help |
| 598 | 598 | ** --highlight N Used VT100 color N for matching text. 0 means "off". |
| 599 | 599 | ** -n|--limit N Limit output to N matches |
| @@ -2865,11 +2865,11 @@ | ||
| 2865 | 2865 | rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb); |
| 2866 | 2866 | } |
| 2867 | 2867 | if( rc!=SQLITE_OK ){ |
| 2868 | 2868 | sqlite3_result_error_code(pCtx, rc); |
| 2869 | 2869 | }else{ |
| 2870 | - /* No errors has occured, so return a copy of the array of integers. */ | |
| 2870 | + /* No error has occurred, so return a copy of the array of integers. */ | |
| 2871 | 2871 | int nByte = p->nRet * sizeof(u32); |
| 2872 | 2872 | sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT); |
| 2873 | 2873 | } |
| 2874 | 2874 | } |
| 2875 | 2875 | |
| 2876 | 2876 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -589,11 +589,11 @@ | |
| 589 | ** or use "--highlight 91" to force it on. Change the argument to --highlight |
| 590 | ** to change the color. |
| 591 | ** |
| 592 | ** Options: |
| 593 | ** -a|--all Search everything |
| 594 | ** -c|--checkins Search checkin comments |
| 595 | ** --docs Search embedded documentation |
| 596 | ** --forum Search forum posts |
| 597 | ** -h|--bi-help Search built-in help |
| 598 | ** --highlight N Used VT100 color N for matching text. 0 means "off". |
| 599 | ** -n|--limit N Limit output to N matches |
| @@ -2865,11 +2865,11 @@ | |
| 2865 | rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb); |
| 2866 | } |
| 2867 | if( rc!=SQLITE_OK ){ |
| 2868 | sqlite3_result_error_code(pCtx, rc); |
| 2869 | }else{ |
| 2870 | /* No errors has occured, so return a copy of the array of integers. */ |
| 2871 | int nByte = p->nRet * sizeof(u32); |
| 2872 | sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT); |
| 2873 | } |
| 2874 | } |
| 2875 | |
| 2876 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -589,11 +589,11 @@ | |
| 589 | ** or use "--highlight 91" to force it on. Change the argument to --highlight |
| 590 | ** to change the color. |
| 591 | ** |
| 592 | ** Options: |
| 593 | ** -a|--all Search everything |
| 594 | ** -c|--checkins Search check-in comments |
| 595 | ** --docs Search embedded documentation |
| 596 | ** --forum Search forum posts |
| 597 | ** -h|--bi-help Search built-in help |
| 598 | ** --highlight N Used VT100 color N for matching text. 0 means "off". |
| 599 | ** -n|--limit N Limit output to N matches |
| @@ -2865,11 +2865,11 @@ | |
| 2865 | rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb); |
| 2866 | } |
| 2867 | if( rc!=SQLITE_OK ){ |
| 2868 | sqlite3_result_error_code(pCtx, rc); |
| 2869 | }else{ |
| 2870 | /* No error has occurred, so return a copy of the array of integers. */ |
| 2871 | int nByte = p->nRet * sizeof(u32); |
| 2872 | sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT); |
| 2873 | } |
| 2874 | } |
| 2875 | |
| 2876 |
+2
-2
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -351,11 +351,11 @@ | ||
| 351 | 351 | */ |
| 352 | 352 | if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'" |
| 353 | 353 | " AND length(value)>0") ){ |
| 354 | 354 | @ <li><p><b>WARNING:</b> |
| 355 | 355 | @ TH1 scripts might be configured to run on any sync, push, pull, or |
| 356 | - @ clone operation. See the the <a href="%R/xfersetup">/xfersetup</a> | |
| 356 | + @ clone operation. See the <a href="%R/xfersetup">/xfersetup</a> | |
| 357 | 357 | @ page for more information. These TH1 scripts are a potential |
| 358 | 358 | @ security concern and so should be carefully audited by a human. |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | 361 | /* The strict-manifest-syntax setting should be on. */ |
| @@ -389,11 +389,11 @@ | ||
| 389 | 389 | @ <a href="setup_ulist">User Configuration</a> page in case we |
| 390 | 390 | @ ever reuse the letter for another purpose. |
| 391 | 391 | } |
| 392 | 392 | |
| 393 | 393 | /* If anonymous users are allowed to create new Wiki, then |
| 394 | - ** wiki moderation should be activated to pervent spam. | |
| 394 | + ** wiki moderation should be activated to prevent spam. | |
| 395 | 395 | */ |
| 396 | 396 | if( hasAnyCap(zAnonCap, "fk") ){ |
| 397 | 397 | if( db_get_boolean("modreq-wiki",0)==0 ){ |
| 398 | 398 | @ <li><p><b>WARNING:</b> |
| 399 | 399 | @ Anonymous users can create or edit wiki without moderation. |
| 400 | 400 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -351,11 +351,11 @@ | |
| 351 | */ |
| 352 | if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'" |
| 353 | " AND length(value)>0") ){ |
| 354 | @ <li><p><b>WARNING:</b> |
| 355 | @ TH1 scripts might be configured to run on any sync, push, pull, or |
| 356 | @ clone operation. See the the <a href="%R/xfersetup">/xfersetup</a> |
| 357 | @ page for more information. These TH1 scripts are a potential |
| 358 | @ security concern and so should be carefully audited by a human. |
| 359 | } |
| 360 | |
| 361 | /* The strict-manifest-syntax setting should be on. */ |
| @@ -389,11 +389,11 @@ | |
| 389 | @ <a href="setup_ulist">User Configuration</a> page in case we |
| 390 | @ ever reuse the letter for another purpose. |
| 391 | } |
| 392 | |
| 393 | /* If anonymous users are allowed to create new Wiki, then |
| 394 | ** wiki moderation should be activated to pervent spam. |
| 395 | */ |
| 396 | if( hasAnyCap(zAnonCap, "fk") ){ |
| 397 | if( db_get_boolean("modreq-wiki",0)==0 ){ |
| 398 | @ <li><p><b>WARNING:</b> |
| 399 | @ Anonymous users can create or edit wiki without moderation. |
| 400 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -351,11 +351,11 @@ | |
| 351 | */ |
| 352 | if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'" |
| 353 | " AND length(value)>0") ){ |
| 354 | @ <li><p><b>WARNING:</b> |
| 355 | @ TH1 scripts might be configured to run on any sync, push, pull, or |
| 356 | @ clone operation. See the <a href="%R/xfersetup">/xfersetup</a> |
| 357 | @ page for more information. These TH1 scripts are a potential |
| 358 | @ security concern and so should be carefully audited by a human. |
| 359 | } |
| 360 | |
| 361 | /* The strict-manifest-syntax setting should be on. */ |
| @@ -389,11 +389,11 @@ | |
| 389 | @ <a href="setup_ulist">User Configuration</a> page in case we |
| 390 | @ ever reuse the letter for another purpose. |
| 391 | } |
| 392 | |
| 393 | /* If anonymous users are allowed to create new Wiki, then |
| 394 | ** wiki moderation should be activated to prevent spam. |
| 395 | */ |
| 396 | if( hasAnyCap(zAnonCap, "fk") ){ |
| 397 | if( db_get_boolean("modreq-wiki",0)==0 ){ |
| 398 | @ <li><p><b>WARNING:</b> |
| 399 | @ Anonymous users can create or edit wiki without moderation. |
| 400 |
+1
-1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -485,11 +485,11 @@ | ||
| 485 | 485 | @ <hr> |
| 486 | 486 | @ <p><b>Do not allow robots access to these pages.</b><br> |
| 487 | 487 | @ If the page name matches the GLOB pattern of this setting, and the |
| 488 | 488 | @ users is "nobody", and the client has not previously passed a captcha |
| 489 | 489 | @ test to show that it is not a robot, then the page is not displayed. |
| 490 | - @ A captcha test is is rendered instead. | |
| 490 | + @ A captcha test is rendered instead. | |
| 491 | 491 | @ The default value for this setting is: |
| 492 | 492 | @ <p> |
| 493 | 493 | @    <tt>%h(robot_restrict_default())</tt> |
| 494 | 494 | @ <p> |
| 495 | 495 | @ Usually the tag should exactly match the page name. Exceptions: |
| 496 | 496 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -485,11 +485,11 @@ | |
| 485 | @ <hr> |
| 486 | @ <p><b>Do not allow robots access to these pages.</b><br> |
| 487 | @ If the page name matches the GLOB pattern of this setting, and the |
| 488 | @ users is "nobody", and the client has not previously passed a captcha |
| 489 | @ test to show that it is not a robot, then the page is not displayed. |
| 490 | @ A captcha test is is rendered instead. |
| 491 | @ The default value for this setting is: |
| 492 | @ <p> |
| 493 | @    <tt>%h(robot_restrict_default())</tt> |
| 494 | @ <p> |
| 495 | @ Usually the tag should exactly match the page name. Exceptions: |
| 496 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -485,11 +485,11 @@ | |
| 485 | @ <hr> |
| 486 | @ <p><b>Do not allow robots access to these pages.</b><br> |
| 487 | @ If the page name matches the GLOB pattern of this setting, and the |
| 488 | @ users is "nobody", and the client has not previously passed a captcha |
| 489 | @ test to show that it is not a robot, then the page is not displayed. |
| 490 | @ A captcha test is rendered instead. |
| 491 | @ The default value for this setting is: |
| 492 | @ <p> |
| 493 | @    <tt>%h(robot_restrict_default())</tt> |
| 494 | @ <p> |
| 495 | @ Usually the tag should exactly match the page name. Exceptions: |
| 496 |
+1
-1
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -617,11 +617,11 @@ | ||
| 617 | 617 | zLogin, zLogin |
| 618 | 618 | ); |
| 619 | 619 | zOldLogin = zLogin; |
| 620 | 620 | } |
| 621 | 621 | #if 0 |
| 622 | - /* Problem: when renaming a user we need to update the subcriber | |
| 622 | + /* Problem: when renaming a user we need to update the subscriber | |
| 623 | 623 | ** names to match but we cannot know from here if each member of |
| 624 | 624 | ** the login group has the subscriber tables, so we cannot blindly |
| 625 | 625 | ** include this SQL. */ |
| 626 | 626 | else if( fossil_strcmp(zLogin, zOldLogin)!=0 |
| 627 | 627 | && alert_tables_exist() ){ |
| 628 | 628 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -617,11 +617,11 @@ | |
| 617 | zLogin, zLogin |
| 618 | ); |
| 619 | zOldLogin = zLogin; |
| 620 | } |
| 621 | #if 0 |
| 622 | /* Problem: when renaming a user we need to update the subcriber |
| 623 | ** names to match but we cannot know from here if each member of |
| 624 | ** the login group has the subscriber tables, so we cannot blindly |
| 625 | ** include this SQL. */ |
| 626 | else if( fossil_strcmp(zLogin, zOldLogin)!=0 |
| 627 | && alert_tables_exist() ){ |
| 628 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -617,11 +617,11 @@ | |
| 617 | zLogin, zLogin |
| 618 | ); |
| 619 | zOldLogin = zLogin; |
| 620 | } |
| 621 | #if 0 |
| 622 | /* Problem: when renaming a user we need to update the subscriber |
| 623 | ** names to match but we cannot know from here if each member of |
| 624 | ** the login group has the subscriber tables, so we cannot blindly |
| 625 | ** include this SQL. */ |
| 626 | else if( fossil_strcmp(zLogin, zOldLogin)!=0 |
| 627 | && alert_tables_exist() ){ |
| 628 |
+1
-1
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -257,11 +257,11 @@ | ||
| 257 | 257 | for (i = 0; i < 20; i++) |
| 258 | 258 | digest[i] = (unsigned char) |
| 259 | 259 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 260 | 260 | } |
| 261 | 261 | } |
| 262 | -#endif /* Built-in SHA1 implemenation */ | |
| 262 | +#endif /* Built-in SHA1 implementation */ | |
| 263 | 263 | |
| 264 | 264 | /* |
| 265 | 265 | ** Convert a digest into base-16. digest should be declared as |
| 266 | 266 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 267 | 267 | ** digest is stored in the first 20 bytes. zBuf should |
| 268 | 268 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -257,11 +257,11 @@ | |
| 257 | for (i = 0; i < 20; i++) |
| 258 | digest[i] = (unsigned char) |
| 259 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 260 | } |
| 261 | } |
| 262 | #endif /* Built-in SHA1 implemenation */ |
| 263 | |
| 264 | /* |
| 265 | ** Convert a digest into base-16. digest should be declared as |
| 266 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 267 | ** digest is stored in the first 20 bytes. zBuf should |
| 268 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -257,11 +257,11 @@ | |
| 257 | for (i = 0; i < 20; i++) |
| 258 | digest[i] = (unsigned char) |
| 259 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 260 | } |
| 261 | } |
| 262 | #endif /* Built-in SHA1 implementation */ |
| 263 | |
| 264 | /* |
| 265 | ** Convert a digest into base-16. digest should be declared as |
| 266 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 267 | ** digest is stored in the first 20 bytes. zBuf should |
| 268 |
+1
-1
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -162,11 +162,11 @@ | ||
| 162 | 162 | ** 2) The "skin" display setting cookie or URL argument, in that |
| 163 | 163 | ** order. If the "skin" URL argument is provided and refers to a legal |
| 164 | 164 | ** skin then that will update the display cookie. If the skin name is |
| 165 | 165 | ** illegal it is silently ignored. |
| 166 | 166 | ** |
| 167 | -** 3) The built-in skin identfied by the "default-skin" setting, if such | |
| 167 | +** 3) The built-in skin identified by the "default-skin" setting, if such | |
| 168 | 168 | ** a setting exists and matches one of the built-in skin names. |
| 169 | 169 | ** |
| 170 | 170 | ** 4) Skin properties (settings "css", "details", "footer", "header", |
| 171 | 171 | ** and "js") from the CONFIG db table |
| 172 | 172 | ** |
| 173 | 173 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -162,11 +162,11 @@ | |
| 162 | ** 2) The "skin" display setting cookie or URL argument, in that |
| 163 | ** order. If the "skin" URL argument is provided and refers to a legal |
| 164 | ** skin then that will update the display cookie. If the skin name is |
| 165 | ** illegal it is silently ignored. |
| 166 | ** |
| 167 | ** 3) The built-in skin identfied by the "default-skin" setting, if such |
| 168 | ** a setting exists and matches one of the built-in skin names. |
| 169 | ** |
| 170 | ** 4) Skin properties (settings "css", "details", "footer", "header", |
| 171 | ** and "js") from the CONFIG db table |
| 172 | ** |
| 173 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -162,11 +162,11 @@ | |
| 162 | ** 2) The "skin" display setting cookie or URL argument, in that |
| 163 | ** order. If the "skin" URL argument is provided and refers to a legal |
| 164 | ** skin then that will update the display cookie. If the skin name is |
| 165 | ** illegal it is silently ignored. |
| 166 | ** |
| 167 | ** 3) The built-in skin identified by the "default-skin" setting, if such |
| 168 | ** a setting exists and matches one of the built-in skin names. |
| 169 | ** |
| 170 | ** 4) Skin properties (settings "css", "details", "footer", "header", |
| 171 | ** and "js") from the CONFIG db table |
| 172 | ** |
| 173 |
+1
-1
| --- src/smtp.c | ||
| +++ src/smtp.c | ||
| @@ -565,11 +565,11 @@ | ||
| 565 | 565 | ** Return 0 on success. Otherwise an error code. |
| 566 | 566 | */ |
| 567 | 567 | int smtp_send_msg( |
| 568 | 568 | SmtpSession *p, /* The SMTP server to which the message is sent */ |
| 569 | 569 | const char *zFrom, /* Who the message is from */ |
| 570 | - int nTo, /* Number of receipients */ | |
| 570 | + int nTo, /* Number of recipients */ | |
| 571 | 571 | const char **azTo, /* Email address of each recipient */ |
| 572 | 572 | const char *zMsg /* Body of the message */ |
| 573 | 573 | ){ |
| 574 | 574 | int i; |
| 575 | 575 | int iCode = 0; |
| 576 | 576 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -565,11 +565,11 @@ | |
| 565 | ** Return 0 on success. Otherwise an error code. |
| 566 | */ |
| 567 | int smtp_send_msg( |
| 568 | SmtpSession *p, /* The SMTP server to which the message is sent */ |
| 569 | const char *zFrom, /* Who the message is from */ |
| 570 | int nTo, /* Number of receipients */ |
| 571 | const char **azTo, /* Email address of each recipient */ |
| 572 | const char *zMsg /* Body of the message */ |
| 573 | ){ |
| 574 | int i; |
| 575 | int iCode = 0; |
| 576 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -565,11 +565,11 @@ | |
| 565 | ** Return 0 on success. Otherwise an error code. |
| 566 | */ |
| 567 | int smtp_send_msg( |
| 568 | SmtpSession *p, /* The SMTP server to which the message is sent */ |
| 569 | const char *zFrom, /* Who the message is from */ |
| 570 | int nTo, /* Number of recipients */ |
| 571 | const char **azTo, /* Email address of each recipient */ |
| 572 | const char *zMsg /* Body of the message */ |
| 573 | ){ |
| 574 | int i; |
| 575 | int iCode = 0; |
| 576 |
+16
-12
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -168,11 +168,11 @@ | ||
| 168 | 168 | ** WARNING: |
| 169 | 169 | ** Do not instantiate these functions for any Fossil webpage or command |
| 170 | 170 | ** method other than the "fossil sql" command. If an attacker gains access |
| 171 | 171 | ** to these functions, he will be able to disable other defense mechanisms. |
| 172 | 172 | ** |
| 173 | -** This routines are for interactiving testing only. They are experimental | |
| 173 | +** This routines are for interactive testing only. They are experimental | |
| 174 | 174 | ** and undocumented (apart from this comments) and might go away or change |
| 175 | 175 | ** in future releases. |
| 176 | 176 | ** |
| 177 | 177 | ** 2020-11-29: These functions are now only available if the "fossil sql" |
| 178 | 178 | ** command is started with the --test option. |
| @@ -221,12 +221,14 @@ | ||
| 221 | 221 | helptext_vtab_register(db); |
| 222 | 222 | builtin_vtab_register(db); |
| 223 | 223 | g.repositoryOpen = 1; |
| 224 | 224 | g.db = db; |
| 225 | 225 | sqlite3_busy_timeout(db, 10000); |
| 226 | - sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); | |
| 227 | - db_maybe_set_encryption_key(db, g.zRepositoryName); | |
| 226 | + if( g.zRepositoryName ){ | |
| 227 | + sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); | |
| 228 | + db_maybe_set_encryption_key(db, g.zRepositoryName); | |
| 229 | + } | |
| 228 | 230 | if( g.zLocalDbName ){ |
| 229 | 231 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''", |
| 230 | 232 | g.zLocalDbName); |
| 231 | 233 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 232 | 234 | sqlite3_free(zSql); |
| @@ -240,19 +242,21 @@ | ||
| 240 | 242 | (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */ |
| 241 | 243 | /* Arrange to trace close operations so that static prepared statements |
| 242 | 244 | ** will get cleaned up when the shell closes the database connection */ |
| 243 | 245 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 244 | 246 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 245 | - db_protect_only(PROTECT_NONE); | |
| 246 | - sqlite3_set_authorizer(db, db_top_authorizer, db); | |
| 247 | - if( local_bSqlCmdTest ){ | |
| 248 | - sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 249 | - sqlcmd_db_protect, 0, 0); | |
| 250 | - sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 251 | - sqlcmd_db_protect_pop, 0, 0); | |
| 252 | - sqlite3_create_function(db, "shared_secret", 2, SQLITE_UTF8, 0, | |
| 253 | - sha1_shared_secret_sql_function, 0, 0); | |
| 247 | + if( g.zRepositoryName ){ | |
| 248 | + db_protect_only(PROTECT_NONE); | |
| 249 | + sqlite3_set_authorizer(db, db_top_authorizer, db); | |
| 250 | + if( local_bSqlCmdTest ){ | |
| 251 | + sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 252 | + sqlcmd_db_protect, 0, 0); | |
| 253 | + sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 254 | + sqlcmd_db_protect_pop, 0, 0); | |
| 255 | + sqlite3_create_function(db, "shared_secret", 2, SQLITE_UTF8, 0, | |
| 256 | + sha1_shared_secret_sql_function, 0, 0); | |
| 257 | + } | |
| 254 | 258 | } |
| 255 | 259 | return SQLITE_OK; |
| 256 | 260 | } |
| 257 | 261 | |
| 258 | 262 | /* |
| 259 | 263 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -168,11 +168,11 @@ | |
| 168 | ** WARNING: |
| 169 | ** Do not instantiate these functions for any Fossil webpage or command |
| 170 | ** method other than the "fossil sql" command. If an attacker gains access |
| 171 | ** to these functions, he will be able to disable other defense mechanisms. |
| 172 | ** |
| 173 | ** This routines are for interactiving testing only. They are experimental |
| 174 | ** and undocumented (apart from this comments) and might go away or change |
| 175 | ** in future releases. |
| 176 | ** |
| 177 | ** 2020-11-29: These functions are now only available if the "fossil sql" |
| 178 | ** command is started with the --test option. |
| @@ -221,12 +221,14 @@ | |
| 221 | helptext_vtab_register(db); |
| 222 | builtin_vtab_register(db); |
| 223 | g.repositoryOpen = 1; |
| 224 | g.db = db; |
| 225 | sqlite3_busy_timeout(db, 10000); |
| 226 | sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); |
| 227 | db_maybe_set_encryption_key(db, g.zRepositoryName); |
| 228 | if( g.zLocalDbName ){ |
| 229 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''", |
| 230 | g.zLocalDbName); |
| 231 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 232 | sqlite3_free(zSql); |
| @@ -240,19 +242,21 @@ | |
| 240 | (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */ |
| 241 | /* Arrange to trace close operations so that static prepared statements |
| 242 | ** will get cleaned up when the shell closes the database connection */ |
| 243 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 244 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 245 | db_protect_only(PROTECT_NONE); |
| 246 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 247 | if( local_bSqlCmdTest ){ |
| 248 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 249 | sqlcmd_db_protect, 0, 0); |
| 250 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 251 | sqlcmd_db_protect_pop, 0, 0); |
| 252 | sqlite3_create_function(db, "shared_secret", 2, SQLITE_UTF8, 0, |
| 253 | sha1_shared_secret_sql_function, 0, 0); |
| 254 | } |
| 255 | return SQLITE_OK; |
| 256 | } |
| 257 | |
| 258 | /* |
| 259 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -168,11 +168,11 @@ | |
| 168 | ** WARNING: |
| 169 | ** Do not instantiate these functions for any Fossil webpage or command |
| 170 | ** method other than the "fossil sql" command. If an attacker gains access |
| 171 | ** to these functions, he will be able to disable other defense mechanisms. |
| 172 | ** |
| 173 | ** This routines are for interactive testing only. They are experimental |
| 174 | ** and undocumented (apart from this comments) and might go away or change |
| 175 | ** in future releases. |
| 176 | ** |
| 177 | ** 2020-11-29: These functions are now only available if the "fossil sql" |
| 178 | ** command is started with the --test option. |
| @@ -221,12 +221,14 @@ | |
| 221 | helptext_vtab_register(db); |
| 222 | builtin_vtab_register(db); |
| 223 | g.repositoryOpen = 1; |
| 224 | g.db = db; |
| 225 | sqlite3_busy_timeout(db, 10000); |
| 226 | if( g.zRepositoryName ){ |
| 227 | sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); |
| 228 | db_maybe_set_encryption_key(db, g.zRepositoryName); |
| 229 | } |
| 230 | if( g.zLocalDbName ){ |
| 231 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''", |
| 232 | g.zLocalDbName); |
| 233 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 234 | sqlite3_free(zSql); |
| @@ -240,19 +242,21 @@ | |
| 242 | (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */ |
| 243 | /* Arrange to trace close operations so that static prepared statements |
| 244 | ** will get cleaned up when the shell closes the database connection */ |
| 245 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 246 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 247 | if( g.zRepositoryName ){ |
| 248 | db_protect_only(PROTECT_NONE); |
| 249 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 250 | if( local_bSqlCmdTest ){ |
| 251 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 252 | sqlcmd_db_protect, 0, 0); |
| 253 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 254 | sqlcmd_db_protect_pop, 0, 0); |
| 255 | sqlite3_create_function(db, "shared_secret", 2, SQLITE_UTF8, 0, |
| 256 | sha1_shared_secret_sql_function, 0, 0); |
| 257 | } |
| 258 | } |
| 259 | return SQLITE_OK; |
| 260 | } |
| 261 | |
| 262 | /* |
| 263 |
+1
-1
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -503,11 +503,11 @@ | ||
| 503 | 503 | ** access URL. |
| 504 | 504 | ** |
| 505 | 505 | ** Algorithm: |
| 506 | 506 | ** |
| 507 | 507 | ** The public URL is given by the email-url property. But it is only |
| 508 | -** returned if there have been one more more accesses (as recorded by | |
| 508 | +** returned if there have been one or more accesses (as recorded by | |
| 509 | 509 | ** "baseurl:URL" entries in the CONFIG table). |
| 510 | 510 | */ |
| 511 | 511 | const char *public_url(void){ |
| 512 | 512 | const char *zUrl = db_get("email-url", 0); |
| 513 | 513 | if( zUrl==0 ) return 0; |
| 514 | 514 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -503,11 +503,11 @@ | |
| 503 | ** access URL. |
| 504 | ** |
| 505 | ** Algorithm: |
| 506 | ** |
| 507 | ** The public URL is given by the email-url property. But it is only |
| 508 | ** returned if there have been one more more accesses (as recorded by |
| 509 | ** "baseurl:URL" entries in the CONFIG table). |
| 510 | */ |
| 511 | const char *public_url(void){ |
| 512 | const char *zUrl = db_get("email-url", 0); |
| 513 | if( zUrl==0 ) return 0; |
| 514 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -503,11 +503,11 @@ | |
| 503 | ** access URL. |
| 504 | ** |
| 505 | ** Algorithm: |
| 506 | ** |
| 507 | ** The public URL is given by the email-url property. But it is only |
| 508 | ** returned if there have been one or more accesses (as recorded by |
| 509 | ** "baseurl:URL" entries in the CONFIG table). |
| 510 | */ |
| 511 | const char *public_url(void){ |
| 512 | const char *zUrl = db_get("email-url", 0); |
| 513 | if( zUrl==0 ) return 0; |
| 514 |
+10
-5
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -226,11 +226,11 @@ | ||
| 226 | 226 | ** |
| 227 | 227 | ** As a defense against robots, the action=ARG might instead by data-action=ARG |
| 228 | 228 | ** and javascript (href.js) added to the page so that the data-action= is |
| 229 | 229 | ** changed into action= after the page loads. Whether or not this happens |
| 230 | 230 | ** depends on if the user has the "h" privilege and whether or not the |
| 231 | -** auto-hyperlink setting is on. These setings determine the values of | |
| 231 | +** auto-hyperlink setting is on. These settings determine the values of | |
| 232 | 232 | ** variables g.perm.Hyperlink and g.jsHref. |
| 233 | 233 | ** |
| 234 | 234 | ** User has "h" auto-hyperlink g.perm.Hyperlink g.jsHref |
| 235 | 235 | ** ------------ -------------- ---------------- -------- |
| 236 | 236 | ** 1: 0 0 0 0 |
| @@ -913,14 +913,14 @@ | ||
| 913 | 913 | @ </script> |
| 914 | 914 | builtin_fulfill_js_requests(); |
| 915 | 915 | } |
| 916 | 916 | |
| 917 | 917 | /* |
| 918 | -** Transorm input string into a token that is safe for inclusion into | |
| 918 | +** Transform input string into a token that is safe for inclusion into | |
| 919 | 919 | ** class attribute. Digits and low-case letter are passed unchanged, |
| 920 | 920 | ** upper-case letters are transformed to low-case, everything else is |
| 921 | -** tranformed into hyphens; consequtive and pending hyphens are squeezed. | |
| 921 | +** transformed into hyphens; consecutive and pending hyphens are squeezed. | |
| 922 | 922 | ** If result does not fit into szOut chars then it is truncated. |
| 923 | 923 | ** Result is always terminated with null. |
| 924 | 924 | */ |
| 925 | 925 | void style_derive_classname(const char *zIn, char *zOut, int szOut){ |
| 926 | 926 | assert( zOut ); |
| @@ -1219,11 +1219,11 @@ | ||
| 1219 | 1219 | |
| 1220 | 1220 | /* |
| 1221 | 1221 | ** Check for "name" or "page" query parameters on an /style.css |
| 1222 | 1222 | ** page request. If present, then page-specific CSS is requested, |
| 1223 | 1223 | ** so add that CSS to pOut. If the "name" and "page" query parameters |
| 1224 | -** are omitted, then pOut is unchnaged. | |
| 1224 | +** are omitted, then pOut is unchanged. | |
| 1225 | 1225 | */ |
| 1226 | 1226 | static void page_style_css_append_page_style(Blob *pOut){ |
| 1227 | 1227 | const char *zPage = PD("name",P("page")); |
| 1228 | 1228 | char * zFile; |
| 1229 | 1229 | int nFile = 0; |
| @@ -1252,11 +1252,11 @@ | ||
| 1252 | 1252 | } |
| 1253 | 1253 | |
| 1254 | 1254 | /* |
| 1255 | 1255 | ** WEBPAGE: style.css loadavg-exempt |
| 1256 | 1256 | ** |
| 1257 | -** Return the style sheet. The style sheet is assemblied from | |
| 1257 | +** Return the style sheet. The style sheet is assembled from | |
| 1258 | 1258 | ** multiple sources, in order: |
| 1259 | 1259 | ** |
| 1260 | 1260 | ** (1) The built-in "default.css" style sheet containing basic defaults. |
| 1261 | 1261 | ** |
| 1262 | 1262 | ** (2) The page-specific style sheet taken from the built-in |
| @@ -1442,10 +1442,15 @@ | ||
| 1442 | 1442 | @ g.zHttpsURL = %h(g.zHttpsURL)<br> |
| 1443 | 1443 | @ g.zTop = %h(g.zTop)<br> |
| 1444 | 1444 | @ g.zPath = %h(g.zPath)<br> |
| 1445 | 1445 | @ g.userUid = %d(g.userUid)<br> |
| 1446 | 1446 | @ g.zLogin = %h(g.zLogin)<br> |
| 1447 | + if( g.eAuthMethod!=AUTH_NONE ){ | |
| 1448 | + const char *zMethod[] = { "COOKIE", "LOCAL", "PW", "ENV", "HTTP" }; | |
| 1449 | + @ g.eAuthMethod = %d(g.eAuthMethod) (%h(zMethod[g.eAuthMethod-1]))\ | |
| 1450 | + @ <br> | |
| 1451 | + } | |
| 1447 | 1452 | @ g.isRobot = %d(g.isRobot)<br> |
| 1448 | 1453 | @ g.jsHref = %d(g.jsHref)<br> |
| 1449 | 1454 | if( g.zLocalRoot ){ |
| 1450 | 1455 | @ g.zLocalRoot = %h(g.zLocalRoot)<br> |
| 1451 | 1456 | }else{ |
| 1452 | 1457 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -226,11 +226,11 @@ | |
| 226 | ** |
| 227 | ** As a defense against robots, the action=ARG might instead by data-action=ARG |
| 228 | ** and javascript (href.js) added to the page so that the data-action= is |
| 229 | ** changed into action= after the page loads. Whether or not this happens |
| 230 | ** depends on if the user has the "h" privilege and whether or not the |
| 231 | ** auto-hyperlink setting is on. These setings determine the values of |
| 232 | ** variables g.perm.Hyperlink and g.jsHref. |
| 233 | ** |
| 234 | ** User has "h" auto-hyperlink g.perm.Hyperlink g.jsHref |
| 235 | ** ------------ -------------- ---------------- -------- |
| 236 | ** 1: 0 0 0 0 |
| @@ -913,14 +913,14 @@ | |
| 913 | @ </script> |
| 914 | builtin_fulfill_js_requests(); |
| 915 | } |
| 916 | |
| 917 | /* |
| 918 | ** Transorm input string into a token that is safe for inclusion into |
| 919 | ** class attribute. Digits and low-case letter are passed unchanged, |
| 920 | ** upper-case letters are transformed to low-case, everything else is |
| 921 | ** tranformed into hyphens; consequtive and pending hyphens are squeezed. |
| 922 | ** If result does not fit into szOut chars then it is truncated. |
| 923 | ** Result is always terminated with null. |
| 924 | */ |
| 925 | void style_derive_classname(const char *zIn, char *zOut, int szOut){ |
| 926 | assert( zOut ); |
| @@ -1219,11 +1219,11 @@ | |
| 1219 | |
| 1220 | /* |
| 1221 | ** Check for "name" or "page" query parameters on an /style.css |
| 1222 | ** page request. If present, then page-specific CSS is requested, |
| 1223 | ** so add that CSS to pOut. If the "name" and "page" query parameters |
| 1224 | ** are omitted, then pOut is unchnaged. |
| 1225 | */ |
| 1226 | static void page_style_css_append_page_style(Blob *pOut){ |
| 1227 | const char *zPage = PD("name",P("page")); |
| 1228 | char * zFile; |
| 1229 | int nFile = 0; |
| @@ -1252,11 +1252,11 @@ | |
| 1252 | } |
| 1253 | |
| 1254 | /* |
| 1255 | ** WEBPAGE: style.css loadavg-exempt |
| 1256 | ** |
| 1257 | ** Return the style sheet. The style sheet is assemblied from |
| 1258 | ** multiple sources, in order: |
| 1259 | ** |
| 1260 | ** (1) The built-in "default.css" style sheet containing basic defaults. |
| 1261 | ** |
| 1262 | ** (2) The page-specific style sheet taken from the built-in |
| @@ -1442,10 +1442,15 @@ | |
| 1442 | @ g.zHttpsURL = %h(g.zHttpsURL)<br> |
| 1443 | @ g.zTop = %h(g.zTop)<br> |
| 1444 | @ g.zPath = %h(g.zPath)<br> |
| 1445 | @ g.userUid = %d(g.userUid)<br> |
| 1446 | @ g.zLogin = %h(g.zLogin)<br> |
| 1447 | @ g.isRobot = %d(g.isRobot)<br> |
| 1448 | @ g.jsHref = %d(g.jsHref)<br> |
| 1449 | if( g.zLocalRoot ){ |
| 1450 | @ g.zLocalRoot = %h(g.zLocalRoot)<br> |
| 1451 | }else{ |
| 1452 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -226,11 +226,11 @@ | |
| 226 | ** |
| 227 | ** As a defense against robots, the action=ARG might instead by data-action=ARG |
| 228 | ** and javascript (href.js) added to the page so that the data-action= is |
| 229 | ** changed into action= after the page loads. Whether or not this happens |
| 230 | ** depends on if the user has the "h" privilege and whether or not the |
| 231 | ** auto-hyperlink setting is on. These settings determine the values of |
| 232 | ** variables g.perm.Hyperlink and g.jsHref. |
| 233 | ** |
| 234 | ** User has "h" auto-hyperlink g.perm.Hyperlink g.jsHref |
| 235 | ** ------------ -------------- ---------------- -------- |
| 236 | ** 1: 0 0 0 0 |
| @@ -913,14 +913,14 @@ | |
| 913 | @ </script> |
| 914 | builtin_fulfill_js_requests(); |
| 915 | } |
| 916 | |
| 917 | /* |
| 918 | ** Transform input string into a token that is safe for inclusion into |
| 919 | ** class attribute. Digits and low-case letter are passed unchanged, |
| 920 | ** upper-case letters are transformed to low-case, everything else is |
| 921 | ** transformed into hyphens; consecutive and pending hyphens are squeezed. |
| 922 | ** If result does not fit into szOut chars then it is truncated. |
| 923 | ** Result is always terminated with null. |
| 924 | */ |
| 925 | void style_derive_classname(const char *zIn, char *zOut, int szOut){ |
| 926 | assert( zOut ); |
| @@ -1219,11 +1219,11 @@ | |
| 1219 | |
| 1220 | /* |
| 1221 | ** Check for "name" or "page" query parameters on an /style.css |
| 1222 | ** page request. If present, then page-specific CSS is requested, |
| 1223 | ** so add that CSS to pOut. If the "name" and "page" query parameters |
| 1224 | ** are omitted, then pOut is unchanged. |
| 1225 | */ |
| 1226 | static void page_style_css_append_page_style(Blob *pOut){ |
| 1227 | const char *zPage = PD("name",P("page")); |
| 1228 | char * zFile; |
| 1229 | int nFile = 0; |
| @@ -1252,11 +1252,11 @@ | |
| 1252 | } |
| 1253 | |
| 1254 | /* |
| 1255 | ** WEBPAGE: style.css loadavg-exempt |
| 1256 | ** |
| 1257 | ** Return the style sheet. The style sheet is assembled from |
| 1258 | ** multiple sources, in order: |
| 1259 | ** |
| 1260 | ** (1) The built-in "default.css" style sheet containing basic defaults. |
| 1261 | ** |
| 1262 | ** (2) The page-specific style sheet taken from the built-in |
| @@ -1442,10 +1442,15 @@ | |
| 1442 | @ g.zHttpsURL = %h(g.zHttpsURL)<br> |
| 1443 | @ g.zTop = %h(g.zTop)<br> |
| 1444 | @ g.zPath = %h(g.zPath)<br> |
| 1445 | @ g.userUid = %d(g.userUid)<br> |
| 1446 | @ g.zLogin = %h(g.zLogin)<br> |
| 1447 | if( g.eAuthMethod!=AUTH_NONE ){ |
| 1448 | const char *zMethod[] = { "COOKIE", "LOCAL", "PW", "ENV", "HTTP" }; |
| 1449 | @ g.eAuthMethod = %d(g.eAuthMethod) (%h(zMethod[g.eAuthMethod-1]))\ |
| 1450 | @ <br> |
| 1451 | } |
| 1452 | @ g.isRobot = %d(g.isRobot)<br> |
| 1453 | @ g.jsHref = %d(g.jsHref)<br> |
| 1454 | if( g.zLocalRoot ){ |
| 1455 | @ g.zLocalRoot = %h(g.zLocalRoot)<br> |
| 1456 | }else{ |
| 1457 |
+17
-5
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -23,11 +23,11 @@ | ||
| 23 | 23 | |
| 24 | 24 | /* |
| 25 | 25 | ** Explain what type of sync operation is about to occur |
| 26 | 26 | */ |
| 27 | 27 | static void sync_explain(unsigned syncFlags){ |
| 28 | - if( g.url.isAlias ){ | |
| 28 | + if( g.url.isAlias && (syncFlags & SYNC_QUIET)==0 ){ | |
| 29 | 29 | const char *url; |
| 30 | 30 | if( g.url.useProxy ){ |
| 31 | 31 | url = g.url.proxyUrlCanonical; |
| 32 | 32 | }else{ |
| 33 | 33 | url = g.url.canonical; |
| @@ -36,10 +36,12 @@ | ||
| 36 | 36 | fossil_print("Sync with %s\n", url); |
| 37 | 37 | }else if( syncFlags & SYNC_PUSH ){ |
| 38 | 38 | fossil_print("Push to %s\n", url); |
| 39 | 39 | }else if( syncFlags & SYNC_PULL ){ |
| 40 | 40 | fossil_print("Pull from %s\n", url); |
| 41 | + }else if( syncFlags & SYNC_PING ){ | |
| 42 | + fossil_print("Ping %s\n", url); | |
| 41 | 43 | } |
| 42 | 44 | } |
| 43 | 45 | } |
| 44 | 46 | |
| 45 | 47 | |
| @@ -307,11 +309,11 @@ | ||
| 307 | 309 | ** URL is the server name. PAYLOAD is the name of a temporary file |
| 308 | 310 | ** that will contain the xfer-protocol payload to send to the server. |
| 309 | 311 | ** REPLY is a temporary filename in which COMMAND should write the |
| 310 | 312 | ** content of the reply from the server. |
| 311 | 313 | ** |
| 312 | - ** CMD is reponsible for HTTP redirects. The following Fossil command | |
| 314 | + ** CMD is responsible for HTTP redirects. The following Fossil command | |
| 313 | 315 | ** can be used for CMD to achieve a working sync: |
| 314 | 316 | ** |
| 315 | 317 | ** fossil test-httpmsg --xfer |
| 316 | 318 | */ |
| 317 | 319 | g.zHttpCmd = find_option("transport-command",0,1); |
| @@ -482,12 +484,14 @@ | ||
| 482 | 484 | ** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol, |
| 483 | 485 | ** if required by the remote website |
| 484 | 486 | ** --ipv4 Use only IPv4, not IPv6 |
| 485 | 487 | ** --no-http-compression Do not compress HTTP traffic |
| 486 | 488 | ** --once Do not remember URL for subsequent syncs |
| 489 | +** --ping Just verify that the server is alive | |
| 487 | 490 | ** --proxy PROXY Use the specified HTTP proxy |
| 488 | 491 | ** --private Sync private branches too |
| 492 | +** -q|--quiet Omit all output | |
| 489 | 493 | ** -R|--repository REPO Local repository to sync with |
| 490 | 494 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 491 | 495 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 492 | 496 | ** --transport-command CMD Use external command CMD to move message |
| 493 | 497 | ** between the client and the server |
| @@ -502,19 +506,27 @@ | ||
| 502 | 506 | void sync_cmd(void){ |
| 503 | 507 | unsigned configFlags = 0; |
| 504 | 508 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 505 | 509 | if( find_option("unversioned","u",0)!=0 ){ |
| 506 | 510 | syncFlags |= SYNC_UNVERSIONED; |
| 511 | + } | |
| 512 | + if( find_option("ping",0,0)!=0 ){ | |
| 513 | + syncFlags = SYNC_PING; | |
| 514 | + } | |
| 515 | + if( g.fQuiet ){ | |
| 516 | + syncFlags |= SYNC_QUIET; | |
| 507 | 517 | } |
| 508 | 518 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| 509 | 519 | |
| 510 | 520 | /* We should be done with options.. */ |
| 511 | 521 | verify_all_options(); |
| 512 | 522 | |
| 513 | - if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; | |
| 514 | - if( (syncFlags & SYNC_PUSH)==0 ){ | |
| 515 | - fossil_warning("pull only: the 'dont-push' option is set"); | |
| 523 | + if( (syncFlags & SYNC_PING)==0 ){ | |
| 524 | + if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; | |
| 525 | + if( (syncFlags & SYNC_PUSH)==0 ){ | |
| 526 | + fossil_warning("pull only: the 'dont-push' option is set"); | |
| 527 | + } | |
| 516 | 528 | } |
| 517 | 529 | client_sync_all_urls(syncFlags, configFlags, 0, 0); |
| 518 | 530 | } |
| 519 | 531 | |
| 520 | 532 | /* |
| 521 | 533 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -23,11 +23,11 @@ | |
| 23 | |
| 24 | /* |
| 25 | ** Explain what type of sync operation is about to occur |
| 26 | */ |
| 27 | static void sync_explain(unsigned syncFlags){ |
| 28 | if( g.url.isAlias ){ |
| 29 | const char *url; |
| 30 | if( g.url.useProxy ){ |
| 31 | url = g.url.proxyUrlCanonical; |
| 32 | }else{ |
| 33 | url = g.url.canonical; |
| @@ -36,10 +36,12 @@ | |
| 36 | fossil_print("Sync with %s\n", url); |
| 37 | }else if( syncFlags & SYNC_PUSH ){ |
| 38 | fossil_print("Push to %s\n", url); |
| 39 | }else if( syncFlags & SYNC_PULL ){ |
| 40 | fossil_print("Pull from %s\n", url); |
| 41 | } |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | |
| @@ -307,11 +309,11 @@ | |
| 307 | ** URL is the server name. PAYLOAD is the name of a temporary file |
| 308 | ** that will contain the xfer-protocol payload to send to the server. |
| 309 | ** REPLY is a temporary filename in which COMMAND should write the |
| 310 | ** content of the reply from the server. |
| 311 | ** |
| 312 | ** CMD is reponsible for HTTP redirects. The following Fossil command |
| 313 | ** can be used for CMD to achieve a working sync: |
| 314 | ** |
| 315 | ** fossil test-httpmsg --xfer |
| 316 | */ |
| 317 | g.zHttpCmd = find_option("transport-command",0,1); |
| @@ -482,12 +484,14 @@ | |
| 482 | ** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol, |
| 483 | ** if required by the remote website |
| 484 | ** --ipv4 Use only IPv4, not IPv6 |
| 485 | ** --no-http-compression Do not compress HTTP traffic |
| 486 | ** --once Do not remember URL for subsequent syncs |
| 487 | ** --proxy PROXY Use the specified HTTP proxy |
| 488 | ** --private Sync private branches too |
| 489 | ** -R|--repository REPO Local repository to sync with |
| 490 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 491 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 492 | ** --transport-command CMD Use external command CMD to move message |
| 493 | ** between the client and the server |
| @@ -502,19 +506,27 @@ | |
| 502 | void sync_cmd(void){ |
| 503 | unsigned configFlags = 0; |
| 504 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 505 | if( find_option("unversioned","u",0)!=0 ){ |
| 506 | syncFlags |= SYNC_UNVERSIONED; |
| 507 | } |
| 508 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| 509 | |
| 510 | /* We should be done with options.. */ |
| 511 | verify_all_options(); |
| 512 | |
| 513 | if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; |
| 514 | if( (syncFlags & SYNC_PUSH)==0 ){ |
| 515 | fossil_warning("pull only: the 'dont-push' option is set"); |
| 516 | } |
| 517 | client_sync_all_urls(syncFlags, configFlags, 0, 0); |
| 518 | } |
| 519 | |
| 520 | /* |
| 521 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -23,11 +23,11 @@ | |
| 23 | |
| 24 | /* |
| 25 | ** Explain what type of sync operation is about to occur |
| 26 | */ |
| 27 | static void sync_explain(unsigned syncFlags){ |
| 28 | if( g.url.isAlias && (syncFlags & SYNC_QUIET)==0 ){ |
| 29 | const char *url; |
| 30 | if( g.url.useProxy ){ |
| 31 | url = g.url.proxyUrlCanonical; |
| 32 | }else{ |
| 33 | url = g.url.canonical; |
| @@ -36,10 +36,12 @@ | |
| 36 | fossil_print("Sync with %s\n", url); |
| 37 | }else if( syncFlags & SYNC_PUSH ){ |
| 38 | fossil_print("Push to %s\n", url); |
| 39 | }else if( syncFlags & SYNC_PULL ){ |
| 40 | fossil_print("Pull from %s\n", url); |
| 41 | }else if( syncFlags & SYNC_PING ){ |
| 42 | fossil_print("Ping %s\n", url); |
| 43 | } |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | |
| @@ -307,11 +309,11 @@ | |
| 309 | ** URL is the server name. PAYLOAD is the name of a temporary file |
| 310 | ** that will contain the xfer-protocol payload to send to the server. |
| 311 | ** REPLY is a temporary filename in which COMMAND should write the |
| 312 | ** content of the reply from the server. |
| 313 | ** |
| 314 | ** CMD is responsible for HTTP redirects. The following Fossil command |
| 315 | ** can be used for CMD to achieve a working sync: |
| 316 | ** |
| 317 | ** fossil test-httpmsg --xfer |
| 318 | */ |
| 319 | g.zHttpCmd = find_option("transport-command",0,1); |
| @@ -482,12 +484,14 @@ | |
| 484 | ** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol, |
| 485 | ** if required by the remote website |
| 486 | ** --ipv4 Use only IPv4, not IPv6 |
| 487 | ** --no-http-compression Do not compress HTTP traffic |
| 488 | ** --once Do not remember URL for subsequent syncs |
| 489 | ** --ping Just verify that the server is alive |
| 490 | ** --proxy PROXY Use the specified HTTP proxy |
| 491 | ** --private Sync private branches too |
| 492 | ** -q|--quiet Omit all output |
| 493 | ** -R|--repository REPO Local repository to sync with |
| 494 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 495 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 496 | ** --transport-command CMD Use external command CMD to move message |
| 497 | ** between the client and the server |
| @@ -502,19 +506,27 @@ | |
| 506 | void sync_cmd(void){ |
| 507 | unsigned configFlags = 0; |
| 508 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 509 | if( find_option("unversioned","u",0)!=0 ){ |
| 510 | syncFlags |= SYNC_UNVERSIONED; |
| 511 | } |
| 512 | if( find_option("ping",0,0)!=0 ){ |
| 513 | syncFlags = SYNC_PING; |
| 514 | } |
| 515 | if( g.fQuiet ){ |
| 516 | syncFlags |= SYNC_QUIET; |
| 517 | } |
| 518 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| 519 | |
| 520 | /* We should be done with options.. */ |
| 521 | verify_all_options(); |
| 522 | |
| 523 | if( (syncFlags & SYNC_PING)==0 ){ |
| 524 | if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; |
| 525 | if( (syncFlags & SYNC_PUSH)==0 ){ |
| 526 | fossil_warning("pull only: the 'dont-push' option is set"); |
| 527 | } |
| 528 | } |
| 529 | client_sync_all_urls(syncFlags, configFlags, 0, 0); |
| 530 | } |
| 531 | |
| 532 | /* |
| 533 |
+1
-1
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -422,11 +422,11 @@ | ||
| 422 | 422 | ** |
| 423 | 423 | ** > fossil tag cancel ?--raw? TAGNAME ARTIFACT-ID |
| 424 | 424 | ** |
| 425 | 425 | ** Remove the tag TAGNAME from the artifact referenced by |
| 426 | 426 | ** ARTIFACT-ID, and also remove the propagation of the tag to |
| 427 | -** any descendants. Use the the -n|--dry-run option to see | |
| 427 | +** any descendants. Use the -n|--dry-run option to see | |
| 428 | 428 | ** what would have happened. Certain tag name prefixes are |
| 429 | 429 | ** forbidden, as documented for the 'add' subcommand. |
| 430 | 430 | ** |
| 431 | 431 | ** Options: |
| 432 | 432 | ** --date-override DATETIME Set date and time deleted |
| 433 | 433 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -422,11 +422,11 @@ | |
| 422 | ** |
| 423 | ** > fossil tag cancel ?--raw? TAGNAME ARTIFACT-ID |
| 424 | ** |
| 425 | ** Remove the tag TAGNAME from the artifact referenced by |
| 426 | ** ARTIFACT-ID, and also remove the propagation of the tag to |
| 427 | ** any descendants. Use the the -n|--dry-run option to see |
| 428 | ** what would have happened. Certain tag name prefixes are |
| 429 | ** forbidden, as documented for the 'add' subcommand. |
| 430 | ** |
| 431 | ** Options: |
| 432 | ** --date-override DATETIME Set date and time deleted |
| 433 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -422,11 +422,11 @@ | |
| 422 | ** |
| 423 | ** > fossil tag cancel ?--raw? TAGNAME ARTIFACT-ID |
| 424 | ** |
| 425 | ** Remove the tag TAGNAME from the artifact referenced by |
| 426 | ** ARTIFACT-ID, and also remove the propagation of the tag to |
| 427 | ** any descendants. Use the -n|--dry-run option to see |
| 428 | ** what would have happened. Certain tag name prefixes are |
| 429 | ** forbidden, as documented for the 'add' subcommand. |
| 430 | ** |
| 431 | ** Options: |
| 432 | ** --date-override DATETIME Set date and time deleted |
| 433 |
+3
-3
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -809,11 +809,11 @@ | ||
| 809 | 809 | ** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz |
| 810 | 810 | ** |
| 811 | 811 | ** In other words, filename itself contains sufficient information to |
| 812 | 812 | ** uniquely identify the check-in, including a timestamp of the form |
| 813 | 813 | ** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp |
| 814 | -** and hash must immediately preceed the first "." in the name. | |
| 814 | +** and hash must immediately precede the first "." in the name. | |
| 815 | 815 | */ |
| 816 | 816 | char *tar_uuid_from_name(char **pzName){ |
| 817 | 817 | char *zName = *pzName; /* Original input */ |
| 818 | 818 | int n1 = 0; /* Bytes in first prefix (tag-name) */ |
| 819 | 819 | int n2 = 0; /* Bytes in second prefix (check-in-name) */ |
| @@ -915,11 +915,11 @@ | ||
| 915 | 915 | ** part of the name= value after the / is the download |
| 916 | 916 | ** filename. If no check-in is specified by either |
| 917 | 917 | ** name= or r=, then the name of the main branch |
| 918 | 918 | ** (usually "trunk") is used. |
| 919 | 919 | ** |
| 920 | -** in=PATTERN Only include files that match the comma-separate | |
| 920 | +** in=PATTERN Only include files that match the comma-separated | |
| 921 | 921 | ** list of GLOB patterns in PATTERN, as with ex= |
| 922 | 922 | ** |
| 923 | 923 | ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a |
| 924 | 924 | ** comma-separated list of GLOB patterns, where each |
| 925 | 925 | ** pattern can optionally be quoted using ".." or '..'. |
| @@ -1167,11 +1167,11 @@ | ||
| 1167 | 1167 | &azItem, &anItem, &nItem); |
| 1168 | 1168 | bPlainTextCom = db_get_boolean("timeline-plaintext",0); |
| 1169 | 1169 | for(i=0; i<nItem-3; i+=4){ |
| 1170 | 1170 | int cnt; /* The number of instances of zLabel to use */ |
| 1171 | 1171 | char *zLabel; /* The label to match */ |
| 1172 | - double rStart; /* Starting time, julian day number */ | |
| 1172 | + double rStart; /* Starting time, Julian day number */ | |
| 1173 | 1173 | char *zComment = 0; /* Comment to apply */ |
| 1174 | 1174 | if( anItem[i]==1 && azItem[i][0]=='*' ){ |
| 1175 | 1175 | cnt = -1; |
| 1176 | 1176 | }else if( anItem[i]<1 ){ |
| 1177 | 1177 | cnt = 0; |
| 1178 | 1178 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -809,11 +809,11 @@ | |
| 809 | ** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz |
| 810 | ** |
| 811 | ** In other words, filename itself contains sufficient information to |
| 812 | ** uniquely identify the check-in, including a timestamp of the form |
| 813 | ** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp |
| 814 | ** and hash must immediately preceed the first "." in the name. |
| 815 | */ |
| 816 | char *tar_uuid_from_name(char **pzName){ |
| 817 | char *zName = *pzName; /* Original input */ |
| 818 | int n1 = 0; /* Bytes in first prefix (tag-name) */ |
| 819 | int n2 = 0; /* Bytes in second prefix (check-in-name) */ |
| @@ -915,11 +915,11 @@ | |
| 915 | ** part of the name= value after the / is the download |
| 916 | ** filename. If no check-in is specified by either |
| 917 | ** name= or r=, then the name of the main branch |
| 918 | ** (usually "trunk") is used. |
| 919 | ** |
| 920 | ** in=PATTERN Only include files that match the comma-separate |
| 921 | ** list of GLOB patterns in PATTERN, as with ex= |
| 922 | ** |
| 923 | ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a |
| 924 | ** comma-separated list of GLOB patterns, where each |
| 925 | ** pattern can optionally be quoted using ".." or '..'. |
| @@ -1167,11 +1167,11 @@ | |
| 1167 | &azItem, &anItem, &nItem); |
| 1168 | bPlainTextCom = db_get_boolean("timeline-plaintext",0); |
| 1169 | for(i=0; i<nItem-3; i+=4){ |
| 1170 | int cnt; /* The number of instances of zLabel to use */ |
| 1171 | char *zLabel; /* The label to match */ |
| 1172 | double rStart; /* Starting time, julian day number */ |
| 1173 | char *zComment = 0; /* Comment to apply */ |
| 1174 | if( anItem[i]==1 && azItem[i][0]=='*' ){ |
| 1175 | cnt = -1; |
| 1176 | }else if( anItem[i]<1 ){ |
| 1177 | cnt = 0; |
| 1178 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -809,11 +809,11 @@ | |
| 809 | ** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz |
| 810 | ** |
| 811 | ** In other words, filename itself contains sufficient information to |
| 812 | ** uniquely identify the check-in, including a timestamp of the form |
| 813 | ** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp |
| 814 | ** and hash must immediately precede the first "." in the name. |
| 815 | */ |
| 816 | char *tar_uuid_from_name(char **pzName){ |
| 817 | char *zName = *pzName; /* Original input */ |
| 818 | int n1 = 0; /* Bytes in first prefix (tag-name) */ |
| 819 | int n2 = 0; /* Bytes in second prefix (check-in-name) */ |
| @@ -915,11 +915,11 @@ | |
| 915 | ** part of the name= value after the / is the download |
| 916 | ** filename. If no check-in is specified by either |
| 917 | ** name= or r=, then the name of the main branch |
| 918 | ** (usually "trunk") is used. |
| 919 | ** |
| 920 | ** in=PATTERN Only include files that match the comma-separated |
| 921 | ** list of GLOB patterns in PATTERN, as with ex= |
| 922 | ** |
| 923 | ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a |
| 924 | ** comma-separated list of GLOB patterns, where each |
| 925 | ** pattern can optionally be quoted using ".." or '..'. |
| @@ -1167,11 +1167,11 @@ | |
| 1167 | &azItem, &anItem, &nItem); |
| 1168 | bPlainTextCom = db_get_boolean("timeline-plaintext",0); |
| 1169 | for(i=0; i<nItem-3; i+=4){ |
| 1170 | int cnt; /* The number of instances of zLabel to use */ |
| 1171 | char *zLabel; /* The label to match */ |
| 1172 | double rStart; /* Starting time, Julian day number */ |
| 1173 | char *zComment = 0; /* Comment to apply */ |
| 1174 | if( anItem[i]==1 && azItem[i][0]=='*' ){ |
| 1175 | cnt = -1; |
| 1176 | }else if( anItem[i]<1 ){ |
| 1177 | cnt = 0; |
| 1178 |
+1
-1
| --- src/terminal.c | ||
| +++ src/terminal.c | ||
| @@ -45,11 +45,11 @@ | ||
| 45 | 45 | #endif |
| 46 | 46 | |
| 47 | 47 | |
| 48 | 48 | /* Get the current terminal size by calling a system service. |
| 49 | 49 | ** |
| 50 | -** Return 1 on success. This sets the size parameters to the values retured by | |
| 50 | +** Return 1 on success. This sets the size parameters to the values returned by | |
| 51 | 51 | ** the system call, when such is supported; set the size to zero otherwise. |
| 52 | 52 | ** Return 0 on the system service call failure. |
| 53 | 53 | ** |
| 54 | 54 | ** Under Linux/bash the size info is also available from env $LINES, $COLUMNS. |
| 55 | 55 | ** Or it can be queried using tput `echo -e "lines\ncols"|tput -S`. |
| 56 | 56 |
| --- src/terminal.c | |
| +++ src/terminal.c | |
| @@ -45,11 +45,11 @@ | |
| 45 | #endif |
| 46 | |
| 47 | |
| 48 | /* Get the current terminal size by calling a system service. |
| 49 | ** |
| 50 | ** Return 1 on success. This sets the size parameters to the values retured by |
| 51 | ** the system call, when such is supported; set the size to zero otherwise. |
| 52 | ** Return 0 on the system service call failure. |
| 53 | ** |
| 54 | ** Under Linux/bash the size info is also available from env $LINES, $COLUMNS. |
| 55 | ** Or it can be queried using tput `echo -e "lines\ncols"|tput -S`. |
| 56 |
| --- src/terminal.c | |
| +++ src/terminal.c | |
| @@ -45,11 +45,11 @@ | |
| 45 | #endif |
| 46 | |
| 47 | |
| 48 | /* Get the current terminal size by calling a system service. |
| 49 | ** |
| 50 | ** Return 1 on success. This sets the size parameters to the values returned by |
| 51 | ** the system call, when such is supported; set the size to zero otherwise. |
| 52 | ** Return 0 on the system service call failure. |
| 53 | ** |
| 54 | ** Under Linux/bash the size info is also available from env $LINES, $COLUMNS. |
| 55 | ** Or it can be queried using tput `echo -e "lines\ncols"|tput -S`. |
| 56 |
M
src/th.c
+1
-1
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -1029,11 +1029,11 @@ | ||
| 1029 | 1029 | ** * If iFrame is negative, then the nth frame up the stack, where |
| 1030 | 1030 | ** n is the absolute value of iFrame. A value of -1 means the |
| 1031 | 1031 | ** calling procedure. |
| 1032 | 1032 | ** |
| 1033 | 1033 | ** * If iFrame is +ve, then the nth frame from the bottom of the |
| 1034 | -** stack. An iFrame value of 1 means the toplevel (global) frame. | |
| 1034 | +** stack. An iFrame value of 1 means the top level (global) frame. | |
| 1035 | 1035 | */ |
| 1036 | 1036 | static Th_Frame *getFrame(Th_Interp *interp, int iFrame){ |
| 1037 | 1037 | Th_Frame *p = interp->pFrame; |
| 1038 | 1038 | int i; |
| 1039 | 1039 | if( iFrame>0 ){ |
| 1040 | 1040 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1029,11 +1029,11 @@ | |
| 1029 | ** * If iFrame is negative, then the nth frame up the stack, where |
| 1030 | ** n is the absolute value of iFrame. A value of -1 means the |
| 1031 | ** calling procedure. |
| 1032 | ** |
| 1033 | ** * If iFrame is +ve, then the nth frame from the bottom of the |
| 1034 | ** stack. An iFrame value of 1 means the toplevel (global) frame. |
| 1035 | */ |
| 1036 | static Th_Frame *getFrame(Th_Interp *interp, int iFrame){ |
| 1037 | Th_Frame *p = interp->pFrame; |
| 1038 | int i; |
| 1039 | if( iFrame>0 ){ |
| 1040 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1029,11 +1029,11 @@ | |
| 1029 | ** * If iFrame is negative, then the nth frame up the stack, where |
| 1030 | ** n is the absolute value of iFrame. A value of -1 means the |
| 1031 | ** calling procedure. |
| 1032 | ** |
| 1033 | ** * If iFrame is +ve, then the nth frame from the bottom of the |
| 1034 | ** stack. An iFrame value of 1 means the top level (global) frame. |
| 1035 | */ |
| 1036 | static Th_Frame *getFrame(Th_Interp *interp, int iFrame){ |
| 1037 | Th_Frame *p = interp->pFrame; |
| 1038 | int i; |
| 1039 | if( iFrame>0 ){ |
| 1040 |
+5
-5
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -74,11 +74,11 @@ | ||
| 74 | 74 | */ |
| 75 | 75 | #if defined(TH_MEMDEBUG) |
| 76 | 76 | /* |
| 77 | 77 | ** Global variable counting the number of outstanding calls to malloc() |
| 78 | 78 | ** made by the th1 implementation. This is used to catch memory leaks |
| 79 | -** in the interpreter. Obviously, it also means th1 is not threadsafe. | |
| 79 | +** in the interpreter. Obviously, it also means th1 is not thread-safe. | |
| 80 | 80 | */ |
| 81 | 81 | static int nOutstandingMalloc = 0; |
| 82 | 82 | |
| 83 | 83 | /* |
| 84 | 84 | ** Implementations of malloc() and free() to pass to the interpreter. |
| @@ -151,13 +151,13 @@ | ||
| 151 | 151 | fossil_print("\n------------------- END TRACE LOG -------------------\n"); |
| 152 | 152 | } |
| 153 | 153 | } |
| 154 | 154 | |
| 155 | 155 | /* |
| 156 | -** - adopted from ls_cmd_rev in checkin.c | |
| 157 | -** - adopted commands/error handling for usage within th1 | |
| 158 | -** - interface adopted to allow result creation as TH1 List | |
| 156 | +** - adapted from ls_cmd_rev in checkin.c | |
| 157 | +** - adapted commands/error handling for usage within th1 | |
| 158 | +** - interface adapted to allow result creation as TH1 List | |
| 159 | 159 | ** |
| 160 | 160 | ** Takes a check-in identifier in zRev and an optiona glob pattern in zGLOB |
| 161 | 161 | ** as parameter returns a TH list in pzList,pnList with filenames matching |
| 162 | 162 | ** glob pattern with the checking |
| 163 | 163 | */ |
| @@ -1065,11 +1065,11 @@ | ||
| 1065 | 1065 | |
| 1066 | 1066 | |
| 1067 | 1067 | /* |
| 1068 | 1068 | ** TH1 command: anycap STRING |
| 1069 | 1069 | ** |
| 1070 | -** Return true if the current user user | |
| 1070 | +** Return true if the current user | |
| 1071 | 1071 | ** has any one of the capabilities listed in STRING. |
| 1072 | 1072 | */ |
| 1073 | 1073 | static int anycapCmd( |
| 1074 | 1074 | Th_Interp *interp, |
| 1075 | 1075 | void *p, |
| 1076 | 1076 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -74,11 +74,11 @@ | |
| 74 | */ |
| 75 | #if defined(TH_MEMDEBUG) |
| 76 | /* |
| 77 | ** Global variable counting the number of outstanding calls to malloc() |
| 78 | ** made by the th1 implementation. This is used to catch memory leaks |
| 79 | ** in the interpreter. Obviously, it also means th1 is not threadsafe. |
| 80 | */ |
| 81 | static int nOutstandingMalloc = 0; |
| 82 | |
| 83 | /* |
| 84 | ** Implementations of malloc() and free() to pass to the interpreter. |
| @@ -151,13 +151,13 @@ | |
| 151 | fossil_print("\n------------------- END TRACE LOG -------------------\n"); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | /* |
| 156 | ** - adopted from ls_cmd_rev in checkin.c |
| 157 | ** - adopted commands/error handling for usage within th1 |
| 158 | ** - interface adopted to allow result creation as TH1 List |
| 159 | ** |
| 160 | ** Takes a check-in identifier in zRev and an optiona glob pattern in zGLOB |
| 161 | ** as parameter returns a TH list in pzList,pnList with filenames matching |
| 162 | ** glob pattern with the checking |
| 163 | */ |
| @@ -1065,11 +1065,11 @@ | |
| 1065 | |
| 1066 | |
| 1067 | /* |
| 1068 | ** TH1 command: anycap STRING |
| 1069 | ** |
| 1070 | ** Return true if the current user user |
| 1071 | ** has any one of the capabilities listed in STRING. |
| 1072 | */ |
| 1073 | static int anycapCmd( |
| 1074 | Th_Interp *interp, |
| 1075 | void *p, |
| 1076 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -74,11 +74,11 @@ | |
| 74 | */ |
| 75 | #if defined(TH_MEMDEBUG) |
| 76 | /* |
| 77 | ** Global variable counting the number of outstanding calls to malloc() |
| 78 | ** made by the th1 implementation. This is used to catch memory leaks |
| 79 | ** in the interpreter. Obviously, it also means th1 is not thread-safe. |
| 80 | */ |
| 81 | static int nOutstandingMalloc = 0; |
| 82 | |
| 83 | /* |
| 84 | ** Implementations of malloc() and free() to pass to the interpreter. |
| @@ -151,13 +151,13 @@ | |
| 151 | fossil_print("\n------------------- END TRACE LOG -------------------\n"); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | /* |
| 156 | ** - adapted from ls_cmd_rev in checkin.c |
| 157 | ** - adapted commands/error handling for usage within th1 |
| 158 | ** - interface adapted to allow result creation as TH1 List |
| 159 | ** |
| 160 | ** Takes a check-in identifier in zRev and an optiona glob pattern in zGLOB |
| 161 | ** as parameter returns a TH list in pzList,pnList with filenames matching |
| 162 | ** glob pattern with the checking |
| 163 | */ |
| @@ -1065,11 +1065,11 @@ | |
| 1065 | |
| 1066 | |
| 1067 | /* |
| 1068 | ** TH1 command: anycap STRING |
| 1069 | ** |
| 1070 | ** Return true if the current user |
| 1071 | ** has any one of the capabilities listed in STRING. |
| 1072 | */ |
| 1073 | static int anycapCmd( |
| 1074 | Th_Interp *interp, |
| 1075 | void *p, |
| 1076 |
+19
-18
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -22,11 +22,11 @@ | ||
| 22 | 22 | #include <string.h> |
| 23 | 23 | #include <time.h> |
| 24 | 24 | #include "timeline.h" |
| 25 | 25 | |
| 26 | 26 | /* |
| 27 | -** The value of one second in julianday notation | |
| 27 | +** The value of one second in Julian day notation | |
| 28 | 28 | */ |
| 29 | 29 | #define ONE_SECOND (1.0/86400.0) |
| 30 | 30 | |
| 31 | 31 | /* |
| 32 | 32 | ** timeline mode options |
| @@ -730,10 +730,14 @@ | ||
| 730 | 730 | ** will simply be rendered in the older format. */ |
| 731 | 731 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 732 | 732 | } |
| 733 | 733 | wiki_hyperlink_override(0); |
| 734 | 734 | }else{ |
| 735 | + if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ | |
| 736 | + blob_truncate_utf8(&comment, mxWikiLen); | |
| 737 | + blob_append(&comment, "...", 3); | |
| 738 | + } | |
| 735 | 739 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 736 | 740 | } |
| 737 | 741 | }else{ |
| 738 | 742 | if( bCommentGitStyle ){ |
| 739 | 743 | /* Truncate comment at first blank line */ |
| @@ -747,16 +751,13 @@ | ||
| 747 | 751 | } |
| 748 | 752 | } |
| 749 | 753 | z[ii] = 0; |
| 750 | 754 | cgi_printf("%W",z); |
| 751 | 755 | }else if( mxWikiLen>0 && (int)blob_size(&comment)>mxWikiLen ){ |
| 752 | - Blob truncated; | |
| 753 | - blob_zero(&truncated); | |
| 754 | - blob_append(&truncated, blob_buffer(&comment), mxWikiLen); | |
| 755 | - blob_append(&truncated, "...", 3); | |
| 756 | - @ %W(blob_str(&truncated)) | |
| 757 | - blob_reset(&truncated); | |
| 756 | + blob_truncate_utf8(&comment, mxWikiLen); | |
| 757 | + blob_append(&comment, "...", 3); | |
| 758 | + @ %W(blob_str(&comment)) | |
| 758 | 759 | drawDetailEllipsis = 0; |
| 759 | 760 | }else{ |
| 760 | 761 | cgi_printf("%W",blob_str(&comment)); |
| 761 | 762 | } |
| 762 | 763 | } |
| @@ -1045,11 +1046,11 @@ | ||
| 1045 | 1046 | ** Mnemonic: "Same Branch". |
| 1046 | 1047 | ** f: 0x01: a leaf node, 0x02: a closed leaf node. |
| 1047 | 1048 | ** au: An array of integers that define thick-line risers for branches. |
| 1048 | 1049 | ** The integers are in pairs. For each pair, the first integer is |
| 1049 | 1050 | ** is the rail on which the riser should run and the second integer |
| 1050 | - ** is the id of the node upto which the riser should run. If there | |
| 1051 | + ** is the id of the node up to which the riser should run. If there | |
| 1051 | 1052 | ** are no risers, this array does not exist. |
| 1052 | 1053 | ** mi: "merge-in". An array of integer rail positions from which |
| 1053 | 1054 | ** merge arrows should be drawn into this node. If the value is |
| 1054 | 1055 | ** negative, then the rail position is -1-mi[] and a thin merge-arrow |
| 1055 | 1056 | ** descender is drawn to the bottom of the screen. This array is |
| @@ -1483,11 +1484,11 @@ | ||
| 1483 | 1484 | |
| 1484 | 1485 | /* |
| 1485 | 1486 | ** Find the first check-in encountered with a particular tag |
| 1486 | 1487 | ** when moving either forwards are backwards in time from a |
| 1487 | 1488 | ** particular starting point (iFrom). Return the rid of that |
| 1488 | -** first check-in. If there are no check-ins in the descendent | |
| 1489 | +** first check-in. If there are no check-ins in the descendant | |
| 1489 | 1490 | ** or ancestor set of check-in iFrom that match the tag, then |
| 1490 | 1491 | ** return 0. |
| 1491 | 1492 | */ |
| 1492 | 1493 | static int timeline_endpoint( |
| 1493 | 1494 | int iFrom, /* Starting point */ |
| @@ -1623,12 +1624,12 @@ | ||
| 1623 | 1624 | ** COMMAND: test-endpoint |
| 1624 | 1625 | ** |
| 1625 | 1626 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1626 | 1627 | ** |
| 1627 | 1628 | ** Show the first check-in with TAG that is a descendant or ancestor |
| 1628 | -** of BASE. The first descendant checkin is shown by default. Use | |
| 1629 | -** the --backto to see the first ancestor checkin. | |
| 1629 | +** of BASE. The first descendant check-in is shown by default. Use | |
| 1630 | +** the --backto to see the first ancestor check-in. | |
| 1630 | 1631 | ** |
| 1631 | 1632 | ** Options: |
| 1632 | 1633 | ** |
| 1633 | 1634 | ** --backto Show ancestor. Others defaults to descendants. |
| 1634 | 1635 | */ |
| @@ -1681,14 +1682,14 @@ | ||
| 1681 | 1682 | ** d2=CKIN2 ... Use CKIN2 if CHECKIN is not found |
| 1682 | 1683 | ** ft=DESCENDANT ... going forward to DESCENDANT |
| 1683 | 1684 | ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' |
| 1684 | 1685 | ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' |
| 1685 | 1686 | ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" |
| 1686 | -** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN | |
| 1687 | +** bt=CHECKIN "Back To". Show ancestors going back to CHECKIN | |
| 1687 | 1688 | ** p=CX ... from CX back to time of CHECKIN |
| 1688 | 1689 | ** from=CX ... path from CX back to CHECKIN |
| 1689 | -** ft=CHECKIN "Forward To": Show descendents forward to CHECKIN | |
| 1690 | +** ft=CHECKIN "Forward To": Show descendants forward to CHECKIN | |
| 1690 | 1691 | ** d=CX ... from CX up to the time of CHECKIN |
| 1691 | 1692 | ** from=CX ... path from CX up to CHECKIN |
| 1692 | 1693 | ** t=TAG Show only check-ins with the given TAG |
| 1693 | 1694 | ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" |
| 1694 | 1695 | ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" |
| @@ -1726,11 +1727,11 @@ | ||
| 1726 | 1727 | ** rel ... also show related checkins |
| 1727 | 1728 | ** uf=FILE_HASH Show only check-ins that contain the given file version |
| 1728 | 1729 | ** All qualifying check-ins are shown unless there is |
| 1729 | 1730 | ** also an n= or n1= query parameter. |
| 1730 | 1731 | ** chng=GLOBLIST Show only check-ins that involve changes to a file whose |
| 1731 | -** name matches one of the comma-separate GLOBLIST | |
| 1732 | +** name matches one of the comma-separated GLOBLIST | |
| 1732 | 1733 | ** brbg Background color determined by branch name |
| 1733 | 1734 | ** ubg Background color determined by user |
| 1734 | 1735 | ** deltabg Background color red for delta manifests or green |
| 1735 | 1736 | ** for baseline manifests |
| 1736 | 1737 | ** namechng Show only check-ins that have filename changes |
| @@ -1800,11 +1801,11 @@ | ||
| 1800 | 1801 | int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ |
| 1801 | 1802 | int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */ |
| 1802 | 1803 | int forkOnly = PB("forks"); /* Show only forks and their children */ |
| 1803 | 1804 | int bisectLocal = PB("bisect"); /* Show the check-ins of the bisect */ |
| 1804 | 1805 | const char *zBisect = P("bid"); /* Bisect description */ |
| 1805 | - int cpOnly = PB("cherrypicks"); /* Show all cherrypick checkins */ | |
| 1806 | + int cpOnly = PB("cherrypicks"); /* Show all cherrypick check-ins */ | |
| 1806 | 1807 | int tmFlags = 0; /* Timeline flags */ |
| 1807 | 1808 | const char *zThisTag = 0; /* Suppress links to this tag */ |
| 1808 | 1809 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1809 | 1810 | HQuery url; /* URL for various branch links */ |
| 1810 | 1811 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| @@ -2003,11 +2004,11 @@ | ||
| 2003 | 2004 | /* Finish preliminary processing of tag match queries. */ |
| 2004 | 2005 | matchStyle = match_style(zMatchStyle, MS_EXACT); |
| 2005 | 2006 | if( zTagName ){ |
| 2006 | 2007 | zType = "ci"; |
| 2007 | 2008 | if( matchStyle==MS_EXACT ){ |
| 2008 | - /* For exact maching, inhibit links to the selected tag. */ | |
| 2009 | + /* For exact matching, inhibit links to the selected tag. */ | |
| 2009 | 2010 | zThisTag = zTagName; |
| 2010 | 2011 | Th_StoreUnsafe("current_checkin", zTagName); |
| 2011 | 2012 | } |
| 2012 | 2013 | |
| 2013 | 2014 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| @@ -3670,11 +3671,11 @@ | ||
| 3670 | 3671 | && fossil_isdigit(z[0]) |
| 3671 | 3672 | && fossil_isdigit(z[5]); |
| 3672 | 3673 | } |
| 3673 | 3674 | |
| 3674 | 3675 | /* |
| 3675 | -** Return true if the input string can be converted to a julianday. | |
| 3676 | +** Return true if the input string can be converted to a Julian day. | |
| 3676 | 3677 | */ |
| 3677 | 3678 | static int fossil_is_julianday(const char *zDate){ |
| 3678 | 3679 | return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd" |
| 3679 | 3680 | " WHERE jd IS NOT NULL)", zDate); |
| 3680 | 3681 | } |
| @@ -3728,11 +3729,11 @@ | ||
| 3728 | 3729 | ** -n|--limit N If N is positive, output the first N entries. If |
| 3729 | 3730 | ** N is negative, output the first -N lines. If N is |
| 3730 | 3731 | ** zero, no limit. Default is -20 meaning 20 lines. |
| 3731 | 3732 | ** --offset P Skip P changes |
| 3732 | 3733 | ** -p|--path PATH Output items affecting PATH only. |
| 3733 | -** PATH can be a file or a sub directory. | |
| 3734 | +** PATH can be a file or a subdirectory. | |
| 3734 | 3735 | ** -r|--reverse Show items in chronological order. |
| 3735 | 3736 | ** -R REPO_FILE Specifies the repository db to use. Default is |
| 3736 | 3737 | ** the current check-out's repository. |
| 3737 | 3738 | ** --sql Show the SQL used to generate the timeline |
| 3738 | 3739 | ** -t|--type TYPE Output items from the given types only, such as: |
| 3739 | 3740 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | #include <string.h> |
| 23 | #include <time.h> |
| 24 | #include "timeline.h" |
| 25 | |
| 26 | /* |
| 27 | ** The value of one second in julianday notation |
| 28 | */ |
| 29 | #define ONE_SECOND (1.0/86400.0) |
| 30 | |
| 31 | /* |
| 32 | ** timeline mode options |
| @@ -730,10 +730,14 @@ | |
| 730 | ** will simply be rendered in the older format. */ |
| 731 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 732 | } |
| 733 | wiki_hyperlink_override(0); |
| 734 | }else{ |
| 735 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 736 | } |
| 737 | }else{ |
| 738 | if( bCommentGitStyle ){ |
| 739 | /* Truncate comment at first blank line */ |
| @@ -747,16 +751,13 @@ | |
| 747 | } |
| 748 | } |
| 749 | z[ii] = 0; |
| 750 | cgi_printf("%W",z); |
| 751 | }else if( mxWikiLen>0 && (int)blob_size(&comment)>mxWikiLen ){ |
| 752 | Blob truncated; |
| 753 | blob_zero(&truncated); |
| 754 | blob_append(&truncated, blob_buffer(&comment), mxWikiLen); |
| 755 | blob_append(&truncated, "...", 3); |
| 756 | @ %W(blob_str(&truncated)) |
| 757 | blob_reset(&truncated); |
| 758 | drawDetailEllipsis = 0; |
| 759 | }else{ |
| 760 | cgi_printf("%W",blob_str(&comment)); |
| 761 | } |
| 762 | } |
| @@ -1045,11 +1046,11 @@ | |
| 1045 | ** Mnemonic: "Same Branch". |
| 1046 | ** f: 0x01: a leaf node, 0x02: a closed leaf node. |
| 1047 | ** au: An array of integers that define thick-line risers for branches. |
| 1048 | ** The integers are in pairs. For each pair, the first integer is |
| 1049 | ** is the rail on which the riser should run and the second integer |
| 1050 | ** is the id of the node upto which the riser should run. If there |
| 1051 | ** are no risers, this array does not exist. |
| 1052 | ** mi: "merge-in". An array of integer rail positions from which |
| 1053 | ** merge arrows should be drawn into this node. If the value is |
| 1054 | ** negative, then the rail position is -1-mi[] and a thin merge-arrow |
| 1055 | ** descender is drawn to the bottom of the screen. This array is |
| @@ -1483,11 +1484,11 @@ | |
| 1483 | |
| 1484 | /* |
| 1485 | ** Find the first check-in encountered with a particular tag |
| 1486 | ** when moving either forwards are backwards in time from a |
| 1487 | ** particular starting point (iFrom). Return the rid of that |
| 1488 | ** first check-in. If there are no check-ins in the descendent |
| 1489 | ** or ancestor set of check-in iFrom that match the tag, then |
| 1490 | ** return 0. |
| 1491 | */ |
| 1492 | static int timeline_endpoint( |
| 1493 | int iFrom, /* Starting point */ |
| @@ -1623,12 +1624,12 @@ | |
| 1623 | ** COMMAND: test-endpoint |
| 1624 | ** |
| 1625 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1626 | ** |
| 1627 | ** Show the first check-in with TAG that is a descendant or ancestor |
| 1628 | ** of BASE. The first descendant checkin is shown by default. Use |
| 1629 | ** the --backto to see the first ancestor checkin. |
| 1630 | ** |
| 1631 | ** Options: |
| 1632 | ** |
| 1633 | ** --backto Show ancestor. Others defaults to descendants. |
| 1634 | */ |
| @@ -1681,14 +1682,14 @@ | |
| 1681 | ** d2=CKIN2 ... Use CKIN2 if CHECKIN is not found |
| 1682 | ** ft=DESCENDANT ... going forward to DESCENDANT |
| 1683 | ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' |
| 1684 | ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' |
| 1685 | ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" |
| 1686 | ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN |
| 1687 | ** p=CX ... from CX back to time of CHECKIN |
| 1688 | ** from=CX ... path from CX back to CHECKIN |
| 1689 | ** ft=CHECKIN "Forward To": Show descendents forward to CHECKIN |
| 1690 | ** d=CX ... from CX up to the time of CHECKIN |
| 1691 | ** from=CX ... path from CX up to CHECKIN |
| 1692 | ** t=TAG Show only check-ins with the given TAG |
| 1693 | ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" |
| 1694 | ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" |
| @@ -1726,11 +1727,11 @@ | |
| 1726 | ** rel ... also show related checkins |
| 1727 | ** uf=FILE_HASH Show only check-ins that contain the given file version |
| 1728 | ** All qualifying check-ins are shown unless there is |
| 1729 | ** also an n= or n1= query parameter. |
| 1730 | ** chng=GLOBLIST Show only check-ins that involve changes to a file whose |
| 1731 | ** name matches one of the comma-separate GLOBLIST |
| 1732 | ** brbg Background color determined by branch name |
| 1733 | ** ubg Background color determined by user |
| 1734 | ** deltabg Background color red for delta manifests or green |
| 1735 | ** for baseline manifests |
| 1736 | ** namechng Show only check-ins that have filename changes |
| @@ -1800,11 +1801,11 @@ | |
| 1800 | int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ |
| 1801 | int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */ |
| 1802 | int forkOnly = PB("forks"); /* Show only forks and their children */ |
| 1803 | int bisectLocal = PB("bisect"); /* Show the check-ins of the bisect */ |
| 1804 | const char *zBisect = P("bid"); /* Bisect description */ |
| 1805 | int cpOnly = PB("cherrypicks"); /* Show all cherrypick checkins */ |
| 1806 | int tmFlags = 0; /* Timeline flags */ |
| 1807 | const char *zThisTag = 0; /* Suppress links to this tag */ |
| 1808 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1809 | HQuery url; /* URL for various branch links */ |
| 1810 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| @@ -2003,11 +2004,11 @@ | |
| 2003 | /* Finish preliminary processing of tag match queries. */ |
| 2004 | matchStyle = match_style(zMatchStyle, MS_EXACT); |
| 2005 | if( zTagName ){ |
| 2006 | zType = "ci"; |
| 2007 | if( matchStyle==MS_EXACT ){ |
| 2008 | /* For exact maching, inhibit links to the selected tag. */ |
| 2009 | zThisTag = zTagName; |
| 2010 | Th_StoreUnsafe("current_checkin", zTagName); |
| 2011 | } |
| 2012 | |
| 2013 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| @@ -3670,11 +3671,11 @@ | |
| 3670 | && fossil_isdigit(z[0]) |
| 3671 | && fossil_isdigit(z[5]); |
| 3672 | } |
| 3673 | |
| 3674 | /* |
| 3675 | ** Return true if the input string can be converted to a julianday. |
| 3676 | */ |
| 3677 | static int fossil_is_julianday(const char *zDate){ |
| 3678 | return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd" |
| 3679 | " WHERE jd IS NOT NULL)", zDate); |
| 3680 | } |
| @@ -3728,11 +3729,11 @@ | |
| 3728 | ** -n|--limit N If N is positive, output the first N entries. If |
| 3729 | ** N is negative, output the first -N lines. If N is |
| 3730 | ** zero, no limit. Default is -20 meaning 20 lines. |
| 3731 | ** --offset P Skip P changes |
| 3732 | ** -p|--path PATH Output items affecting PATH only. |
| 3733 | ** PATH can be a file or a sub directory. |
| 3734 | ** -r|--reverse Show items in chronological order. |
| 3735 | ** -R REPO_FILE Specifies the repository db to use. Default is |
| 3736 | ** the current check-out's repository. |
| 3737 | ** --sql Show the SQL used to generate the timeline |
| 3738 | ** -t|--type TYPE Output items from the given types only, such as: |
| 3739 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | #include <string.h> |
| 23 | #include <time.h> |
| 24 | #include "timeline.h" |
| 25 | |
| 26 | /* |
| 27 | ** The value of one second in Julian day notation |
| 28 | */ |
| 29 | #define ONE_SECOND (1.0/86400.0) |
| 30 | |
| 31 | /* |
| 32 | ** timeline mode options |
| @@ -730,10 +730,14 @@ | |
| 730 | ** will simply be rendered in the older format. */ |
| 731 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 732 | } |
| 733 | wiki_hyperlink_override(0); |
| 734 | }else{ |
| 735 | if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ |
| 736 | blob_truncate_utf8(&comment, mxWikiLen); |
| 737 | blob_append(&comment, "...", 3); |
| 738 | } |
| 739 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 740 | } |
| 741 | }else{ |
| 742 | if( bCommentGitStyle ){ |
| 743 | /* Truncate comment at first blank line */ |
| @@ -747,16 +751,13 @@ | |
| 751 | } |
| 752 | } |
| 753 | z[ii] = 0; |
| 754 | cgi_printf("%W",z); |
| 755 | }else if( mxWikiLen>0 && (int)blob_size(&comment)>mxWikiLen ){ |
| 756 | blob_truncate_utf8(&comment, mxWikiLen); |
| 757 | blob_append(&comment, "...", 3); |
| 758 | @ %W(blob_str(&comment)) |
| 759 | drawDetailEllipsis = 0; |
| 760 | }else{ |
| 761 | cgi_printf("%W",blob_str(&comment)); |
| 762 | } |
| 763 | } |
| @@ -1045,11 +1046,11 @@ | |
| 1046 | ** Mnemonic: "Same Branch". |
| 1047 | ** f: 0x01: a leaf node, 0x02: a closed leaf node. |
| 1048 | ** au: An array of integers that define thick-line risers for branches. |
| 1049 | ** The integers are in pairs. For each pair, the first integer is |
| 1050 | ** is the rail on which the riser should run and the second integer |
| 1051 | ** is the id of the node up to which the riser should run. If there |
| 1052 | ** are no risers, this array does not exist. |
| 1053 | ** mi: "merge-in". An array of integer rail positions from which |
| 1054 | ** merge arrows should be drawn into this node. If the value is |
| 1055 | ** negative, then the rail position is -1-mi[] and a thin merge-arrow |
| 1056 | ** descender is drawn to the bottom of the screen. This array is |
| @@ -1483,11 +1484,11 @@ | |
| 1484 | |
| 1485 | /* |
| 1486 | ** Find the first check-in encountered with a particular tag |
| 1487 | ** when moving either forwards are backwards in time from a |
| 1488 | ** particular starting point (iFrom). Return the rid of that |
| 1489 | ** first check-in. If there are no check-ins in the descendant |
| 1490 | ** or ancestor set of check-in iFrom that match the tag, then |
| 1491 | ** return 0. |
| 1492 | */ |
| 1493 | static int timeline_endpoint( |
| 1494 | int iFrom, /* Starting point */ |
| @@ -1623,12 +1624,12 @@ | |
| 1624 | ** COMMAND: test-endpoint |
| 1625 | ** |
| 1626 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1627 | ** |
| 1628 | ** Show the first check-in with TAG that is a descendant or ancestor |
| 1629 | ** of BASE. The first descendant check-in is shown by default. Use |
| 1630 | ** the --backto to see the first ancestor check-in. |
| 1631 | ** |
| 1632 | ** Options: |
| 1633 | ** |
| 1634 | ** --backto Show ancestor. Others defaults to descendants. |
| 1635 | */ |
| @@ -1681,14 +1682,14 @@ | |
| 1682 | ** d2=CKIN2 ... Use CKIN2 if CHECKIN is not found |
| 1683 | ** ft=DESCENDANT ... going forward to DESCENDANT |
| 1684 | ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' |
| 1685 | ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' |
| 1686 | ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" |
| 1687 | ** bt=CHECKIN "Back To". Show ancestors going back to CHECKIN |
| 1688 | ** p=CX ... from CX back to time of CHECKIN |
| 1689 | ** from=CX ... path from CX back to CHECKIN |
| 1690 | ** ft=CHECKIN "Forward To": Show descendants forward to CHECKIN |
| 1691 | ** d=CX ... from CX up to the time of CHECKIN |
| 1692 | ** from=CX ... path from CX up to CHECKIN |
| 1693 | ** t=TAG Show only check-ins with the given TAG |
| 1694 | ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" |
| 1695 | ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" |
| @@ -1726,11 +1727,11 @@ | |
| 1727 | ** rel ... also show related checkins |
| 1728 | ** uf=FILE_HASH Show only check-ins that contain the given file version |
| 1729 | ** All qualifying check-ins are shown unless there is |
| 1730 | ** also an n= or n1= query parameter. |
| 1731 | ** chng=GLOBLIST Show only check-ins that involve changes to a file whose |
| 1732 | ** name matches one of the comma-separated GLOBLIST |
| 1733 | ** brbg Background color determined by branch name |
| 1734 | ** ubg Background color determined by user |
| 1735 | ** deltabg Background color red for delta manifests or green |
| 1736 | ** for baseline manifests |
| 1737 | ** namechng Show only check-ins that have filename changes |
| @@ -1800,11 +1801,11 @@ | |
| 1801 | int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ |
| 1802 | int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */ |
| 1803 | int forkOnly = PB("forks"); /* Show only forks and their children */ |
| 1804 | int bisectLocal = PB("bisect"); /* Show the check-ins of the bisect */ |
| 1805 | const char *zBisect = P("bid"); /* Bisect description */ |
| 1806 | int cpOnly = PB("cherrypicks"); /* Show all cherrypick check-ins */ |
| 1807 | int tmFlags = 0; /* Timeline flags */ |
| 1808 | const char *zThisTag = 0; /* Suppress links to this tag */ |
| 1809 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1810 | HQuery url; /* URL for various branch links */ |
| 1811 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| @@ -2003,11 +2004,11 @@ | |
| 2004 | /* Finish preliminary processing of tag match queries. */ |
| 2005 | matchStyle = match_style(zMatchStyle, MS_EXACT); |
| 2006 | if( zTagName ){ |
| 2007 | zType = "ci"; |
| 2008 | if( matchStyle==MS_EXACT ){ |
| 2009 | /* For exact matching, inhibit links to the selected tag. */ |
| 2010 | zThisTag = zTagName; |
| 2011 | Th_StoreUnsafe("current_checkin", zTagName); |
| 2012 | } |
| 2013 | |
| 2014 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| @@ -3670,11 +3671,11 @@ | |
| 3671 | && fossil_isdigit(z[0]) |
| 3672 | && fossil_isdigit(z[5]); |
| 3673 | } |
| 3674 | |
| 3675 | /* |
| 3676 | ** Return true if the input string can be converted to a Julian day. |
| 3677 | */ |
| 3678 | static int fossil_is_julianday(const char *zDate){ |
| 3679 | return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd" |
| 3680 | " WHERE jd IS NOT NULL)", zDate); |
| 3681 | } |
| @@ -3728,11 +3729,11 @@ | |
| 3729 | ** -n|--limit N If N is positive, output the first N entries. If |
| 3730 | ** N is negative, output the first -N lines. If N is |
| 3731 | ** zero, no limit. Default is -20 meaning 20 lines. |
| 3732 | ** --offset P Skip P changes |
| 3733 | ** -p|--path PATH Output items affecting PATH only. |
| 3734 | ** PATH can be a file or a subdirectory. |
| 3735 | ** -r|--reverse Show items in chronological order. |
| 3736 | ** -R REPO_FILE Specifies the repository db to use. Default is |
| 3737 | ** the current check-out's repository. |
| 3738 | ** --sql Show the SQL used to generate the timeline |
| 3739 | ** -t|--type TYPE Output items from the given types only, such as: |
| 3740 |
+7
-7
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -1520,19 +1520,19 @@ | ||
| 1520 | 1520 | ** |
| 1521 | 1521 | ** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS? |
| 1522 | 1522 | ** |
| 1523 | 1523 | ** Options: |
| 1524 | 1524 | ** -l|--limit LIMITCHAR |
| 1525 | -** -q|--quote | |
| 1525 | +** --quote | |
| 1526 | 1526 | ** -R|--repository REPO |
| 1527 | 1527 | ** |
| 1528 | 1528 | ** Run the ticket report, identified by the report format title |
| 1529 | 1529 | ** used in the GUI. The data is written as flat file on stdout, |
| 1530 | 1530 | ** using TAB as separator. The separator can be changed using |
| 1531 | 1531 | ** the -l or --limit option. |
| 1532 | 1532 | ** |
| 1533 | -** If TICKETFILTER is given on the commandline, the query is | |
| 1533 | +** If TICKETFILTER is given on the command line, the query is | |
| 1534 | 1534 | ** limited with a new WHERE-condition. |
| 1535 | 1535 | ** example: Report lists a column # with the uuid |
| 1536 | 1536 | ** TICKETFILTER may be [#]='uuuuuuuuu' |
| 1537 | 1537 | ** example: Report only lists rows with status not open |
| 1538 | 1538 | ** TICKETFILTER: status != 'open' |
| @@ -1555,12 +1555,12 @@ | ||
| 1555 | 1555 | ** > fossil ticket list reports |
| 1556 | 1556 | ** > fossil ticket ls reports |
| 1557 | 1557 | ** |
| 1558 | 1558 | ** List all ticket reports defined in the fossil repository. |
| 1559 | 1559 | ** |
| 1560 | -** > fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote? | |
| 1561 | -** > fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote? | |
| 1560 | +** > fossil ticket set TICKETUUID (FIELD VALUE)+ ?--quote? | |
| 1561 | +** > fossil ticket change TICKETUUID (FIELD VALUE)+ ?--quote? | |
| 1562 | 1562 | ** |
| 1563 | 1563 | ** Change ticket identified by TICKETUUID to set the values of |
| 1564 | 1564 | ** each field FIELD to VALUE. |
| 1565 | 1565 | ** |
| 1566 | 1566 | ** Field names as defined in the TICKET table. By default, these |
| @@ -1567,16 +1567,16 @@ | ||
| 1567 | 1567 | ** names include: type, status, subsystem, priority, severity, foundin, |
| 1568 | 1568 | ** resolution, title, and comment, but other field names can be added |
| 1569 | 1569 | ** or substituted in customized installations. |
| 1570 | 1570 | ** |
| 1571 | 1571 | ** If you use +FIELD, the VALUE is appended to the field FIELD. You |
| 1572 | -** can use more than one field/value pair on the commandline. Using | |
| 1572 | +** can use more than one field/value pair on the command line. Using | |
| 1573 | 1573 | ** --quote enables the special character decoding as in "ticket |
| 1574 | 1574 | ** show", which allows setting multiline text or text with special |
| 1575 | 1575 | ** characters. |
| 1576 | 1576 | ** |
| 1577 | -** > fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? | |
| 1577 | +** > fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?--quote? | |
| 1578 | 1578 | ** |
| 1579 | 1579 | ** Like set, but create a new ticket with the given values. |
| 1580 | 1580 | ** |
| 1581 | 1581 | ** > fossil ticket history TICKETUUID |
| 1582 | 1582 | ** |
| @@ -1642,11 +1642,11 @@ | ||
| 1642 | 1642 | } |
| 1643 | 1643 | }else{ |
| 1644 | 1644 | /* add a new ticket or set fields on existing tickets */ |
| 1645 | 1645 | tTktShowEncoding tktEncoding; |
| 1646 | 1646 | |
| 1647 | - tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; | |
| 1647 | + tktEncoding = find_option("quote",0,0) ? tktFossilize : tktNoTab; | |
| 1648 | 1648 | |
| 1649 | 1649 | if( strncmp(g.argv[2],"show",n)==0 ){ |
| 1650 | 1650 | if( g.argc==3 ){ |
| 1651 | 1651 | usage("show REPORTNR"); |
| 1652 | 1652 | }else{ |
| 1653 | 1653 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -1520,19 +1520,19 @@ | |
| 1520 | ** |
| 1521 | ** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS? |
| 1522 | ** |
| 1523 | ** Options: |
| 1524 | ** -l|--limit LIMITCHAR |
| 1525 | ** -q|--quote |
| 1526 | ** -R|--repository REPO |
| 1527 | ** |
| 1528 | ** Run the ticket report, identified by the report format title |
| 1529 | ** used in the GUI. The data is written as flat file on stdout, |
| 1530 | ** using TAB as separator. The separator can be changed using |
| 1531 | ** the -l or --limit option. |
| 1532 | ** |
| 1533 | ** If TICKETFILTER is given on the commandline, the query is |
| 1534 | ** limited with a new WHERE-condition. |
| 1535 | ** example: Report lists a column # with the uuid |
| 1536 | ** TICKETFILTER may be [#]='uuuuuuuuu' |
| 1537 | ** example: Report only lists rows with status not open |
| 1538 | ** TICKETFILTER: status != 'open' |
| @@ -1555,12 +1555,12 @@ | |
| 1555 | ** > fossil ticket list reports |
| 1556 | ** > fossil ticket ls reports |
| 1557 | ** |
| 1558 | ** List all ticket reports defined in the fossil repository. |
| 1559 | ** |
| 1560 | ** > fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote? |
| 1561 | ** > fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote? |
| 1562 | ** |
| 1563 | ** Change ticket identified by TICKETUUID to set the values of |
| 1564 | ** each field FIELD to VALUE. |
| 1565 | ** |
| 1566 | ** Field names as defined in the TICKET table. By default, these |
| @@ -1567,16 +1567,16 @@ | |
| 1567 | ** names include: type, status, subsystem, priority, severity, foundin, |
| 1568 | ** resolution, title, and comment, but other field names can be added |
| 1569 | ** or substituted in customized installations. |
| 1570 | ** |
| 1571 | ** If you use +FIELD, the VALUE is appended to the field FIELD. You |
| 1572 | ** can use more than one field/value pair on the commandline. Using |
| 1573 | ** --quote enables the special character decoding as in "ticket |
| 1574 | ** show", which allows setting multiline text or text with special |
| 1575 | ** characters. |
| 1576 | ** |
| 1577 | ** > fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? |
| 1578 | ** |
| 1579 | ** Like set, but create a new ticket with the given values. |
| 1580 | ** |
| 1581 | ** > fossil ticket history TICKETUUID |
| 1582 | ** |
| @@ -1642,11 +1642,11 @@ | |
| 1642 | } |
| 1643 | }else{ |
| 1644 | /* add a new ticket or set fields on existing tickets */ |
| 1645 | tTktShowEncoding tktEncoding; |
| 1646 | |
| 1647 | tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; |
| 1648 | |
| 1649 | if( strncmp(g.argv[2],"show",n)==0 ){ |
| 1650 | if( g.argc==3 ){ |
| 1651 | usage("show REPORTNR"); |
| 1652 | }else{ |
| 1653 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -1520,19 +1520,19 @@ | |
| 1520 | ** |
| 1521 | ** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS? |
| 1522 | ** |
| 1523 | ** Options: |
| 1524 | ** -l|--limit LIMITCHAR |
| 1525 | ** --quote |
| 1526 | ** -R|--repository REPO |
| 1527 | ** |
| 1528 | ** Run the ticket report, identified by the report format title |
| 1529 | ** used in the GUI. The data is written as flat file on stdout, |
| 1530 | ** using TAB as separator. The separator can be changed using |
| 1531 | ** the -l or --limit option. |
| 1532 | ** |
| 1533 | ** If TICKETFILTER is given on the command line, the query is |
| 1534 | ** limited with a new WHERE-condition. |
| 1535 | ** example: Report lists a column # with the uuid |
| 1536 | ** TICKETFILTER may be [#]='uuuuuuuuu' |
| 1537 | ** example: Report only lists rows with status not open |
| 1538 | ** TICKETFILTER: status != 'open' |
| @@ -1555,12 +1555,12 @@ | |
| 1555 | ** > fossil ticket list reports |
| 1556 | ** > fossil ticket ls reports |
| 1557 | ** |
| 1558 | ** List all ticket reports defined in the fossil repository. |
| 1559 | ** |
| 1560 | ** > fossil ticket set TICKETUUID (FIELD VALUE)+ ?--quote? |
| 1561 | ** > fossil ticket change TICKETUUID (FIELD VALUE)+ ?--quote? |
| 1562 | ** |
| 1563 | ** Change ticket identified by TICKETUUID to set the values of |
| 1564 | ** each field FIELD to VALUE. |
| 1565 | ** |
| 1566 | ** Field names as defined in the TICKET table. By default, these |
| @@ -1567,16 +1567,16 @@ | |
| 1567 | ** names include: type, status, subsystem, priority, severity, foundin, |
| 1568 | ** resolution, title, and comment, but other field names can be added |
| 1569 | ** or substituted in customized installations. |
| 1570 | ** |
| 1571 | ** If you use +FIELD, the VALUE is appended to the field FIELD. You |
| 1572 | ** can use more than one field/value pair on the command line. Using |
| 1573 | ** --quote enables the special character decoding as in "ticket |
| 1574 | ** show", which allows setting multiline text or text with special |
| 1575 | ** characters. |
| 1576 | ** |
| 1577 | ** > fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?--quote? |
| 1578 | ** |
| 1579 | ** Like set, but create a new ticket with the given values. |
| 1580 | ** |
| 1581 | ** > fossil ticket history TICKETUUID |
| 1582 | ** |
| @@ -1642,11 +1642,11 @@ | |
| 1642 | } |
| 1643 | }else{ |
| 1644 | /* add a new ticket or set fields on existing tickets */ |
| 1645 | tTktShowEncoding tktEncoding; |
| 1646 | |
| 1647 | tktEncoding = find_option("quote",0,0) ? tktFossilize : tktNoTab; |
| 1648 | |
| 1649 | if( strncmp(g.argv[2],"show",n)==0 ){ |
| 1650 | if( g.argc==3 ){ |
| 1651 | usage("show REPORTNR"); |
| 1652 | }else{ |
| 1653 |
+1
-1
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -1018,11 +1018,11 @@ | ||
| 1018 | 1018 | } |
| 1019 | 1019 | |
| 1020 | 1020 | /* |
| 1021 | 1021 | ** WEBPAGE: tktsetup_timeline |
| 1022 | 1022 | ** |
| 1023 | -** Administrative page used ot configure how tickets are | |
| 1023 | +** Administrative page used to configure how tickets are | |
| 1024 | 1024 | ** rendered on timeline views. |
| 1025 | 1025 | */ |
| 1026 | 1026 | void tktsetup_timeline_page(void){ |
| 1027 | 1027 | login_check_credentials(); |
| 1028 | 1028 | if( !g.perm.Setup ){ |
| 1029 | 1029 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -1018,11 +1018,11 @@ | |
| 1018 | } |
| 1019 | |
| 1020 | /* |
| 1021 | ** WEBPAGE: tktsetup_timeline |
| 1022 | ** |
| 1023 | ** Administrative page used ot configure how tickets are |
| 1024 | ** rendered on timeline views. |
| 1025 | */ |
| 1026 | void tktsetup_timeline_page(void){ |
| 1027 | login_check_credentials(); |
| 1028 | if( !g.perm.Setup ){ |
| 1029 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -1018,11 +1018,11 @@ | |
| 1018 | } |
| 1019 | |
| 1020 | /* |
| 1021 | ** WEBPAGE: tktsetup_timeline |
| 1022 | ** |
| 1023 | ** Administrative page used to configure how tickets are |
| 1024 | ** rendered on timeline views. |
| 1025 | */ |
| 1026 | void tktsetup_timeline_page(void){ |
| 1027 | login_check_credentials(); |
| 1028 | if( !g.perm.Setup ){ |
| 1029 |
+2
-2
| --- src/undo.c | ||
| +++ src/undo.c | ||
| @@ -23,11 +23,11 @@ | ||
| 23 | 23 | #if INTERFACE |
| 24 | 24 | /* |
| 25 | 25 | ** Possible return values from the undo_maybe_save() routine. |
| 26 | 26 | */ |
| 27 | 27 | #define UNDO_NONE (0) /* Placeholder only used to initialize vars. */ |
| 28 | -#define UNDO_SAVED_OK (1) /* The specified file was saved succesfully. */ | |
| 28 | +#define UNDO_SAVED_OK (1) /* The specified file was saved successfully. */ | |
| 29 | 29 | #define UNDO_DISABLED (2) /* File not saved, subsystem is disabled. */ |
| 30 | 30 | #define UNDO_INACTIVE (3) /* File not saved, subsystem is not active. */ |
| 31 | 31 | #define UNDO_TOOBIG (4) /* File not saved, it exceeded a size limit. */ |
| 32 | 32 | #endif |
| 33 | 33 | |
| @@ -289,11 +289,11 @@ | ||
| 289 | 289 | ** function instead. |
| 290 | 290 | ** |
| 291 | 291 | ** The return value of this function will always be one of the |
| 292 | 292 | ** following codes: |
| 293 | 293 | ** |
| 294 | -** UNDO_SAVED_OK: The specified file was saved succesfully. | |
| 294 | +** UNDO_SAVED_OK: The specified file was saved successfully. | |
| 295 | 295 | ** |
| 296 | 296 | ** UNDO_DISABLED: The specified file was NOT saved, because the |
| 297 | 297 | ** "undo subsystem" is disabled. This error may |
| 298 | 298 | ** indicate that a call to undo_disable() was |
| 299 | 299 | ** issued. |
| 300 | 300 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -23,11 +23,11 @@ | |
| 23 | #if INTERFACE |
| 24 | /* |
| 25 | ** Possible return values from the undo_maybe_save() routine. |
| 26 | */ |
| 27 | #define UNDO_NONE (0) /* Placeholder only used to initialize vars. */ |
| 28 | #define UNDO_SAVED_OK (1) /* The specified file was saved succesfully. */ |
| 29 | #define UNDO_DISABLED (2) /* File not saved, subsystem is disabled. */ |
| 30 | #define UNDO_INACTIVE (3) /* File not saved, subsystem is not active. */ |
| 31 | #define UNDO_TOOBIG (4) /* File not saved, it exceeded a size limit. */ |
| 32 | #endif |
| 33 | |
| @@ -289,11 +289,11 @@ | |
| 289 | ** function instead. |
| 290 | ** |
| 291 | ** The return value of this function will always be one of the |
| 292 | ** following codes: |
| 293 | ** |
| 294 | ** UNDO_SAVED_OK: The specified file was saved succesfully. |
| 295 | ** |
| 296 | ** UNDO_DISABLED: The specified file was NOT saved, because the |
| 297 | ** "undo subsystem" is disabled. This error may |
| 298 | ** indicate that a call to undo_disable() was |
| 299 | ** issued. |
| 300 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -23,11 +23,11 @@ | |
| 23 | #if INTERFACE |
| 24 | /* |
| 25 | ** Possible return values from the undo_maybe_save() routine. |
| 26 | */ |
| 27 | #define UNDO_NONE (0) /* Placeholder only used to initialize vars. */ |
| 28 | #define UNDO_SAVED_OK (1) /* The specified file was saved successfully. */ |
| 29 | #define UNDO_DISABLED (2) /* File not saved, subsystem is disabled. */ |
| 30 | #define UNDO_INACTIVE (3) /* File not saved, subsystem is not active. */ |
| 31 | #define UNDO_TOOBIG (4) /* File not saved, it exceeded a size limit. */ |
| 32 | #endif |
| 33 | |
| @@ -289,11 +289,11 @@ | |
| 289 | ** function instead. |
| 290 | ** |
| 291 | ** The return value of this function will always be one of the |
| 292 | ** following codes: |
| 293 | ** |
| 294 | ** UNDO_SAVED_OK: The specified file was saved successfully. |
| 295 | ** |
| 296 | ** UNDO_DISABLED: The specified file was NOT saved, because the |
| 297 | ** "undo subsystem" is disabled. This error may |
| 298 | ** indicate that a call to undo_disable() was |
| 299 | ** issued. |
| 300 |
+1
-1
| --- src/unicode.c | ||
| +++ src/unicode.c | ||
| @@ -189,11 +189,11 @@ | ||
| 189 | 189 | /* |
| 190 | 190 | ** If the argument is a codepoint corresponding to a lowercase letter |
| 191 | 191 | ** in the ASCII range with a diacritic added, return the codepoint |
| 192 | 192 | ** of the ASCII letter only. For example, if passed 235 - "LATIN |
| 193 | 193 | ** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER |
| 194 | -** E"). The resuls of passing a codepoint that corresponds to an | |
| 194 | +** E"). The results of passing a codepoint that corresponds to an | |
| 195 | 195 | ** uppercase letter are undefined. |
| 196 | 196 | */ |
| 197 | 197 | static int unicode_remove_diacritic(int c, int bComplex){ |
| 198 | 198 | static const unsigned short aDia[] = { |
| 199 | 199 | 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, |
| 200 | 200 |
| --- src/unicode.c | |
| +++ src/unicode.c | |
| @@ -189,11 +189,11 @@ | |
| 189 | /* |
| 190 | ** If the argument is a codepoint corresponding to a lowercase letter |
| 191 | ** in the ASCII range with a diacritic added, return the codepoint |
| 192 | ** of the ASCII letter only. For example, if passed 235 - "LATIN |
| 193 | ** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER |
| 194 | ** E"). The resuls of passing a codepoint that corresponds to an |
| 195 | ** uppercase letter are undefined. |
| 196 | */ |
| 197 | static int unicode_remove_diacritic(int c, int bComplex){ |
| 198 | static const unsigned short aDia[] = { |
| 199 | 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, |
| 200 |
| --- src/unicode.c | |
| +++ src/unicode.c | |
| @@ -189,11 +189,11 @@ | |
| 189 | /* |
| 190 | ** If the argument is a codepoint corresponding to a lowercase letter |
| 191 | ** in the ASCII range with a diacritic added, return the codepoint |
| 192 | ** of the ASCII letter only. For example, if passed 235 - "LATIN |
| 193 | ** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER |
| 194 | ** E"). The results of passing a codepoint that corresponds to an |
| 195 | ** uppercase letter are undefined. |
| 196 | */ |
| 197 | static int unicode_remove_diacritic(int c, int bComplex){ |
| 198 | static const unsigned short aDia[] = { |
| 199 | 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, |
| 200 |
+3
-3
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -88,11 +88,11 @@ | ||
| 88 | 88 | ** The -n or --dry-run option causes this command to do a "dry run". |
| 89 | 89 | ** It prints out what would have happened but does not actually make |
| 90 | 90 | ** any changes to the current check-out or the repository. |
| 91 | 91 | ** |
| 92 | 92 | ** The -v or --verbose option prints status information about |
| 93 | -** unchanged files in addition to those file that actually do change. | |
| 93 | +** unchanged files in addition to those files that actually do change. | |
| 94 | 94 | ** |
| 95 | 95 | ** Options: |
| 96 | 96 | ** --case-sensitive BOOL Override case-sensitive setting |
| 97 | 97 | ** --debug Print debug information on stdout |
| 98 | 98 | ** -n|--dry-run If given, display instead of run actions |
| @@ -256,11 +256,11 @@ | ||
| 256 | 256 | fossil_fatal("missing content, unable to update"); |
| 257 | 257 | }; |
| 258 | 258 | |
| 259 | 259 | /* |
| 260 | 260 | ** The record.fn field is used to match files against each other. The |
| 261 | - ** FV table contains one row for each each unique filename in | |
| 261 | + ** FV table contains one row for each unique filename in | |
| 262 | 262 | ** in the current check-out, the pivot, and the version being merged. |
| 263 | 263 | */ |
| 264 | 264 | db_multi_exec( |
| 265 | 265 | "DROP TABLE IF EXISTS fv;" |
| 266 | 266 | "CREATE TEMP TABLE fv(" |
| @@ -451,11 +451,11 @@ | ||
| 451 | 451 | }else if( idt>0 && idv==0 ){ |
| 452 | 452 | /* File added in the target. */ |
| 453 | 453 | if( file_isfile_or_link(zFullPath) ){ |
| 454 | 454 | /* Name of backup file with Original content */ |
| 455 | 455 | char *zOrig = file_newname(zFullPath, "original", 1); |
| 456 | - /* Backup previously unanaged file before to be overwritten */ | |
| 456 | + /* Backup previously unmanaged file before being overwritten */ | |
| 457 | 457 | file_copy(zFullPath, zOrig); |
| 458 | 458 | fossil_free(zOrig); |
| 459 | 459 | fossil_print("ADD %s - overwrites an unmanaged file", zName); |
| 460 | 460 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 461 | 461 | fossil_print("\n"); |
| 462 | 462 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -88,11 +88,11 @@ | |
| 88 | ** The -n or --dry-run option causes this command to do a "dry run". |
| 89 | ** It prints out what would have happened but does not actually make |
| 90 | ** any changes to the current check-out or the repository. |
| 91 | ** |
| 92 | ** The -v or --verbose option prints status information about |
| 93 | ** unchanged files in addition to those file that actually do change. |
| 94 | ** |
| 95 | ** Options: |
| 96 | ** --case-sensitive BOOL Override case-sensitive setting |
| 97 | ** --debug Print debug information on stdout |
| 98 | ** -n|--dry-run If given, display instead of run actions |
| @@ -256,11 +256,11 @@ | |
| 256 | fossil_fatal("missing content, unable to update"); |
| 257 | }; |
| 258 | |
| 259 | /* |
| 260 | ** The record.fn field is used to match files against each other. The |
| 261 | ** FV table contains one row for each each unique filename in |
| 262 | ** in the current check-out, the pivot, and the version being merged. |
| 263 | */ |
| 264 | db_multi_exec( |
| 265 | "DROP TABLE IF EXISTS fv;" |
| 266 | "CREATE TEMP TABLE fv(" |
| @@ -451,11 +451,11 @@ | |
| 451 | }else if( idt>0 && idv==0 ){ |
| 452 | /* File added in the target. */ |
| 453 | if( file_isfile_or_link(zFullPath) ){ |
| 454 | /* Name of backup file with Original content */ |
| 455 | char *zOrig = file_newname(zFullPath, "original", 1); |
| 456 | /* Backup previously unanaged file before to be overwritten */ |
| 457 | file_copy(zFullPath, zOrig); |
| 458 | fossil_free(zOrig); |
| 459 | fossil_print("ADD %s - overwrites an unmanaged file", zName); |
| 460 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 461 | fossil_print("\n"); |
| 462 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -88,11 +88,11 @@ | |
| 88 | ** The -n or --dry-run option causes this command to do a "dry run". |
| 89 | ** It prints out what would have happened but does not actually make |
| 90 | ** any changes to the current check-out or the repository. |
| 91 | ** |
| 92 | ** The -v or --verbose option prints status information about |
| 93 | ** unchanged files in addition to those files that actually do change. |
| 94 | ** |
| 95 | ** Options: |
| 96 | ** --case-sensitive BOOL Override case-sensitive setting |
| 97 | ** --debug Print debug information on stdout |
| 98 | ** -n|--dry-run If given, display instead of run actions |
| @@ -256,11 +256,11 @@ | |
| 256 | fossil_fatal("missing content, unable to update"); |
| 257 | }; |
| 258 | |
| 259 | /* |
| 260 | ** The record.fn field is used to match files against each other. The |
| 261 | ** FV table contains one row for each unique filename in |
| 262 | ** in the current check-out, the pivot, and the version being merged. |
| 263 | */ |
| 264 | db_multi_exec( |
| 265 | "DROP TABLE IF EXISTS fv;" |
| 266 | "CREATE TEMP TABLE fv(" |
| @@ -451,11 +451,11 @@ | |
| 451 | }else if( idt>0 && idv==0 ){ |
| 452 | /* File added in the target. */ |
| 453 | if( file_isfile_or_link(zFullPath) ){ |
| 454 | /* Name of backup file with Original content */ |
| 455 | char *zOrig = file_newname(zFullPath, "original", 1); |
| 456 | /* Backup previously unmanaged file before being overwritten */ |
| 457 | file_copy(zFullPath, zOrig); |
| 458 | fossil_free(zOrig); |
| 459 | fossil_print("ADD %s - overwrites an unmanaged file", zName); |
| 460 | if( !dryRunFlag ) fossil_print(", original copy backed up locally"); |
| 461 | fossil_print("\n"); |
| 462 |
+8
-5
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -234,11 +234,11 @@ | ||
| 234 | 234 | |
| 235 | 235 | /* |
| 236 | 236 | ** Check the input string to ensure that it is safe to pass into system(). |
| 237 | 237 | ** A string is unsafe for system() on unix if it contains any of the following: |
| 238 | 238 | ** |
| 239 | -** * Any occurrance of '$' or '`' except single-quoted or after \ | |
| 239 | +** * Any occurrence of '$' or '`' except single-quoted or after \ | |
| 240 | 240 | ** * Any of the following characters, unquoted: ;|& or \n except |
| 241 | 241 | ** these characters are allowed as the very last character in the |
| 242 | 242 | ** string. |
| 243 | 243 | ** * Unbalanced single or double quotes |
| 244 | 244 | ** |
| @@ -671,20 +671,23 @@ | ||
| 671 | 671 | ** (1) The value of the --editor command-line argument |
| 672 | 672 | ** (2) The local "editor" setting |
| 673 | 673 | ** (3) The global "editor" setting |
| 674 | 674 | ** (4) The VISUAL environment variable |
| 675 | 675 | ** (5) The EDITOR environment variable |
| 676 | -** (6) Any of the following programs that are available: | |
| 677 | -** notepad, nano, pico, jove, edit, vi, vim, ed, | |
| 676 | +** (6) Any of several common editors that might be available, such as: | |
| 677 | +** notepad, nano, pico, jove, edit, vi, vim, ed | |
| 678 | 678 | ** |
| 679 | 679 | ** The search only occurs once, the first time this routine is called. |
| 680 | 680 | ** Second and subsequent invocations always return the same value. |
| 681 | 681 | */ |
| 682 | 682 | const char *fossil_text_editor(void){ |
| 683 | 683 | static const char *zEditor = 0; |
| 684 | 684 | const char *azStdEd[] = { |
| 685 | - "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed" | |
| 685 | +#ifdef _WIN32 | |
| 686 | + "notepad", | |
| 687 | +#endif | |
| 688 | + "nano", "pico", "jove", "edit", "vi", "vim", "ed" | |
| 686 | 689 | }; |
| 687 | 690 | int i = 0; |
| 688 | 691 | if( zEditor==0 ){ |
| 689 | 692 | zEditor = find_option("editor",0,1); |
| 690 | 693 | } |
| @@ -915,11 +918,11 @@ | ||
| 915 | 918 | unsigned char *p = zStr; |
| 916 | 919 | int i, k; |
| 917 | 920 | |
| 918 | 921 | sqlite3_randomness(16, aBlob); |
| 919 | 922 | aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */ |
| 920 | - aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 1000 xxxx */ | |
| 923 | + aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 10xx xxxx */ | |
| 921 | 924 | |
| 922 | 925 | for(i=0, k=0x550; i<16; i++, k=k>>1){ |
| 923 | 926 | if( k&1 ){ |
| 924 | 927 | *p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */ |
| 925 | 928 | } |
| 926 | 929 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -234,11 +234,11 @@ | |
| 234 | |
| 235 | /* |
| 236 | ** Check the input string to ensure that it is safe to pass into system(). |
| 237 | ** A string is unsafe for system() on unix if it contains any of the following: |
| 238 | ** |
| 239 | ** * Any occurrance of '$' or '`' except single-quoted or after \ |
| 240 | ** * Any of the following characters, unquoted: ;|& or \n except |
| 241 | ** these characters are allowed as the very last character in the |
| 242 | ** string. |
| 243 | ** * Unbalanced single or double quotes |
| 244 | ** |
| @@ -671,20 +671,23 @@ | |
| 671 | ** (1) The value of the --editor command-line argument |
| 672 | ** (2) The local "editor" setting |
| 673 | ** (3) The global "editor" setting |
| 674 | ** (4) The VISUAL environment variable |
| 675 | ** (5) The EDITOR environment variable |
| 676 | ** (6) Any of the following programs that are available: |
| 677 | ** notepad, nano, pico, jove, edit, vi, vim, ed, |
| 678 | ** |
| 679 | ** The search only occurs once, the first time this routine is called. |
| 680 | ** Second and subsequent invocations always return the same value. |
| 681 | */ |
| 682 | const char *fossil_text_editor(void){ |
| 683 | static const char *zEditor = 0; |
| 684 | const char *azStdEd[] = { |
| 685 | "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed" |
| 686 | }; |
| 687 | int i = 0; |
| 688 | if( zEditor==0 ){ |
| 689 | zEditor = find_option("editor",0,1); |
| 690 | } |
| @@ -915,11 +918,11 @@ | |
| 915 | unsigned char *p = zStr; |
| 916 | int i, k; |
| 917 | |
| 918 | sqlite3_randomness(16, aBlob); |
| 919 | aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */ |
| 920 | aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 1000 xxxx */ |
| 921 | |
| 922 | for(i=0, k=0x550; i<16; i++, k=k>>1){ |
| 923 | if( k&1 ){ |
| 924 | *p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */ |
| 925 | } |
| 926 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -234,11 +234,11 @@ | |
| 234 | |
| 235 | /* |
| 236 | ** Check the input string to ensure that it is safe to pass into system(). |
| 237 | ** A string is unsafe for system() on unix if it contains any of the following: |
| 238 | ** |
| 239 | ** * Any occurrence of '$' or '`' except single-quoted or after \ |
| 240 | ** * Any of the following characters, unquoted: ;|& or \n except |
| 241 | ** these characters are allowed as the very last character in the |
| 242 | ** string. |
| 243 | ** * Unbalanced single or double quotes |
| 244 | ** |
| @@ -671,20 +671,23 @@ | |
| 671 | ** (1) The value of the --editor command-line argument |
| 672 | ** (2) The local "editor" setting |
| 673 | ** (3) The global "editor" setting |
| 674 | ** (4) The VISUAL environment variable |
| 675 | ** (5) The EDITOR environment variable |
| 676 | ** (6) Any of several common editors that might be available, such as: |
| 677 | ** notepad, nano, pico, jove, edit, vi, vim, ed |
| 678 | ** |
| 679 | ** The search only occurs once, the first time this routine is called. |
| 680 | ** Second and subsequent invocations always return the same value. |
| 681 | */ |
| 682 | const char *fossil_text_editor(void){ |
| 683 | static const char *zEditor = 0; |
| 684 | const char *azStdEd[] = { |
| 685 | #ifdef _WIN32 |
| 686 | "notepad", |
| 687 | #endif |
| 688 | "nano", "pico", "jove", "edit", "vi", "vim", "ed" |
| 689 | }; |
| 690 | int i = 0; |
| 691 | if( zEditor==0 ){ |
| 692 | zEditor = find_option("editor",0,1); |
| 693 | } |
| @@ -915,11 +918,11 @@ | |
| 918 | unsigned char *p = zStr; |
| 919 | int i, k; |
| 920 | |
| 921 | sqlite3_randomness(16, aBlob); |
| 922 | aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */ |
| 923 | aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 10xx xxxx */ |
| 924 | |
| 925 | for(i=0, k=0x550; i<16; i++, k=k>>1){ |
| 926 | if( k&1 ){ |
| 927 | *p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */ |
| 928 | } |
| 929 |
+1
-1
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -571,11 +571,11 @@ | ||
| 571 | 571 | } |
| 572 | 572 | } |
| 573 | 573 | |
| 574 | 574 | /* |
| 575 | 575 | ** Scans the specified base directory for any directories within it, while |
| 576 | -** keeping a count of how many files they each contains, either directly or | |
| 576 | +** keeping a count of how many files they each contain, either directly or | |
| 577 | 577 | ** indirectly. |
| 578 | 578 | ** |
| 579 | 579 | ** Subdirectories are scanned recursively. |
| 580 | 580 | ** Omit files named in VFILE. |
| 581 | 581 | ** |
| 582 | 582 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -571,11 +571,11 @@ | |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | /* |
| 575 | ** Scans the specified base directory for any directories within it, while |
| 576 | ** keeping a count of how many files they each contains, either directly or |
| 577 | ** indirectly. |
| 578 | ** |
| 579 | ** Subdirectories are scanned recursively. |
| 580 | ** Omit files named in VFILE. |
| 581 | ** |
| 582 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -571,11 +571,11 @@ | |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | /* |
| 575 | ** Scans the specified base directory for any directories within it, while |
| 576 | ** keeping a count of how many files they each contain, either directly or |
| 577 | ** indirectly. |
| 578 | ** |
| 579 | ** Subdirectories are scanned recursively. |
| 580 | ** Omit files named in VFILE. |
| 581 | ** |
| 582 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -2273,11 +2273,11 @@ | ||
| 2273 | 2273 | ** of timestamp with the most recent |
| 2274 | 2274 | ** first. |
| 2275 | 2275 | ** -a|--show-associated Show wiki pages associated with |
| 2276 | 2276 | ** check-ins and branches. |
| 2277 | 2277 | ** -s|--show-technote-ids The id of the tech note will be listed |
| 2278 | -** along side the timestamp. The tech note | |
| 2278 | +** alongside the timestamp. The tech note | |
| 2279 | 2279 | ** id will be the first word on each line. |
| 2280 | 2280 | ** This option only applies if the |
| 2281 | 2281 | ** --technote option is also specified. |
| 2282 | 2282 | ** |
| 2283 | 2283 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 2284 | 2284 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -2273,11 +2273,11 @@ | |
| 2273 | ** of timestamp with the most recent |
| 2274 | ** first. |
| 2275 | ** -a|--show-associated Show wiki pages associated with |
| 2276 | ** check-ins and branches. |
| 2277 | ** -s|--show-technote-ids The id of the tech note will be listed |
| 2278 | ** along side the timestamp. The tech note |
| 2279 | ** id will be the first word on each line. |
| 2280 | ** This option only applies if the |
| 2281 | ** --technote option is also specified. |
| 2282 | ** |
| 2283 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 2284 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -2273,11 +2273,11 @@ | |
| 2273 | ** of timestamp with the most recent |
| 2274 | ** first. |
| 2275 | ** -a|--show-associated Show wiki pages associated with |
| 2276 | ** check-ins and branches. |
| 2277 | ** -s|--show-technote-ids The id of the tech note will be listed |
| 2278 | ** alongside the timestamp. The tech note |
| 2279 | ** id will be the first word on each line. |
| 2280 | ** This option only applies if the |
| 2281 | ** --technote option is also specified. |
| 2282 | ** |
| 2283 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 2284 |
+5
-5
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -25,11 +25,11 @@ | ||
| 25 | 25 | /* |
| 26 | 26 | ** Allowed wiki transformation operations |
| 27 | 27 | */ |
| 28 | 28 | #define WIKI_HTMLONLY 0x0001 /* HTML markup only. No wiki */ |
| 29 | 29 | #define WIKI_INLINE 0x0002 /* Do not surround with <p>..</p> */ |
| 30 | -/* avalable for reuse: 0x0004 --- formerly WIKI_NOBLOCK */ | |
| 30 | +/* available for reuse: 0x0004 --- formerly WIKI_NOBLOCK */ | |
| 31 | 31 | #define WIKI_BUTTONS 0x0008 /* Allow sub-menu buttons */ |
| 32 | 32 | #define WIKI_NOBADLINKS 0x0010 /* Ignore broken hyperlinks */ |
| 33 | 33 | #define WIKI_LINKSONLY 0x0020 /* No markup. Only decorate links */ |
| 34 | 34 | #define WIKI_NEWLINE 0x0040 /* Honor \n - break lines at each \n */ |
| 35 | 35 | #define WIKI_MARKDOWNLINKS 0x0080 /* Resolve hyperlinks as in markdown */ |
| @@ -566,11 +566,11 @@ | ||
| 566 | 566 | ** & |
| 567 | 567 | ** \n |
| 568 | 568 | ** [ |
| 569 | 569 | ** |
| 570 | 570 | ** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI. |
| 571 | -** The "\n" is only considered interesting if the flags constains ALLOW_WIKI. | |
| 571 | +** The "\n" is only considered interesting if the flags contains ALLOW_WIKI. | |
| 572 | 572 | */ |
| 573 | 573 | static int textLength(const char *z, int flags){ |
| 574 | 574 | const char *zReject; |
| 575 | 575 | if( flags & ALLOW_WIKI ){ |
| 576 | 576 | zReject = "<&[\n"; |
| @@ -1811,11 +1811,11 @@ | ||
| 1811 | 1811 | /* Enter <verbatim> processing. With verbatim enabled, all other |
| 1812 | 1812 | ** markup other than the corresponding end-tag with the same ID is |
| 1813 | 1813 | ** ignored. |
| 1814 | 1814 | */ |
| 1815 | 1815 | if( markup.iCode==MARKUP_VERBATIM ){ |
| 1816 | - int ii; //, vAttrDidAppend=0; | |
| 1816 | + int ii; /*, vAttrDidAppend=0;*/ | |
| 1817 | 1817 | const char *zClass = 0; |
| 1818 | 1818 | p->zVerbatimId = 0; |
| 1819 | 1819 | p->inVerbatim = 1; |
| 1820 | 1820 | p->preVerbState = p->state; |
| 1821 | 1821 | p->state &= ~ALLOW_WIKI; |
| @@ -2745,11 +2745,11 @@ | ||
| 2745 | 2745 | p->nAlloc = 0; |
| 2746 | 2746 | p->aStack = p->aSpace; |
| 2747 | 2747 | } |
| 2748 | 2748 | |
| 2749 | 2749 | /* |
| 2750 | -** Push a new element onto the tag statk | |
| 2750 | +** Push a new element onto the tag stack | |
| 2751 | 2751 | */ |
| 2752 | 2752 | static void html_tagstack_push(HtmlTagStack *p, int e){ |
| 2753 | 2753 | if( p->n>=ArraySize(p->aSpace) && p->n>=p->nAlloc ){ |
| 2754 | 2754 | if( p->nAlloc==0 ){ |
| 2755 | 2755 | int *aNew; |
| @@ -2813,11 +2813,11 @@ | ||
| 2813 | 2813 | ** |
| 2814 | 2814 | ** When safe_html() is asked to sanitize some HTML, it will ignore |
| 2815 | 2815 | ** any text in between two consecutive instances of the nonce. The |
| 2816 | 2816 | ** nonce itself is an HTML comment so it is harmless to keep the |
| 2817 | 2817 | ** nonce in the middle of the HTML stream. A different nonce is |
| 2818 | -** choosen each time Fossil is run, using a lot of randomness, so | |
| 2818 | +** chosen each time Fossil is run, using a lot of randomness, so | |
| 2819 | 2819 | ** an attacker will be unable to guess the nonce in advance. |
| 2820 | 2820 | ** |
| 2821 | 2821 | ** The original use-case for this mechanism is to allow Pikchr-generated |
| 2822 | 2822 | ** SVG in the middle of HTML generated from Markdown. The Markdown |
| 2823 | 2823 | ** output will normally be processed by safe_html() to prevent accidental |
| 2824 | 2824 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -25,11 +25,11 @@ | |
| 25 | /* |
| 26 | ** Allowed wiki transformation operations |
| 27 | */ |
| 28 | #define WIKI_HTMLONLY 0x0001 /* HTML markup only. No wiki */ |
| 29 | #define WIKI_INLINE 0x0002 /* Do not surround with <p>..</p> */ |
| 30 | /* avalable for reuse: 0x0004 --- formerly WIKI_NOBLOCK */ |
| 31 | #define WIKI_BUTTONS 0x0008 /* Allow sub-menu buttons */ |
| 32 | #define WIKI_NOBADLINKS 0x0010 /* Ignore broken hyperlinks */ |
| 33 | #define WIKI_LINKSONLY 0x0020 /* No markup. Only decorate links */ |
| 34 | #define WIKI_NEWLINE 0x0040 /* Honor \n - break lines at each \n */ |
| 35 | #define WIKI_MARKDOWNLINKS 0x0080 /* Resolve hyperlinks as in markdown */ |
| @@ -566,11 +566,11 @@ | |
| 566 | ** & |
| 567 | ** \n |
| 568 | ** [ |
| 569 | ** |
| 570 | ** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI. |
| 571 | ** The "\n" is only considered interesting if the flags constains ALLOW_WIKI. |
| 572 | */ |
| 573 | static int textLength(const char *z, int flags){ |
| 574 | const char *zReject; |
| 575 | if( flags & ALLOW_WIKI ){ |
| 576 | zReject = "<&[\n"; |
| @@ -1811,11 +1811,11 @@ | |
| 1811 | /* Enter <verbatim> processing. With verbatim enabled, all other |
| 1812 | ** markup other than the corresponding end-tag with the same ID is |
| 1813 | ** ignored. |
| 1814 | */ |
| 1815 | if( markup.iCode==MARKUP_VERBATIM ){ |
| 1816 | int ii; //, vAttrDidAppend=0; |
| 1817 | const char *zClass = 0; |
| 1818 | p->zVerbatimId = 0; |
| 1819 | p->inVerbatim = 1; |
| 1820 | p->preVerbState = p->state; |
| 1821 | p->state &= ~ALLOW_WIKI; |
| @@ -2745,11 +2745,11 @@ | |
| 2745 | p->nAlloc = 0; |
| 2746 | p->aStack = p->aSpace; |
| 2747 | } |
| 2748 | |
| 2749 | /* |
| 2750 | ** Push a new element onto the tag statk |
| 2751 | */ |
| 2752 | static void html_tagstack_push(HtmlTagStack *p, int e){ |
| 2753 | if( p->n>=ArraySize(p->aSpace) && p->n>=p->nAlloc ){ |
| 2754 | if( p->nAlloc==0 ){ |
| 2755 | int *aNew; |
| @@ -2813,11 +2813,11 @@ | |
| 2813 | ** |
| 2814 | ** When safe_html() is asked to sanitize some HTML, it will ignore |
| 2815 | ** any text in between two consecutive instances of the nonce. The |
| 2816 | ** nonce itself is an HTML comment so it is harmless to keep the |
| 2817 | ** nonce in the middle of the HTML stream. A different nonce is |
| 2818 | ** choosen each time Fossil is run, using a lot of randomness, so |
| 2819 | ** an attacker will be unable to guess the nonce in advance. |
| 2820 | ** |
| 2821 | ** The original use-case for this mechanism is to allow Pikchr-generated |
| 2822 | ** SVG in the middle of HTML generated from Markdown. The Markdown |
| 2823 | ** output will normally be processed by safe_html() to prevent accidental |
| 2824 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -25,11 +25,11 @@ | |
| 25 | /* |
| 26 | ** Allowed wiki transformation operations |
| 27 | */ |
| 28 | #define WIKI_HTMLONLY 0x0001 /* HTML markup only. No wiki */ |
| 29 | #define WIKI_INLINE 0x0002 /* Do not surround with <p>..</p> */ |
| 30 | /* available for reuse: 0x0004 --- formerly WIKI_NOBLOCK */ |
| 31 | #define WIKI_BUTTONS 0x0008 /* Allow sub-menu buttons */ |
| 32 | #define WIKI_NOBADLINKS 0x0010 /* Ignore broken hyperlinks */ |
| 33 | #define WIKI_LINKSONLY 0x0020 /* No markup. Only decorate links */ |
| 34 | #define WIKI_NEWLINE 0x0040 /* Honor \n - break lines at each \n */ |
| 35 | #define WIKI_MARKDOWNLINKS 0x0080 /* Resolve hyperlinks as in markdown */ |
| @@ -566,11 +566,11 @@ | |
| 566 | ** & |
| 567 | ** \n |
| 568 | ** [ |
| 569 | ** |
| 570 | ** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI. |
| 571 | ** The "\n" is only considered interesting if the flags contains ALLOW_WIKI. |
| 572 | */ |
| 573 | static int textLength(const char *z, int flags){ |
| 574 | const char *zReject; |
| 575 | if( flags & ALLOW_WIKI ){ |
| 576 | zReject = "<&[\n"; |
| @@ -1811,11 +1811,11 @@ | |
| 1811 | /* Enter <verbatim> processing. With verbatim enabled, all other |
| 1812 | ** markup other than the corresponding end-tag with the same ID is |
| 1813 | ** ignored. |
| 1814 | */ |
| 1815 | if( markup.iCode==MARKUP_VERBATIM ){ |
| 1816 | int ii; /*, vAttrDidAppend=0;*/ |
| 1817 | const char *zClass = 0; |
| 1818 | p->zVerbatimId = 0; |
| 1819 | p->inVerbatim = 1; |
| 1820 | p->preVerbState = p->state; |
| 1821 | p->state &= ~ALLOW_WIKI; |
| @@ -2745,11 +2745,11 @@ | |
| 2745 | p->nAlloc = 0; |
| 2746 | p->aStack = p->aSpace; |
| 2747 | } |
| 2748 | |
| 2749 | /* |
| 2750 | ** Push a new element onto the tag stack |
| 2751 | */ |
| 2752 | static void html_tagstack_push(HtmlTagStack *p, int e){ |
| 2753 | if( p->n>=ArraySize(p->aSpace) && p->n>=p->nAlloc ){ |
| 2754 | if( p->nAlloc==0 ){ |
| 2755 | int *aNew; |
| @@ -2813,11 +2813,11 @@ | |
| 2813 | ** |
| 2814 | ** When safe_html() is asked to sanitize some HTML, it will ignore |
| 2815 | ** any text in between two consecutive instances of the nonce. The |
| 2816 | ** nonce itself is an HTML comment so it is harmless to keep the |
| 2817 | ** nonce in the middle of the HTML stream. A different nonce is |
| 2818 | ** chosen each time Fossil is run, using a lot of randomness, so |
| 2819 | ** an attacker will be unable to guess the nonce in advance. |
| 2820 | ** |
| 2821 | ** The original use-case for this mechanism is to allow Pikchr-generated |
| 2822 | ** SVG in the middle of HTML generated from Markdown. The Markdown |
| 2823 | ** output will normally be processed by safe_html() to prevent accidental |
| 2824 |
+1
-1
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -805,11 +805,11 @@ | ||
| 805 | 805 | (LPWSTR) &tmp, |
| 806 | 806 | 0, |
| 807 | 807 | NULL |
| 808 | 808 | ); |
| 809 | 809 | if( !nMsg ){ |
| 810 | - /* No english, get what the system has available. */ | |
| 810 | + /* No English, get what the system has available. */ | |
| 811 | 811 | nMsg = FormatMessageW( |
| 812 | 812 | FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| 813 | 813 | FORMAT_MESSAGE_FROM_SYSTEM | |
| 814 | 814 | FORMAT_MESSAGE_IGNORE_INSERTS, |
| 815 | 815 | NULL, |
| 816 | 816 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -805,11 +805,11 @@ | |
| 805 | (LPWSTR) &tmp, |
| 806 | 0, |
| 807 | NULL |
| 808 | ); |
| 809 | if( !nMsg ){ |
| 810 | /* No english, get what the system has available. */ |
| 811 | nMsg = FormatMessageW( |
| 812 | FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| 813 | FORMAT_MESSAGE_FROM_SYSTEM | |
| 814 | FORMAT_MESSAGE_IGNORE_INSERTS, |
| 815 | NULL, |
| 816 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -805,11 +805,11 @@ | |
| 805 | (LPWSTR) &tmp, |
| 806 | 0, |
| 807 | NULL |
| 808 | ); |
| 809 | if( !nMsg ){ |
| 810 | /* No English, get what the system has available. */ |
| 811 | nMsg = FormatMessageW( |
| 812 | FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| 813 | FORMAT_MESSAGE_FROM_SYSTEM | |
| 814 | FORMAT_MESSAGE_IGNORE_INSERTS, |
| 815 | NULL, |
| 816 |
+18
-6
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -52,11 +52,11 @@ | ||
| 52 | 52 | int resync; /* Send igot cards for all holdings */ |
| 53 | 53 | u8 syncPrivate; /* True to enable syncing private content */ |
| 54 | 54 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 55 | 55 | u32 remoteVersion; /* Version of fossil running on the other side */ |
| 56 | 56 | u32 remoteDate; /* Date for specific client software edition */ |
| 57 | - u32 remoteTime; /* Time of date correspoding on remoteDate */ | |
| 57 | + u32 remoteTime; /* Time of date corresponding on remoteDate */ | |
| 58 | 58 | time_t maxTime; /* Time when this transfer should be finished */ |
| 59 | 59 | }; |
| 60 | 60 | |
| 61 | 61 | |
| 62 | 62 | /* |
| @@ -1216,11 +1216,11 @@ | ||
| 1216 | 1216 | return xfer_run_script(xfer_common_code(), 0, 0); |
| 1217 | 1217 | } |
| 1218 | 1218 | |
| 1219 | 1219 | /* |
| 1220 | 1220 | ** This routine makes a "syncwith:URL" entry in the CONFIG table to |
| 1221 | -** indicate that a sync is occuring with zUrl. | |
| 1221 | +** indicate that a sync is occurring with zUrl. | |
| 1222 | 1222 | ** |
| 1223 | 1223 | ** Add a "syncfrom:URL" entry instead of "syncwith:URL" if bSyncFrom is true. |
| 1224 | 1224 | */ |
| 1225 | 1225 | static void xfer_syncwith(const char *zUrl, int bSyncFrom){ |
| 1226 | 1226 | UrlData x; |
| @@ -2049,10 +2049,12 @@ | ||
| 2049 | 2049 | #define SYNC_CKIN_LOCK 0x02000 /* Lock the current check-in */ |
| 2050 | 2050 | #define SYNC_NOHTTPCOMPRESS 0x04000 /* Do not compression HTTP messages */ |
| 2051 | 2051 | #define SYNC_ALLURL 0x08000 /* The --all flag - sync to all URLs */ |
| 2052 | 2052 | #define SYNC_SHARE_LINKS 0x10000 /* Request alternate repo links */ |
| 2053 | 2053 | #define SYNC_XVERBOSE 0x20000 /* Extra verbose. Network traffic */ |
| 2054 | +#define SYNC_PING 0x40000 /* Verify server is alive */ | |
| 2055 | +#define SYNC_QUIET 0x80000 /* No output */ | |
| 2054 | 2056 | #endif |
| 2055 | 2057 | |
| 2056 | 2058 | /* |
| 2057 | 2059 | ** Floating-point absolute value |
| 2058 | 2060 | */ |
| @@ -2115,11 +2117,12 @@ | ||
| 2115 | 2117 | unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ |
| 2116 | 2118 | const int bOutIsTty = fossil_isatty(fossil_fileno(stdout)); |
| 2117 | 2119 | |
| 2118 | 2120 | if( pnRcvd ) *pnRcvd = 0; |
| 2119 | 2121 | if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; |
| 2120 | - if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0 | |
| 2122 | + if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED|SYNC_PING)) | |
| 2123 | + ==0 | |
| 2121 | 2124 | && configRcvMask==0 |
| 2122 | 2125 | && configSendMask==0 |
| 2123 | 2126 | ){ |
| 2124 | 2127 | return 0; /* Nothing to do */ |
| 2125 | 2128 | } |
| @@ -2384,11 +2387,11 @@ | ||
| 2384 | 2387 | zCkinLock = 0; |
| 2385 | 2388 | }else if( zClientId ){ |
| 2386 | 2389 | blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); |
| 2387 | 2390 | } |
| 2388 | 2391 | /* Append randomness to the end of the uplink message. This makes all |
| 2389 | - ** messages unique so that that the login-card nonce will always | |
| 2392 | + ** messages unique so that the login-card nonce will always | |
| 2390 | 2393 | ** be unique. |
| 2391 | 2394 | */ |
| 2392 | 2395 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 2393 | 2396 | blob_appendf(&send, "# %s\n", zRandomness); |
| 2394 | 2397 | fossil_free(zRandomness); |
| @@ -2410,10 +2413,13 @@ | ||
| 2410 | 2413 | mHttpFlags |= HTTP_NOCOMPRESS; |
| 2411 | 2414 | } |
| 2412 | 2415 | if( syncFlags & SYNC_XVERBOSE ){ |
| 2413 | 2416 | mHttpFlags |= HTTP_VERBOSE; |
| 2414 | 2417 | } |
| 2418 | + if( syncFlags & SYNC_QUIET ){ | |
| 2419 | + mHttpFlags |= HTTP_QUIET; | |
| 2420 | + } | |
| 2415 | 2421 | |
| 2416 | 2422 | /* Do the round-trip to the server */ |
| 2417 | 2423 | if( http_exchange(&send, &recv, mHttpFlags, MAX_REDIRECTS, 0) ){ |
| 2418 | 2424 | nErr++; |
| 2419 | 2425 | go = 2; |
| @@ -2431,10 +2437,12 @@ | ||
| 2431 | 2437 | nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; |
| 2432 | 2438 | if( syncFlags & SYNC_VERBOSE ){ |
| 2433 | 2439 | fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:", |
| 2434 | 2440 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 2435 | 2441 | xfer.nFileSent, xfer.nDeltaSent); |
| 2442 | + }else if( syncFlags & SYNC_QUIET ){ | |
| 2443 | + /* No-op */ | |
| 2436 | 2444 | }else{ |
| 2437 | 2445 | if( bOutIsTty!=0 ){ |
| 2438 | 2446 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 2439 | 2447 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 2440 | 2448 | } |
| @@ -2954,10 +2962,12 @@ | ||
| 2954 | 2962 | origConfigRcvMask = 0; |
| 2955 | 2963 | if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){ |
| 2956 | 2964 | fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:", |
| 2957 | 2965 | blob_size(&recv), nCardRcvd, |
| 2958 | 2966 | xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile); |
| 2967 | + }else if( syncFlags & SYNC_QUIET ){ | |
| 2968 | + /* No-op */ | |
| 2959 | 2969 | }else{ |
| 2960 | 2970 | if( bOutIsTty!=0 ){ |
| 2961 | 2971 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 2962 | 2972 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 2963 | 2973 | } |
| @@ -3016,18 +3026,20 @@ | ||
| 3016 | 3026 | }else if( rSkew*24.0*3600.0 < -10.0 ){ |
| 3017 | 3027 | fossil_warning("*** time skew *** server is slow by %s", |
| 3018 | 3028 | db_timespan_name(-rSkew)); |
| 3019 | 3029 | g.clockSkewSeen = 1; |
| 3020 | 3030 | } |
| 3021 | - if( bOutIsTty==0 ){ | |
| 3031 | + if( bOutIsTty==0 && (syncFlags & SYNC_QUIET)==0 ){ | |
| 3022 | 3032 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 3023 | 3033 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 3024 | 3034 | fossil_force_newline(); |
| 3025 | 3035 | } |
| 3026 | 3036 | fossil_force_newline(); |
| 3027 | 3037 | if( g.zHttpCmd==0 ){ |
| 3028 | - if( syncFlags & SYNC_VERBOSE ){ | |
| 3038 | + if( syncFlags & SYNC_QUIET ){ | |
| 3039 | + /* no-op */ | |
| 3040 | + }else if( syncFlags & SYNC_VERBOSE ){ | |
| 3029 | 3041 | fossil_print( |
| 3030 | 3042 | "%s done, wire bytes sent: %lld received: %lld remote: %s%s\n", |
| 3031 | 3043 | zOpType, nSent, nRcvd, |
| 3032 | 3044 | (g.url.name && g.url.name[0]!='\0') ? g.url.name : "", |
| 3033 | 3045 | (g.zIpAddr && g.zIpAddr[0]!='\0' |
| 3034 | 3046 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -52,11 +52,11 @@ | |
| 52 | int resync; /* Send igot cards for all holdings */ |
| 53 | u8 syncPrivate; /* True to enable syncing private content */ |
| 54 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 55 | u32 remoteVersion; /* Version of fossil running on the other side */ |
| 56 | u32 remoteDate; /* Date for specific client software edition */ |
| 57 | u32 remoteTime; /* Time of date correspoding on remoteDate */ |
| 58 | time_t maxTime; /* Time when this transfer should be finished */ |
| 59 | }; |
| 60 | |
| 61 | |
| 62 | /* |
| @@ -1216,11 +1216,11 @@ | |
| 1216 | return xfer_run_script(xfer_common_code(), 0, 0); |
| 1217 | } |
| 1218 | |
| 1219 | /* |
| 1220 | ** This routine makes a "syncwith:URL" entry in the CONFIG table to |
| 1221 | ** indicate that a sync is occuring with zUrl. |
| 1222 | ** |
| 1223 | ** Add a "syncfrom:URL" entry instead of "syncwith:URL" if bSyncFrom is true. |
| 1224 | */ |
| 1225 | static void xfer_syncwith(const char *zUrl, int bSyncFrom){ |
| 1226 | UrlData x; |
| @@ -2049,10 +2049,12 @@ | |
| 2049 | #define SYNC_CKIN_LOCK 0x02000 /* Lock the current check-in */ |
| 2050 | #define SYNC_NOHTTPCOMPRESS 0x04000 /* Do not compression HTTP messages */ |
| 2051 | #define SYNC_ALLURL 0x08000 /* The --all flag - sync to all URLs */ |
| 2052 | #define SYNC_SHARE_LINKS 0x10000 /* Request alternate repo links */ |
| 2053 | #define SYNC_XVERBOSE 0x20000 /* Extra verbose. Network traffic */ |
| 2054 | #endif |
| 2055 | |
| 2056 | /* |
| 2057 | ** Floating-point absolute value |
| 2058 | */ |
| @@ -2115,11 +2117,12 @@ | |
| 2115 | unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ |
| 2116 | const int bOutIsTty = fossil_isatty(fossil_fileno(stdout)); |
| 2117 | |
| 2118 | if( pnRcvd ) *pnRcvd = 0; |
| 2119 | if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; |
| 2120 | if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0 |
| 2121 | && configRcvMask==0 |
| 2122 | && configSendMask==0 |
| 2123 | ){ |
| 2124 | return 0; /* Nothing to do */ |
| 2125 | } |
| @@ -2384,11 +2387,11 @@ | |
| 2384 | zCkinLock = 0; |
| 2385 | }else if( zClientId ){ |
| 2386 | blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); |
| 2387 | } |
| 2388 | /* Append randomness to the end of the uplink message. This makes all |
| 2389 | ** messages unique so that that the login-card nonce will always |
| 2390 | ** be unique. |
| 2391 | */ |
| 2392 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 2393 | blob_appendf(&send, "# %s\n", zRandomness); |
| 2394 | fossil_free(zRandomness); |
| @@ -2410,10 +2413,13 @@ | |
| 2410 | mHttpFlags |= HTTP_NOCOMPRESS; |
| 2411 | } |
| 2412 | if( syncFlags & SYNC_XVERBOSE ){ |
| 2413 | mHttpFlags |= HTTP_VERBOSE; |
| 2414 | } |
| 2415 | |
| 2416 | /* Do the round-trip to the server */ |
| 2417 | if( http_exchange(&send, &recv, mHttpFlags, MAX_REDIRECTS, 0) ){ |
| 2418 | nErr++; |
| 2419 | go = 2; |
| @@ -2431,10 +2437,12 @@ | |
| 2431 | nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; |
| 2432 | if( syncFlags & SYNC_VERBOSE ){ |
| 2433 | fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:", |
| 2434 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 2435 | xfer.nFileSent, xfer.nDeltaSent); |
| 2436 | }else{ |
| 2437 | if( bOutIsTty!=0 ){ |
| 2438 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 2439 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 2440 | } |
| @@ -2954,10 +2962,12 @@ | |
| 2954 | origConfigRcvMask = 0; |
| 2955 | if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){ |
| 2956 | fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:", |
| 2957 | blob_size(&recv), nCardRcvd, |
| 2958 | xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile); |
| 2959 | }else{ |
| 2960 | if( bOutIsTty!=0 ){ |
| 2961 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 2962 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 2963 | } |
| @@ -3016,18 +3026,20 @@ | |
| 3016 | }else if( rSkew*24.0*3600.0 < -10.0 ){ |
| 3017 | fossil_warning("*** time skew *** server is slow by %s", |
| 3018 | db_timespan_name(-rSkew)); |
| 3019 | g.clockSkewSeen = 1; |
| 3020 | } |
| 3021 | if( bOutIsTty==0 ){ |
| 3022 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 3023 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 3024 | fossil_force_newline(); |
| 3025 | } |
| 3026 | fossil_force_newline(); |
| 3027 | if( g.zHttpCmd==0 ){ |
| 3028 | if( syncFlags & SYNC_VERBOSE ){ |
| 3029 | fossil_print( |
| 3030 | "%s done, wire bytes sent: %lld received: %lld remote: %s%s\n", |
| 3031 | zOpType, nSent, nRcvd, |
| 3032 | (g.url.name && g.url.name[0]!='\0') ? g.url.name : "", |
| 3033 | (g.zIpAddr && g.zIpAddr[0]!='\0' |
| 3034 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -52,11 +52,11 @@ | |
| 52 | int resync; /* Send igot cards for all holdings */ |
| 53 | u8 syncPrivate; /* True to enable syncing private content */ |
| 54 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 55 | u32 remoteVersion; /* Version of fossil running on the other side */ |
| 56 | u32 remoteDate; /* Date for specific client software edition */ |
| 57 | u32 remoteTime; /* Time of date corresponding on remoteDate */ |
| 58 | time_t maxTime; /* Time when this transfer should be finished */ |
| 59 | }; |
| 60 | |
| 61 | |
| 62 | /* |
| @@ -1216,11 +1216,11 @@ | |
| 1216 | return xfer_run_script(xfer_common_code(), 0, 0); |
| 1217 | } |
| 1218 | |
| 1219 | /* |
| 1220 | ** This routine makes a "syncwith:URL" entry in the CONFIG table to |
| 1221 | ** indicate that a sync is occurring with zUrl. |
| 1222 | ** |
| 1223 | ** Add a "syncfrom:URL" entry instead of "syncwith:URL" if bSyncFrom is true. |
| 1224 | */ |
| 1225 | static void xfer_syncwith(const char *zUrl, int bSyncFrom){ |
| 1226 | UrlData x; |
| @@ -2049,10 +2049,12 @@ | |
| 2049 | #define SYNC_CKIN_LOCK 0x02000 /* Lock the current check-in */ |
| 2050 | #define SYNC_NOHTTPCOMPRESS 0x04000 /* Do not compression HTTP messages */ |
| 2051 | #define SYNC_ALLURL 0x08000 /* The --all flag - sync to all URLs */ |
| 2052 | #define SYNC_SHARE_LINKS 0x10000 /* Request alternate repo links */ |
| 2053 | #define SYNC_XVERBOSE 0x20000 /* Extra verbose. Network traffic */ |
| 2054 | #define SYNC_PING 0x40000 /* Verify server is alive */ |
| 2055 | #define SYNC_QUIET 0x80000 /* No output */ |
| 2056 | #endif |
| 2057 | |
| 2058 | /* |
| 2059 | ** Floating-point absolute value |
| 2060 | */ |
| @@ -2115,11 +2117,12 @@ | |
| 2117 | unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ |
| 2118 | const int bOutIsTty = fossil_isatty(fossil_fileno(stdout)); |
| 2119 | |
| 2120 | if( pnRcvd ) *pnRcvd = 0; |
| 2121 | if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; |
| 2122 | if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED|SYNC_PING)) |
| 2123 | ==0 |
| 2124 | && configRcvMask==0 |
| 2125 | && configSendMask==0 |
| 2126 | ){ |
| 2127 | return 0; /* Nothing to do */ |
| 2128 | } |
| @@ -2384,11 +2387,11 @@ | |
| 2387 | zCkinLock = 0; |
| 2388 | }else if( zClientId ){ |
| 2389 | blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); |
| 2390 | } |
| 2391 | /* Append randomness to the end of the uplink message. This makes all |
| 2392 | ** messages unique so that the login-card nonce will always |
| 2393 | ** be unique. |
| 2394 | */ |
| 2395 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 2396 | blob_appendf(&send, "# %s\n", zRandomness); |
| 2397 | fossil_free(zRandomness); |
| @@ -2410,10 +2413,13 @@ | |
| 2413 | mHttpFlags |= HTTP_NOCOMPRESS; |
| 2414 | } |
| 2415 | if( syncFlags & SYNC_XVERBOSE ){ |
| 2416 | mHttpFlags |= HTTP_VERBOSE; |
| 2417 | } |
| 2418 | if( syncFlags & SYNC_QUIET ){ |
| 2419 | mHttpFlags |= HTTP_QUIET; |
| 2420 | } |
| 2421 | |
| 2422 | /* Do the round-trip to the server */ |
| 2423 | if( http_exchange(&send, &recv, mHttpFlags, MAX_REDIRECTS, 0) ){ |
| 2424 | nErr++; |
| 2425 | go = 2; |
| @@ -2431,10 +2437,12 @@ | |
| 2437 | nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; |
| 2438 | if( syncFlags & SYNC_VERBOSE ){ |
| 2439 | fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:", |
| 2440 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 2441 | xfer.nFileSent, xfer.nDeltaSent); |
| 2442 | }else if( syncFlags & SYNC_QUIET ){ |
| 2443 | /* No-op */ |
| 2444 | }else{ |
| 2445 | if( bOutIsTty!=0 ){ |
| 2446 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 2447 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 2448 | } |
| @@ -2954,10 +2962,12 @@ | |
| 2962 | origConfigRcvMask = 0; |
| 2963 | if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){ |
| 2964 | fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:", |
| 2965 | blob_size(&recv), nCardRcvd, |
| 2966 | xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile); |
| 2967 | }else if( syncFlags & SYNC_QUIET ){ |
| 2968 | /* No-op */ |
| 2969 | }else{ |
| 2970 | if( bOutIsTty!=0 ){ |
| 2971 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 2972 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 2973 | } |
| @@ -3016,18 +3026,20 @@ | |
| 3026 | }else if( rSkew*24.0*3600.0 < -10.0 ){ |
| 3027 | fossil_warning("*** time skew *** server is slow by %s", |
| 3028 | db_timespan_name(-rSkew)); |
| 3029 | g.clockSkewSeen = 1; |
| 3030 | } |
| 3031 | if( bOutIsTty==0 && (syncFlags & SYNC_QUIET)==0 ){ |
| 3032 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 3033 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 3034 | fossil_force_newline(); |
| 3035 | } |
| 3036 | fossil_force_newline(); |
| 3037 | if( g.zHttpCmd==0 ){ |
| 3038 | if( syncFlags & SYNC_QUIET ){ |
| 3039 | /* no-op */ |
| 3040 | }else if( syncFlags & SYNC_VERBOSE ){ |
| 3041 | fossil_print( |
| 3042 | "%s done, wire bytes sent: %lld received: %lld remote: %s%s\n", |
| 3043 | zOpType, nSent, nRcvd, |
| 3044 | (g.url.name && g.url.name[0]!='\0') ? g.url.name : "", |
| 3045 | (g.zIpAddr && g.zIpAddr[0]!='\0' |
| 3046 |
+81
-3
| --- src/xsystem.c | ||
| +++ src/xsystem.c | ||
| @@ -31,10 +31,13 @@ | ||
| 31 | 31 | */ |
| 32 | 32 | #include "config.h" |
| 33 | 33 | #include "xsystem.h" |
| 34 | 34 | #include "qrf.h" |
| 35 | 35 | #include <time.h> |
| 36 | +#ifdef _WIN32 | |
| 37 | +# include <windows.h> | |
| 38 | +#endif | |
| 36 | 39 | |
| 37 | 40 | |
| 38 | 41 | /* Date and time */ |
| 39 | 42 | void xsystem_date(int argc, char **argv){ |
| 40 | 43 | (void)argc; |
| @@ -41,11 +44,11 @@ | ||
| 41 | 44 | (void)argv; |
| 42 | 45 | fossil_print("%z = ", cgi_iso8601_datestamp()); |
| 43 | 46 | fossil_print("%z\n", cgi_rfc822_datestamp(time(0))); |
| 44 | 47 | } |
| 45 | 48 | |
| 46 | -/* Present working diretory */ | |
| 49 | +/* Present working directory */ | |
| 47 | 50 | void xsystem_pwd(int argc, char **argv){ |
| 48 | 51 | char *zPwd = file_getcwd(0, 0); |
| 49 | 52 | fossil_print("%z\n", zPwd); |
| 50 | 53 | } |
| 51 | 54 | |
| @@ -168,11 +171,11 @@ | ||
| 168 | 171 | file_directory_list_free(azList); |
| 169 | 172 | } |
| 170 | 173 | } |
| 171 | 174 | |
| 172 | 175 | /* |
| 173 | -** Return arguments to ORDER BY that will correctly sort the entires. | |
| 176 | +** Return arguments to ORDER BY that will correctly sort the entries. | |
| 174 | 177 | */ |
| 175 | 178 | static const char *xsystem_ls_orderby(int mFlags){ |
| 176 | 179 | static const char *zSortTypes[] = { |
| 177 | 180 | "fn COLLATE NOCASE", |
| 178 | 181 | "mtime DESC", |
| @@ -343,11 +346,11 @@ | ||
| 343 | 346 | |
| 344 | 347 | /* List files "ls" |
| 345 | 348 | ** Options: |
| 346 | 349 | ** |
| 347 | 350 | ** -a Show files that begin with "." |
| 348 | -** -C List by colums | |
| 351 | +** -C List by columns | |
| 349 | 352 | ** --color=WHEN Colorize output? |
| 350 | 353 | ** -d Show just directory names, not content |
| 351 | 354 | ** -l Long listing |
| 352 | 355 | ** -m Comma-separated list |
| 353 | 356 | ** -r Reverse sort |
| @@ -439,10 +442,76 @@ | ||
| 439 | 442 | } |
| 440 | 443 | } |
| 441 | 444 | sqlite3_finalize(pStmt); |
| 442 | 445 | sqlite3_close(db); |
| 443 | 446 | } |
| 447 | + | |
| 448 | +/* | |
| 449 | +** unzip [-l] ZIPFILE | |
| 450 | +*/ | |
| 451 | +void xsystem_unzip(int argc, char **argv){ | |
| 452 | + const char *zZipfile = 0; | |
| 453 | + int doList = 0; | |
| 454 | + int i; | |
| 455 | + char *a[5]; | |
| 456 | + int n; | |
| 457 | + extern int sqlite3_shell(int, char**); | |
| 458 | + | |
| 459 | + for(i=1; i<argc; i++){ | |
| 460 | + const char *z = argv[i]; | |
| 461 | + if( z[0]=='-' ){ | |
| 462 | + if( z[1]=='-' && z[2]!=0 ) z++; | |
| 463 | + if( strcmp(z,"-l")==0 ){ | |
| 464 | + doList = 1; | |
| 465 | + }else | |
| 466 | + { | |
| 467 | + fossil_fatal("unknown option: %s", argv[i]); | |
| 468 | + } | |
| 469 | + }else if( zZipfile!=0 ){ | |
| 470 | + fossil_fatal("extra argument: %s", z); | |
| 471 | + }else{ | |
| 472 | + zZipfile = z; | |
| 473 | + } | |
| 474 | + } | |
| 475 | + if( zZipfile==0 ){ | |
| 476 | + fossil_fatal("Usage: fossil sys unzip [-l] ZIPFILE"); | |
| 477 | + }else if( file_size(zZipfile, ExtFILE)<0 ){ | |
| 478 | + fossil_fatal("No such file: %s\n", zZipfile); | |
| 479 | + } | |
| 480 | + g.zRepositoryName = 0; | |
| 481 | + g.zLocalDbName = 0; | |
| 482 | + g.zConfigDbName = 0; | |
| 483 | + sqlite3_shutdown(); | |
| 484 | + a[0] = argv[0]; | |
| 485 | + a[1] = (char*)zZipfile; | |
| 486 | + if( doList ){ | |
| 487 | + a[2] = ".mode column"; | |
| 488 | + a[3] = "SELECT sz AS Size, date(mtime,'unixepoch') AS Date," | |
| 489 | + " time(mtime,'unixepoch') AS Time, name AS Name" | |
| 490 | + " FROM zip;"; | |
| 491 | + n = 4; | |
| 492 | + }else{ | |
| 493 | + a[2] = ".mode list"; | |
| 494 | + a[3] = "SELECT if(writefile(name,data,mode,mtime) IS NULL," | |
| 495 | + "'error: '||name,'extracting: '||name) FROM zip;"; | |
| 496 | + n = 4; | |
| 497 | + } | |
| 498 | + a[n] = 0; | |
| 499 | + sqlite3_shell(n,a); | |
| 500 | +} | |
| 501 | + | |
| 502 | +/* | |
| 503 | +** zip [OPTIONS] ZIPFILE FILE ... | |
| 504 | +*/ | |
| 505 | +void xsystem_zip(int argc, char **argv){ | |
| 506 | + int i; | |
| 507 | + for(i=0; i<argc; i++){ | |
| 508 | + g.argv[i+1] = argv[i]; | |
| 509 | + } | |
| 510 | + g.argc = argc+1; | |
| 511 | + filezip_cmd(); | |
| 512 | +} | |
| 444 | 513 | |
| 445 | 514 | /* |
| 446 | 515 | ** Available system commands. |
| 447 | 516 | */ |
| 448 | 517 | typedef struct XSysCmd XSysCmd; |
| @@ -473,17 +542,26 @@ | ||
| 473 | 542 | "Show the Present Working Directory name\n" |
| 474 | 543 | }, |
| 475 | 544 | { "stty", xsystem_stty, |
| 476 | 545 | "\n" |
| 477 | 546 | "Show the size of the TTY\n" |
| 547 | + }, | |
| 548 | + { "unzip", xsystem_unzip, | |
| 549 | + "[-l] ZIPFILE\n\n" | |
| 550 | + "Extract content from ZIPFILE, or list the content if the -l option\n" | |
| 551 | + "is used.\n" | |
| 478 | 552 | }, |
| 479 | 553 | { "which", xsystem_which, |
| 480 | 554 | "EXE ...\n" |
| 481 | 555 | "Show the location on PATH of executables EXE\n" |
| 482 | 556 | "Options:\n" |
| 483 | 557 | " -a Show all path locations rather than just the first\n" |
| 484 | 558 | }, |
| 559 | + { "zip", xsystem_zip, | |
| 560 | + "ZIPFILE FILE ...\n\n" | |
| 561 | + "Create a new ZIP archive named ZIPFILE using listed files as content\n" | |
| 562 | + }, | |
| 485 | 563 | }; |
| 486 | 564 | |
| 487 | 565 | /* |
| 488 | 566 | ** COMMAND: system |
| 489 | 567 | ** |
| 490 | 568 |
| --- src/xsystem.c | |
| +++ src/xsystem.c | |
| @@ -31,10 +31,13 @@ | |
| 31 | */ |
| 32 | #include "config.h" |
| 33 | #include "xsystem.h" |
| 34 | #include "qrf.h" |
| 35 | #include <time.h> |
| 36 | |
| 37 | |
| 38 | /* Date and time */ |
| 39 | void xsystem_date(int argc, char **argv){ |
| 40 | (void)argc; |
| @@ -41,11 +44,11 @@ | |
| 41 | (void)argv; |
| 42 | fossil_print("%z = ", cgi_iso8601_datestamp()); |
| 43 | fossil_print("%z\n", cgi_rfc822_datestamp(time(0))); |
| 44 | } |
| 45 | |
| 46 | /* Present working diretory */ |
| 47 | void xsystem_pwd(int argc, char **argv){ |
| 48 | char *zPwd = file_getcwd(0, 0); |
| 49 | fossil_print("%z\n", zPwd); |
| 50 | } |
| 51 | |
| @@ -168,11 +171,11 @@ | |
| 168 | file_directory_list_free(azList); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | /* |
| 173 | ** Return arguments to ORDER BY that will correctly sort the entires. |
| 174 | */ |
| 175 | static const char *xsystem_ls_orderby(int mFlags){ |
| 176 | static const char *zSortTypes[] = { |
| 177 | "fn COLLATE NOCASE", |
| 178 | "mtime DESC", |
| @@ -343,11 +346,11 @@ | |
| 343 | |
| 344 | /* List files "ls" |
| 345 | ** Options: |
| 346 | ** |
| 347 | ** -a Show files that begin with "." |
| 348 | ** -C List by colums |
| 349 | ** --color=WHEN Colorize output? |
| 350 | ** -d Show just directory names, not content |
| 351 | ** -l Long listing |
| 352 | ** -m Comma-separated list |
| 353 | ** -r Reverse sort |
| @@ -439,10 +442,76 @@ | |
| 439 | } |
| 440 | } |
| 441 | sqlite3_finalize(pStmt); |
| 442 | sqlite3_close(db); |
| 443 | } |
| 444 | |
| 445 | /* |
| 446 | ** Available system commands. |
| 447 | */ |
| 448 | typedef struct XSysCmd XSysCmd; |
| @@ -473,17 +542,26 @@ | |
| 473 | "Show the Present Working Directory name\n" |
| 474 | }, |
| 475 | { "stty", xsystem_stty, |
| 476 | "\n" |
| 477 | "Show the size of the TTY\n" |
| 478 | }, |
| 479 | { "which", xsystem_which, |
| 480 | "EXE ...\n" |
| 481 | "Show the location on PATH of executables EXE\n" |
| 482 | "Options:\n" |
| 483 | " -a Show all path locations rather than just the first\n" |
| 484 | }, |
| 485 | }; |
| 486 | |
| 487 | /* |
| 488 | ** COMMAND: system |
| 489 | ** |
| 490 |
| --- src/xsystem.c | |
| +++ src/xsystem.c | |
| @@ -31,10 +31,13 @@ | |
| 31 | */ |
| 32 | #include "config.h" |
| 33 | #include "xsystem.h" |
| 34 | #include "qrf.h" |
| 35 | #include <time.h> |
| 36 | #ifdef _WIN32 |
| 37 | # include <windows.h> |
| 38 | #endif |
| 39 | |
| 40 | |
| 41 | /* Date and time */ |
| 42 | void xsystem_date(int argc, char **argv){ |
| 43 | (void)argc; |
| @@ -41,11 +44,11 @@ | |
| 44 | (void)argv; |
| 45 | fossil_print("%z = ", cgi_iso8601_datestamp()); |
| 46 | fossil_print("%z\n", cgi_rfc822_datestamp(time(0))); |
| 47 | } |
| 48 | |
| 49 | /* Present working directory */ |
| 50 | void xsystem_pwd(int argc, char **argv){ |
| 51 | char *zPwd = file_getcwd(0, 0); |
| 52 | fossil_print("%z\n", zPwd); |
| 53 | } |
| 54 | |
| @@ -168,11 +171,11 @@ | |
| 171 | file_directory_list_free(azList); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | ** Return arguments to ORDER BY that will correctly sort the entries. |
| 177 | */ |
| 178 | static const char *xsystem_ls_orderby(int mFlags){ |
| 179 | static const char *zSortTypes[] = { |
| 180 | "fn COLLATE NOCASE", |
| 181 | "mtime DESC", |
| @@ -343,11 +346,11 @@ | |
| 346 | |
| 347 | /* List files "ls" |
| 348 | ** Options: |
| 349 | ** |
| 350 | ** -a Show files that begin with "." |
| 351 | ** -C List by columns |
| 352 | ** --color=WHEN Colorize output? |
| 353 | ** -d Show just directory names, not content |
| 354 | ** -l Long listing |
| 355 | ** -m Comma-separated list |
| 356 | ** -r Reverse sort |
| @@ -439,10 +442,76 @@ | |
| 442 | } |
| 443 | } |
| 444 | sqlite3_finalize(pStmt); |
| 445 | sqlite3_close(db); |
| 446 | } |
| 447 | |
| 448 | /* |
| 449 | ** unzip [-l] ZIPFILE |
| 450 | */ |
| 451 | void xsystem_unzip(int argc, char **argv){ |
| 452 | const char *zZipfile = 0; |
| 453 | int doList = 0; |
| 454 | int i; |
| 455 | char *a[5]; |
| 456 | int n; |
| 457 | extern int sqlite3_shell(int, char**); |
| 458 | |
| 459 | for(i=1; i<argc; i++){ |
| 460 | const char *z = argv[i]; |
| 461 | if( z[0]=='-' ){ |
| 462 | if( z[1]=='-' && z[2]!=0 ) z++; |
| 463 | if( strcmp(z,"-l")==0 ){ |
| 464 | doList = 1; |
| 465 | }else |
| 466 | { |
| 467 | fossil_fatal("unknown option: %s", argv[i]); |
| 468 | } |
| 469 | }else if( zZipfile!=0 ){ |
| 470 | fossil_fatal("extra argument: %s", z); |
| 471 | }else{ |
| 472 | zZipfile = z; |
| 473 | } |
| 474 | } |
| 475 | if( zZipfile==0 ){ |
| 476 | fossil_fatal("Usage: fossil sys unzip [-l] ZIPFILE"); |
| 477 | }else if( file_size(zZipfile, ExtFILE)<0 ){ |
| 478 | fossil_fatal("No such file: %s\n", zZipfile); |
| 479 | } |
| 480 | g.zRepositoryName = 0; |
| 481 | g.zLocalDbName = 0; |
| 482 | g.zConfigDbName = 0; |
| 483 | sqlite3_shutdown(); |
| 484 | a[0] = argv[0]; |
| 485 | a[1] = (char*)zZipfile; |
| 486 | if( doList ){ |
| 487 | a[2] = ".mode column"; |
| 488 | a[3] = "SELECT sz AS Size, date(mtime,'unixepoch') AS Date," |
| 489 | " time(mtime,'unixepoch') AS Time, name AS Name" |
| 490 | " FROM zip;"; |
| 491 | n = 4; |
| 492 | }else{ |
| 493 | a[2] = ".mode list"; |
| 494 | a[3] = "SELECT if(writefile(name,data,mode,mtime) IS NULL," |
| 495 | "'error: '||name,'extracting: '||name) FROM zip;"; |
| 496 | n = 4; |
| 497 | } |
| 498 | a[n] = 0; |
| 499 | sqlite3_shell(n,a); |
| 500 | } |
| 501 | |
| 502 | /* |
| 503 | ** zip [OPTIONS] ZIPFILE FILE ... |
| 504 | */ |
| 505 | void xsystem_zip(int argc, char **argv){ |
| 506 | int i; |
| 507 | for(i=0; i<argc; i++){ |
| 508 | g.argv[i+1] = argv[i]; |
| 509 | } |
| 510 | g.argc = argc+1; |
| 511 | filezip_cmd(); |
| 512 | } |
| 513 | |
| 514 | /* |
| 515 | ** Available system commands. |
| 516 | */ |
| 517 | typedef struct XSysCmd XSysCmd; |
| @@ -473,17 +542,26 @@ | |
| 542 | "Show the Present Working Directory name\n" |
| 543 | }, |
| 544 | { "stty", xsystem_stty, |
| 545 | "\n" |
| 546 | "Show the size of the TTY\n" |
| 547 | }, |
| 548 | { "unzip", xsystem_unzip, |
| 549 | "[-l] ZIPFILE\n\n" |
| 550 | "Extract content from ZIPFILE, or list the content if the -l option\n" |
| 551 | "is used.\n" |
| 552 | }, |
| 553 | { "which", xsystem_which, |
| 554 | "EXE ...\n" |
| 555 | "Show the location on PATH of executables EXE\n" |
| 556 | "Options:\n" |
| 557 | " -a Show all path locations rather than just the first\n" |
| 558 | }, |
| 559 | { "zip", xsystem_zip, |
| 560 | "ZIPFILE FILE ...\n\n" |
| 561 | "Create a new ZIP archive named ZIPFILE using listed files as content\n" |
| 562 | }, |
| 563 | }; |
| 564 | |
| 565 | /* |
| 566 | ** COMMAND: system |
| 567 | ** |
| 568 |
+3
-3
| --- src/zip.c | ||
| +++ src/zip.c | ||
| @@ -230,11 +230,11 @@ | ||
| 230 | 230 | dosTime = (H<<11) + (M<<5) + (S>>1); |
| 231 | 231 | dosDate = ((y-1980)<<9) + (m<<5) + d; |
| 232 | 232 | } |
| 233 | 233 | |
| 234 | 234 | /* |
| 235 | -** Set the date and time from a julian day number. | |
| 235 | +** Set the date and time from a Julian day number. | |
| 236 | 236 | */ |
| 237 | 237 | void zip_set_timedate(double rDate){ |
| 238 | 238 | char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate); |
| 239 | 239 | zip_set_timedate_from_str(zDate); |
| 240 | 240 | fossil_free(zDate); |
| @@ -267,11 +267,11 @@ | ||
| 267 | 267 | char zHdr[30]; |
| 268 | 268 | char zExTime[13]; |
| 269 | 269 | char zBuf[100]; |
| 270 | 270 | char zOutBuf[100000]; |
| 271 | 271 | |
| 272 | - /* Fill in as much of the header as we know. | |
| 272 | + /* Fill inasmuch of the header as we know. | |
| 273 | 273 | */ |
| 274 | 274 | nameLen = (int)strlen(zName); |
| 275 | 275 | if( nameLen==0 ) return; |
| 276 | 276 | nBlob = pFile ? blob_size(pFile) : 0; |
| 277 | 277 | if( pFile ){ /* This is a file, possibly empty... */ |
| @@ -984,11 +984,11 @@ | ||
| 984 | 984 | ** part of the name= value after the / is the download |
| 985 | 985 | ** filename. If no check-in is specified by either |
| 986 | 986 | ** name= or r=, then the name of the main branch |
| 987 | 987 | ** (usually "trunk") is used. |
| 988 | 988 | ** |
| 989 | -** in=PATTERN Only include files that match the comma-separate | |
| 989 | +** in=PATTERN Only include files that match the comma-separated | |
| 990 | 990 | ** list of GLOB patterns in PATTERN, as with ex= |
| 991 | 991 | ** |
| 992 | 992 | ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a |
| 993 | 993 | ** comma-separated list of GLOB patterns, where each |
| 994 | 994 | ** pattern can optionally be quoted using ".." or '..'. |
| 995 | 995 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -230,11 +230,11 @@ | |
| 230 | dosTime = (H<<11) + (M<<5) + (S>>1); |
| 231 | dosDate = ((y-1980)<<9) + (m<<5) + d; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | ** Set the date and time from a julian day number. |
| 236 | */ |
| 237 | void zip_set_timedate(double rDate){ |
| 238 | char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate); |
| 239 | zip_set_timedate_from_str(zDate); |
| 240 | fossil_free(zDate); |
| @@ -267,11 +267,11 @@ | |
| 267 | char zHdr[30]; |
| 268 | char zExTime[13]; |
| 269 | char zBuf[100]; |
| 270 | char zOutBuf[100000]; |
| 271 | |
| 272 | /* Fill in as much of the header as we know. |
| 273 | */ |
| 274 | nameLen = (int)strlen(zName); |
| 275 | if( nameLen==0 ) return; |
| 276 | nBlob = pFile ? blob_size(pFile) : 0; |
| 277 | if( pFile ){ /* This is a file, possibly empty... */ |
| @@ -984,11 +984,11 @@ | |
| 984 | ** part of the name= value after the / is the download |
| 985 | ** filename. If no check-in is specified by either |
| 986 | ** name= or r=, then the name of the main branch |
| 987 | ** (usually "trunk") is used. |
| 988 | ** |
| 989 | ** in=PATTERN Only include files that match the comma-separate |
| 990 | ** list of GLOB patterns in PATTERN, as with ex= |
| 991 | ** |
| 992 | ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a |
| 993 | ** comma-separated list of GLOB patterns, where each |
| 994 | ** pattern can optionally be quoted using ".." or '..'. |
| 995 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -230,11 +230,11 @@ | |
| 230 | dosTime = (H<<11) + (M<<5) + (S>>1); |
| 231 | dosDate = ((y-1980)<<9) + (m<<5) + d; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | ** Set the date and time from a Julian day number. |
| 236 | */ |
| 237 | void zip_set_timedate(double rDate){ |
| 238 | char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate); |
| 239 | zip_set_timedate_from_str(zDate); |
| 240 | fossil_free(zDate); |
| @@ -267,11 +267,11 @@ | |
| 267 | char zHdr[30]; |
| 268 | char zExTime[13]; |
| 269 | char zBuf[100]; |
| 270 | char zOutBuf[100000]; |
| 271 | |
| 272 | /* Fill inasmuch of the header as we know. |
| 273 | */ |
| 274 | nameLen = (int)strlen(zName); |
| 275 | if( nameLen==0 ) return; |
| 276 | nBlob = pFile ? blob_size(pFile) : 0; |
| 277 | if( pFile ){ /* This is a file, possibly empty... */ |
| @@ -984,11 +984,11 @@ | |
| 984 | ** part of the name= value after the / is the download |
| 985 | ** filename. If no check-in is specified by either |
| 986 | ** name= or r=, then the name of the main branch |
| 987 | ** (usually "trunk") is used. |
| 988 | ** |
| 989 | ** in=PATTERN Only include files that match the comma-separated |
| 990 | ** list of GLOB patterns in PATTERN, as with ex= |
| 991 | ** |
| 992 | ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a |
| 993 | ** comma-separated list of GLOB patterns, where each |
| 994 | ** pattern can optionally be quoted using ".." or '..'. |
| 995 |
+1
-1
| --- tools/codecheck1.c | ||
| +++ tools/codecheck1.c | ||
| @@ -443,11 +443,11 @@ | ||
| 443 | 443 | const struct FmtFunc *pB = (const struct FmtFunc*)pBB; |
| 444 | 444 | return strcmp(pA->zFName, pB->zFName); |
| 445 | 445 | } |
| 446 | 446 | |
| 447 | 447 | /* |
| 448 | -** Determine if the indentifier zIdent of length nIndent is a Fossil | |
| 448 | +** Determine if the identifier zIdent of length nIndent is a Fossil | |
| 449 | 449 | ** internal interface that uses a printf-style argument. Return zero if not. |
| 450 | 450 | ** Return the index of the format string if true with the left-most |
| 451 | 451 | ** argument having an index of 1. |
| 452 | 452 | */ |
| 453 | 453 | static int isFormatFunc(const char *zIdent, int nIdent, unsigned *pFlags){ |
| 454 | 454 |
| --- tools/codecheck1.c | |
| +++ tools/codecheck1.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | const struct FmtFunc *pB = (const struct FmtFunc*)pBB; |
| 444 | return strcmp(pA->zFName, pB->zFName); |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | ** Determine if the indentifier zIdent of length nIndent is a Fossil |
| 449 | ** internal interface that uses a printf-style argument. Return zero if not. |
| 450 | ** Return the index of the format string if true with the left-most |
| 451 | ** argument having an index of 1. |
| 452 | */ |
| 453 | static int isFormatFunc(const char *zIdent, int nIdent, unsigned *pFlags){ |
| 454 |
| --- tools/codecheck1.c | |
| +++ tools/codecheck1.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | const struct FmtFunc *pB = (const struct FmtFunc*)pBB; |
| 444 | return strcmp(pA->zFName, pB->zFName); |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | ** Determine if the identifier zIdent of length nIndent is a Fossil |
| 449 | ** internal interface that uses a printf-style argument. Return zero if not. |
| 450 | ** Return the index of the format string if true with the left-most |
| 451 | ** argument having an index of 1. |
| 452 | */ |
| 453 | static int isFormatFunc(const char *zIdent, int nIdent, unsigned *pFlags){ |
| 454 |
+61
-23
| --- tools/mkindex.c | ||
| +++ tools/mkindex.c | ||
| @@ -17,15 +17,38 @@ | ||
| 17 | 17 | ** |
| 18 | 18 | ** This utility program scans Fossil source text looking for specially |
| 19 | 19 | ** formatted comments and generates C source code for constant tables |
| 20 | 20 | ** that define the behavior of commands, webpages, and settings. |
| 21 | 21 | ** |
| 22 | +** USAGE: | |
| 23 | +** | |
| 24 | +** mkindex *.c >page_index.h | |
| 25 | +** | |
| 26 | +** Run this command with arguments that are all input source files to | |
| 27 | +** scan. Generated C code appears on standard output. The generated | |
| 28 | +** C code includes structures that: | |
| 29 | +** | |
| 30 | +** * Map command names to the C-language functions that implement | |
| 31 | +** those command. | |
| 32 | +** | |
| 33 | +** * Map webpage names to the C-language functions that implement | |
| 34 | +** those web pages. | |
| 35 | +** | |
| 36 | +** * Map settings into attributes, such as they default value for | |
| 37 | +** each setting, and the kind of value (boolean, multi-line, etc). | |
| 38 | +** | |
| 39 | +** * Provide help text for commands, webpages, settings, and other | |
| 40 | +** miscellanous help topics. | |
| 41 | +** | |
| 42 | +** COMMENT TEXT THAT THIS PROGRAM LOOKS FOR: | |
| 43 | +** | |
| 22 | 44 | ** The source code is scanned for comment lines of the form: |
| 23 | 45 | ** |
| 24 | 46 | ** WEBPAGE: /abc/xyz |
| 25 | 47 | ** COMMAND: cmdname |
| 26 | 48 | ** SETTING: access-log |
| 49 | +** TOPIC: help-topic | |
| 27 | 50 | ** |
| 28 | 51 | ** The WEBPAGE and COMMAND comments should be followed by a function that |
| 29 | 52 | ** implements the webpage or command. The form of this function is: |
| 30 | 53 | ** |
| 31 | 54 | ** void function_name(void){ |
| @@ -75,10 +98,22 @@ | ||
| 75 | 98 | ** SETTING: pgp-command |
| 76 | 99 | ** DEFAULT: gpg --clearsign -o |
| 77 | 100 | ** |
| 78 | 101 | ** If no default is supplied, the default is assumed to be an empty string |
| 79 | 102 | ** or "off" in the case of a boolean. |
| 103 | +** | |
| 104 | +** A TOPIC: is followed by help text for the named topic. | |
| 105 | +** | |
| 106 | +** OUTPUTS: | |
| 107 | +** | |
| 108 | +** The output is C-language text to define and initialize a constant | |
| 109 | +** array of CmdOrPage objects named "aCommand[]". That array is a global | |
| 110 | +** variable. The dispatch.c source file defines the CmdOrPage object and | |
| 111 | +** deals with the aCommand[] global variable. | |
| 112 | +** | |
| 113 | +** The output also contains a constant array of Setting objects named | |
| 114 | +** aSetting[]. The Setting object is defined in db.c. | |
| 80 | 115 | */ |
| 81 | 116 | #include <stdio.h> |
| 82 | 117 | #include <stdlib.h> |
| 83 | 118 | #include <assert.h> |
| 84 | 119 | #include <string.h> |
| @@ -85,26 +120,27 @@ | ||
| 85 | 120 | |
| 86 | 121 | /*************************************************************************** |
| 87 | 122 | ** These macros must match similar macros in dispatch.c. |
| 88 | 123 | ** |
| 89 | 124 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | -#define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */ | |
| 91 | -#define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */ | |
| 92 | -#define CMDFLAG_TEST 0x00004 /* Commands for testing only */ | |
| 93 | -#define CMDFLAG_WEBPAGE 0x00008 /* Web pages */ | |
| 94 | -#define CMDFLAG_COMMAND 0x00010 /* A command */ | |
| 95 | -#define CMDFLAG_SETTING 0x00020 /* A setting */ | |
| 96 | -#define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */ | |
| 97 | -#define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */ | |
| 98 | -#define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */ | |
| 99 | -#define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */ | |
| 100 | -#define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */ | |
| 101 | -#define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */ | |
| 102 | -#define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */ | |
| 103 | -#define CMDFLAG_ALIAS 0x02000 /* Command aliases */ | |
| 104 | -#define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */ | |
| 105 | -#define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */ | |
| 125 | +#define CMDFLAG_1ST_TIER 0x000001 /* Most important commands */ | |
| 126 | +#define CMDFLAG_2ND_TIER 0x000002 /* Obscure and seldom used commands */ | |
| 127 | +#define CMDFLAG_TEST 0x000004 /* Commands for testing only */ | |
| 128 | +#define CMDFLAG_WEBPAGE 0x000008 /* Web pages */ | |
| 129 | +#define CMDFLAG_COMMAND 0x000010 /* A command */ | |
| 130 | +#define CMDFLAG_SETTING 0x000020 /* A setting */ | |
| 131 | +#define CMDFLAG_VERSIONABLE 0x000040 /* A versionable setting */ | |
| 132 | +#define CMDFLAG_BLOCKTEXT 0x000080 /* Multi-line text setting */ | |
| 133 | +#define CMDFLAG_BOOLEAN 0x000100 /* A boolean setting */ | |
| 134 | +#define CMDFLAG_RAWCONTENT 0x000200 /* Do not interpret webpage content */ | |
| 135 | +#define CMDFLAG_SENSITIVE 0x000400 /* Security-sensitive setting */ | |
| 136 | +#define CMDFLAG_HIDDEN 0x000800 /* Elide from most listings */ | |
| 137 | +#define CMDFLAG_LDAVG_EXEMPT 0x001000 /* Exempt from load_control() */ | |
| 138 | +#define CMDFLAG_ALIAS 0x002000 /* Command aliases */ | |
| 139 | +#define CMDFLAG_KEEPEMPTY 0x004000 /* Do not unset empty settings */ | |
| 140 | +#define CMDFLAG_ABBREVSUBCMD 0x008000 /* Abbreviated subcmd in help text */ | |
| 141 | +#define CMDFLAG_TOPIC 0x010000 /* A help topic */ | |
| 106 | 142 | /**************************************************************************/ |
| 107 | 143 | |
| 108 | 144 | /* |
| 109 | 145 | ** Each entry looks like this: |
| 110 | 146 | */ |
| @@ -343,20 +379,21 @@ | ||
| 343 | 379 | ** Scan a line for a function that implements a web page or command. |
| 344 | 380 | */ |
| 345 | 381 | void scan_for_func(char *zLine){ |
| 346 | 382 | int i,j,k; |
| 347 | 383 | char *z; |
| 348 | - int isSetting; | |
| 384 | + int hasFunc; | |
| 349 | 385 | if( nUsed<=nFixed ) return; |
| 350 | 386 | if( strncmp(zLine, "**", 2)==0 |
| 351 | 387 | && fossil_isspace(zLine[2]) |
| 352 | 388 | && strlen(zLine)<sizeof(zHelp)-nHelp-1 |
| 353 | 389 | && nUsed>nFixed |
| 354 | 390 | && strncmp(zLine,"** COMMAND:",11)!=0 |
| 355 | 391 | && strncmp(zLine,"** WEBPAGE:",11)!=0 |
| 356 | 392 | && strncmp(zLine,"** SETTING:",11)!=0 |
| 357 | 393 | && strncmp(zLine,"** DEFAULT:",11)!=0 |
| 394 | + && strncmp(zLine,"** TOPIC:",9)!=0 | |
| 358 | 395 | ){ |
| 359 | 396 | if( zLine[2]=='\n' ){ |
| 360 | 397 | zHelp[nHelp++] = '\n'; |
| 361 | 398 | }else{ |
| 362 | 399 | if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0; |
| @@ -365,12 +402,12 @@ | ||
| 365 | 402 | } |
| 366 | 403 | return; |
| 367 | 404 | } |
| 368 | 405 | for(i=0; fossil_isspace(zLine[i]); i++){} |
| 369 | 406 | if( zLine[i]==0 ) return; |
| 370 | - isSetting = (aEntry[nFixed].eType & CMDFLAG_SETTING)!=0; | |
| 371 | - if( !isSetting ){ | |
| 407 | + hasFunc = (aEntry[nFixed].eType & (CMDFLAG_SETTING|CMDFLAG_TOPIC))==0; | |
| 408 | + if( hasFunc ){ | |
| 372 | 409 | if( strncmp(&zLine[i],"void",4)!=0 ){ |
| 373 | 410 | if( zLine[i]!='*' ) goto page_skip; |
| 374 | 411 | return; |
| 375 | 412 | } |
| 376 | 413 | i += 4; |
| @@ -390,16 +427,16 @@ | ||
| 390 | 427 | }else{ |
| 391 | 428 | z = ""; |
| 392 | 429 | } |
| 393 | 430 | for(k=nFixed; k<nUsed; k++){ |
| 394 | 431 | aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0; |
| 395 | - aEntry[k].zFunc = isSetting ? "0" : string_dup(&zLine[i], j); | |
| 432 | + aEntry[k].zFunc = hasFunc ? string_dup(&zLine[i], j) : "0"; | |
| 396 | 433 | aEntry[k].zHelp = z; |
| 397 | 434 | z = 0; |
| 398 | 435 | aEntry[k].iHelp = nFixed; |
| 399 | 436 | } |
| 400 | - if( !isSetting ){ | |
| 437 | + if( hasFunc ){ | |
| 401 | 438 | i+=j; |
| 402 | 439 | while( fossil_isspace(zLine[i]) ){ i++; } |
| 403 | 440 | if( zLine[i]!='(' ) goto page_skip; |
| 404 | 441 | } |
| 405 | 442 | nFixed = nUsed; |
| @@ -442,11 +479,11 @@ | ||
| 442 | 479 | "*/\n" |
| 443 | 480 | ); |
| 444 | 481 | |
| 445 | 482 | /* Output declarations for all the action functions */ |
| 446 | 483 | for(i=0; i<nFixed; i++){ |
| 447 | - if( aEntry[i].eType & CMDFLAG_SETTING ) continue; | |
| 484 | + if( aEntry[i].eType & (CMDFLAG_SETTING|CMDFLAG_TOPIC) ) continue; | |
| 448 | 485 | if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); |
| 449 | 486 | printf("extern void %s(void);\n", aEntry[i].zFunc); |
| 450 | 487 | if( aEntry[i].zIf ) printf("#endif\n"); |
| 451 | 488 | } |
| 452 | 489 | |
| @@ -479,11 +516,11 @@ | ||
| 479 | 516 | if( aEntry[i].zIf ){ |
| 480 | 517 | printf("%s", aEntry[i].zIf); |
| 481 | 518 | }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){ |
| 482 | 519 | nWeb++; |
| 483 | 520 | } |
| 484 | - printf(" { \"%.*s\",%*s%s,%*szHelp%03d, %3d, 0x%03x },\n", | |
| 521 | + printf(" { \"%.*s\",%*s%s,%*szHelp%03d, %3d, 0x%05x },\n", | |
| 485 | 522 | n, z, |
| 486 | 523 | 25-n, "", |
| 487 | 524 | aEntry[i].zFunc, |
| 488 | 525 | (int)(29-strlen(aEntry[i].zFunc)), "", |
| 489 | 526 | aEntry[i].iHelp, |
| @@ -550,10 +587,11 @@ | ||
| 550 | 587 | scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE); |
| 551 | 588 | scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND); |
| 552 | 589 | scan_for_func(zLine); |
| 553 | 590 | scan_for_label("SETTING:",zLine,CMDFLAG_SETTING); |
| 554 | 591 | scan_for_default(zLine); |
| 592 | + scan_for_label("TOPIC:",zLine,CMDFLAG_TOPIC); | |
| 555 | 593 | } |
| 556 | 594 | fclose(in); |
| 557 | 595 | nUsed = nFixed; |
| 558 | 596 | } |
| 559 | 597 | |
| 560 | 598 |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -17,15 +17,38 @@ | |
| 17 | ** |
| 18 | ** This utility program scans Fossil source text looking for specially |
| 19 | ** formatted comments and generates C source code for constant tables |
| 20 | ** that define the behavior of commands, webpages, and settings. |
| 21 | ** |
| 22 | ** The source code is scanned for comment lines of the form: |
| 23 | ** |
| 24 | ** WEBPAGE: /abc/xyz |
| 25 | ** COMMAND: cmdname |
| 26 | ** SETTING: access-log |
| 27 | ** |
| 28 | ** The WEBPAGE and COMMAND comments should be followed by a function that |
| 29 | ** implements the webpage or command. The form of this function is: |
| 30 | ** |
| 31 | ** void function_name(void){ |
| @@ -75,10 +98,22 @@ | |
| 75 | ** SETTING: pgp-command |
| 76 | ** DEFAULT: gpg --clearsign -o |
| 77 | ** |
| 78 | ** If no default is supplied, the default is assumed to be an empty string |
| 79 | ** or "off" in the case of a boolean. |
| 80 | */ |
| 81 | #include <stdio.h> |
| 82 | #include <stdlib.h> |
| 83 | #include <assert.h> |
| 84 | #include <string.h> |
| @@ -85,26 +120,27 @@ | |
| 85 | |
| 86 | /*************************************************************************** |
| 87 | ** These macros must match similar macros in dispatch.c. |
| 88 | ** |
| 89 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | #define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */ |
| 91 | #define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */ |
| 92 | #define CMDFLAG_TEST 0x00004 /* Commands for testing only */ |
| 93 | #define CMDFLAG_WEBPAGE 0x00008 /* Web pages */ |
| 94 | #define CMDFLAG_COMMAND 0x00010 /* A command */ |
| 95 | #define CMDFLAG_SETTING 0x00020 /* A setting */ |
| 96 | #define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */ |
| 97 | #define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */ |
| 98 | #define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */ |
| 99 | #define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */ |
| 100 | #define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */ |
| 101 | #define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */ |
| 102 | #define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */ |
| 103 | #define CMDFLAG_ALIAS 0x02000 /* Command aliases */ |
| 104 | #define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */ |
| 105 | #define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */ |
| 106 | /**************************************************************************/ |
| 107 | |
| 108 | /* |
| 109 | ** Each entry looks like this: |
| 110 | */ |
| @@ -343,20 +379,21 @@ | |
| 343 | ** Scan a line for a function that implements a web page or command. |
| 344 | */ |
| 345 | void scan_for_func(char *zLine){ |
| 346 | int i,j,k; |
| 347 | char *z; |
| 348 | int isSetting; |
| 349 | if( nUsed<=nFixed ) return; |
| 350 | if( strncmp(zLine, "**", 2)==0 |
| 351 | && fossil_isspace(zLine[2]) |
| 352 | && strlen(zLine)<sizeof(zHelp)-nHelp-1 |
| 353 | && nUsed>nFixed |
| 354 | && strncmp(zLine,"** COMMAND:",11)!=0 |
| 355 | && strncmp(zLine,"** WEBPAGE:",11)!=0 |
| 356 | && strncmp(zLine,"** SETTING:",11)!=0 |
| 357 | && strncmp(zLine,"** DEFAULT:",11)!=0 |
| 358 | ){ |
| 359 | if( zLine[2]=='\n' ){ |
| 360 | zHelp[nHelp++] = '\n'; |
| 361 | }else{ |
| 362 | if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0; |
| @@ -365,12 +402,12 @@ | |
| 365 | } |
| 366 | return; |
| 367 | } |
| 368 | for(i=0; fossil_isspace(zLine[i]); i++){} |
| 369 | if( zLine[i]==0 ) return; |
| 370 | isSetting = (aEntry[nFixed].eType & CMDFLAG_SETTING)!=0; |
| 371 | if( !isSetting ){ |
| 372 | if( strncmp(&zLine[i],"void",4)!=0 ){ |
| 373 | if( zLine[i]!='*' ) goto page_skip; |
| 374 | return; |
| 375 | } |
| 376 | i += 4; |
| @@ -390,16 +427,16 @@ | |
| 390 | }else{ |
| 391 | z = ""; |
| 392 | } |
| 393 | for(k=nFixed; k<nUsed; k++){ |
| 394 | aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0; |
| 395 | aEntry[k].zFunc = isSetting ? "0" : string_dup(&zLine[i], j); |
| 396 | aEntry[k].zHelp = z; |
| 397 | z = 0; |
| 398 | aEntry[k].iHelp = nFixed; |
| 399 | } |
| 400 | if( !isSetting ){ |
| 401 | i+=j; |
| 402 | while( fossil_isspace(zLine[i]) ){ i++; } |
| 403 | if( zLine[i]!='(' ) goto page_skip; |
| 404 | } |
| 405 | nFixed = nUsed; |
| @@ -442,11 +479,11 @@ | |
| 442 | "*/\n" |
| 443 | ); |
| 444 | |
| 445 | /* Output declarations for all the action functions */ |
| 446 | for(i=0; i<nFixed; i++){ |
| 447 | if( aEntry[i].eType & CMDFLAG_SETTING ) continue; |
| 448 | if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); |
| 449 | printf("extern void %s(void);\n", aEntry[i].zFunc); |
| 450 | if( aEntry[i].zIf ) printf("#endif\n"); |
| 451 | } |
| 452 | |
| @@ -479,11 +516,11 @@ | |
| 479 | if( aEntry[i].zIf ){ |
| 480 | printf("%s", aEntry[i].zIf); |
| 481 | }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){ |
| 482 | nWeb++; |
| 483 | } |
| 484 | printf(" { \"%.*s\",%*s%s,%*szHelp%03d, %3d, 0x%03x },\n", |
| 485 | n, z, |
| 486 | 25-n, "", |
| 487 | aEntry[i].zFunc, |
| 488 | (int)(29-strlen(aEntry[i].zFunc)), "", |
| 489 | aEntry[i].iHelp, |
| @@ -550,10 +587,11 @@ | |
| 550 | scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE); |
| 551 | scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND); |
| 552 | scan_for_func(zLine); |
| 553 | scan_for_label("SETTING:",zLine,CMDFLAG_SETTING); |
| 554 | scan_for_default(zLine); |
| 555 | } |
| 556 | fclose(in); |
| 557 | nUsed = nFixed; |
| 558 | } |
| 559 | |
| 560 |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -17,15 +17,38 @@ | |
| 17 | ** |
| 18 | ** This utility program scans Fossil source text looking for specially |
| 19 | ** formatted comments and generates C source code for constant tables |
| 20 | ** that define the behavior of commands, webpages, and settings. |
| 21 | ** |
| 22 | ** USAGE: |
| 23 | ** |
| 24 | ** mkindex *.c >page_index.h |
| 25 | ** |
| 26 | ** Run this command with arguments that are all input source files to |
| 27 | ** scan. Generated C code appears on standard output. The generated |
| 28 | ** C code includes structures that: |
| 29 | ** |
| 30 | ** * Map command names to the C-language functions that implement |
| 31 | ** those command. |
| 32 | ** |
| 33 | ** * Map webpage names to the C-language functions that implement |
| 34 | ** those web pages. |
| 35 | ** |
| 36 | ** * Map settings into attributes, such as they default value for |
| 37 | ** each setting, and the kind of value (boolean, multi-line, etc). |
| 38 | ** |
| 39 | ** * Provide help text for commands, webpages, settings, and other |
| 40 | ** miscellanous help topics. |
| 41 | ** |
| 42 | ** COMMENT TEXT THAT THIS PROGRAM LOOKS FOR: |
| 43 | ** |
| 44 | ** The source code is scanned for comment lines of the form: |
| 45 | ** |
| 46 | ** WEBPAGE: /abc/xyz |
| 47 | ** COMMAND: cmdname |
| 48 | ** SETTING: access-log |
| 49 | ** TOPIC: help-topic |
| 50 | ** |
| 51 | ** The WEBPAGE and COMMAND comments should be followed by a function that |
| 52 | ** implements the webpage or command. The form of this function is: |
| 53 | ** |
| 54 | ** void function_name(void){ |
| @@ -75,10 +98,22 @@ | |
| 98 | ** SETTING: pgp-command |
| 99 | ** DEFAULT: gpg --clearsign -o |
| 100 | ** |
| 101 | ** If no default is supplied, the default is assumed to be an empty string |
| 102 | ** or "off" in the case of a boolean. |
| 103 | ** |
| 104 | ** A TOPIC: is followed by help text for the named topic. |
| 105 | ** |
| 106 | ** OUTPUTS: |
| 107 | ** |
| 108 | ** The output is C-language text to define and initialize a constant |
| 109 | ** array of CmdOrPage objects named "aCommand[]". That array is a global |
| 110 | ** variable. The dispatch.c source file defines the CmdOrPage object and |
| 111 | ** deals with the aCommand[] global variable. |
| 112 | ** |
| 113 | ** The output also contains a constant array of Setting objects named |
| 114 | ** aSetting[]. The Setting object is defined in db.c. |
| 115 | */ |
| 116 | #include <stdio.h> |
| 117 | #include <stdlib.h> |
| 118 | #include <assert.h> |
| 119 | #include <string.h> |
| @@ -85,26 +120,27 @@ | |
| 120 | |
| 121 | /*************************************************************************** |
| 122 | ** These macros must match similar macros in dispatch.c. |
| 123 | ** |
| 124 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 125 | #define CMDFLAG_1ST_TIER 0x000001 /* Most important commands */ |
| 126 | #define CMDFLAG_2ND_TIER 0x000002 /* Obscure and seldom used commands */ |
| 127 | #define CMDFLAG_TEST 0x000004 /* Commands for testing only */ |
| 128 | #define CMDFLAG_WEBPAGE 0x000008 /* Web pages */ |
| 129 | #define CMDFLAG_COMMAND 0x000010 /* A command */ |
| 130 | #define CMDFLAG_SETTING 0x000020 /* A setting */ |
| 131 | #define CMDFLAG_VERSIONABLE 0x000040 /* A versionable setting */ |
| 132 | #define CMDFLAG_BLOCKTEXT 0x000080 /* Multi-line text setting */ |
| 133 | #define CMDFLAG_BOOLEAN 0x000100 /* A boolean setting */ |
| 134 | #define CMDFLAG_RAWCONTENT 0x000200 /* Do not interpret webpage content */ |
| 135 | #define CMDFLAG_SENSITIVE 0x000400 /* Security-sensitive setting */ |
| 136 | #define CMDFLAG_HIDDEN 0x000800 /* Elide from most listings */ |
| 137 | #define CMDFLAG_LDAVG_EXEMPT 0x001000 /* Exempt from load_control() */ |
| 138 | #define CMDFLAG_ALIAS 0x002000 /* Command aliases */ |
| 139 | #define CMDFLAG_KEEPEMPTY 0x004000 /* Do not unset empty settings */ |
| 140 | #define CMDFLAG_ABBREVSUBCMD 0x008000 /* Abbreviated subcmd in help text */ |
| 141 | #define CMDFLAG_TOPIC 0x010000 /* A help topic */ |
| 142 | /**************************************************************************/ |
| 143 | |
| 144 | /* |
| 145 | ** Each entry looks like this: |
| 146 | */ |
| @@ -343,20 +379,21 @@ | |
| 379 | ** Scan a line for a function that implements a web page or command. |
| 380 | */ |
| 381 | void scan_for_func(char *zLine){ |
| 382 | int i,j,k; |
| 383 | char *z; |
| 384 | int hasFunc; |
| 385 | if( nUsed<=nFixed ) return; |
| 386 | if( strncmp(zLine, "**", 2)==0 |
| 387 | && fossil_isspace(zLine[2]) |
| 388 | && strlen(zLine)<sizeof(zHelp)-nHelp-1 |
| 389 | && nUsed>nFixed |
| 390 | && strncmp(zLine,"** COMMAND:",11)!=0 |
| 391 | && strncmp(zLine,"** WEBPAGE:",11)!=0 |
| 392 | && strncmp(zLine,"** SETTING:",11)!=0 |
| 393 | && strncmp(zLine,"** DEFAULT:",11)!=0 |
| 394 | && strncmp(zLine,"** TOPIC:",9)!=0 |
| 395 | ){ |
| 396 | if( zLine[2]=='\n' ){ |
| 397 | zHelp[nHelp++] = '\n'; |
| 398 | }else{ |
| 399 | if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0; |
| @@ -365,12 +402,12 @@ | |
| 402 | } |
| 403 | return; |
| 404 | } |
| 405 | for(i=0; fossil_isspace(zLine[i]); i++){} |
| 406 | if( zLine[i]==0 ) return; |
| 407 | hasFunc = (aEntry[nFixed].eType & (CMDFLAG_SETTING|CMDFLAG_TOPIC))==0; |
| 408 | if( hasFunc ){ |
| 409 | if( strncmp(&zLine[i],"void",4)!=0 ){ |
| 410 | if( zLine[i]!='*' ) goto page_skip; |
| 411 | return; |
| 412 | } |
| 413 | i += 4; |
| @@ -390,16 +427,16 @@ | |
| 427 | }else{ |
| 428 | z = ""; |
| 429 | } |
| 430 | for(k=nFixed; k<nUsed; k++){ |
| 431 | aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0; |
| 432 | aEntry[k].zFunc = hasFunc ? string_dup(&zLine[i], j) : "0"; |
| 433 | aEntry[k].zHelp = z; |
| 434 | z = 0; |
| 435 | aEntry[k].iHelp = nFixed; |
| 436 | } |
| 437 | if( hasFunc ){ |
| 438 | i+=j; |
| 439 | while( fossil_isspace(zLine[i]) ){ i++; } |
| 440 | if( zLine[i]!='(' ) goto page_skip; |
| 441 | } |
| 442 | nFixed = nUsed; |
| @@ -442,11 +479,11 @@ | |
| 479 | "*/\n" |
| 480 | ); |
| 481 | |
| 482 | /* Output declarations for all the action functions */ |
| 483 | for(i=0; i<nFixed; i++){ |
| 484 | if( aEntry[i].eType & (CMDFLAG_SETTING|CMDFLAG_TOPIC) ) continue; |
| 485 | if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); |
| 486 | printf("extern void %s(void);\n", aEntry[i].zFunc); |
| 487 | if( aEntry[i].zIf ) printf("#endif\n"); |
| 488 | } |
| 489 | |
| @@ -479,11 +516,11 @@ | |
| 516 | if( aEntry[i].zIf ){ |
| 517 | printf("%s", aEntry[i].zIf); |
| 518 | }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){ |
| 519 | nWeb++; |
| 520 | } |
| 521 | printf(" { \"%.*s\",%*s%s,%*szHelp%03d, %3d, 0x%05x },\n", |
| 522 | n, z, |
| 523 | 25-n, "", |
| 524 | aEntry[i].zFunc, |
| 525 | (int)(29-strlen(aEntry[i].zFunc)), "", |
| 526 | aEntry[i].iHelp, |
| @@ -550,10 +587,11 @@ | |
| 587 | scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE); |
| 588 | scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND); |
| 589 | scan_for_func(zLine); |
| 590 | scan_for_label("SETTING:",zLine,CMDFLAG_SETTING); |
| 591 | scan_for_default(zLine); |
| 592 | scan_for_label("TOPIC:",zLine,CMDFLAG_TOPIC); |
| 593 | } |
| 594 | fclose(in); |
| 595 | nUsed = nFixed; |
| 596 | } |
| 597 | |
| 598 |
+1
-1
| --- www/antibot.wiki | ||
| +++ www/antibot.wiki | ||
| @@ -139,11 +139,11 @@ | ||
| 139 | 139 | The [/help/robot-restrict|robot-restrict setting] is a comma-separated |
| 140 | 140 | list of GLOB patterns for pages for which robot access is prohibited. |
| 141 | 141 | The default value is: |
| 142 | 142 | |
| 143 | 143 | <blockquote><pre> |
| 144 | -timelineX,diff,annotate,fileage,file,finfo,reports | |
| 144 | +timelineX,diff,annotate,fileage,file,finfo,reports,tree,hexdump,download | |
| 145 | 145 | </pre></blockquote> |
| 146 | 146 | |
| 147 | 147 | Each entry corresponds to the first path element on the URI for a |
| 148 | 148 | Fossil-generated page. If Fossil does not know for certain that the |
| 149 | 149 | HTTP request is coming from a human, then any attempt to access one of |
| 150 | 150 |
| --- www/antibot.wiki | |
| +++ www/antibot.wiki | |
| @@ -139,11 +139,11 @@ | |
| 139 | The [/help/robot-restrict|robot-restrict setting] is a comma-separated |
| 140 | list of GLOB patterns for pages for which robot access is prohibited. |
| 141 | The default value is: |
| 142 | |
| 143 | <blockquote><pre> |
| 144 | timelineX,diff,annotate,fileage,file,finfo,reports |
| 145 | </pre></blockquote> |
| 146 | |
| 147 | Each entry corresponds to the first path element on the URI for a |
| 148 | Fossil-generated page. If Fossil does not know for certain that the |
| 149 | HTTP request is coming from a human, then any attempt to access one of |
| 150 |
| --- www/antibot.wiki | |
| +++ www/antibot.wiki | |
| @@ -139,11 +139,11 @@ | |
| 139 | The [/help/robot-restrict|robot-restrict setting] is a comma-separated |
| 140 | list of GLOB patterns for pages for which robot access is prohibited. |
| 141 | The default value is: |
| 142 | |
| 143 | <blockquote><pre> |
| 144 | timelineX,diff,annotate,fileage,file,finfo,reports,tree,hexdump,download |
| 145 | </pre></blockquote> |
| 146 | |
| 147 | Each entry corresponds to the first path element on the URI for a |
| 148 | Fossil-generated page. If Fossil does not know for certain that the |
| 149 | HTTP request is coming from a human, then any attempt to access one of |
| 150 |
+4
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -23,10 +23,12 @@ | ||
| 23 | 23 | [/help/suggested-downloads|suggested-downloads setting], a |
| 24 | 24 | link to [/download] named "Tarballs and ZIPs" appears in the |
| 25 | 25 | [/sitemap] and thus on the hamburger menu. |
| 26 | 26 | <li> The filenames for tarballs and ZIPs are now standardized to |
| 27 | 27 | include a timestamp and a hash prefix. |
| 28 | + <li> New "[/help/get|fossil get]" command downloads and unpacks a specific | |
| 29 | + check-in without having to clone the repository. | |
| 28 | 30 | </ol> |
| 29 | 31 | <li> Timeline enhancements:<ol type="a"> |
| 30 | 32 | <li> A new "Simple" view is available. This is compromise between "Verbose" |
| 31 | 33 | and "Compact" that shows only the check-in hash rather than the full |
| 32 | 34 | detail section. There is an ellipsis that one can click on to see the |
| @@ -42,10 +44,12 @@ | ||
| 42 | 44 | <li> "No-graph" timelines (using the "ng" query parameter) now show |
| 43 | 45 | branch colors and bare check-in circles on the left. The check-in |
| 44 | 46 | circles appear, but no lines connecting them. |
| 45 | 47 | ([/timeline?ng|example]). |
| 46 | 48 | </ol> |
| 49 | + <li> Labels in Markdown now have IDs generated using the GitHub "slugify" | |
| 50 | + algorithm. | |
| 47 | 51 | <li> The [/help/timeline|timeline command] is enhanced with the new options |
| 48 | 52 | "<tt>-u|--for-user</tt>" to filter by user, and "<tt>-r</tt>" to display |
| 49 | 53 | entries in chronological order. |
| 50 | 54 | <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag |
| 51 | 55 | can be used to fix a checkout after moving its repository file. |
| 52 | 56 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -23,10 +23,12 @@ | |
| 23 | [/help/suggested-downloads|suggested-downloads setting], a |
| 24 | link to [/download] named "Tarballs and ZIPs" appears in the |
| 25 | [/sitemap] and thus on the hamburger menu. |
| 26 | <li> The filenames for tarballs and ZIPs are now standardized to |
| 27 | include a timestamp and a hash prefix. |
| 28 | </ol> |
| 29 | <li> Timeline enhancements:<ol type="a"> |
| 30 | <li> A new "Simple" view is available. This is compromise between "Verbose" |
| 31 | and "Compact" that shows only the check-in hash rather than the full |
| 32 | detail section. There is an ellipsis that one can click on to see the |
| @@ -42,10 +44,12 @@ | |
| 42 | <li> "No-graph" timelines (using the "ng" query parameter) now show |
| 43 | branch colors and bare check-in circles on the left. The check-in |
| 44 | circles appear, but no lines connecting them. |
| 45 | ([/timeline?ng|example]). |
| 46 | </ol> |
| 47 | <li> The [/help/timeline|timeline command] is enhanced with the new options |
| 48 | "<tt>-u|--for-user</tt>" to filter by user, and "<tt>-r</tt>" to display |
| 49 | entries in chronological order. |
| 50 | <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag |
| 51 | can be used to fix a checkout after moving its repository file. |
| 52 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -23,10 +23,12 @@ | |
| 23 | [/help/suggested-downloads|suggested-downloads setting], a |
| 24 | link to [/download] named "Tarballs and ZIPs" appears in the |
| 25 | [/sitemap] and thus on the hamburger menu. |
| 26 | <li> The filenames for tarballs and ZIPs are now standardized to |
| 27 | include a timestamp and a hash prefix. |
| 28 | <li> New "[/help/get|fossil get]" command downloads and unpacks a specific |
| 29 | check-in without having to clone the repository. |
| 30 | </ol> |
| 31 | <li> Timeline enhancements:<ol type="a"> |
| 32 | <li> A new "Simple" view is available. This is compromise between "Verbose" |
| 33 | and "Compact" that shows only the check-in hash rather than the full |
| 34 | detail section. There is an ellipsis that one can click on to see the |
| @@ -42,10 +44,12 @@ | |
| 44 | <li> "No-graph" timelines (using the "ng" query parameter) now show |
| 45 | branch colors and bare check-in circles on the left. The check-in |
| 46 | circles appear, but no lines connecting them. |
| 47 | ([/timeline?ng|example]). |
| 48 | </ol> |
| 49 | <li> Labels in Markdown now have IDs generated using the GitHub "slugify" |
| 50 | algorithm. |
| 51 | <li> The [/help/timeline|timeline command] is enhanced with the new options |
| 52 | "<tt>-u|--for-user</tt>" to filter by user, and "<tt>-r</tt>" to display |
| 53 | entries in chronological order. |
| 54 | <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag |
| 55 | can be used to fix a checkout after moving its repository file. |
| 56 |
+18
-3
| --- www/customskin.md | ||
| +++ www/customskin.md | ||
| @@ -336,11 +336,12 @@ | ||
| 336 | 336 | output and is instead run as a TH1 script. That TH1 |
| 337 | 337 | script has the opportunity to insert new text in place of itself, |
| 338 | 338 | or to inhibit or enable the output of subsequent text. |
| 339 | 339 | |
| 340 | 340 | * Text of the form "$NAME" or "$<NAME>" is replaced with |
| 341 | - the value of the TH1 variable NAME. | |
| 341 | + the value of the TH1 variable NAME. See the [TH1 Variables](#vars) | |
| 342 | + section for more information on the two possible variable formats. | |
| 342 | 343 | |
| 343 | 344 | For example, first few lines of a typical Skin Header will look |
| 344 | 345 | like this: |
| 345 | 346 | |
| 346 | 347 | <div class="header"> |
| @@ -424,18 +425,32 @@ | ||
| 424 | 425 | ## <a id="vars"></a>TH1 Variables |
| 425 | 426 | |
| 426 | 427 | Before expanding the TH1 within the header and footer, Fossil first |
| 427 | 428 | initializes a number of TH1 variables to values that depend on |
| 428 | 429 | repository settings and the specific page being generated. |
| 430 | + | |
| 431 | +Variables holding text that is loaded from "external, potentially untrusted" | |
| 432 | +sources (including the repository settings) are treated as [tainted strings] | |
| 433 | +(./th1.md#taint) and must be noted in the `$<NAME>` form, instead of `$NAME`, | |
| 434 | +or they may trigger an error (see the linked document for details). The | |
| 435 | +`$<NAME>` form corresponds to the TH1 statement `puts [ htmlize "$NAME" ]`, | |
| 436 | +where the [htmlize](./th1.md#htmlize) function escapes the tainted string, | |
| 437 | +making it safe for output in HTML code. | |
| 438 | + | |
| 429 | 439 | |
| 430 | 440 | * **`project_name`** - The project_name variable is filled with the |
| 431 | 441 | name of the project as configured under the Admin/Configuration |
| 432 | - menu. | |
| 442 | + menu. This is a [tainted string](./th1.md#taint) variable and must | |
| 443 | + be used as `$<project_name>`. | |
| 433 | 444 | |
| 434 | 445 | * **`project_description`** - The project_description variable is |
| 435 | 446 | filled with the description of the project as configured under |
| 436 | - the Admin/Configuration menu. | |
| 447 | + the Admin/Configuration menu. This is a [tainted string] | |
| 448 | + (./th1.md#taint) variable and must be used as `$<project_description>`. | |
| 449 | + | |
| 450 | + * **`mainmenu`** - The mainmenu variable contains a TCL list with the main | |
| 451 | + menu entries. See the [mainmenu](/help/mainmenu) setting for details. | |
| 437 | 452 | |
| 438 | 453 | * **`title`** - The title variable holds the title of the page being |
| 439 | 454 | generated. |
| 440 | 455 | |
| 441 | 456 | The title variable is special in that it is deleted after |
| 442 | 457 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -336,11 +336,12 @@ | |
| 336 | output and is instead run as a TH1 script. That TH1 |
| 337 | script has the opportunity to insert new text in place of itself, |
| 338 | or to inhibit or enable the output of subsequent text. |
| 339 | |
| 340 | * Text of the form "$NAME" or "$<NAME>" is replaced with |
| 341 | the value of the TH1 variable NAME. |
| 342 | |
| 343 | For example, first few lines of a typical Skin Header will look |
| 344 | like this: |
| 345 | |
| 346 | <div class="header"> |
| @@ -424,18 +425,32 @@ | |
| 424 | ## <a id="vars"></a>TH1 Variables |
| 425 | |
| 426 | Before expanding the TH1 within the header and footer, Fossil first |
| 427 | initializes a number of TH1 variables to values that depend on |
| 428 | repository settings and the specific page being generated. |
| 429 | |
| 430 | * **`project_name`** - The project_name variable is filled with the |
| 431 | name of the project as configured under the Admin/Configuration |
| 432 | menu. |
| 433 | |
| 434 | * **`project_description`** - The project_description variable is |
| 435 | filled with the description of the project as configured under |
| 436 | the Admin/Configuration menu. |
| 437 | |
| 438 | * **`title`** - The title variable holds the title of the page being |
| 439 | generated. |
| 440 | |
| 441 | The title variable is special in that it is deleted after |
| 442 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -336,11 +336,12 @@ | |
| 336 | output and is instead run as a TH1 script. That TH1 |
| 337 | script has the opportunity to insert new text in place of itself, |
| 338 | or to inhibit or enable the output of subsequent text. |
| 339 | |
| 340 | * Text of the form "$NAME" or "$<NAME>" is replaced with |
| 341 | the value of the TH1 variable NAME. See the [TH1 Variables](#vars) |
| 342 | section for more information on the two possible variable formats. |
| 343 | |
| 344 | For example, first few lines of a typical Skin Header will look |
| 345 | like this: |
| 346 | |
| 347 | <div class="header"> |
| @@ -424,18 +425,32 @@ | |
| 425 | ## <a id="vars"></a>TH1 Variables |
| 426 | |
| 427 | Before expanding the TH1 within the header and footer, Fossil first |
| 428 | initializes a number of TH1 variables to values that depend on |
| 429 | repository settings and the specific page being generated. |
| 430 | |
| 431 | Variables holding text that is loaded from "external, potentially untrusted" |
| 432 | sources (including the repository settings) are treated as [tainted strings] |
| 433 | (./th1.md#taint) and must be noted in the `$<NAME>` form, instead of `$NAME`, |
| 434 | or they may trigger an error (see the linked document for details). The |
| 435 | `$<NAME>` form corresponds to the TH1 statement `puts [ htmlize "$NAME" ]`, |
| 436 | where the [htmlize](./th1.md#htmlize) function escapes the tainted string, |
| 437 | making it safe for output in HTML code. |
| 438 | |
| 439 | |
| 440 | * **`project_name`** - The project_name variable is filled with the |
| 441 | name of the project as configured under the Admin/Configuration |
| 442 | menu. This is a [tainted string](./th1.md#taint) variable and must |
| 443 | be used as `$<project_name>`. |
| 444 | |
| 445 | * **`project_description`** - The project_description variable is |
| 446 | filled with the description of the project as configured under |
| 447 | the Admin/Configuration menu. This is a [tainted string] |
| 448 | (./th1.md#taint) variable and must be used as `$<project_description>`. |
| 449 | |
| 450 | * **`mainmenu`** - The mainmenu variable contains a TCL list with the main |
| 451 | menu entries. See the [mainmenu](/help/mainmenu) setting for details. |
| 452 | |
| 453 | * **`title`** - The title variable holds the title of the page being |
| 454 | generated. |
| 455 | |
| 456 | The title variable is special in that it is deleted after |
| 457 |
+1
-1
| --- www/serverext.wiki | ||
| +++ www/serverext.wiki | ||
| @@ -139,11 +139,11 @@ | ||
| 139 | 139 | For example, the author of this documentation page is running |
| 140 | 140 | "<tt>fossil ui --extpage www/serverext.wiki</tt>" while editing this |
| 141 | 141 | very paragraph, and presses Reload from time to time to view his |
| 142 | 142 | edits. |
| 143 | 143 | |
| 144 | -A same idea applies when developing new CGI applications using a script | |
| 144 | +The same idea applies when developing new CGI applications using a script | |
| 145 | 145 | language (for example using [https://wapp.tcl.tk|Wapp]). Run the |
| 146 | 146 | command "<tt>fossil ui --extpage SCRIPT</tt>" where SCRIPT is the name |
| 147 | 147 | of the application script, while editing that script in a separate |
| 148 | 148 | window, then press Reload periodically on the web browser to test the |
| 149 | 149 | script. |
| 150 | 150 |
| --- www/serverext.wiki | |
| +++ www/serverext.wiki | |
| @@ -139,11 +139,11 @@ | |
| 139 | For example, the author of this documentation page is running |
| 140 | "<tt>fossil ui --extpage www/serverext.wiki</tt>" while editing this |
| 141 | very paragraph, and presses Reload from time to time to view his |
| 142 | edits. |
| 143 | |
| 144 | A same idea applies when developing new CGI applications using a script |
| 145 | language (for example using [https://wapp.tcl.tk|Wapp]). Run the |
| 146 | command "<tt>fossil ui --extpage SCRIPT</tt>" where SCRIPT is the name |
| 147 | of the application script, while editing that script in a separate |
| 148 | window, then press Reload periodically on the web browser to test the |
| 149 | script. |
| 150 |
| --- www/serverext.wiki | |
| +++ www/serverext.wiki | |
| @@ -139,11 +139,11 @@ | |
| 139 | For example, the author of this documentation page is running |
| 140 | "<tt>fossil ui --extpage www/serverext.wiki</tt>" while editing this |
| 141 | very paragraph, and presses Reload from time to time to view his |
| 142 | edits. |
| 143 | |
| 144 | The same idea applies when developing new CGI applications using a script |
| 145 | language (for example using [https://wapp.tcl.tk|Wapp]). Run the |
| 146 | command "<tt>fossil ui --extpage SCRIPT</tt>" where SCRIPT is the name |
| 147 | of the application script, while editing that script in a separate |
| 148 | window, then press Reload periodically on the web browser to test the |
| 149 | script. |
| 150 |