Fossil SCM
merge [trunk]
Commit
19dfb072791bf0cb83d9b10d7399f12f5d813144e80c98d1e81b58f214f7e758
Parent
0b63227882a8342…
95 files changed
+2
-2
+1
-1
+168
-68
+1
-1
+6
+14
-1
+144
-75
+10
-2
+1
-1
+11
-10
+38
-14
+38
-14
+17
-31
+1
+12
+122
-61
+19
+27
-2
+43
-6
+101
-1
+15
-11
+172
-19
+5
-1
+3
-1
+228
-121
+220
-203
+1
-4
+62
-14
+37
-6
+24
+4
-2
+60
-13
+7
+1
-1
+57
-3
+234
-39
+84
-14
+19
-1
+16
-1
+45
-13
+5
-6
+280
-102
+2
-2
+55
+1673
-736
+107
-31
+2
-1
+1
-13
+1
-1
+1
-1
+9
+51
-5
+32
-6
+51
-10
+18
-14
+15
+46
-23
+3
-21
+153
-56
+1
-1
+2
+1
-1
+72
+16
-4
+25
-1
+26
-1
+21
-1
+1
-1
+1
-1
+43
-4
+1
-2
+32
-44
+3
-1
+8
-8
+143
+72
+21
-72
+38
-16
+6
-3
+11
-3
+15
-2
+4
-4
+12
-10
+4
-4
+26
-13
+7
+51
-10
+1
+6
-3
+3
-2
~
auto.def
~
skins/default/css.txt
~
src/alerts.c
~
src/allrepo.c
~
src/backlink.c
~
src/branch.c
~
src/browse.c
~
src/builtin.c
~
src/capabilities.c
~
src/cgi.c
~
src/checkin.c
~
src/checkin.c
~
src/comformat.c
~
src/configure.c
~
src/content.c
~
src/db.c
~
src/default_css.txt
~
src/diff.c
~
src/etag.c
~
src/file.c
~
src/finfo.c
~
src/forum.c
~
src/fshell.c
~
src/graph.c
~
src/http_ssl.c
~
src/info.c
~
src/json_branch.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/makemake.tcl
~
src/manifest.c
~
src/merge.c
~
src/merge3.c
~
src/mkversion.c
~
src/name.c
~
src/rebuild.c
~
src/schema.c
~
src/security_audit.c
~
src/setup.c
~
src/setupuser.c
~
src/shell.c
~
src/shun.c
~
src/skins.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/style.c
~
src/sync.c
~
src/tag.c
~
src/tar.c
~
src/terminal.c
~
src/timeline.c
~
src/tkt.c
~
src/unversioned.c
~
src/url.c
~
src/util.c
~
src/wiki.c
~
src/wikiformat.c
~
src/xfer.c
~
src/zip.c
~
test/subdir with spaces/filename with spaces.txt
~
test/tester.tcl
~
tools/email-monitor.tcl
~
tools/encode_math.sh
~
tools/fossil-autocomplete.bash
~
tools/fossil-diff-log
~
tools/fossil-stress.tcl
~
tools/fossil_chat.tcl
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
www/antibot.wiki
~
www/build.wiki
~
www/changes.wiki
~
www/css-tricks.md
~
www/env-opts.md
~
www/faq.wiki
~
www/fossil-v-git.wiki
~
www/gitusers.md
~
www/history.md
~
www/index.wiki
~
www/mirrorlimitations.md
~
www/mkindex.tcl
~
www/permutedindex.html
~
www/private.wiki
~
www/qandc.wiki
~
www/quickstart.wiki
~
www/rebaseharm.md
~
www/ssl.wiki
~
www/sync.wiki
~
www/tech_overview.wiki
~
www/userlinks.wiki
~
www/whyusefossil.wiki
~
www/wikitheory.wiki
M
auto.def
+2
-2
| --- auto.def | ||
| +++ auto.def | ||
| @@ -273,11 +273,11 @@ | ||
| 273 | 273 | |
| 274 | 274 | # Check for libraries that need to be sorted out early |
| 275 | 275 | cc-check-function-in-lib iconv iconv |
| 276 | 276 | |
| 277 | 277 | # Helper for OpenSSL checking |
| 278 | -proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto}}} { | |
| 278 | +proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} { | |
| 279 | 279 | msg-checking "Checking for $msg..." |
| 280 | 280 | set rc 0 |
| 281 | 281 | if {[is_mingw]} { |
| 282 | 282 | lappend libs -lgdi32 -lwsock32 -lcrypt32 |
| 283 | 283 | } |
| @@ -351,11 +351,11 @@ | ||
| 351 | 351 | user-error "The OpenSSL in source tree directory does not exist" |
| 352 | 352 | } |
| 353 | 353 | set msg "ssl in $ssldir" |
| 354 | 354 | set cflags "-I$ssldir/include" |
| 355 | 355 | set ldflags "-L$ssldir" |
| 356 | - set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a" | |
| 356 | + set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread" | |
| 357 | 357 | set found [check-for-openssl "ssl in source tree" "$cflags $ldflags" $ssllibs] |
| 358 | 358 | } else { |
| 359 | 359 | if {$ssldirs in {auto ""}} { |
| 360 | 360 | catch { |
| 361 | 361 | set cflags [exec pkg-config openssl --cflags-only-I] |
| 362 | 362 |
| --- auto.def | |
| +++ auto.def | |
| @@ -273,11 +273,11 @@ | |
| 273 | |
| 274 | # Check for libraries that need to be sorted out early |
| 275 | cc-check-function-in-lib iconv iconv |
| 276 | |
| 277 | # Helper for OpenSSL checking |
| 278 | proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto}}} { |
| 279 | msg-checking "Checking for $msg..." |
| 280 | set rc 0 |
| 281 | if {[is_mingw]} { |
| 282 | lappend libs -lgdi32 -lwsock32 -lcrypt32 |
| 283 | } |
| @@ -351,11 +351,11 @@ | |
| 351 | user-error "The OpenSSL in source tree directory does not exist" |
| 352 | } |
| 353 | set msg "ssl in $ssldir" |
| 354 | set cflags "-I$ssldir/include" |
| 355 | set ldflags "-L$ssldir" |
| 356 | set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a" |
| 357 | set found [check-for-openssl "ssl in source tree" "$cflags $ldflags" $ssllibs] |
| 358 | } else { |
| 359 | if {$ssldirs in {auto ""}} { |
| 360 | catch { |
| 361 | set cflags [exec pkg-config openssl --cflags-only-I] |
| 362 |
| --- auto.def | |
| +++ auto.def | |
| @@ -273,11 +273,11 @@ | |
| 273 | |
| 274 | # Check for libraries that need to be sorted out early |
| 275 | cc-check-function-in-lib iconv iconv |
| 276 | |
| 277 | # Helper for OpenSSL checking |
| 278 | proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} { |
| 279 | msg-checking "Checking for $msg..." |
| 280 | set rc 0 |
| 281 | if {[is_mingw]} { |
| 282 | lappend libs -lgdi32 -lwsock32 -lcrypt32 |
| 283 | } |
| @@ -351,11 +351,11 @@ | |
| 351 | user-error "The OpenSSL in source tree directory does not exist" |
| 352 | } |
| 353 | set msg "ssl in $ssldir" |
| 354 | set cflags "-I$ssldir/include" |
| 355 | set ldflags "-L$ssldir" |
| 356 | set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread" |
| 357 | set found [check-for-openssl "ssl in source tree" "$cflags $ldflags" $ssllibs] |
| 358 | } else { |
| 359 | if {$ssldirs in {auto ""}} { |
| 360 | catch { |
| 361 | set cflags [exec pkg-config openssl --cflags-only-I] |
| 362 |
+1
-1
| --- skins/default/css.txt | ||
| +++ skins/default/css.txt | ||
| @@ -145,11 +145,11 @@ | ||
| 145 | 145 | border-radius: 5px; |
| 146 | 146 | } |
| 147 | 147 | .content blockquote { |
| 148 | 148 | padding: 0 15px; |
| 149 | 149 | } |
| 150 | -div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote { | |
| 150 | +div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote, div.forumTime blockquote, div.forumTimeline blockquote { | |
| 151 | 151 | background-color: rgba(65, 131, 196, 0.1); |
| 152 | 152 | border-left: 3px solid #254769; |
| 153 | 153 | padding: .1em 1em; |
| 154 | 154 | } |
| 155 | 155 | |
| 156 | 156 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -145,11 +145,11 @@ | |
| 145 | border-radius: 5px; |
| 146 | } |
| 147 | .content blockquote { |
| 148 | padding: 0 15px; |
| 149 | } |
| 150 | div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote { |
| 151 | background-color: rgba(65, 131, 196, 0.1); |
| 152 | border-left: 3px solid #254769; |
| 153 | padding: .1em 1em; |
| 154 | } |
| 155 | |
| 156 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -145,11 +145,11 @@ | |
| 145 | border-radius: 5px; |
| 146 | } |
| 147 | .content blockquote { |
| 148 | padding: 0 15px; |
| 149 | } |
| 150 | div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote, div.forumTime blockquote, div.forumTimeline blockquote { |
| 151 | background-color: rgba(65, 131, 196, 0.1); |
| 152 | border-left: 3px solid #254769; |
| 153 | padding: .1em 1em; |
| 154 | } |
| 155 | |
| 156 |
+168
-68
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -1208,10 +1208,19 @@ | ||
| 1208 | 1208 | int i, j, n; |
| 1209 | 1209 | char c; |
| 1210 | 1210 | |
| 1211 | 1211 | *peErr = 0; |
| 1212 | 1212 | *pzErr = 0; |
| 1213 | + | |
| 1214 | + /* Verify the captcha first */ | |
| 1215 | + if( needCaptcha ){ | |
| 1216 | + if( !captcha_is_correct(1) ){ | |
| 1217 | + *peErr = 2; | |
| 1218 | + *pzErr = mprintf("incorrect security code"); | |
| 1219 | + return 0; | |
| 1220 | + } | |
| 1221 | + } | |
| 1213 | 1222 | |
| 1214 | 1223 | /* Check the validity of the email address. |
| 1215 | 1224 | ** |
| 1216 | 1225 | ** (1) Exactly one '@' character. |
| 1217 | 1226 | ** (2) No other characters besides [a-zA-Z0-9._+-] |
| @@ -1219,11 +1228,15 @@ | ||
| 1219 | 1228 | ** The local part is currently more restrictive than RFC 5322 allows: |
| 1220 | 1229 | ** https://stackoverflow.com/a/2049510/142454 We will expand this as |
| 1221 | 1230 | ** necessary. |
| 1222 | 1231 | */ |
| 1223 | 1232 | zEAddr = P("e"); |
| 1224 | - if( zEAddr==0 ) return 0; | |
| 1233 | + if( zEAddr==0 ){ | |
| 1234 | + *peErr = 1; | |
| 1235 | + *pzErr = mprintf("required"); | |
| 1236 | + return 0; | |
| 1237 | + } | |
| 1225 | 1238 | for(i=j=n=0; (c = zEAddr[i])!=0; i++){ |
| 1226 | 1239 | if( c=='@' ){ |
| 1227 | 1240 | n = i; |
| 1228 | 1241 | j++; |
| 1229 | 1242 | continue; |
| @@ -1249,14 +1262,13 @@ | ||
| 1249 | 1262 | *peErr = 1; |
| 1250 | 1263 | *pzErr = mprintf("email domain too short"); |
| 1251 | 1264 | return 0; |
| 1252 | 1265 | } |
| 1253 | 1266 | |
| 1254 | - /* Verify the captcha */ | |
| 1255 | - if( needCaptcha && !captcha_is_correct(1) ){ | |
| 1256 | - *peErr = 2; | |
| 1257 | - *pzErr = mprintf("incorrect security code"); | |
| 1267 | + if( authorized_subscription_email(zEAddr)==0 ){ | |
| 1268 | + *peErr = 1; | |
| 1269 | + *pzErr = mprintf("not an authorized email address"); | |
| 1258 | 1270 | return 0; |
| 1259 | 1271 | } |
| 1260 | 1272 | |
| 1261 | 1273 | /* Check to make sure the email address is available for reuse */ |
| 1262 | 1274 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ |
| @@ -1347,10 +1359,14 @@ | ||
| 1347 | 1359 | /* Everybody else jumps to the page to administer their own |
| 1348 | 1360 | ** account only. */ |
| 1349 | 1361 | cgi_redirectf("%R/alerts"); |
| 1350 | 1362 | return; |
| 1351 | 1363 | } |
| 1364 | + } | |
| 1365 | + if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){ | |
| 1366 | + register_page(); | |
| 1367 | + return; | |
| 1352 | 1368 | } |
| 1353 | 1369 | alert_submenu_common(); |
| 1354 | 1370 | needCaptcha = !login_is_individual(); |
| 1355 | 1371 | if( P("submit") |
| 1356 | 1372 | && cgi_csrf_safe(1) |
| @@ -1368,10 +1384,11 @@ | ||
| 1368 | 1384 | if( PB("sa") ) ssub[nsub++] = 'a'; |
| 1369 | 1385 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1370 | 1386 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1371 | 1387 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1372 | 1388 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1389 | + if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; | |
| 1373 | 1390 | ssub[nsub] = 0; |
| 1374 | 1391 | db_multi_exec( |
| 1375 | 1392 | "INSERT INTO subscriber(semail,suname," |
| 1376 | 1393 | " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" |
| 1377 | 1394 | "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)", |
| @@ -1388,11 +1405,15 @@ | ||
| 1388 | 1405 | id); |
| 1389 | 1406 | if( !needCaptcha ){ |
| 1390 | 1407 | /* The new subscription has been added on behalf of a logged-in user. |
| 1391 | 1408 | ** No verification is required. Jump immediately to /alerts page. |
| 1392 | 1409 | */ |
| 1393 | - cgi_redirectf("%R/alerts/%s", zCode); | |
| 1410 | + if( g.perm.Admin ){ | |
| 1411 | + cgi_redirectf("%R/alerts/%.32s", zCode); | |
| 1412 | + }else{ | |
| 1413 | + cgi_redirectf("%R/alerts"); | |
| 1414 | + } | |
| 1394 | 1415 | return; |
| 1395 | 1416 | }else{ |
| 1396 | 1417 | /* We need to send a verification email */ |
| 1397 | 1418 | Blob hdr, body; |
| 1398 | 1419 | AlertSender *pSender = alert_sender_new(0,0); |
| @@ -1410,11 +1431,11 @@ | ||
| 1410 | 1431 | @ <blockquote><pre> |
| 1411 | 1432 | @ %h(pSender->zErr) |
| 1412 | 1433 | @ </pre></blockquote> |
| 1413 | 1434 | }else{ |
| 1414 | 1435 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1415 | - @ hyperlink that you must click on in order to activate your | |
| 1436 | + @ hyperlink that you must click to activate your | |
| 1416 | 1437 | @ subscription.</p> |
| 1417 | 1438 | } |
| 1418 | 1439 | alert_sender_free(pSender); |
| 1419 | 1440 | style_footer(); |
| 1420 | 1441 | } |
| @@ -1442,16 +1463,22 @@ | ||
| 1442 | 1463 | if( eErr==1 ){ |
| 1443 | 1464 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1444 | 1465 | } |
| 1445 | 1466 | @ </tr> |
| 1446 | 1467 | if( needCaptcha ){ |
| 1447 | - uSeed = captcha_seed(); | |
| 1468 | + const char *zInit = ""; | |
| 1469 | + if( P("captchaseed")!=0 && eErr!=2 ){ | |
| 1470 | + uSeed = strtoul(P("captchaseed"),0,10); | |
| 1471 | + zInit = P("captcha"); | |
| 1472 | + }else{ | |
| 1473 | + uSeed = captcha_seed(); | |
| 1474 | + } | |
| 1448 | 1475 | zDecoded = captcha_decode(uSeed); |
| 1449 | 1476 | zCaptcha = captcha_render(zDecoded); |
| 1450 | 1477 | @ <tr> |
| 1451 | 1478 | @ <td class="form_label">Security Code:</td> |
| 1452 | - @ <td><input type="text" name="captcha" value="" size="30"> | |
| 1479 | + @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> | |
| 1453 | 1480 | captcha_speakit_button(uSeed, "Speak the code"); |
| 1454 | 1481 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> |
| 1455 | 1482 | @ </tr> |
| 1456 | 1483 | if( eErr==2 ){ |
| 1457 | 1484 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| @@ -1478,10 +1505,12 @@ | ||
| 1478 | 1505 | @ Check-ins</label><br> |
| 1479 | 1506 | } |
| 1480 | 1507 | if( g.perm.RdForum ){ |
| 1481 | 1508 | @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \ |
| 1482 | 1509 | @ Forum Posts</label><br> |
| 1510 | + @ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \ | |
| 1511 | + @ Forum Edits</label><br> | |
| 1483 | 1512 | } |
| 1484 | 1513 | if( g.perm.RdTkt ){ |
| 1485 | 1514 | @ <label><input type="checkbox" name="st" %s(PCK("st"))> \ |
| 1486 | 1515 | @ Ticket changes</label><br> |
| 1487 | 1516 | } |
| @@ -1531,21 +1560,20 @@ | ||
| 1531 | 1560 | /* |
| 1532 | 1561 | ** Either shutdown or completely delete a subscription entry given |
| 1533 | 1562 | ** by the hex value zName. Then paint a webpage that explains that |
| 1534 | 1563 | ** the entry has been removed. |
| 1535 | 1564 | */ |
| 1536 | -static void alert_unsubscribe(const char *zName){ | |
| 1565 | +static void alert_unsubscribe(int sid){ | |
| 1537 | 1566 | char *zEmail; |
| 1538 | 1567 | zEmail = db_text(0, "SELECT semail FROM subscriber" |
| 1539 | - " WHERE subscriberCode=hextoblob(%Q)", zName); | |
| 1568 | + " WHERE subscriberId=%d", sid); | |
| 1540 | 1569 | if( zEmail==0 ){ |
| 1541 | 1570 | style_header("Unsubscribe Fail"); |
| 1542 | 1571 | @ <p>Unable to locate a subscriber with the requested key</p> |
| 1543 | 1572 | }else{ |
| 1544 | 1573 | db_multi_exec( |
| 1545 | - "DELETE FROM subscriber WHERE subscriberCode=hextoblob(%Q)", | |
| 1546 | - zName | |
| 1574 | + "DELETE FROM subscriber WHERE subscriberId=%d", sid | |
| 1547 | 1575 | ); |
| 1548 | 1576 | style_header("Unsubscribed"); |
| 1549 | 1577 | @ <p>The "%h(zEmail)" email address has been delisted. |
| 1550 | 1578 | @ All traces of that email address have been removed</p> |
| 1551 | 1579 | } |
| @@ -1556,48 +1584,83 @@ | ||
| 1556 | 1584 | /* |
| 1557 | 1585 | ** WEBPAGE: alerts |
| 1558 | 1586 | ** |
| 1559 | 1587 | ** Edit email alert and notification settings. |
| 1560 | 1588 | ** |
| 1561 | -** The subscriber is identified in either of two ways: | |
| 1589 | +** The subscriber is identified in several ways: | |
| 1590 | +** | |
| 1591 | +** (1) The name= query parameter contains the complete subscriberCode. | |
| 1592 | +** This only happens when the user receives a verification | |
| 1593 | +** email and clicks on the link in the email. When a | |
| 1594 | +** compilete subscriberCode is seen on the name= query parameter, | |
| 1595 | +** that constitutes verification of the email address. | |
| 1562 | 1596 | ** |
| 1563 | -** (1) The name= query parameter contains the subscriberCode. | |
| 1597 | +** (2) The sid= query parameter contains an integer subscriberId. | |
| 1598 | +** This only works for the administrator. It allows the | |
| 1599 | +** administrator to edit any subscription. | |
| 1564 | 1600 | ** |
| 1565 | -** (2) The user is logged into an account other than "nobody" or | |
| 1601 | +** (3) The user is logged into an account other than "nobody" or | |
| 1566 | 1602 | ** "anonymous". In that case the notification settings |
| 1567 | 1603 | ** associated with that account can be edited without needing |
| 1568 | 1604 | ** to know the subscriber code. |
| 1605 | +** | |
| 1606 | +** (4) The name= query parameter contains a 32-digit prefix of | |
| 1607 | +** subscriber code. (Subscriber codes are normally 64 hex digits | |
| 1608 | +** in length.) This uniquely identifies the subscriber without | |
| 1609 | +** revealing the complete subscriber code, and hence without | |
| 1610 | +** verifying the email address. | |
| 1569 | 1611 | */ |
| 1570 | 1612 | void alert_page(void){ |
| 1571 | - const char *zName = P("name"); | |
| 1572 | - Stmt q; | |
| 1573 | - int sa, sc, sf, st, sw, sx; | |
| 1574 | - int sdigest = 0, sdonotcall = 0, sverified = 0; | |
| 1575 | - int isLogin; /* Logged in as an individual */ | |
| 1576 | - const char *ssub = 0; | |
| 1577 | - const char *semail = 0; | |
| 1578 | - const char *smip; | |
| 1579 | - const char *suname = 0; | |
| 1580 | - const char *mtime; | |
| 1581 | - const char *sctime; | |
| 1582 | - int eErr = 0; | |
| 1583 | - char *zErr = 0; | |
| 1584 | - | |
| 1585 | - if( alert_webpages_disabled() ) return; | |
| 1613 | + const char *zName = 0; /* Value of the name= query parameter */ | |
| 1614 | + Stmt q; /* For querying the database */ | |
| 1615 | + int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ | |
| 1616 | + int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ | |
| 1617 | + int isLogin; /* True if logged in as an individual */ | |
| 1618 | + const char *ssub = 0; /* Subscription flags */ | |
| 1619 | + const char *semail = 0; /* Email address */ | |
| 1620 | + const char *smip; /* */ | |
| 1621 | + const char *suname = 0; /* Corresponding user.login value */ | |
| 1622 | + const char *mtime; /* */ | |
| 1623 | + const char *sctime; /* Time subscription created */ | |
| 1624 | + int eErr = 0; /* Type of error */ | |
| 1625 | + char *zErr = 0; /* Error message text */ | |
| 1626 | + int sid = 0; /* Subscriber ID */ | |
| 1627 | + int nName; /* Length of zName in bytes */ | |
| 1628 | + char *zHalfCode; /* prefix of subscriberCode */ | |
| 1629 | + | |
| 1630 | + db_begin_transaction(); | |
| 1631 | + if( alert_webpages_disabled() ){ | |
| 1632 | + db_commit_transaction(); | |
| 1633 | + return; | |
| 1634 | + } | |
| 1586 | 1635 | login_check_credentials(); |
| 1587 | 1636 | if( !g.perm.EmailAlert ){ |
| 1637 | + db_commit_transaction(); | |
| 1588 | 1638 | login_needed(g.anon.EmailAlert); |
| 1589 | - return; | |
| 1639 | + /*NOTREACHED*/ | |
| 1590 | 1640 | } |
| 1591 | 1641 | isLogin = login_is_individual(); |
| 1592 | - if( zName==0 && isLogin ){ | |
| 1593 | - zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber" | |
| 1594 | - " WHERE suname=%Q", g.zLogin); | |
| 1642 | + zName = P("name"); | |
| 1643 | + nName = zName ? (int)strlen(zName) : 0; | |
| 1644 | + if( g.perm.Admin && P("sid")!=0 ){ | |
| 1645 | + sid = atoi(P("sid")); | |
| 1646 | + } | |
| 1647 | + if( sid==0 && nName>=32 ){ | |
| 1648 | + sid = db_int(0, | |
| 1649 | + "SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')" | |
| 1650 | + " THEN subscriberId ELSE 0 END" | |
| 1651 | + " FROM subscriber WHERE subscriberCode>=hextoblob(%Q)" | |
| 1652 | + " LIMIT 1", zName, zName); | |
| 1653 | + } | |
| 1654 | + if( sid==0 && isLogin ){ | |
| 1655 | + sid = db_int(0, "SELECT subscriberId FROM subscriber" | |
| 1656 | + " WHERE suname=%Q", g.zLogin); | |
| 1595 | 1657 | } |
| 1596 | - if( zName==0 || !validate16(zName, -1) ){ | |
| 1658 | + if( sid==0 ){ | |
| 1659 | + db_commit_transaction(); | |
| 1597 | 1660 | cgi_redirect("subscribe"); |
| 1598 | - return; | |
| 1661 | + /*NOTREACHED*/ | |
| 1599 | 1662 | } |
| 1600 | 1663 | alert_submenu_common(); |
| 1601 | 1664 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 1602 | 1665 | char newSsub[10]; |
| 1603 | 1666 | int nsub = 0; |
| @@ -1641,11 +1704,11 @@ | ||
| 1641 | 1704 | if( semail==0 || email_address_is_valid(semail,0)==0 ){ |
| 1642 | 1705 | eErr = 8; |
| 1643 | 1706 | } |
| 1644 | 1707 | blob_append_sql(&update, ", semail=%Q", semail); |
| 1645 | 1708 | } |
| 1646 | - blob_append_sql(&update," WHERE subscriberCode=hextoblob(%Q)", zName); | |
| 1709 | + blob_append_sql(&update," WHERE subscriberId=%d", sid); | |
| 1647 | 1710 | if( eErr==0 ){ |
| 1648 | 1711 | db_exec_sql(blob_str(&update)); |
| 1649 | 1712 | ssub = 0; |
| 1650 | 1713 | } |
| 1651 | 1714 | blob_reset(&update); |
| @@ -1654,12 +1717,13 @@ | ||
| 1654 | 1717 | if( !PB("dodelete") ){ |
| 1655 | 1718 | eErr = 9; |
| 1656 | 1719 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1657 | 1720 | " unsubscribe"); |
| 1658 | 1721 | }else{ |
| 1659 | - alert_unsubscribe(zName); | |
| 1660 | - return; | |
| 1722 | + alert_unsubscribe(sid); | |
| 1723 | + db_commit_transaction(); | |
| 1724 | + return; | |
| 1661 | 1725 | } |
| 1662 | 1726 | } |
| 1663 | 1727 | style_header("Update Subscription"); |
| 1664 | 1728 | db_prepare(&q, |
| 1665 | 1729 | "SELECT" |
| @@ -1669,16 +1733,18 @@ | ||
| 1669 | 1733 | " sdigest," /* 3 */ |
| 1670 | 1734 | " ssub," /* 4 */ |
| 1671 | 1735 | " smip," /* 5 */ |
| 1672 | 1736 | " suname," /* 6 */ |
| 1673 | 1737 | " datetime(mtime,'unixepoch')," /* 7 */ |
| 1674 | - " datetime(sctime,'unixepoch')" /* 8 */ | |
| 1675 | - " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName); | |
| 1738 | + " datetime(sctime,'unixepoch')," /* 8 */ | |
| 1739 | + " hex(subscriberCode)" /* 9 */ | |
| 1740 | + " FROM subscriber WHERE subscriberId=%d", sid); | |
| 1676 | 1741 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1677 | 1742 | db_finalize(&q); |
| 1743 | + db_commit_transaction(); | |
| 1678 | 1744 | cgi_redirect("subscribe"); |
| 1679 | - return; | |
| 1745 | + /*NOTREACHED*/ | |
| 1680 | 1746 | } |
| 1681 | 1747 | if( ssub==0 ){ |
| 1682 | 1748 | semail = db_column_text(&q, 0); |
| 1683 | 1749 | sdonotcall = db_column_int(&q, 2); |
| 1684 | 1750 | sdigest = db_column_int(&q, 3); |
| @@ -1696,23 +1762,45 @@ | ||
| 1696 | 1762 | sx = strchr(ssub,'x')!=0; |
| 1697 | 1763 | smip = db_column_text(&q, 5); |
| 1698 | 1764 | mtime = db_column_text(&q, 7); |
| 1699 | 1765 | sctime = db_column_text(&q, 8); |
| 1700 | 1766 | if( !g.perm.Admin && !sverified ){ |
| 1701 | - db_multi_exec( | |
| 1702 | - "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)", | |
| 1703 | - zName); | |
| 1704 | - @ <h1>Your email alert subscription has been verified!</h1> | |
| 1705 | - @ <p>Use the form below to update your subscription information.</p> | |
| 1706 | - @ <p>Hint: Bookmark this page so that you can more easily update | |
| 1707 | - @ your subscription information in the future</p> | |
| 1767 | + if( nName==64 ){ | |
| 1768 | + db_multi_exec( | |
| 1769 | + "UPDATE subscriber SET sverified=1" | |
| 1770 | + " WHERE subscriberCode=hextoblob(%Q)", | |
| 1771 | + zName); | |
| 1772 | + if( db_get_boolean("selfreg-verify",0) ){ | |
| 1773 | + char *zNewCap = db_get("default-perms","u"); | |
| 1774 | + db_multi_exec( | |
| 1775 | + "UPDATE user" | |
| 1776 | + " SET cap=%Q" | |
| 1777 | + " WHERE cap='7' AND login=(" | |
| 1778 | + " SELECT suname FROM subscriber" | |
| 1779 | + " WHERE subscriberCode=hextoblob(%Q))", | |
| 1780 | + zNewCap, zName | |
| 1781 | + ); | |
| 1782 | + login_set_capabilities(zNewCap, 0); | |
| 1783 | + } | |
| 1784 | + @ <h1>Your email alert subscription has been verified!</h1> | |
| 1785 | + @ <p>Use the form below to update your subscription information.</p> | |
| 1786 | + @ <p>Hint: Bookmark this page so that you can more easily update | |
| 1787 | + @ your subscription information in the future</p> | |
| 1788 | + }else{ | |
| 1789 | + @ <h2>Your email address is unverified</h2> | |
| 1790 | + @ <p>You should have received an email message containing a link | |
| 1791 | + @ that you must visit to verify your account. No email notifications | |
| 1792 | + @ will be sent until your email address has been verified.</p> | |
| 1793 | + } | |
| 1708 | 1794 | }else{ |
| 1709 | 1795 | @ <p>Make changes to the email subscription shown below and |
| 1710 | 1796 | @ press "Submit".</p> |
| 1711 | 1797 | } |
| 1712 | 1798 | form_begin(0, "%R/alerts"); |
| 1713 | - @ <input type="hidden" name="name" value="%h(zName)"> | |
| 1799 | + zHalfCode = db_text("x","SELECT hex(substr(subscriberCode,1,16))" | |
| 1800 | + " FROM subscriber WHERE subscriberId=%d", sid); | |
| 1801 | + @ <input type="hidden" name="name" value="%h(zHalfCode)"> | |
| 1714 | 1802 | @ <table class="subscribe"> |
| 1715 | 1803 | @ <tr> |
| 1716 | 1804 | @ <td class="form_label">Email Address:</td> |
| 1717 | 1805 | if( isLogin ){ |
| 1718 | 1806 | @ <td><input type="text" name="semail" value="%h(semail)" size="30">\ |
| @@ -1739,10 +1827,13 @@ | ||
| 1739 | 1827 | @ </tr> |
| 1740 | 1828 | @ <tr> |
| 1741 | 1829 | @ <td class='form_label'>IP Address:</td> |
| 1742 | 1830 | @ <td>%h(smip)</td> |
| 1743 | 1831 | @ </tr> |
| 1832 | + @ <tr> | |
| 1833 | + @ <td class='form_label'>Subscriber Code:</td> | |
| 1834 | + @ <td>%h(db_column_text(&q,9))</td> | |
| 1744 | 1835 | @ <tr> |
| 1745 | 1836 | @ <td class="form_label">User:</td> |
| 1746 | 1837 | @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \ |
| 1747 | 1838 | @ size="30">\ |
| 1748 | 1839 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname); |
| @@ -1780,14 +1871,10 @@ | ||
| 1780 | 1871 | @ <td><select size="1" name="sdigest"> |
| 1781 | 1872 | @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option> |
| 1782 | 1873 | @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option> |
| 1783 | 1874 | @ </select></td> |
| 1784 | 1875 | @ </tr> |
| 1785 | -#if 0 | |
| 1786 | - @ <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\ | |
| 1787 | - @ Daily digest only</label><br> | |
| 1788 | -#endif | |
| 1789 | 1876 | if( g.perm.Admin ){ |
| 1790 | 1877 | @ <tr> |
| 1791 | 1878 | @ <td class="form_label">Admin Options:</td><td> |
| 1792 | 1879 | @ <label><input type="checkbox" name="sdonotcall" \ |
| 1793 | 1880 | @ %s(sdonotcall?"checked":"")> Do not disturb</label><br> |
| @@ -1811,10 +1898,12 @@ | ||
| 1811 | 1898 | @ </table> |
| 1812 | 1899 | @ </form> |
| 1813 | 1900 | fossil_free(zErr); |
| 1814 | 1901 | db_finalize(&q); |
| 1815 | 1902 | style_footer(); |
| 1903 | + db_commit_transaction(); | |
| 1904 | + return; | |
| 1816 | 1905 | } |
| 1817 | 1906 | |
| 1818 | 1907 | /* This is the message that gets sent to describe how to change |
| 1819 | 1908 | ** or modify a subscription |
| 1820 | 1909 | */ |
| @@ -1852,18 +1941,19 @@ | ||
| 1852 | 1941 | char *zCaptcha = 0; |
| 1853 | 1942 | int dx; |
| 1854 | 1943 | int bSubmit; |
| 1855 | 1944 | const char *zEAddr; |
| 1856 | 1945 | char *zCode = 0; |
| 1946 | + int sid = 0; | |
| 1857 | 1947 | |
| 1858 | 1948 | /* If a valid subscriber code is supplied, then unsubscribe immediately. |
| 1859 | 1949 | */ |
| 1860 | 1950 | if( zName |
| 1861 | - && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)", | |
| 1862 | - zName) | |
| 1951 | + && (sid = db_int(0, "SELECT subscriberId FROM subscriber" | |
| 1952 | + " WHERE subscriberCode=hextoblob(%Q)", zName))!=0 | |
| 1863 | 1953 | ){ |
| 1864 | - alert_unsubscribe(zName); | |
| 1954 | + alert_unsubscribe(sid); | |
| 1865 | 1955 | return; |
| 1866 | 1956 | } |
| 1867 | 1957 | |
| 1868 | 1958 | /* Logged in users are redirected to the /alerts page */ |
| 1869 | 1959 | login_check_credentials(); |
| @@ -2021,11 +2111,11 @@ | ||
| 2021 | 2111 | if( nDel>0 ){ |
| 2022 | 2112 | @ <p>*** %d(nDel) pending subscriptions deleted ***</p> |
| 2023 | 2113 | } |
| 2024 | 2114 | blob_init(&sql, 0, 0); |
| 2025 | 2115 | blob_append_sql(&sql, |
| 2026 | - "SELECT hex(subscriberCode)," /* 0 */ | |
| 2116 | + "SELECT subscriberId," /* 0 */ | |
| 2027 | 2117 | " semail," /* 1 */ |
| 2028 | 2118 | " ssub," /* 2 */ |
| 2029 | 2119 | " suname," /* 3 */ |
| 2030 | 2120 | " sverified," /* 4 */ |
| 2031 | 2121 | " sdigest," /* 5 */ |
| @@ -2058,11 +2148,11 @@ | ||
| 2058 | 2148 | sqlite3_int64 iMtime = db_column_int64(&q, 6); |
| 2059 | 2149 | double rAge = (iNow - iMtime)/86400.0; |
| 2060 | 2150 | int uid = db_column_int(&q, 8); |
| 2061 | 2151 | const char *zUname = db_column_text(&q, 3); |
| 2062 | 2152 | @ <tr> |
| 2063 | - @ <td><a href='%R/alerts/%s(db_column_text(&q,0))'>\ | |
| 2153 | + @ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\ | |
| 2064 | 2154 | @ %h(db_column_text(&q,1))</a></td> |
| 2065 | 2155 | @ <td>%h(db_column_text(&q,2))</td> |
| 2066 | 2156 | @ <td>%s(db_column_int(&q,5)?"digest":"")</td> |
| 2067 | 2157 | if( uid ){ |
| 2068 | 2158 | @ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a> |
| @@ -2210,18 +2300,28 @@ | ||
| 2210 | 2300 | ** is a normal email alert. Construct full-text forum post alerts |
| 2211 | 2301 | ** using a format that enables them to be sent as separate emails. |
| 2212 | 2302 | */ |
| 2213 | 2303 | db_prepare(&q, |
| 2214 | 2304 | "SELECT" |
| 2215 | - " forumpost.fpid," /* 0 */ | |
| 2216 | - " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */ | |
| 2217 | - " datetime(event.mtime)," /* 2 */ | |
| 2218 | - " substr(comment,instr(comment,':')+2)," /* 3 */ | |
| 2219 | - " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */ | |
| 2220 | - " wantalert.needMod," /* 5 */ | |
| 2221 | - " coalesce(display_name(info),euser,user)," /* 6 */ | |
| 2222 | - " forumpost.fprev IS NULL" /* 7 */ | |
| 2305 | + " forumpost.fpid," /* 0: fpid */ | |
| 2306 | + " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1: hash */ | |
| 2307 | + " datetime(event.mtime)," /* 2: date/time */ | |
| 2308 | + " substr(comment,instr(comment,':')+2)," /* 3: comment */ | |
| 2309 | + " (WITH thread(fpid,fprev) AS (" | |
| 2310 | + " SELECT fpid,fprev FROM forumpost AS tx" | |
| 2311 | + " WHERE tx.froot=forumpost.froot)," | |
| 2312 | + " basepid(fpid,bpid) AS (" | |
| 2313 | + " SELECT fpid, fpid FROM thread WHERE fprev IS NULL" | |
| 2314 | + " UNION ALL" | |
| 2315 | + " SELECT thread.fpid, basepid.bpid FROM basepid, thread" | |
| 2316 | + " WHERE basepid.fpid=thread.fprev)" | |
| 2317 | + " SELECT uuid FROM blob, basepid" | |
| 2318 | + " WHERE basepid.fpid=forumpost.firt" | |
| 2319 | + " AND blob.rid=basepid.bpid)," /* 4: in-reply-to */ | |
| 2320 | + " wantalert.needMod," /* 5: moderated */ | |
| 2321 | + " coalesce(display_name(info),euser,user)," /* 6: user */ | |
| 2322 | + " forumpost.fprev IS NULL" /* 7: is an edit */ | |
| 2223 | 2323 | " FROM temp.wantalert, event, forumpost" |
| 2224 | 2324 | " LEFT JOIN user ON (login=coalesce(euser,user))" |
| 2225 | 2325 | " WHERE event.objid=substr(wantalert.eventId,2)+0" |
| 2226 | 2326 | " AND eventId GLOB 'f*'" |
| 2227 | 2327 | " AND forumpost.fpid=event.objid" |
| 2228 | 2328 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1208,10 +1208,19 @@ | |
| 1208 | int i, j, n; |
| 1209 | char c; |
| 1210 | |
| 1211 | *peErr = 0; |
| 1212 | *pzErr = 0; |
| 1213 | |
| 1214 | /* Check the validity of the email address. |
| 1215 | ** |
| 1216 | ** (1) Exactly one '@' character. |
| 1217 | ** (2) No other characters besides [a-zA-Z0-9._+-] |
| @@ -1219,11 +1228,15 @@ | |
| 1219 | ** The local part is currently more restrictive than RFC 5322 allows: |
| 1220 | ** https://stackoverflow.com/a/2049510/142454 We will expand this as |
| 1221 | ** necessary. |
| 1222 | */ |
| 1223 | zEAddr = P("e"); |
| 1224 | if( zEAddr==0 ) return 0; |
| 1225 | for(i=j=n=0; (c = zEAddr[i])!=0; i++){ |
| 1226 | if( c=='@' ){ |
| 1227 | n = i; |
| 1228 | j++; |
| 1229 | continue; |
| @@ -1249,14 +1262,13 @@ | |
| 1249 | *peErr = 1; |
| 1250 | *pzErr = mprintf("email domain too short"); |
| 1251 | return 0; |
| 1252 | } |
| 1253 | |
| 1254 | /* Verify the captcha */ |
| 1255 | if( needCaptcha && !captcha_is_correct(1) ){ |
| 1256 | *peErr = 2; |
| 1257 | *pzErr = mprintf("incorrect security code"); |
| 1258 | return 0; |
| 1259 | } |
| 1260 | |
| 1261 | /* Check to make sure the email address is available for reuse */ |
| 1262 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ |
| @@ -1347,10 +1359,14 @@ | |
| 1347 | /* Everybody else jumps to the page to administer their own |
| 1348 | ** account only. */ |
| 1349 | cgi_redirectf("%R/alerts"); |
| 1350 | return; |
| 1351 | } |
| 1352 | } |
| 1353 | alert_submenu_common(); |
| 1354 | needCaptcha = !login_is_individual(); |
| 1355 | if( P("submit") |
| 1356 | && cgi_csrf_safe(1) |
| @@ -1368,10 +1384,11 @@ | |
| 1368 | if( PB("sa") ) ssub[nsub++] = 'a'; |
| 1369 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1370 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1371 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1372 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1373 | ssub[nsub] = 0; |
| 1374 | db_multi_exec( |
| 1375 | "INSERT INTO subscriber(semail,suname," |
| 1376 | " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" |
| 1377 | "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)", |
| @@ -1388,11 +1405,15 @@ | |
| 1388 | id); |
| 1389 | if( !needCaptcha ){ |
| 1390 | /* The new subscription has been added on behalf of a logged-in user. |
| 1391 | ** No verification is required. Jump immediately to /alerts page. |
| 1392 | */ |
| 1393 | cgi_redirectf("%R/alerts/%s", zCode); |
| 1394 | return; |
| 1395 | }else{ |
| 1396 | /* We need to send a verification email */ |
| 1397 | Blob hdr, body; |
| 1398 | AlertSender *pSender = alert_sender_new(0,0); |
| @@ -1410,11 +1431,11 @@ | |
| 1410 | @ <blockquote><pre> |
| 1411 | @ %h(pSender->zErr) |
| 1412 | @ </pre></blockquote> |
| 1413 | }else{ |
| 1414 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1415 | @ hyperlink that you must click on in order to activate your |
| 1416 | @ subscription.</p> |
| 1417 | } |
| 1418 | alert_sender_free(pSender); |
| 1419 | style_footer(); |
| 1420 | } |
| @@ -1442,16 +1463,22 @@ | |
| 1442 | if( eErr==1 ){ |
| 1443 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1444 | } |
| 1445 | @ </tr> |
| 1446 | if( needCaptcha ){ |
| 1447 | uSeed = captcha_seed(); |
| 1448 | zDecoded = captcha_decode(uSeed); |
| 1449 | zCaptcha = captcha_render(zDecoded); |
| 1450 | @ <tr> |
| 1451 | @ <td class="form_label">Security Code:</td> |
| 1452 | @ <td><input type="text" name="captcha" value="" size="30"> |
| 1453 | captcha_speakit_button(uSeed, "Speak the code"); |
| 1454 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> |
| 1455 | @ </tr> |
| 1456 | if( eErr==2 ){ |
| 1457 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| @@ -1478,10 +1505,12 @@ | |
| 1478 | @ Check-ins</label><br> |
| 1479 | } |
| 1480 | if( g.perm.RdForum ){ |
| 1481 | @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \ |
| 1482 | @ Forum Posts</label><br> |
| 1483 | } |
| 1484 | if( g.perm.RdTkt ){ |
| 1485 | @ <label><input type="checkbox" name="st" %s(PCK("st"))> \ |
| 1486 | @ Ticket changes</label><br> |
| 1487 | } |
| @@ -1531,21 +1560,20 @@ | |
| 1531 | /* |
| 1532 | ** Either shutdown or completely delete a subscription entry given |
| 1533 | ** by the hex value zName. Then paint a webpage that explains that |
| 1534 | ** the entry has been removed. |
| 1535 | */ |
| 1536 | static void alert_unsubscribe(const char *zName){ |
| 1537 | char *zEmail; |
| 1538 | zEmail = db_text(0, "SELECT semail FROM subscriber" |
| 1539 | " WHERE subscriberCode=hextoblob(%Q)", zName); |
| 1540 | if( zEmail==0 ){ |
| 1541 | style_header("Unsubscribe Fail"); |
| 1542 | @ <p>Unable to locate a subscriber with the requested key</p> |
| 1543 | }else{ |
| 1544 | db_multi_exec( |
| 1545 | "DELETE FROM subscriber WHERE subscriberCode=hextoblob(%Q)", |
| 1546 | zName |
| 1547 | ); |
| 1548 | style_header("Unsubscribed"); |
| 1549 | @ <p>The "%h(zEmail)" email address has been delisted. |
| 1550 | @ All traces of that email address have been removed</p> |
| 1551 | } |
| @@ -1556,48 +1584,83 @@ | |
| 1556 | /* |
| 1557 | ** WEBPAGE: alerts |
| 1558 | ** |
| 1559 | ** Edit email alert and notification settings. |
| 1560 | ** |
| 1561 | ** The subscriber is identified in either of two ways: |
| 1562 | ** |
| 1563 | ** (1) The name= query parameter contains the subscriberCode. |
| 1564 | ** |
| 1565 | ** (2) The user is logged into an account other than "nobody" or |
| 1566 | ** "anonymous". In that case the notification settings |
| 1567 | ** associated with that account can be edited without needing |
| 1568 | ** to know the subscriber code. |
| 1569 | */ |
| 1570 | void alert_page(void){ |
| 1571 | const char *zName = P("name"); |
| 1572 | Stmt q; |
| 1573 | int sa, sc, sf, st, sw, sx; |
| 1574 | int sdigest = 0, sdonotcall = 0, sverified = 0; |
| 1575 | int isLogin; /* Logged in as an individual */ |
| 1576 | const char *ssub = 0; |
| 1577 | const char *semail = 0; |
| 1578 | const char *smip; |
| 1579 | const char *suname = 0; |
| 1580 | const char *mtime; |
| 1581 | const char *sctime; |
| 1582 | int eErr = 0; |
| 1583 | char *zErr = 0; |
| 1584 | |
| 1585 | if( alert_webpages_disabled() ) return; |
| 1586 | login_check_credentials(); |
| 1587 | if( !g.perm.EmailAlert ){ |
| 1588 | login_needed(g.anon.EmailAlert); |
| 1589 | return; |
| 1590 | } |
| 1591 | isLogin = login_is_individual(); |
| 1592 | if( zName==0 && isLogin ){ |
| 1593 | zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber" |
| 1594 | " WHERE suname=%Q", g.zLogin); |
| 1595 | } |
| 1596 | if( zName==0 || !validate16(zName, -1) ){ |
| 1597 | cgi_redirect("subscribe"); |
| 1598 | return; |
| 1599 | } |
| 1600 | alert_submenu_common(); |
| 1601 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 1602 | char newSsub[10]; |
| 1603 | int nsub = 0; |
| @@ -1641,11 +1704,11 @@ | |
| 1641 | if( semail==0 || email_address_is_valid(semail,0)==0 ){ |
| 1642 | eErr = 8; |
| 1643 | } |
| 1644 | blob_append_sql(&update, ", semail=%Q", semail); |
| 1645 | } |
| 1646 | blob_append_sql(&update," WHERE subscriberCode=hextoblob(%Q)", zName); |
| 1647 | if( eErr==0 ){ |
| 1648 | db_exec_sql(blob_str(&update)); |
| 1649 | ssub = 0; |
| 1650 | } |
| 1651 | blob_reset(&update); |
| @@ -1654,12 +1717,13 @@ | |
| 1654 | if( !PB("dodelete") ){ |
| 1655 | eErr = 9; |
| 1656 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1657 | " unsubscribe"); |
| 1658 | }else{ |
| 1659 | alert_unsubscribe(zName); |
| 1660 | return; |
| 1661 | } |
| 1662 | } |
| 1663 | style_header("Update Subscription"); |
| 1664 | db_prepare(&q, |
| 1665 | "SELECT" |
| @@ -1669,16 +1733,18 @@ | |
| 1669 | " sdigest," /* 3 */ |
| 1670 | " ssub," /* 4 */ |
| 1671 | " smip," /* 5 */ |
| 1672 | " suname," /* 6 */ |
| 1673 | " datetime(mtime,'unixepoch')," /* 7 */ |
| 1674 | " datetime(sctime,'unixepoch')" /* 8 */ |
| 1675 | " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName); |
| 1676 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1677 | db_finalize(&q); |
| 1678 | cgi_redirect("subscribe"); |
| 1679 | return; |
| 1680 | } |
| 1681 | if( ssub==0 ){ |
| 1682 | semail = db_column_text(&q, 0); |
| 1683 | sdonotcall = db_column_int(&q, 2); |
| 1684 | sdigest = db_column_int(&q, 3); |
| @@ -1696,23 +1762,45 @@ | |
| 1696 | sx = strchr(ssub,'x')!=0; |
| 1697 | smip = db_column_text(&q, 5); |
| 1698 | mtime = db_column_text(&q, 7); |
| 1699 | sctime = db_column_text(&q, 8); |
| 1700 | if( !g.perm.Admin && !sverified ){ |
| 1701 | db_multi_exec( |
| 1702 | "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)", |
| 1703 | zName); |
| 1704 | @ <h1>Your email alert subscription has been verified!</h1> |
| 1705 | @ <p>Use the form below to update your subscription information.</p> |
| 1706 | @ <p>Hint: Bookmark this page so that you can more easily update |
| 1707 | @ your subscription information in the future</p> |
| 1708 | }else{ |
| 1709 | @ <p>Make changes to the email subscription shown below and |
| 1710 | @ press "Submit".</p> |
| 1711 | } |
| 1712 | form_begin(0, "%R/alerts"); |
| 1713 | @ <input type="hidden" name="name" value="%h(zName)"> |
| 1714 | @ <table class="subscribe"> |
| 1715 | @ <tr> |
| 1716 | @ <td class="form_label">Email Address:</td> |
| 1717 | if( isLogin ){ |
| 1718 | @ <td><input type="text" name="semail" value="%h(semail)" size="30">\ |
| @@ -1739,10 +1827,13 @@ | |
| 1739 | @ </tr> |
| 1740 | @ <tr> |
| 1741 | @ <td class='form_label'>IP Address:</td> |
| 1742 | @ <td>%h(smip)</td> |
| 1743 | @ </tr> |
| 1744 | @ <tr> |
| 1745 | @ <td class="form_label">User:</td> |
| 1746 | @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \ |
| 1747 | @ size="30">\ |
| 1748 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname); |
| @@ -1780,14 +1871,10 @@ | |
| 1780 | @ <td><select size="1" name="sdigest"> |
| 1781 | @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option> |
| 1782 | @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option> |
| 1783 | @ </select></td> |
| 1784 | @ </tr> |
| 1785 | #if 0 |
| 1786 | @ <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\ |
| 1787 | @ Daily digest only</label><br> |
| 1788 | #endif |
| 1789 | if( g.perm.Admin ){ |
| 1790 | @ <tr> |
| 1791 | @ <td class="form_label">Admin Options:</td><td> |
| 1792 | @ <label><input type="checkbox" name="sdonotcall" \ |
| 1793 | @ %s(sdonotcall?"checked":"")> Do not disturb</label><br> |
| @@ -1811,10 +1898,12 @@ | |
| 1811 | @ </table> |
| 1812 | @ </form> |
| 1813 | fossil_free(zErr); |
| 1814 | db_finalize(&q); |
| 1815 | style_footer(); |
| 1816 | } |
| 1817 | |
| 1818 | /* This is the message that gets sent to describe how to change |
| 1819 | ** or modify a subscription |
| 1820 | */ |
| @@ -1852,18 +1941,19 @@ | |
| 1852 | char *zCaptcha = 0; |
| 1853 | int dx; |
| 1854 | int bSubmit; |
| 1855 | const char *zEAddr; |
| 1856 | char *zCode = 0; |
| 1857 | |
| 1858 | /* If a valid subscriber code is supplied, then unsubscribe immediately. |
| 1859 | */ |
| 1860 | if( zName |
| 1861 | && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)", |
| 1862 | zName) |
| 1863 | ){ |
| 1864 | alert_unsubscribe(zName); |
| 1865 | return; |
| 1866 | } |
| 1867 | |
| 1868 | /* Logged in users are redirected to the /alerts page */ |
| 1869 | login_check_credentials(); |
| @@ -2021,11 +2111,11 @@ | |
| 2021 | if( nDel>0 ){ |
| 2022 | @ <p>*** %d(nDel) pending subscriptions deleted ***</p> |
| 2023 | } |
| 2024 | blob_init(&sql, 0, 0); |
| 2025 | blob_append_sql(&sql, |
| 2026 | "SELECT hex(subscriberCode)," /* 0 */ |
| 2027 | " semail," /* 1 */ |
| 2028 | " ssub," /* 2 */ |
| 2029 | " suname," /* 3 */ |
| 2030 | " sverified," /* 4 */ |
| 2031 | " sdigest," /* 5 */ |
| @@ -2058,11 +2148,11 @@ | |
| 2058 | sqlite3_int64 iMtime = db_column_int64(&q, 6); |
| 2059 | double rAge = (iNow - iMtime)/86400.0; |
| 2060 | int uid = db_column_int(&q, 8); |
| 2061 | const char *zUname = db_column_text(&q, 3); |
| 2062 | @ <tr> |
| 2063 | @ <td><a href='%R/alerts/%s(db_column_text(&q,0))'>\ |
| 2064 | @ %h(db_column_text(&q,1))</a></td> |
| 2065 | @ <td>%h(db_column_text(&q,2))</td> |
| 2066 | @ <td>%s(db_column_int(&q,5)?"digest":"")</td> |
| 2067 | if( uid ){ |
| 2068 | @ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a> |
| @@ -2210,18 +2300,28 @@ | |
| 2210 | ** is a normal email alert. Construct full-text forum post alerts |
| 2211 | ** using a format that enables them to be sent as separate emails. |
| 2212 | */ |
| 2213 | db_prepare(&q, |
| 2214 | "SELECT" |
| 2215 | " forumpost.fpid," /* 0 */ |
| 2216 | " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */ |
| 2217 | " datetime(event.mtime)," /* 2 */ |
| 2218 | " substr(comment,instr(comment,':')+2)," /* 3 */ |
| 2219 | " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */ |
| 2220 | " wantalert.needMod," /* 5 */ |
| 2221 | " coalesce(display_name(info),euser,user)," /* 6 */ |
| 2222 | " forumpost.fprev IS NULL" /* 7 */ |
| 2223 | " FROM temp.wantalert, event, forumpost" |
| 2224 | " LEFT JOIN user ON (login=coalesce(euser,user))" |
| 2225 | " WHERE event.objid=substr(wantalert.eventId,2)+0" |
| 2226 | " AND eventId GLOB 'f*'" |
| 2227 | " AND forumpost.fpid=event.objid" |
| 2228 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1208,10 +1208,19 @@ | |
| 1208 | int i, j, n; |
| 1209 | char c; |
| 1210 | |
| 1211 | *peErr = 0; |
| 1212 | *pzErr = 0; |
| 1213 | |
| 1214 | /* Verify the captcha first */ |
| 1215 | if( needCaptcha ){ |
| 1216 | if( !captcha_is_correct(1) ){ |
| 1217 | *peErr = 2; |
| 1218 | *pzErr = mprintf("incorrect security code"); |
| 1219 | return 0; |
| 1220 | } |
| 1221 | } |
| 1222 | |
| 1223 | /* Check the validity of the email address. |
| 1224 | ** |
| 1225 | ** (1) Exactly one '@' character. |
| 1226 | ** (2) No other characters besides [a-zA-Z0-9._+-] |
| @@ -1219,11 +1228,15 @@ | |
| 1228 | ** The local part is currently more restrictive than RFC 5322 allows: |
| 1229 | ** https://stackoverflow.com/a/2049510/142454 We will expand this as |
| 1230 | ** necessary. |
| 1231 | */ |
| 1232 | zEAddr = P("e"); |
| 1233 | if( zEAddr==0 ){ |
| 1234 | *peErr = 1; |
| 1235 | *pzErr = mprintf("required"); |
| 1236 | return 0; |
| 1237 | } |
| 1238 | for(i=j=n=0; (c = zEAddr[i])!=0; i++){ |
| 1239 | if( c=='@' ){ |
| 1240 | n = i; |
| 1241 | j++; |
| 1242 | continue; |
| @@ -1249,14 +1262,13 @@ | |
| 1262 | *peErr = 1; |
| 1263 | *pzErr = mprintf("email domain too short"); |
| 1264 | return 0; |
| 1265 | } |
| 1266 | |
| 1267 | if( authorized_subscription_email(zEAddr)==0 ){ |
| 1268 | *peErr = 1; |
| 1269 | *pzErr = mprintf("not an authorized email address"); |
| 1270 | return 0; |
| 1271 | } |
| 1272 | |
| 1273 | /* Check to make sure the email address is available for reuse */ |
| 1274 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ |
| @@ -1347,10 +1359,14 @@ | |
| 1359 | /* Everybody else jumps to the page to administer their own |
| 1360 | ** account only. */ |
| 1361 | cgi_redirectf("%R/alerts"); |
| 1362 | return; |
| 1363 | } |
| 1364 | } |
| 1365 | if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){ |
| 1366 | register_page(); |
| 1367 | return; |
| 1368 | } |
| 1369 | alert_submenu_common(); |
| 1370 | needCaptcha = !login_is_individual(); |
| 1371 | if( P("submit") |
| 1372 | && cgi_csrf_safe(1) |
| @@ -1368,10 +1384,11 @@ | |
| 1384 | if( PB("sa") ) ssub[nsub++] = 'a'; |
| 1385 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1386 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1387 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1388 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1389 | if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; |
| 1390 | ssub[nsub] = 0; |
| 1391 | db_multi_exec( |
| 1392 | "INSERT INTO subscriber(semail,suname," |
| 1393 | " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" |
| 1394 | "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)", |
| @@ -1388,11 +1405,15 @@ | |
| 1405 | id); |
| 1406 | if( !needCaptcha ){ |
| 1407 | /* The new subscription has been added on behalf of a logged-in user. |
| 1408 | ** No verification is required. Jump immediately to /alerts page. |
| 1409 | */ |
| 1410 | if( g.perm.Admin ){ |
| 1411 | cgi_redirectf("%R/alerts/%.32s", zCode); |
| 1412 | }else{ |
| 1413 | cgi_redirectf("%R/alerts"); |
| 1414 | } |
| 1415 | return; |
| 1416 | }else{ |
| 1417 | /* We need to send a verification email */ |
| 1418 | Blob hdr, body; |
| 1419 | AlertSender *pSender = alert_sender_new(0,0); |
| @@ -1410,11 +1431,11 @@ | |
| 1431 | @ <blockquote><pre> |
| 1432 | @ %h(pSender->zErr) |
| 1433 | @ </pre></blockquote> |
| 1434 | }else{ |
| 1435 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1436 | @ hyperlink that you must click to activate your |
| 1437 | @ subscription.</p> |
| 1438 | } |
| 1439 | alert_sender_free(pSender); |
| 1440 | style_footer(); |
| 1441 | } |
| @@ -1442,16 +1463,22 @@ | |
| 1463 | if( eErr==1 ){ |
| 1464 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1465 | } |
| 1466 | @ </tr> |
| 1467 | if( needCaptcha ){ |
| 1468 | const char *zInit = ""; |
| 1469 | if( P("captchaseed")!=0 && eErr!=2 ){ |
| 1470 | uSeed = strtoul(P("captchaseed"),0,10); |
| 1471 | zInit = P("captcha"); |
| 1472 | }else{ |
| 1473 | uSeed = captcha_seed(); |
| 1474 | } |
| 1475 | zDecoded = captcha_decode(uSeed); |
| 1476 | zCaptcha = captcha_render(zDecoded); |
| 1477 | @ <tr> |
| 1478 | @ <td class="form_label">Security Code:</td> |
| 1479 | @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> |
| 1480 | captcha_speakit_button(uSeed, "Speak the code"); |
| 1481 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> |
| 1482 | @ </tr> |
| 1483 | if( eErr==2 ){ |
| 1484 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| @@ -1478,10 +1505,12 @@ | |
| 1505 | @ Check-ins</label><br> |
| 1506 | } |
| 1507 | if( g.perm.RdForum ){ |
| 1508 | @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \ |
| 1509 | @ Forum Posts</label><br> |
| 1510 | @ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \ |
| 1511 | @ Forum Edits</label><br> |
| 1512 | } |
| 1513 | if( g.perm.RdTkt ){ |
| 1514 | @ <label><input type="checkbox" name="st" %s(PCK("st"))> \ |
| 1515 | @ Ticket changes</label><br> |
| 1516 | } |
| @@ -1531,21 +1560,20 @@ | |
| 1560 | /* |
| 1561 | ** Either shutdown or completely delete a subscription entry given |
| 1562 | ** by the hex value zName. Then paint a webpage that explains that |
| 1563 | ** the entry has been removed. |
| 1564 | */ |
| 1565 | static void alert_unsubscribe(int sid){ |
| 1566 | char *zEmail; |
| 1567 | zEmail = db_text(0, "SELECT semail FROM subscriber" |
| 1568 | " WHERE subscriberId=%d", sid); |
| 1569 | if( zEmail==0 ){ |
| 1570 | style_header("Unsubscribe Fail"); |
| 1571 | @ <p>Unable to locate a subscriber with the requested key</p> |
| 1572 | }else{ |
| 1573 | db_multi_exec( |
| 1574 | "DELETE FROM subscriber WHERE subscriberId=%d", sid |
| 1575 | ); |
| 1576 | style_header("Unsubscribed"); |
| 1577 | @ <p>The "%h(zEmail)" email address has been delisted. |
| 1578 | @ All traces of that email address have been removed</p> |
| 1579 | } |
| @@ -1556,48 +1584,83 @@ | |
| 1584 | /* |
| 1585 | ** WEBPAGE: alerts |
| 1586 | ** |
| 1587 | ** Edit email alert and notification settings. |
| 1588 | ** |
| 1589 | ** The subscriber is identified in several ways: |
| 1590 | ** |
| 1591 | ** (1) The name= query parameter contains the complete subscriberCode. |
| 1592 | ** This only happens when the user receives a verification |
| 1593 | ** email and clicks on the link in the email. When a |
| 1594 | ** compilete subscriberCode is seen on the name= query parameter, |
| 1595 | ** that constitutes verification of the email address. |
| 1596 | ** |
| 1597 | ** (2) The sid= query parameter contains an integer subscriberId. |
| 1598 | ** This only works for the administrator. It allows the |
| 1599 | ** administrator to edit any subscription. |
| 1600 | ** |
| 1601 | ** (3) The user is logged into an account other than "nobody" or |
| 1602 | ** "anonymous". In that case the notification settings |
| 1603 | ** associated with that account can be edited without needing |
| 1604 | ** to know the subscriber code. |
| 1605 | ** |
| 1606 | ** (4) The name= query parameter contains a 32-digit prefix of |
| 1607 | ** subscriber code. (Subscriber codes are normally 64 hex digits |
| 1608 | ** in length.) This uniquely identifies the subscriber without |
| 1609 | ** revealing the complete subscriber code, and hence without |
| 1610 | ** verifying the email address. |
| 1611 | */ |
| 1612 | void alert_page(void){ |
| 1613 | const char *zName = 0; /* Value of the name= query parameter */ |
| 1614 | Stmt q; /* For querying the database */ |
| 1615 | int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ |
| 1616 | int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1617 | int isLogin; /* True if logged in as an individual */ |
| 1618 | const char *ssub = 0; /* Subscription flags */ |
| 1619 | const char *semail = 0; /* Email address */ |
| 1620 | const char *smip; /* */ |
| 1621 | const char *suname = 0; /* Corresponding user.login value */ |
| 1622 | const char *mtime; /* */ |
| 1623 | const char *sctime; /* Time subscription created */ |
| 1624 | int eErr = 0; /* Type of error */ |
| 1625 | char *zErr = 0; /* Error message text */ |
| 1626 | int sid = 0; /* Subscriber ID */ |
| 1627 | int nName; /* Length of zName in bytes */ |
| 1628 | char *zHalfCode; /* prefix of subscriberCode */ |
| 1629 | |
| 1630 | db_begin_transaction(); |
| 1631 | if( alert_webpages_disabled() ){ |
| 1632 | db_commit_transaction(); |
| 1633 | return; |
| 1634 | } |
| 1635 | login_check_credentials(); |
| 1636 | if( !g.perm.EmailAlert ){ |
| 1637 | db_commit_transaction(); |
| 1638 | login_needed(g.anon.EmailAlert); |
| 1639 | /*NOTREACHED*/ |
| 1640 | } |
| 1641 | isLogin = login_is_individual(); |
| 1642 | zName = P("name"); |
| 1643 | nName = zName ? (int)strlen(zName) : 0; |
| 1644 | if( g.perm.Admin && P("sid")!=0 ){ |
| 1645 | sid = atoi(P("sid")); |
| 1646 | } |
| 1647 | if( sid==0 && nName>=32 ){ |
| 1648 | sid = db_int(0, |
| 1649 | "SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')" |
| 1650 | " THEN subscriberId ELSE 0 END" |
| 1651 | " FROM subscriber WHERE subscriberCode>=hextoblob(%Q)" |
| 1652 | " LIMIT 1", zName, zName); |
| 1653 | } |
| 1654 | if( sid==0 && isLogin ){ |
| 1655 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 1656 | " WHERE suname=%Q", g.zLogin); |
| 1657 | } |
| 1658 | if( sid==0 ){ |
| 1659 | db_commit_transaction(); |
| 1660 | cgi_redirect("subscribe"); |
| 1661 | /*NOTREACHED*/ |
| 1662 | } |
| 1663 | alert_submenu_common(); |
| 1664 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 1665 | char newSsub[10]; |
| 1666 | int nsub = 0; |
| @@ -1641,11 +1704,11 @@ | |
| 1704 | if( semail==0 || email_address_is_valid(semail,0)==0 ){ |
| 1705 | eErr = 8; |
| 1706 | } |
| 1707 | blob_append_sql(&update, ", semail=%Q", semail); |
| 1708 | } |
| 1709 | blob_append_sql(&update," WHERE subscriberId=%d", sid); |
| 1710 | if( eErr==0 ){ |
| 1711 | db_exec_sql(blob_str(&update)); |
| 1712 | ssub = 0; |
| 1713 | } |
| 1714 | blob_reset(&update); |
| @@ -1654,12 +1717,13 @@ | |
| 1717 | if( !PB("dodelete") ){ |
| 1718 | eErr = 9; |
| 1719 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1720 | " unsubscribe"); |
| 1721 | }else{ |
| 1722 | alert_unsubscribe(sid); |
| 1723 | db_commit_transaction(); |
| 1724 | return; |
| 1725 | } |
| 1726 | } |
| 1727 | style_header("Update Subscription"); |
| 1728 | db_prepare(&q, |
| 1729 | "SELECT" |
| @@ -1669,16 +1733,18 @@ | |
| 1733 | " sdigest," /* 3 */ |
| 1734 | " ssub," /* 4 */ |
| 1735 | " smip," /* 5 */ |
| 1736 | " suname," /* 6 */ |
| 1737 | " datetime(mtime,'unixepoch')," /* 7 */ |
| 1738 | " datetime(sctime,'unixepoch')," /* 8 */ |
| 1739 | " hex(subscriberCode)" /* 9 */ |
| 1740 | " FROM subscriber WHERE subscriberId=%d", sid); |
| 1741 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1742 | db_finalize(&q); |
| 1743 | db_commit_transaction(); |
| 1744 | cgi_redirect("subscribe"); |
| 1745 | /*NOTREACHED*/ |
| 1746 | } |
| 1747 | if( ssub==0 ){ |
| 1748 | semail = db_column_text(&q, 0); |
| 1749 | sdonotcall = db_column_int(&q, 2); |
| 1750 | sdigest = db_column_int(&q, 3); |
| @@ -1696,23 +1762,45 @@ | |
| 1762 | sx = strchr(ssub,'x')!=0; |
| 1763 | smip = db_column_text(&q, 5); |
| 1764 | mtime = db_column_text(&q, 7); |
| 1765 | sctime = db_column_text(&q, 8); |
| 1766 | if( !g.perm.Admin && !sverified ){ |
| 1767 | if( nName==64 ){ |
| 1768 | db_multi_exec( |
| 1769 | "UPDATE subscriber SET sverified=1" |
| 1770 | " WHERE subscriberCode=hextoblob(%Q)", |
| 1771 | zName); |
| 1772 | if( db_get_boolean("selfreg-verify",0) ){ |
| 1773 | char *zNewCap = db_get("default-perms","u"); |
| 1774 | db_multi_exec( |
| 1775 | "UPDATE user" |
| 1776 | " SET cap=%Q" |
| 1777 | " WHERE cap='7' AND login=(" |
| 1778 | " SELECT suname FROM subscriber" |
| 1779 | " WHERE subscriberCode=hextoblob(%Q))", |
| 1780 | zNewCap, zName |
| 1781 | ); |
| 1782 | login_set_capabilities(zNewCap, 0); |
| 1783 | } |
| 1784 | @ <h1>Your email alert subscription has been verified!</h1> |
| 1785 | @ <p>Use the form below to update your subscription information.</p> |
| 1786 | @ <p>Hint: Bookmark this page so that you can more easily update |
| 1787 | @ your subscription information in the future</p> |
| 1788 | }else{ |
| 1789 | @ <h2>Your email address is unverified</h2> |
| 1790 | @ <p>You should have received an email message containing a link |
| 1791 | @ that you must visit to verify your account. No email notifications |
| 1792 | @ will be sent until your email address has been verified.</p> |
| 1793 | } |
| 1794 | }else{ |
| 1795 | @ <p>Make changes to the email subscription shown below and |
| 1796 | @ press "Submit".</p> |
| 1797 | } |
| 1798 | form_begin(0, "%R/alerts"); |
| 1799 | zHalfCode = db_text("x","SELECT hex(substr(subscriberCode,1,16))" |
| 1800 | " FROM subscriber WHERE subscriberId=%d", sid); |
| 1801 | @ <input type="hidden" name="name" value="%h(zHalfCode)"> |
| 1802 | @ <table class="subscribe"> |
| 1803 | @ <tr> |
| 1804 | @ <td class="form_label">Email Address:</td> |
| 1805 | if( isLogin ){ |
| 1806 | @ <td><input type="text" name="semail" value="%h(semail)" size="30">\ |
| @@ -1739,10 +1827,13 @@ | |
| 1827 | @ </tr> |
| 1828 | @ <tr> |
| 1829 | @ <td class='form_label'>IP Address:</td> |
| 1830 | @ <td>%h(smip)</td> |
| 1831 | @ </tr> |
| 1832 | @ <tr> |
| 1833 | @ <td class='form_label'>Subscriber Code:</td> |
| 1834 | @ <td>%h(db_column_text(&q,9))</td> |
| 1835 | @ <tr> |
| 1836 | @ <td class="form_label">User:</td> |
| 1837 | @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \ |
| 1838 | @ size="30">\ |
| 1839 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname); |
| @@ -1780,14 +1871,10 @@ | |
| 1871 | @ <td><select size="1" name="sdigest"> |
| 1872 | @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option> |
| 1873 | @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option> |
| 1874 | @ </select></td> |
| 1875 | @ </tr> |
| 1876 | if( g.perm.Admin ){ |
| 1877 | @ <tr> |
| 1878 | @ <td class="form_label">Admin Options:</td><td> |
| 1879 | @ <label><input type="checkbox" name="sdonotcall" \ |
| 1880 | @ %s(sdonotcall?"checked":"")> Do not disturb</label><br> |
| @@ -1811,10 +1898,12 @@ | |
| 1898 | @ </table> |
| 1899 | @ </form> |
| 1900 | fossil_free(zErr); |
| 1901 | db_finalize(&q); |
| 1902 | style_footer(); |
| 1903 | db_commit_transaction(); |
| 1904 | return; |
| 1905 | } |
| 1906 | |
| 1907 | /* This is the message that gets sent to describe how to change |
| 1908 | ** or modify a subscription |
| 1909 | */ |
| @@ -1852,18 +1941,19 @@ | |
| 1941 | char *zCaptcha = 0; |
| 1942 | int dx; |
| 1943 | int bSubmit; |
| 1944 | const char *zEAddr; |
| 1945 | char *zCode = 0; |
| 1946 | int sid = 0; |
| 1947 | |
| 1948 | /* If a valid subscriber code is supplied, then unsubscribe immediately. |
| 1949 | */ |
| 1950 | if( zName |
| 1951 | && (sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 1952 | " WHERE subscriberCode=hextoblob(%Q)", zName))!=0 |
| 1953 | ){ |
| 1954 | alert_unsubscribe(sid); |
| 1955 | return; |
| 1956 | } |
| 1957 | |
| 1958 | /* Logged in users are redirected to the /alerts page */ |
| 1959 | login_check_credentials(); |
| @@ -2021,11 +2111,11 @@ | |
| 2111 | if( nDel>0 ){ |
| 2112 | @ <p>*** %d(nDel) pending subscriptions deleted ***</p> |
| 2113 | } |
| 2114 | blob_init(&sql, 0, 0); |
| 2115 | blob_append_sql(&sql, |
| 2116 | "SELECT subscriberId," /* 0 */ |
| 2117 | " semail," /* 1 */ |
| 2118 | " ssub," /* 2 */ |
| 2119 | " suname," /* 3 */ |
| 2120 | " sverified," /* 4 */ |
| 2121 | " sdigest," /* 5 */ |
| @@ -2058,11 +2148,11 @@ | |
| 2148 | sqlite3_int64 iMtime = db_column_int64(&q, 6); |
| 2149 | double rAge = (iNow - iMtime)/86400.0; |
| 2150 | int uid = db_column_int(&q, 8); |
| 2151 | const char *zUname = db_column_text(&q, 3); |
| 2152 | @ <tr> |
| 2153 | @ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\ |
| 2154 | @ %h(db_column_text(&q,1))</a></td> |
| 2155 | @ <td>%h(db_column_text(&q,2))</td> |
| 2156 | @ <td>%s(db_column_int(&q,5)?"digest":"")</td> |
| 2157 | if( uid ){ |
| 2158 | @ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a> |
| @@ -2210,18 +2300,28 @@ | |
| 2300 | ** is a normal email alert. Construct full-text forum post alerts |
| 2301 | ** using a format that enables them to be sent as separate emails. |
| 2302 | */ |
| 2303 | db_prepare(&q, |
| 2304 | "SELECT" |
| 2305 | " forumpost.fpid," /* 0: fpid */ |
| 2306 | " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1: hash */ |
| 2307 | " datetime(event.mtime)," /* 2: date/time */ |
| 2308 | " substr(comment,instr(comment,':')+2)," /* 3: comment */ |
| 2309 | " (WITH thread(fpid,fprev) AS (" |
| 2310 | " SELECT fpid,fprev FROM forumpost AS tx" |
| 2311 | " WHERE tx.froot=forumpost.froot)," |
| 2312 | " basepid(fpid,bpid) AS (" |
| 2313 | " SELECT fpid, fpid FROM thread WHERE fprev IS NULL" |
| 2314 | " UNION ALL" |
| 2315 | " SELECT thread.fpid, basepid.bpid FROM basepid, thread" |
| 2316 | " WHERE basepid.fpid=thread.fprev)" |
| 2317 | " SELECT uuid FROM blob, basepid" |
| 2318 | " WHERE basepid.fpid=forumpost.firt" |
| 2319 | " AND blob.rid=basepid.bpid)," /* 4: in-reply-to */ |
| 2320 | " wantalert.needMod," /* 5: moderated */ |
| 2321 | " coalesce(display_name(info),euser,user)," /* 6: user */ |
| 2322 | " forumpost.fprev IS NULL" /* 7: is an edit */ |
| 2323 | " FROM temp.wantalert, event, forumpost" |
| 2324 | " LEFT JOIN user ON (login=coalesce(euser,user))" |
| 2325 | " WHERE event.objid=substr(wantalert.eventId,2)+0" |
| 2326 | " AND eventId GLOB 'f*'" |
| 2327 | " AND forumpost.fpid=event.objid" |
| 2328 |
+1
-1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -128,11 +128,11 @@ | ||
| 128 | 128 | ** supported by the rebuild command itself, if any are |
| 129 | 129 | ** present, are passed along verbatim. The --force and |
| 130 | 130 | ** --randomize options are not supported. |
| 131 | 131 | ** |
| 132 | 132 | ** sync Run a "sync" on all repositories. Only the --verbose |
| 133 | -** option is supported. | |
| 133 | +** and --unversioned options are supported. | |
| 134 | 134 | ** |
| 135 | 135 | ** setting Run the "setting", "set", or "unset" commands on all |
| 136 | 136 | ** set repositories. These command are particularly useful in |
| 137 | 137 | ** unset conjunction with the "max-loadavg" setting which cannot |
| 138 | 138 | ** otherwise be set globally. |
| 139 | 139 | |
| 140 | 140 | ADDED src/backlink.c |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -128,11 +128,11 @@ | |
| 128 | ** supported by the rebuild command itself, if any are |
| 129 | ** present, are passed along verbatim. The --force and |
| 130 | ** --randomize options are not supported. |
| 131 | ** |
| 132 | ** sync Run a "sync" on all repositories. Only the --verbose |
| 133 | ** option is supported. |
| 134 | ** |
| 135 | ** setting Run the "setting", "set", or "unset" commands on all |
| 136 | ** set repositories. These command are particularly useful in |
| 137 | ** unset conjunction with the "max-loadavg" setting which cannot |
| 138 | ** otherwise be set globally. |
| 139 | |
| 140 | DDED src/backlink.c |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -128,11 +128,11 @@ | |
| 128 | ** supported by the rebuild command itself, if any are |
| 129 | ** present, are passed along verbatim. The --force and |
| 130 | ** --randomize options are not supported. |
| 131 | ** |
| 132 | ** sync Run a "sync" on all repositories. Only the --verbose |
| 133 | ** and --unversioned options are supported. |
| 134 | ** |
| 135 | ** setting Run the "setting", "set", or "unset" commands on all |
| 136 | ** set repositories. These command are particularly useful in |
| 137 | ** unset conjunction with the "max-loadavg" setting which cannot |
| 138 | ** otherwise be set globally. |
| 139 | |
| 140 | DDED src/backlink.c |
+6
| --- a/src/backlink.c | ||
| +++ b/src/backlink.c | ||
| @@ -0,0 +1,6 @@ | ||
| 1 | +/* | |
| 2 | +** Copyright (c) 2020 D. Richard Hipp | |
| 3 | +** | |
| 4 | +** This program is free software; you can redistribute it and/or | |
| 5 | +** modify it under the terms of the Simplified BSD License (also | |
| 6 | +** known as the "2-Clau |
| --- a/src/backlink.c | |
| +++ b/src/backlink.c | |
| @@ -0,0 +1,6 @@ | |
| --- a/src/backlink.c | |
| +++ b/src/backlink.c | |
| @@ -0,0 +1,6 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2020 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clau |
+14
-1
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -18,10 +18,24 @@ | ||
| 18 | 18 | ** This file contains code used to create new branches within a repository. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "branch.h" |
| 22 | 22 | #include <assert.h> |
| 23 | + | |
| 24 | +/* | |
| 25 | +** Return true if zBr is the branch name associated with check-in with | |
| 26 | +** blob.uuid value of zUuid | |
| 27 | +*/ | |
| 28 | +int branch_includes_uuid(const char *zBr, const char *zUuid){ | |
| 29 | + return db_exists( | |
| 30 | + "SELECT 1 FROM tagxref, blob" | |
| 31 | + " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid" | |
| 32 | + " AND tagxref.value=%Q AND tagxref.tagtype>0" | |
| 33 | + " AND tagxref.tagid=%d", | |
| 34 | + zUuid, zBr, TAG_BRANCH | |
| 35 | + ); | |
| 36 | +} | |
| 23 | 37 | |
| 24 | 38 | /* |
| 25 | 39 | ** If RID refers to a check-in, return the name of the branch for that |
| 26 | 40 | ** check-in. |
| 27 | 41 | ** |
| @@ -144,11 +158,10 @@ | ||
| 144 | 158 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 145 | 159 | } |
| 146 | 160 | blob_appendf(&branch, "T *branch * %F\n", zBranch); |
| 147 | 161 | blob_appendf(&branch, "T *sym-%F *\n", zBranch); |
| 148 | 162 | if( isPrivate ){ |
| 149 | - blob_appendf(&branch, "T +private *\n"); | |
| 150 | 163 | noSign = 1; |
| 151 | 164 | } |
| 152 | 165 | |
| 153 | 166 | /* Cancel all other symbolic tags */ |
| 154 | 167 | db_prepare(&q, |
| 155 | 168 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -18,10 +18,24 @@ | |
| 18 | ** This file contains code used to create new branches within a repository. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "branch.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** If RID refers to a check-in, return the name of the branch for that |
| 26 | ** check-in. |
| 27 | ** |
| @@ -144,11 +158,10 @@ | |
| 144 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 145 | } |
| 146 | blob_appendf(&branch, "T *branch * %F\n", zBranch); |
| 147 | blob_appendf(&branch, "T *sym-%F *\n", zBranch); |
| 148 | if( isPrivate ){ |
| 149 | blob_appendf(&branch, "T +private *\n"); |
| 150 | noSign = 1; |
| 151 | } |
| 152 | |
| 153 | /* Cancel all other symbolic tags */ |
| 154 | db_prepare(&q, |
| 155 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -18,10 +18,24 @@ | |
| 18 | ** This file contains code used to create new branches within a repository. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "branch.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Return true if zBr is the branch name associated with check-in with |
| 26 | ** blob.uuid value of zUuid |
| 27 | */ |
| 28 | int branch_includes_uuid(const char *zBr, const char *zUuid){ |
| 29 | return db_exists( |
| 30 | "SELECT 1 FROM tagxref, blob" |
| 31 | " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid" |
| 32 | " AND tagxref.value=%Q AND tagxref.tagtype>0" |
| 33 | " AND tagxref.tagid=%d", |
| 34 | zUuid, zBr, TAG_BRANCH |
| 35 | ); |
| 36 | } |
| 37 | |
| 38 | /* |
| 39 | ** If RID refers to a check-in, return the name of the branch for that |
| 40 | ** check-in. |
| 41 | ** |
| @@ -144,11 +158,10 @@ | |
| 158 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 159 | } |
| 160 | blob_appendf(&branch, "T *branch * %F\n", zBranch); |
| 161 | blob_appendf(&branch, "T *sym-%F *\n", zBranch); |
| 162 | if( isPrivate ){ |
| 163 | noSign = 1; |
| 164 | } |
| 165 | |
| 166 | /* Cancel all other symbolic tags */ |
| 167 | db_prepare(&q, |
| 168 |
+144
-75
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -59,10 +59,18 @@ | ||
| 59 | 59 | zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); |
| 60 | 60 | sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); |
| 61 | 61 | } |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | +/* | |
| 65 | +** Flag arguments for hyperlinked_path() | |
| 66 | +*/ | |
| 67 | +#if INTERFACE | |
| 68 | +# define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */ | |
| 69 | +# define LINKPATH_FILE 0x0002 /* Link final term to /file */ | |
| 70 | +#endif | |
| 71 | + | |
| 64 | 72 | /* |
| 65 | 73 | ** Given a pathname which is a relative path from the root of |
| 66 | 74 | ** the repository to a file or directory, compute a string which |
| 67 | 75 | ** is an HTML rendering of that path with hyperlinks on each |
| 68 | 76 | ** directory component of the path where the hyperlink redirects |
| @@ -76,29 +84,36 @@ | ||
| 76 | 84 | void hyperlinked_path( |
| 77 | 85 | const char *zPath, /* Path to render */ |
| 78 | 86 | Blob *pOut, /* Write into this blob */ |
| 79 | 87 | const char *zCI, /* check-in name, or NULL */ |
| 80 | 88 | const char *zURI, /* "dir" or "tree" */ |
| 81 | - const char *zREx /* Extra query parameters */ | |
| 89 | + const char *zREx, /* Extra query parameters */ | |
| 90 | + unsigned int mFlags /* Extra flags */ | |
| 82 | 91 | ){ |
| 83 | 92 | int i, j; |
| 84 | 93 | char *zSep = ""; |
| 85 | 94 | |
| 86 | 95 | for(i=0; zPath[i]; i=j){ |
| 87 | 96 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 88 | - if( zPath[j] && g.perm.Hyperlink ){ | |
| 89 | - if( zCI ){ | |
| 90 | - char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI); | |
| 91 | - blob_appendf(pOut, "%s%z%#h</a>", | |
| 92 | - zSep, zLink, j-i, &zPath[i]); | |
| 93 | - }else{ | |
| 94 | - char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); | |
| 95 | - blob_appendf(pOut, "%s%z%#h</a>", | |
| 96 | - zSep, zLink, j-i, &zPath[i]); | |
| 97 | - } | |
| 98 | - }else{ | |
| 99 | - blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); | |
| 97 | + if( zPath[j]==0 ){ | |
| 98 | + if( mFlags & LINKPATH_FILE ){ | |
| 99 | + zURI = "file"; | |
| 100 | + }else if( mFlags & LINKPATH_FINFO ){ | |
| 101 | + zURI = "finfo"; | |
| 102 | + }else{ | |
| 103 | + blob_appendf(pOut, "/%h", zPath+i); | |
| 104 | + break; | |
| 105 | + } | |
| 106 | + } | |
| 107 | + if( zCI ){ | |
| 108 | + char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI); | |
| 109 | + blob_appendf(pOut, "%s%z%#h</a>", | |
| 110 | + zSep, zLink, j-i, &zPath[i]); | |
| 111 | + }else{ | |
| 112 | + char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); | |
| 113 | + blob_appendf(pOut, "%s%z%#h</a>", | |
| 114 | + zSep, zLink, j-i, &zPath[i]); | |
| 100 | 115 | } |
| 101 | 116 | zSep = "/"; |
| 102 | 117 | while( zPath[j]=='/' ){ j++; } |
| 103 | 118 | } |
| 104 | 119 | } |
| @@ -127,27 +142,24 @@ | ||
| 127 | 142 | char *zPrefix; |
| 128 | 143 | Stmt q; |
| 129 | 144 | const char *zCI = P("ci"); |
| 130 | 145 | int rid = 0; |
| 131 | 146 | char *zUuid = 0; |
| 132 | - Blob dirname; | |
| 133 | 147 | Manifest *pM = 0; |
| 134 | 148 | const char *zSubdirLink; |
| 135 | 149 | int linkTrunk = 1; |
| 136 | 150 | int linkTip = 1; |
| 137 | 151 | HQuery sURI; |
| 152 | + int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */ | |
| 153 | + int isBranchCI = 0; /* True if ci= refers to a branch name */ | |
| 154 | + char *zHeader = 0; | |
| 138 | 155 | |
| 156 | + if( zCI && strlen(zCI)==0 ){ zCI = 0; } | |
| 139 | 157 | if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } |
| 140 | 158 | login_check_credentials(); |
| 141 | 159 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 142 | 160 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 143 | - style_header("File List"); | |
| 144 | - style_adunit_config(ADUNIT_RIGHT_OK); | |
| 145 | - sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, | |
| 146 | - pathelementFunc, 0, 0); | |
| 147 | - url_initialize(&sURI, "dir"); | |
| 148 | - cgi_query_parameters_to_url(&sURI); | |
| 149 | 161 | |
| 150 | 162 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| 151 | 163 | if( zD && strlen(zD)==0 ){ zD = 0; } |
| 152 | 164 | |
| 153 | 165 | /* If a specific check-in is requested, fetch and parse it. If the |
| @@ -159,53 +171,79 @@ | ||
| 159 | 171 | if( pM ){ |
| 160 | 172 | int trunkRid = symbolic_name_to_rid("tag:trunk", "ci"); |
| 161 | 173 | linkTrunk = trunkRid && rid != trunkRid; |
| 162 | 174 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 163 | 175 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 176 | + isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); | |
| 177 | + isBranchCI = branch_includes_uuid(zCI, zUuid); | |
| 164 | 178 | }else{ |
| 165 | 179 | zCI = 0; |
| 166 | 180 | } |
| 167 | 181 | } |
| 168 | 182 | |
| 183 | + assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); | |
| 184 | + if( zD==0 ){ | |
| 185 | + if( zCI ){ | |
| 186 | + zHeader = mprintf("Top-level Files of %s", zCI); | |
| 187 | + }else{ | |
| 188 | + zHeader = mprintf("All Top-level Files"); | |
| 189 | + } | |
| 190 | + }else{ | |
| 191 | + if( zCI ){ | |
| 192 | + zHeader = mprintf("Files in %s/ of %s", zD, zCI); | |
| 193 | + }else{ | |
| 194 | + zHeader = mprintf("All File in %s/", zD); | |
| 195 | + } | |
| 196 | + } | |
| 197 | + style_header("%s", zHeader); | |
| 198 | + fossil_free(zHeader); | |
| 199 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 200 | + sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, | |
| 201 | + pathelementFunc, 0, 0); | |
| 202 | + url_initialize(&sURI, "dir"); | |
| 203 | + cgi_query_parameters_to_url(&sURI); | |
| 204 | + | |
| 169 | 205 | /* Compute the title of the page */ |
| 170 | - blob_zero(&dirname); | |
| 171 | 206 | if( zD ){ |
| 172 | - blob_append(&dirname, "in directory ", -1); | |
| 173 | - hyperlinked_path(zD, &dirname, zCI, "dir", ""); | |
| 207 | + Blob dirname; | |
| 208 | + blob_init(&dirname, 0, 0); | |
| 209 | + hyperlinked_path(zD, &dirname, zCI, "dir", "", 0); | |
| 210 | + @ <h2>Files in directory %s(blob_str(&dirname)) \ | |
| 211 | + blob_reset(&dirname); | |
| 174 | 212 | zPrefix = mprintf("%s/", zD); |
| 175 | 213 | style_submenu_element("Top-Level", "%s", |
| 176 | 214 | url_render(&sURI, "name", 0, 0, 0)); |
| 177 | 215 | }else{ |
| 178 | - blob_append(&dirname, "in the top-level directory", -1); | |
| 216 | + @ <h2>Files in the top-level directory \ | |
| 179 | 217 | zPrefix = ""; |
| 180 | 218 | } |
| 219 | + if( zCI ){ | |
| 220 | + if( fossil_strcmp(zCI,"tip")==0 ){ | |
| 221 | + @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2> | |
| 222 | + }else if( isBranchCI ){ | |
| 223 | + @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \ | |
| 224 | + @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> | |
| 225 | + }else { | |
| 226 | + @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> | |
| 227 | + } | |
| 228 | + zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix); | |
| 229 | + if( nD==0 ){ | |
| 230 | + style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); | |
| 231 | + } | |
| 232 | + }else{ | |
| 233 | + @ in any check-in</h2> | |
| 234 | + zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); | |
| 235 | + } | |
| 181 | 236 | if( linkTrunk ){ |
| 182 | 237 | style_submenu_element("Trunk", "%s", |
| 183 | 238 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| 184 | 239 | } |
| 185 | 240 | if( linkTip ){ |
| 186 | 241 | style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); |
| 187 | 242 | } |
| 188 | - if( zCI ){ | |
| 189 | - @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] | |
| 190 | - @ %s(blob_str(&dirname)) | |
| 191 | - if( zD ){ | |
| 192 | - @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> | |
| 193 | - } | |
| 194 | - @ </h2> | |
| 195 | - zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix); | |
| 196 | - if( nD==0 ){ | |
| 197 | - style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid); | |
| 198 | - } | |
| 199 | - }else{ | |
| 200 | - @ <h2>The union of all files from all check-ins | |
| 201 | - @ %s(blob_str(&dirname)) | |
| 202 | - if( zD ){ | |
| 203 | - @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> | |
| 204 | - } | |
| 205 | - @ </h2> | |
| 206 | - zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); | |
| 243 | + if( zD ){ | |
| 244 | + style_submenu_element("History","%R/timeline?chng=%T/*", zD); | |
| 207 | 245 | } |
| 208 | 246 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 209 | 247 | style_submenu_element("Tree-View", "%s", |
| 210 | 248 | url_render(&sURI, "type", "tree", 0, 0)); |
| 211 | 249 | |
| @@ -282,12 +320,11 @@ | ||
| 282 | 320 | zFN++; |
| 283 | 321 | @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> |
| 284 | 322 | }else{ |
| 285 | 323 | const char *zLink; |
| 286 | 324 | if( zCI ){ |
| 287 | - const char *zUuid = db_column_text(&q, 1); | |
| 288 | - zLink = href("%R/artifact/%!S",zUuid); | |
| 325 | + zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI); | |
| 289 | 326 | }else{ |
| 290 | 327 | zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); |
| 291 | 328 | } |
| 292 | 329 | @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> |
| 293 | 330 | } |
| @@ -612,11 +649,15 @@ | ||
| 612 | 649 | HQuery sURI; /* Hyperlink */ |
| 613 | 650 | int startExpanded; /* True to start out with the tree expanded */ |
| 614 | 651 | int showDirOnly; /* Show directories only. Omit files */ |
| 615 | 652 | int nDir = 0; /* Number of directories. Used for ID attributes */ |
| 616 | 653 | char *zProjectName = db_get("project-name", 0); |
| 654 | + int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */ | |
| 655 | + int isBranchCI = 0; /* ci= refers to a branch name */ | |
| 656 | + char *zHeader = 0; | |
| 617 | 657 | |
| 658 | + if( zCI && strlen(zCI)==0 ){ zCI = 0; } | |
| 618 | 659 | if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; } |
| 619 | 660 | memset(&sTree, 0, sizeof(sTree)); |
| 620 | 661 | login_check_credentials(); |
| 621 | 662 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 622 | 663 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| @@ -624,14 +665,12 @@ | ||
| 624 | 665 | pathelementFunc, 0, 0); |
| 625 | 666 | url_initialize(&sURI, "tree"); |
| 626 | 667 | cgi_query_parameters_to_url(&sURI); |
| 627 | 668 | if( PB("nofiles") ){ |
| 628 | 669 | showDirOnly = 1; |
| 629 | - style_header("Folder Hierarchy"); | |
| 630 | 670 | }else{ |
| 631 | 671 | showDirOnly = 0; |
| 632 | - style_header("File Tree"); | |
| 633 | 672 | } |
| 634 | 673 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 635 | 674 | if( PB("expand") ){ |
| 636 | 675 | startExpanded = 1; |
| 637 | 676 | }else{ |
| @@ -660,37 +699,54 @@ | ||
| 660 | 699 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 661 | 700 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 662 | 701 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 663 | 702 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 664 | 703 | " FROM event WHERE objid=%d", rid); |
| 704 | + isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); | |
| 705 | + isBranchCI = branch_includes_uuid(zCI, zUuid); | |
| 665 | 706 | }else{ |
| 666 | 707 | zCI = 0; |
| 667 | 708 | } |
| 668 | 709 | } |
| 669 | 710 | if( zCI==0 ){ |
| 670 | 711 | rNow = db_double(0.0, "SELECT max(mtime) FROM event"); |
| 671 | 712 | zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event"); |
| 672 | 713 | } |
| 714 | + | |
| 715 | + assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); | |
| 716 | + if( zD==0 ){ | |
| 717 | + if( zCI ){ | |
| 718 | + zHeader = mprintf("Top-level Files of %s", zCI); | |
| 719 | + }else{ | |
| 720 | + zHeader = mprintf("All Top-level Files"); | |
| 721 | + } | |
| 722 | + }else{ | |
| 723 | + if( zCI ){ | |
| 724 | + zHeader = mprintf("Files in %s/ of %s", zD, zCI); | |
| 725 | + }else{ | |
| 726 | + zHeader = mprintf("All File in %s/", zD); | |
| 727 | + } | |
| 728 | + } | |
| 729 | + style_header("%s", zHeader); | |
| 730 | + fossil_free(zHeader); | |
| 673 | 731 | |
| 674 | 732 | /* Compute the title of the page */ |
| 675 | 733 | blob_zero(&dirname); |
| 676 | 734 | if( zD ){ |
| 677 | 735 | blob_append(&dirname, "within directory ", -1); |
| 678 | - hyperlinked_path(zD, &dirname, zCI, "tree", zREx); | |
| 736 | + hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0); | |
| 679 | 737 | if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); |
| 680 | 738 | style_submenu_element("Top-Level", "%s", |
| 681 | 739 | url_render(&sURI, "name", 0, 0, 0)); |
| 682 | - }else{ | |
| 683 | - if( zRE ){ | |
| 684 | - blob_appendf(&dirname, "matching \"%s\"", zRE); | |
| 685 | - } | |
| 740 | + }else if( zRE ){ | |
| 741 | + blob_appendf(&dirname, "matching \"%s\"", zRE); | |
| 686 | 742 | } |
| 687 | 743 | style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); |
| 688 | 744 | if( zCI ){ |
| 689 | 745 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 690 | 746 | if( nD==0 && !showDirOnly ){ |
| 691 | - style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid); | |
| 747 | + style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); | |
| 692 | 748 | } |
| 693 | 749 | } |
| 694 | 750 | if( linkTrunk ){ |
| 695 | 751 | style_submenu_element("Trunk", "%s", |
| 696 | 752 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| @@ -745,10 +801,11 @@ | ||
| 745 | 801 | tree_add_node(&sTree, zName, zUuid, mtime); |
| 746 | 802 | nFile++; |
| 747 | 803 | } |
| 748 | 804 | db_finalize(&q); |
| 749 | 805 | } |
| 806 | + style_submenu_checkbox("nofiles", "Folders Only", 0, 0); | |
| 750 | 807 | |
| 751 | 808 | if( showDirOnly ){ |
| 752 | 809 | for(nFile=0, p=sTree.pFirst; p; p=p->pNext){ |
| 753 | 810 | if( p->pChild!=0 && p->nFullName>nD ) nFile++; |
| 754 | 811 | } |
| @@ -755,18 +812,24 @@ | ||
| 755 | 812 | zObjType = "Folders"; |
| 756 | 813 | }else{ |
| 757 | 814 | zObjType = "Files"; |
| 758 | 815 | } |
| 759 | 816 | |
| 760 | - style_submenu_checkbox("nofiles", "Folders Only", 0, 0); | |
| 761 | - | |
| 762 | - if( zCI ){ | |
| 763 | - @ <h2>%s(zObjType) from | |
| 764 | - if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){ | |
| 765 | - @ "%h(zCI)" | |
| 766 | - } | |
| 767 | - @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname)) | |
| 817 | + if( zCI && strcmp(zCI,"tip")==0 ){ | |
| 818 | + @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a> | |
| 819 | + }else if( isBranchCI ){ | |
| 820 | + @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\ | |
| 821 | + @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a> | |
| 822 | + if( blob_size(&dirname) ){ | |
| 823 | + @ and %s(blob_str(&dirname))</h2> | |
| 824 | + } | |
| 825 | + }else if( zCI ){ | |
| 826 | + @ <h2>%s(zObjType) for check-in \ | |
| 827 | + @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> | |
| 828 | + if( blob_size(&dirname) ){ | |
| 829 | + @ and %s(blob_str(&dirname))</h2> | |
| 830 | + } | |
| 768 | 831 | }else{ |
| 769 | 832 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 770 | 833 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 771 | 834 | } |
| 772 | 835 | if( useMtime ){ |
| @@ -824,11 +887,11 @@ | ||
| 824 | 887 | nDir++; |
| 825 | 888 | }else if( !showDirOnly ){ |
| 826 | 889 | const char *zFileClass = fileext_class(p->zName); |
| 827 | 890 | char *zLink; |
| 828 | 891 | if( zCI ){ |
| 829 | - zLink = href("%R/artifact/%!S",p->zUuid); | |
| 892 | + zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI); | |
| 830 | 893 | }else{ |
| 831 | 894 | zLink = href("%R/finfo?name=%T",p->zFullName); |
| 832 | 895 | } |
| 833 | 896 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 834 | 897 | @ %z(zLink)%h(p->zName)</a> |
| @@ -1001,10 +1064,11 @@ | ||
| 1001 | 1064 | int rid; |
| 1002 | 1065 | const char *zName; |
| 1003 | 1066 | const char *zGlob; |
| 1004 | 1067 | const char *zUuid; |
| 1005 | 1068 | const char *zNow; /* Time of check-in */ |
| 1069 | + int isBranchCI; /* name= is a branch name */ | |
| 1006 | 1070 | int showId = PB("showid"); |
| 1007 | 1071 | Stmt q1, q2; |
| 1008 | 1072 | double baseTime; |
| 1009 | 1073 | login_check_credentials(); |
| 1010 | 1074 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| @@ -1014,28 +1078,34 @@ | ||
| 1014 | 1078 | rid = symbolic_name_to_rid(zName, "ci"); |
| 1015 | 1079 | if( rid==0 ){ |
| 1016 | 1080 | fossil_fatal("not a valid check-in: %s", zName); |
| 1017 | 1081 | } |
| 1018 | 1082 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1083 | + isBranchCI = branch_includes_uuid(zName,zUuid); | |
| 1019 | 1084 | baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); |
| 1020 | 1085 | zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event" |
| 1021 | 1086 | " WHERE objid=%d", rid); |
| 1022 | 1087 | style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); |
| 1023 | 1088 | style_header("File Ages"); |
| 1024 | 1089 | zGlob = P("glob"); |
| 1025 | 1090 | compute_fileage(rid,zGlob); |
| 1026 | 1091 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1027 | 1092 | |
| 1028 | - @ <h1>Files in | |
| 1029 | - @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a> | |
| 1093 | + if( fossil_strcmp(zName,"tip")==0 ){ | |
| 1094 | + @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a> | |
| 1095 | + }else if( isBranchCI ){ | |
| 1096 | + @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a> | |
| 1097 | + @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a> | |
| 1098 | + }else{ | |
| 1099 | + @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a> | |
| 1100 | + } | |
| 1030 | 1101 | if( zGlob && zGlob[0] ){ |
| 1031 | 1102 | @ that match "%h(zGlob)" |
| 1032 | 1103 | } |
| 1033 | 1104 | @ ordered by age</h1> |
| 1034 | 1105 | @ |
| 1035 | - @ <p>File ages are expressed relative to the | |
| 1036 | - @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of | |
| 1106 | + @ <p>File ages are expressed relative to the check-in time of | |
| 1037 | 1107 | @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> |
| 1038 | 1108 | @ |
| 1039 | 1109 | @ <div class='fileage'><table> |
| 1040 | 1110 | @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr> |
| 1041 | 1111 | db_prepare(&q1, |
| @@ -1050,14 +1120,13 @@ | ||
| 1050 | 1120 | " AND blob.rid=event.objid\n" |
| 1051 | 1121 | " ORDER BY event.mtime DESC;", |
| 1052 | 1122 | TAG_BRANCH |
| 1053 | 1123 | ); |
| 1054 | 1124 | db_prepare(&q2, |
| 1055 | - "SELECT blob.uuid, filename.name, fileage.fid\n" | |
| 1056 | - " FROM fileage, blob, filename\n" | |
| 1125 | + "SELECT filename.name, fileage.fid\n" | |
| 1126 | + " FROM fileage, filename\n" | |
| 1057 | 1127 | " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" |
| 1058 | - " AND blob.rid=fileage.fid;" | |
| 1059 | 1128 | ); |
| 1060 | 1129 | while( db_step(&q1)==SQLITE_ROW ){ |
| 1061 | 1130 | double age = baseTime - db_column_double(&q1, 0); |
| 1062 | 1131 | int mid = db_column_int(&q1, 1); |
| 1063 | 1132 | const char *zUuid = db_column_text(&q1, 2); |
| @@ -1067,24 +1136,24 @@ | ||
| 1067 | 1136 | char *zAge = human_readable_age(age); |
| 1068 | 1137 | @ <tr><td>%s(zAge)</td> |
| 1069 | 1138 | @ <td> |
| 1070 | 1139 | db_bind_int(&q2, ":mid", mid); |
| 1071 | 1140 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1072 | - const char *zFUuid = db_column_text(&q2,0); | |
| 1073 | - const char *zFile = db_column_text(&q2,1); | |
| 1074 | - int fid = db_column_int(&q2,2); | |
| 1141 | + const char *zFile = db_column_text(&q2,0); | |
| 1142 | + @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ | |
| 1075 | 1143 | if( showId ){ |
| 1076 | - @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br /> | |
| 1144 | + int fid = db_column_int(&q2,1); | |
| 1145 | + @ (%d(fid))<br /> | |
| 1077 | 1146 | }else{ |
| 1078 | - @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br /> | |
| 1147 | + @ </a><br /> | |
| 1079 | 1148 | } |
| 1080 | 1149 | } |
| 1081 | 1150 | db_reset(&q2); |
| 1082 | 1151 | @ </td> |
| 1083 | 1152 | @ <td> |
| 1084 | 1153 | @ %W(zComment) |
| 1085 | - @ (check-in: %z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>, | |
| 1154 | + @ (check-in: %z(href("%R/info/%!S",zUuid))%S(zUuid)</a>, | |
| 1086 | 1155 | if( showId ){ |
| 1087 | 1156 | @ id: %d(mid) |
| 1088 | 1157 | } |
| 1089 | 1158 | @ user: %z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>, |
| 1090 | 1159 | @ branch: \ |
| 1091 | 1160 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -59,10 +59,18 @@ | |
| 59 | zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); |
| 60 | sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | ** Given a pathname which is a relative path from the root of |
| 66 | ** the repository to a file or directory, compute a string which |
| 67 | ** is an HTML rendering of that path with hyperlinks on each |
| 68 | ** directory component of the path where the hyperlink redirects |
| @@ -76,29 +84,36 @@ | |
| 76 | void hyperlinked_path( |
| 77 | const char *zPath, /* Path to render */ |
| 78 | Blob *pOut, /* Write into this blob */ |
| 79 | const char *zCI, /* check-in name, or NULL */ |
| 80 | const char *zURI, /* "dir" or "tree" */ |
| 81 | const char *zREx /* Extra query parameters */ |
| 82 | ){ |
| 83 | int i, j; |
| 84 | char *zSep = ""; |
| 85 | |
| 86 | for(i=0; zPath[i]; i=j){ |
| 87 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 88 | if( zPath[j] && g.perm.Hyperlink ){ |
| 89 | if( zCI ){ |
| 90 | char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI); |
| 91 | blob_appendf(pOut, "%s%z%#h</a>", |
| 92 | zSep, zLink, j-i, &zPath[i]); |
| 93 | }else{ |
| 94 | char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); |
| 95 | blob_appendf(pOut, "%s%z%#h</a>", |
| 96 | zSep, zLink, j-i, &zPath[i]); |
| 97 | } |
| 98 | }else{ |
| 99 | blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); |
| 100 | } |
| 101 | zSep = "/"; |
| 102 | while( zPath[j]=='/' ){ j++; } |
| 103 | } |
| 104 | } |
| @@ -127,27 +142,24 @@ | |
| 127 | char *zPrefix; |
| 128 | Stmt q; |
| 129 | const char *zCI = P("ci"); |
| 130 | int rid = 0; |
| 131 | char *zUuid = 0; |
| 132 | Blob dirname; |
| 133 | Manifest *pM = 0; |
| 134 | const char *zSubdirLink; |
| 135 | int linkTrunk = 1; |
| 136 | int linkTip = 1; |
| 137 | HQuery sURI; |
| 138 | |
| 139 | if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } |
| 140 | login_check_credentials(); |
| 141 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 142 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 143 | style_header("File List"); |
| 144 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 145 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 146 | pathelementFunc, 0, 0); |
| 147 | url_initialize(&sURI, "dir"); |
| 148 | cgi_query_parameters_to_url(&sURI); |
| 149 | |
| 150 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| 151 | if( zD && strlen(zD)==0 ){ zD = 0; } |
| 152 | |
| 153 | /* If a specific check-in is requested, fetch and parse it. If the |
| @@ -159,53 +171,79 @@ | |
| 159 | if( pM ){ |
| 160 | int trunkRid = symbolic_name_to_rid("tag:trunk", "ci"); |
| 161 | linkTrunk = trunkRid && rid != trunkRid; |
| 162 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 163 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 164 | }else{ |
| 165 | zCI = 0; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | /* Compute the title of the page */ |
| 170 | blob_zero(&dirname); |
| 171 | if( zD ){ |
| 172 | blob_append(&dirname, "in directory ", -1); |
| 173 | hyperlinked_path(zD, &dirname, zCI, "dir", ""); |
| 174 | zPrefix = mprintf("%s/", zD); |
| 175 | style_submenu_element("Top-Level", "%s", |
| 176 | url_render(&sURI, "name", 0, 0, 0)); |
| 177 | }else{ |
| 178 | blob_append(&dirname, "in the top-level directory", -1); |
| 179 | zPrefix = ""; |
| 180 | } |
| 181 | if( linkTrunk ){ |
| 182 | style_submenu_element("Trunk", "%s", |
| 183 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| 184 | } |
| 185 | if( linkTip ){ |
| 186 | style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); |
| 187 | } |
| 188 | if( zCI ){ |
| 189 | @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] |
| 190 | @ %s(blob_str(&dirname)) |
| 191 | if( zD ){ |
| 192 | @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> |
| 193 | } |
| 194 | @ </h2> |
| 195 | zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix); |
| 196 | if( nD==0 ){ |
| 197 | style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid); |
| 198 | } |
| 199 | }else{ |
| 200 | @ <h2>The union of all files from all check-ins |
| 201 | @ %s(blob_str(&dirname)) |
| 202 | if( zD ){ |
| 203 | @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> |
| 204 | } |
| 205 | @ </h2> |
| 206 | zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); |
| 207 | } |
| 208 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 209 | style_submenu_element("Tree-View", "%s", |
| 210 | url_render(&sURI, "type", "tree", 0, 0)); |
| 211 | |
| @@ -282,12 +320,11 @@ | |
| 282 | zFN++; |
| 283 | @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> |
| 284 | }else{ |
| 285 | const char *zLink; |
| 286 | if( zCI ){ |
| 287 | const char *zUuid = db_column_text(&q, 1); |
| 288 | zLink = href("%R/artifact/%!S",zUuid); |
| 289 | }else{ |
| 290 | zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); |
| 291 | } |
| 292 | @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> |
| 293 | } |
| @@ -612,11 +649,15 @@ | |
| 612 | HQuery sURI; /* Hyperlink */ |
| 613 | int startExpanded; /* True to start out with the tree expanded */ |
| 614 | int showDirOnly; /* Show directories only. Omit files */ |
| 615 | int nDir = 0; /* Number of directories. Used for ID attributes */ |
| 616 | char *zProjectName = db_get("project-name", 0); |
| 617 | |
| 618 | if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; } |
| 619 | memset(&sTree, 0, sizeof(sTree)); |
| 620 | login_check_credentials(); |
| 621 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 622 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| @@ -624,14 +665,12 @@ | |
| 624 | pathelementFunc, 0, 0); |
| 625 | url_initialize(&sURI, "tree"); |
| 626 | cgi_query_parameters_to_url(&sURI); |
| 627 | if( PB("nofiles") ){ |
| 628 | showDirOnly = 1; |
| 629 | style_header("Folder Hierarchy"); |
| 630 | }else{ |
| 631 | showDirOnly = 0; |
| 632 | style_header("File Tree"); |
| 633 | } |
| 634 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 635 | if( PB("expand") ){ |
| 636 | startExpanded = 1; |
| 637 | }else{ |
| @@ -660,37 +699,54 @@ | |
| 660 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 661 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 662 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 663 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 664 | " FROM event WHERE objid=%d", rid); |
| 665 | }else{ |
| 666 | zCI = 0; |
| 667 | } |
| 668 | } |
| 669 | if( zCI==0 ){ |
| 670 | rNow = db_double(0.0, "SELECT max(mtime) FROM event"); |
| 671 | zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event"); |
| 672 | } |
| 673 | |
| 674 | /* Compute the title of the page */ |
| 675 | blob_zero(&dirname); |
| 676 | if( zD ){ |
| 677 | blob_append(&dirname, "within directory ", -1); |
| 678 | hyperlinked_path(zD, &dirname, zCI, "tree", zREx); |
| 679 | if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); |
| 680 | style_submenu_element("Top-Level", "%s", |
| 681 | url_render(&sURI, "name", 0, 0, 0)); |
| 682 | }else{ |
| 683 | if( zRE ){ |
| 684 | blob_appendf(&dirname, "matching \"%s\"", zRE); |
| 685 | } |
| 686 | } |
| 687 | style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); |
| 688 | if( zCI ){ |
| 689 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 690 | if( nD==0 && !showDirOnly ){ |
| 691 | style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid); |
| 692 | } |
| 693 | } |
| 694 | if( linkTrunk ){ |
| 695 | style_submenu_element("Trunk", "%s", |
| 696 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| @@ -745,10 +801,11 @@ | |
| 745 | tree_add_node(&sTree, zName, zUuid, mtime); |
| 746 | nFile++; |
| 747 | } |
| 748 | db_finalize(&q); |
| 749 | } |
| 750 | |
| 751 | if( showDirOnly ){ |
| 752 | for(nFile=0, p=sTree.pFirst; p; p=p->pNext){ |
| 753 | if( p->pChild!=0 && p->nFullName>nD ) nFile++; |
| 754 | } |
| @@ -755,18 +812,24 @@ | |
| 755 | zObjType = "Folders"; |
| 756 | }else{ |
| 757 | zObjType = "Files"; |
| 758 | } |
| 759 | |
| 760 | style_submenu_checkbox("nofiles", "Folders Only", 0, 0); |
| 761 | |
| 762 | if( zCI ){ |
| 763 | @ <h2>%s(zObjType) from |
| 764 | if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){ |
| 765 | @ "%h(zCI)" |
| 766 | } |
| 767 | @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname)) |
| 768 | }else{ |
| 769 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 770 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 771 | } |
| 772 | if( useMtime ){ |
| @@ -824,11 +887,11 @@ | |
| 824 | nDir++; |
| 825 | }else if( !showDirOnly ){ |
| 826 | const char *zFileClass = fileext_class(p->zName); |
| 827 | char *zLink; |
| 828 | if( zCI ){ |
| 829 | zLink = href("%R/artifact/%!S",p->zUuid); |
| 830 | }else{ |
| 831 | zLink = href("%R/finfo?name=%T",p->zFullName); |
| 832 | } |
| 833 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 834 | @ %z(zLink)%h(p->zName)</a> |
| @@ -1001,10 +1064,11 @@ | |
| 1001 | int rid; |
| 1002 | const char *zName; |
| 1003 | const char *zGlob; |
| 1004 | const char *zUuid; |
| 1005 | const char *zNow; /* Time of check-in */ |
| 1006 | int showId = PB("showid"); |
| 1007 | Stmt q1, q2; |
| 1008 | double baseTime; |
| 1009 | login_check_credentials(); |
| 1010 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| @@ -1014,28 +1078,34 @@ | |
| 1014 | rid = symbolic_name_to_rid(zName, "ci"); |
| 1015 | if( rid==0 ){ |
| 1016 | fossil_fatal("not a valid check-in: %s", zName); |
| 1017 | } |
| 1018 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1019 | baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); |
| 1020 | zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event" |
| 1021 | " WHERE objid=%d", rid); |
| 1022 | style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); |
| 1023 | style_header("File Ages"); |
| 1024 | zGlob = P("glob"); |
| 1025 | compute_fileage(rid,zGlob); |
| 1026 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1027 | |
| 1028 | @ <h1>Files in |
| 1029 | @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a> |
| 1030 | if( zGlob && zGlob[0] ){ |
| 1031 | @ that match "%h(zGlob)" |
| 1032 | } |
| 1033 | @ ordered by age</h1> |
| 1034 | @ |
| 1035 | @ <p>File ages are expressed relative to the |
| 1036 | @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of |
| 1037 | @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> |
| 1038 | @ |
| 1039 | @ <div class='fileage'><table> |
| 1040 | @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr> |
| 1041 | db_prepare(&q1, |
| @@ -1050,14 +1120,13 @@ | |
| 1050 | " AND blob.rid=event.objid\n" |
| 1051 | " ORDER BY event.mtime DESC;", |
| 1052 | TAG_BRANCH |
| 1053 | ); |
| 1054 | db_prepare(&q2, |
| 1055 | "SELECT blob.uuid, filename.name, fileage.fid\n" |
| 1056 | " FROM fileage, blob, filename\n" |
| 1057 | " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" |
| 1058 | " AND blob.rid=fileage.fid;" |
| 1059 | ); |
| 1060 | while( db_step(&q1)==SQLITE_ROW ){ |
| 1061 | double age = baseTime - db_column_double(&q1, 0); |
| 1062 | int mid = db_column_int(&q1, 1); |
| 1063 | const char *zUuid = db_column_text(&q1, 2); |
| @@ -1067,24 +1136,24 @@ | |
| 1067 | char *zAge = human_readable_age(age); |
| 1068 | @ <tr><td>%s(zAge)</td> |
| 1069 | @ <td> |
| 1070 | db_bind_int(&q2, ":mid", mid); |
| 1071 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1072 | const char *zFUuid = db_column_text(&q2,0); |
| 1073 | const char *zFile = db_column_text(&q2,1); |
| 1074 | int fid = db_column_int(&q2,2); |
| 1075 | if( showId ){ |
| 1076 | @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br /> |
| 1077 | }else{ |
| 1078 | @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br /> |
| 1079 | } |
| 1080 | } |
| 1081 | db_reset(&q2); |
| 1082 | @ </td> |
| 1083 | @ <td> |
| 1084 | @ %W(zComment) |
| 1085 | @ (check-in: %z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>, |
| 1086 | if( showId ){ |
| 1087 | @ id: %d(mid) |
| 1088 | } |
| 1089 | @ user: %z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>, |
| 1090 | @ branch: \ |
| 1091 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -59,10 +59,18 @@ | |
| 59 | zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); |
| 60 | sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | ** Flag arguments for hyperlinked_path() |
| 66 | */ |
| 67 | #if INTERFACE |
| 68 | # define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */ |
| 69 | # define LINKPATH_FILE 0x0002 /* Link final term to /file */ |
| 70 | #endif |
| 71 | |
| 72 | /* |
| 73 | ** Given a pathname which is a relative path from the root of |
| 74 | ** the repository to a file or directory, compute a string which |
| 75 | ** is an HTML rendering of that path with hyperlinks on each |
| 76 | ** directory component of the path where the hyperlink redirects |
| @@ -76,29 +84,36 @@ | |
| 84 | void hyperlinked_path( |
| 85 | const char *zPath, /* Path to render */ |
| 86 | Blob *pOut, /* Write into this blob */ |
| 87 | const char *zCI, /* check-in name, or NULL */ |
| 88 | const char *zURI, /* "dir" or "tree" */ |
| 89 | const char *zREx, /* Extra query parameters */ |
| 90 | unsigned int mFlags /* Extra flags */ |
| 91 | ){ |
| 92 | int i, j; |
| 93 | char *zSep = ""; |
| 94 | |
| 95 | for(i=0; zPath[i]; i=j){ |
| 96 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 97 | if( zPath[j]==0 ){ |
| 98 | if( mFlags & LINKPATH_FILE ){ |
| 99 | zURI = "file"; |
| 100 | }else if( mFlags & LINKPATH_FINFO ){ |
| 101 | zURI = "finfo"; |
| 102 | }else{ |
| 103 | blob_appendf(pOut, "/%h", zPath+i); |
| 104 | break; |
| 105 | } |
| 106 | } |
| 107 | if( zCI ){ |
| 108 | char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI); |
| 109 | blob_appendf(pOut, "%s%z%#h</a>", |
| 110 | zSep, zLink, j-i, &zPath[i]); |
| 111 | }else{ |
| 112 | char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); |
| 113 | blob_appendf(pOut, "%s%z%#h</a>", |
| 114 | zSep, zLink, j-i, &zPath[i]); |
| 115 | } |
| 116 | zSep = "/"; |
| 117 | while( zPath[j]=='/' ){ j++; } |
| 118 | } |
| 119 | } |
| @@ -127,27 +142,24 @@ | |
| 142 | char *zPrefix; |
| 143 | Stmt q; |
| 144 | const char *zCI = P("ci"); |
| 145 | int rid = 0; |
| 146 | char *zUuid = 0; |
| 147 | Manifest *pM = 0; |
| 148 | const char *zSubdirLink; |
| 149 | int linkTrunk = 1; |
| 150 | int linkTip = 1; |
| 151 | HQuery sURI; |
| 152 | int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */ |
| 153 | int isBranchCI = 0; /* True if ci= refers to a branch name */ |
| 154 | char *zHeader = 0; |
| 155 | |
| 156 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 157 | if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } |
| 158 | login_check_credentials(); |
| 159 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 160 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 161 | |
| 162 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| 163 | if( zD && strlen(zD)==0 ){ zD = 0; } |
| 164 | |
| 165 | /* If a specific check-in is requested, fetch and parse it. If the |
| @@ -159,53 +171,79 @@ | |
| 171 | if( pM ){ |
| 172 | int trunkRid = symbolic_name_to_rid("tag:trunk", "ci"); |
| 173 | linkTrunk = trunkRid && rid != trunkRid; |
| 174 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 175 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 176 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); |
| 177 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 178 | }else{ |
| 179 | zCI = 0; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 184 | if( zD==0 ){ |
| 185 | if( zCI ){ |
| 186 | zHeader = mprintf("Top-level Files of %s", zCI); |
| 187 | }else{ |
| 188 | zHeader = mprintf("All Top-level Files"); |
| 189 | } |
| 190 | }else{ |
| 191 | if( zCI ){ |
| 192 | zHeader = mprintf("Files in %s/ of %s", zD, zCI); |
| 193 | }else{ |
| 194 | zHeader = mprintf("All File in %s/", zD); |
| 195 | } |
| 196 | } |
| 197 | style_header("%s", zHeader); |
| 198 | fossil_free(zHeader); |
| 199 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 200 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 201 | pathelementFunc, 0, 0); |
| 202 | url_initialize(&sURI, "dir"); |
| 203 | cgi_query_parameters_to_url(&sURI); |
| 204 | |
| 205 | /* Compute the title of the page */ |
| 206 | if( zD ){ |
| 207 | Blob dirname; |
| 208 | blob_init(&dirname, 0, 0); |
| 209 | hyperlinked_path(zD, &dirname, zCI, "dir", "", 0); |
| 210 | @ <h2>Files in directory %s(blob_str(&dirname)) \ |
| 211 | blob_reset(&dirname); |
| 212 | zPrefix = mprintf("%s/", zD); |
| 213 | style_submenu_element("Top-Level", "%s", |
| 214 | url_render(&sURI, "name", 0, 0, 0)); |
| 215 | }else{ |
| 216 | @ <h2>Files in the top-level directory \ |
| 217 | zPrefix = ""; |
| 218 | } |
| 219 | if( zCI ){ |
| 220 | if( fossil_strcmp(zCI,"tip")==0 ){ |
| 221 | @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2> |
| 222 | }else if( isBranchCI ){ |
| 223 | @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \ |
| 224 | @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> |
| 225 | }else { |
| 226 | @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> |
| 227 | } |
| 228 | zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix); |
| 229 | if( nD==0 ){ |
| 230 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 231 | } |
| 232 | }else{ |
| 233 | @ in any check-in</h2> |
| 234 | zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); |
| 235 | } |
| 236 | if( linkTrunk ){ |
| 237 | style_submenu_element("Trunk", "%s", |
| 238 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| 239 | } |
| 240 | if( linkTip ){ |
| 241 | style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); |
| 242 | } |
| 243 | if( zD ){ |
| 244 | style_submenu_element("History","%R/timeline?chng=%T/*", zD); |
| 245 | } |
| 246 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 247 | style_submenu_element("Tree-View", "%s", |
| 248 | url_render(&sURI, "type", "tree", 0, 0)); |
| 249 | |
| @@ -282,12 +320,11 @@ | |
| 320 | zFN++; |
| 321 | @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> |
| 322 | }else{ |
| 323 | const char *zLink; |
| 324 | if( zCI ){ |
| 325 | zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI); |
| 326 | }else{ |
| 327 | zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); |
| 328 | } |
| 329 | @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> |
| 330 | } |
| @@ -612,11 +649,15 @@ | |
| 649 | HQuery sURI; /* Hyperlink */ |
| 650 | int startExpanded; /* True to start out with the tree expanded */ |
| 651 | int showDirOnly; /* Show directories only. Omit files */ |
| 652 | int nDir = 0; /* Number of directories. Used for ID attributes */ |
| 653 | char *zProjectName = db_get("project-name", 0); |
| 654 | int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */ |
| 655 | int isBranchCI = 0; /* ci= refers to a branch name */ |
| 656 | char *zHeader = 0; |
| 657 | |
| 658 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 659 | if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; } |
| 660 | memset(&sTree, 0, sizeof(sTree)); |
| 661 | login_check_credentials(); |
| 662 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 663 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| @@ -624,14 +665,12 @@ | |
| 665 | pathelementFunc, 0, 0); |
| 666 | url_initialize(&sURI, "tree"); |
| 667 | cgi_query_parameters_to_url(&sURI); |
| 668 | if( PB("nofiles") ){ |
| 669 | showDirOnly = 1; |
| 670 | }else{ |
| 671 | showDirOnly = 0; |
| 672 | } |
| 673 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 674 | if( PB("expand") ){ |
| 675 | startExpanded = 1; |
| 676 | }else{ |
| @@ -660,37 +699,54 @@ | |
| 699 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 700 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 701 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 702 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 703 | " FROM event WHERE objid=%d", rid); |
| 704 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); |
| 705 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 706 | }else{ |
| 707 | zCI = 0; |
| 708 | } |
| 709 | } |
| 710 | if( zCI==0 ){ |
| 711 | rNow = db_double(0.0, "SELECT max(mtime) FROM event"); |
| 712 | zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event"); |
| 713 | } |
| 714 | |
| 715 | assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); |
| 716 | if( zD==0 ){ |
| 717 | if( zCI ){ |
| 718 | zHeader = mprintf("Top-level Files of %s", zCI); |
| 719 | }else{ |
| 720 | zHeader = mprintf("All Top-level Files"); |
| 721 | } |
| 722 | }else{ |
| 723 | if( zCI ){ |
| 724 | zHeader = mprintf("Files in %s/ of %s", zD, zCI); |
| 725 | }else{ |
| 726 | zHeader = mprintf("All File in %s/", zD); |
| 727 | } |
| 728 | } |
| 729 | style_header("%s", zHeader); |
| 730 | fossil_free(zHeader); |
| 731 | |
| 732 | /* Compute the title of the page */ |
| 733 | blob_zero(&dirname); |
| 734 | if( zD ){ |
| 735 | blob_append(&dirname, "within directory ", -1); |
| 736 | hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0); |
| 737 | if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); |
| 738 | style_submenu_element("Top-Level", "%s", |
| 739 | url_render(&sURI, "name", 0, 0, 0)); |
| 740 | }else if( zRE ){ |
| 741 | blob_appendf(&dirname, "matching \"%s\"", zRE); |
| 742 | } |
| 743 | style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); |
| 744 | if( zCI ){ |
| 745 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 746 | if( nD==0 && !showDirOnly ){ |
| 747 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 748 | } |
| 749 | } |
| 750 | if( linkTrunk ){ |
| 751 | style_submenu_element("Trunk", "%s", |
| 752 | url_render(&sURI, "ci", "trunk", 0, 0)); |
| @@ -745,10 +801,11 @@ | |
| 801 | tree_add_node(&sTree, zName, zUuid, mtime); |
| 802 | nFile++; |
| 803 | } |
| 804 | db_finalize(&q); |
| 805 | } |
| 806 | style_submenu_checkbox("nofiles", "Folders Only", 0, 0); |
| 807 | |
| 808 | if( showDirOnly ){ |
| 809 | for(nFile=0, p=sTree.pFirst; p; p=p->pNext){ |
| 810 | if( p->pChild!=0 && p->nFullName>nD ) nFile++; |
| 811 | } |
| @@ -755,18 +812,24 @@ | |
| 812 | zObjType = "Folders"; |
| 813 | }else{ |
| 814 | zObjType = "Files"; |
| 815 | } |
| 816 | |
| 817 | if( zCI && strcmp(zCI,"tip")==0 ){ |
| 818 | @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a> |
| 819 | }else if( isBranchCI ){ |
| 820 | @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\ |
| 821 | @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a> |
| 822 | if( blob_size(&dirname) ){ |
| 823 | @ and %s(blob_str(&dirname))</h2> |
| 824 | } |
| 825 | }else if( zCI ){ |
| 826 | @ <h2>%s(zObjType) for check-in \ |
| 827 | @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2> |
| 828 | if( blob_size(&dirname) ){ |
| 829 | @ and %s(blob_str(&dirname))</h2> |
| 830 | } |
| 831 | }else{ |
| 832 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 833 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 834 | } |
| 835 | if( useMtime ){ |
| @@ -824,11 +887,11 @@ | |
| 887 | nDir++; |
| 888 | }else if( !showDirOnly ){ |
| 889 | const char *zFileClass = fileext_class(p->zName); |
| 890 | char *zLink; |
| 891 | if( zCI ){ |
| 892 | zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI); |
| 893 | }else{ |
| 894 | zLink = href("%R/finfo?name=%T",p->zFullName); |
| 895 | } |
| 896 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 897 | @ %z(zLink)%h(p->zName)</a> |
| @@ -1001,10 +1064,11 @@ | |
| 1064 | int rid; |
| 1065 | const char *zName; |
| 1066 | const char *zGlob; |
| 1067 | const char *zUuid; |
| 1068 | const char *zNow; /* Time of check-in */ |
| 1069 | int isBranchCI; /* name= is a branch name */ |
| 1070 | int showId = PB("showid"); |
| 1071 | Stmt q1, q2; |
| 1072 | double baseTime; |
| 1073 | login_check_credentials(); |
| 1074 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| @@ -1014,28 +1078,34 @@ | |
| 1078 | rid = symbolic_name_to_rid(zName, "ci"); |
| 1079 | if( rid==0 ){ |
| 1080 | fossil_fatal("not a valid check-in: %s", zName); |
| 1081 | } |
| 1082 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1083 | isBranchCI = branch_includes_uuid(zName,zUuid); |
| 1084 | baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); |
| 1085 | zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event" |
| 1086 | " WHERE objid=%d", rid); |
| 1087 | style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); |
| 1088 | style_header("File Ages"); |
| 1089 | zGlob = P("glob"); |
| 1090 | compute_fileage(rid,zGlob); |
| 1091 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1092 | |
| 1093 | if( fossil_strcmp(zName,"tip")==0 ){ |
| 1094 | @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a> |
| 1095 | }else if( isBranchCI ){ |
| 1096 | @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a> |
| 1097 | @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a> |
| 1098 | }else{ |
| 1099 | @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a> |
| 1100 | } |
| 1101 | if( zGlob && zGlob[0] ){ |
| 1102 | @ that match "%h(zGlob)" |
| 1103 | } |
| 1104 | @ ordered by age</h1> |
| 1105 | @ |
| 1106 | @ <p>File ages are expressed relative to the check-in time of |
| 1107 | @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> |
| 1108 | @ |
| 1109 | @ <div class='fileage'><table> |
| 1110 | @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr> |
| 1111 | db_prepare(&q1, |
| @@ -1050,14 +1120,13 @@ | |
| 1120 | " AND blob.rid=event.objid\n" |
| 1121 | " ORDER BY event.mtime DESC;", |
| 1122 | TAG_BRANCH |
| 1123 | ); |
| 1124 | db_prepare(&q2, |
| 1125 | "SELECT filename.name, fileage.fid\n" |
| 1126 | " FROM fileage, filename\n" |
| 1127 | " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" |
| 1128 | ); |
| 1129 | while( db_step(&q1)==SQLITE_ROW ){ |
| 1130 | double age = baseTime - db_column_double(&q1, 0); |
| 1131 | int mid = db_column_int(&q1, 1); |
| 1132 | const char *zUuid = db_column_text(&q1, 2); |
| @@ -1067,24 +1136,24 @@ | |
| 1136 | char *zAge = human_readable_age(age); |
| 1137 | @ <tr><td>%s(zAge)</td> |
| 1138 | @ <td> |
| 1139 | db_bind_int(&q2, ":mid", mid); |
| 1140 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1141 | const char *zFile = db_column_text(&q2,0); |
| 1142 | @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ |
| 1143 | if( showId ){ |
| 1144 | int fid = db_column_int(&q2,1); |
| 1145 | @ (%d(fid))<br /> |
| 1146 | }else{ |
| 1147 | @ </a><br /> |
| 1148 | } |
| 1149 | } |
| 1150 | db_reset(&q2); |
| 1151 | @ </td> |
| 1152 | @ <td> |
| 1153 | @ %W(zComment) |
| 1154 | @ (check-in: %z(href("%R/info/%!S",zUuid))%S(zUuid)</a>, |
| 1155 | if( showId ){ |
| 1156 | @ id: %d(mid) |
| 1157 | } |
| 1158 | @ user: %z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>, |
| 1159 | @ branch: \ |
| 1160 |
+10
-2
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -55,17 +55,25 @@ | ||
| 55 | 55 | return (char*)builtin_file(zFilename, 0); |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | /* |
| 59 | 59 | ** COMMAND: test-builtin-list |
| 60 | +** | |
| 61 | +** If -verbose is used, it outputs a line at the end | |
| 62 | +** with the total item count and size. | |
| 60 | 63 | ** |
| 61 | 64 | ** List the names and sizes of all built-in resources. |
| 62 | 65 | */ |
| 63 | 66 | void test_builtin_list(void){ |
| 64 | - int i; | |
| 67 | + int i, size = 0;; | |
| 65 | 68 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 66 | - fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte); | |
| 69 | + const int n = aBuiltinFiles[i].nByte; | |
| 70 | + fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,n); | |
| 71 | + size += n; | |
| 72 | + } | |
| 73 | + if(find_option("verbose","v",0)!=0){ | |
| 74 | + fossil_print("%d entries totaling %d bytes\n", i, size); | |
| 67 | 75 | } |
| 68 | 76 | } |
| 69 | 77 | |
| 70 | 78 | /* |
| 71 | 79 | ** WEBPAGE: test-builtin-files |
| 72 | 80 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -55,17 +55,25 @@ | |
| 55 | return (char*)builtin_file(zFilename, 0); |
| 56 | } |
| 57 | |
| 58 | /* |
| 59 | ** COMMAND: test-builtin-list |
| 60 | ** |
| 61 | ** List the names and sizes of all built-in resources. |
| 62 | */ |
| 63 | void test_builtin_list(void){ |
| 64 | int i; |
| 65 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 66 | fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | /* |
| 71 | ** WEBPAGE: test-builtin-files |
| 72 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -55,17 +55,25 @@ | |
| 55 | return (char*)builtin_file(zFilename, 0); |
| 56 | } |
| 57 | |
| 58 | /* |
| 59 | ** COMMAND: test-builtin-list |
| 60 | ** |
| 61 | ** If -verbose is used, it outputs a line at the end |
| 62 | ** with the total item count and size. |
| 63 | ** |
| 64 | ** List the names and sizes of all built-in resources. |
| 65 | */ |
| 66 | void test_builtin_list(void){ |
| 67 | int i, size = 0;; |
| 68 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 69 | const int n = aBuiltinFiles[i].nByte; |
| 70 | fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,n); |
| 71 | size += n; |
| 72 | } |
| 73 | if(find_option("verbose","v",0)!=0){ |
| 74 | fossil_print("%d entries totaling %d bytes\n", i, size); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /* |
| 79 | ** WEBPAGE: test-builtin-files |
| 80 |
+1
-1
| --- src/capabilities.c | ||
| +++ src/capabilities.c | ||
| @@ -366,11 +366,11 @@ | ||
| 366 | 366 | CapabilityString *pCap; |
| 367 | 367 | char *zSelfCap; |
| 368 | 368 | char *zPubPages = db_get("public-pages",0); |
| 369 | 369 | int hasPubPages = zPubPages && zPubPages[0]; |
| 370 | 370 | |
| 371 | - pCap = capability_add(0, db_get("default-perms",0)); | |
| 371 | + pCap = capability_add(0, db_get("default-perms","u")); | |
| 372 | 372 | capability_expand(pCap); |
| 373 | 373 | zSelfCap = capability_string(pCap); |
| 374 | 374 | capability_free(pCap); |
| 375 | 375 | |
| 376 | 376 | db_prepare(&q, |
| 377 | 377 |
| --- src/capabilities.c | |
| +++ src/capabilities.c | |
| @@ -366,11 +366,11 @@ | |
| 366 | CapabilityString *pCap; |
| 367 | char *zSelfCap; |
| 368 | char *zPubPages = db_get("public-pages",0); |
| 369 | int hasPubPages = zPubPages && zPubPages[0]; |
| 370 | |
| 371 | pCap = capability_add(0, db_get("default-perms",0)); |
| 372 | capability_expand(pCap); |
| 373 | zSelfCap = capability_string(pCap); |
| 374 | capability_free(pCap); |
| 375 | |
| 376 | db_prepare(&q, |
| 377 |
| --- src/capabilities.c | |
| +++ src/capabilities.c | |
| @@ -366,11 +366,11 @@ | |
| 366 | CapabilityString *pCap; |
| 367 | char *zSelfCap; |
| 368 | char *zPubPages = db_get("public-pages",0); |
| 369 | int hasPubPages = zPubPages && zPubPages[0]; |
| 370 | |
| 371 | pCap = capability_add(0, db_get("default-perms","u")); |
| 372 | capability_expand(pCap); |
| 373 | zSelfCap = capability_string(pCap); |
| 374 | capability_free(pCap); |
| 375 | |
| 376 | db_prepare(&q, |
| 377 |
+11
-10
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -174,12 +174,12 @@ | ||
| 174 | 174 | } |
| 175 | 175 | |
| 176 | 176 | /* |
| 177 | 177 | ** Additional information used to form the HTTP reply |
| 178 | 178 | */ |
| 179 | -static const char *zContentType = "text/html"; /* Content type of the reply */ | |
| 180 | -static const char *zReplyStatus = "OK"; /* Reply status description */ | |
| 179 | +static const char *zContentType = "text/html"; /* Content type of the reply */ | |
| 180 | +static const char *zReplyStatus = "OK"; /* Reply status description */ | |
| 181 | 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | 183 | static int rangeStart = 0; /* Start of Range: */ |
| 184 | 184 | static int rangeEnd = 0; /* End of Range: plus 1 */ |
| 185 | 185 | |
| @@ -293,11 +293,11 @@ | ||
| 293 | 293 | } |
| 294 | 294 | if( g.isConst ){ |
| 295 | 295 | /* isConst means that the reply is guaranteed to be invariant, even |
| 296 | 296 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 297 | 297 | fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n"); |
| 298 | - }else if( etag_tag()!=0 ){ | |
| 298 | + }else if( etag_tag()[0]!=0 ){ | |
| 299 | 299 | fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); |
| 300 | 300 | fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); |
| 301 | 301 | }else{ |
| 302 | 302 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 303 | 303 | } |
| @@ -329,17 +329,17 @@ | ||
| 329 | 329 | */ |
| 330 | 330 | |
| 331 | 331 | /* Content intended for logged in users should only be cached in |
| 332 | 332 | ** the browser, not some shared location. |
| 333 | 333 | */ |
| 334 | - fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); | |
| 335 | - if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ | |
| 336 | - cgi_combine_header_and_body(); | |
| 337 | - blob_compress(&cgiContent[0], &cgiContent[0]); | |
| 338 | - } | |
| 339 | - | |
| 340 | 334 | if( iReplyStatus!=304 ) { |
| 335 | + fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); | |
| 336 | + if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ | |
| 337 | + cgi_combine_header_and_body(); | |
| 338 | + blob_compress(&cgiContent[0], &cgiContent[0]); | |
| 339 | + } | |
| 340 | + | |
| 341 | 341 | if( is_gzippable() && iReplyStatus!=206 ){ |
| 342 | 342 | int i; |
| 343 | 343 | gzip_begin(0); |
| 344 | 344 | for( i=0; i<2; i++ ){ |
| 345 | 345 | int size = blob_size(&cgiContent[i]); |
| @@ -359,11 +359,12 @@ | ||
| 359 | 359 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 360 | 360 | }else{ |
| 361 | 361 | total_size = 0; |
| 362 | 362 | } |
| 363 | 363 | fprintf(g.httpOut, "\r\n"); |
| 364 | - if( total_size>0 && iReplyStatus != 304 | |
| 364 | + if( total_size>0 | |
| 365 | + && iReplyStatus!=304 | |
| 365 | 366 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 366 | 367 | ){ |
| 367 | 368 | int i, size; |
| 368 | 369 | for(i=0; i<2; i++){ |
| 369 | 370 | size = blob_size(&cgiContent[i]); |
| 370 | 371 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -174,12 +174,12 @@ | |
| 174 | } |
| 175 | |
| 176 | /* |
| 177 | ** Additional information used to form the HTTP reply |
| 178 | */ |
| 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | static int rangeStart = 0; /* Start of Range: */ |
| 184 | static int rangeEnd = 0; /* End of Range: plus 1 */ |
| 185 | |
| @@ -293,11 +293,11 @@ | |
| 293 | } |
| 294 | if( g.isConst ){ |
| 295 | /* isConst means that the reply is guaranteed to be invariant, even |
| 296 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 297 | fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n"); |
| 298 | }else if( etag_tag()!=0 ){ |
| 299 | fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); |
| 300 | fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); |
| 301 | }else{ |
| 302 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 303 | } |
| @@ -329,17 +329,17 @@ | |
| 329 | */ |
| 330 | |
| 331 | /* Content intended for logged in users should only be cached in |
| 332 | ** the browser, not some shared location. |
| 333 | */ |
| 334 | fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); |
| 335 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 336 | cgi_combine_header_and_body(); |
| 337 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 338 | } |
| 339 | |
| 340 | if( iReplyStatus!=304 ) { |
| 341 | if( is_gzippable() && iReplyStatus!=206 ){ |
| 342 | int i; |
| 343 | gzip_begin(0); |
| 344 | for( i=0; i<2; i++ ){ |
| 345 | int size = blob_size(&cgiContent[i]); |
| @@ -359,11 +359,12 @@ | |
| 359 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 360 | }else{ |
| 361 | total_size = 0; |
| 362 | } |
| 363 | fprintf(g.httpOut, "\r\n"); |
| 364 | if( total_size>0 && iReplyStatus != 304 |
| 365 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 366 | ){ |
| 367 | int i, size; |
| 368 | for(i=0; i<2; i++){ |
| 369 | size = blob_size(&cgiContent[i]); |
| 370 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -174,12 +174,12 @@ | |
| 174 | } |
| 175 | |
| 176 | /* |
| 177 | ** Additional information used to form the HTTP reply |
| 178 | */ |
| 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | static int rangeStart = 0; /* Start of Range: */ |
| 184 | static int rangeEnd = 0; /* End of Range: plus 1 */ |
| 185 | |
| @@ -293,11 +293,11 @@ | |
| 293 | } |
| 294 | if( g.isConst ){ |
| 295 | /* isConst means that the reply is guaranteed to be invariant, even |
| 296 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 297 | fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n"); |
| 298 | }else if( etag_tag()[0]!=0 ){ |
| 299 | fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); |
| 300 | fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); |
| 301 | }else{ |
| 302 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 303 | } |
| @@ -329,17 +329,17 @@ | |
| 329 | */ |
| 330 | |
| 331 | /* Content intended for logged in users should only be cached in |
| 332 | ** the browser, not some shared location. |
| 333 | */ |
| 334 | if( iReplyStatus!=304 ) { |
| 335 | fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); |
| 336 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 337 | cgi_combine_header_and_body(); |
| 338 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 339 | } |
| 340 | |
| 341 | if( is_gzippable() && iReplyStatus!=206 ){ |
| 342 | int i; |
| 343 | gzip_begin(0); |
| 344 | for( i=0; i<2; i++ ){ |
| 345 | int size = blob_size(&cgiContent[i]); |
| @@ -359,11 +359,12 @@ | |
| 359 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 360 | }else{ |
| 361 | total_size = 0; |
| 362 | } |
| 363 | fprintf(g.httpOut, "\r\n"); |
| 364 | if( total_size>0 |
| 365 | && iReplyStatus!=304 |
| 366 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 367 | ){ |
| 368 | int i, size; |
| 369 | for(i=0; i<2; i++){ |
| 370 | size = blob_size(&cgiContent[i]); |
| 371 |
+38
-14
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -801,11 +801,10 @@ | ||
| 801 | 801 | } |
| 802 | 802 | if (nullSeparateFlag) |
| 803 | 803 | separator='\0'; |
| 804 | 804 | if( showAge ){ |
| 805 | 805 | fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator); |
| 806 | - if (nullSeparateFlag) putchar('\0'); | |
| 807 | 806 | }else{ |
| 808 | 807 | fossil_print("%s%s%c", type, zPathname, separator); |
| 809 | 808 | } |
| 810 | 809 | free(zFullName); |
| 811 | 810 | } |
| @@ -1681,10 +1680,18 @@ | ||
| 1681 | 1680 | while( db_step(&q)==SQLITE_ROW ){ |
| 1682 | 1681 | const char *zIntegrateUuid = db_column_text(&q, 0); |
| 1683 | 1682 | int rid = db_column_int(&q, 1); |
| 1684 | 1683 | if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " |
| 1685 | 1684 | " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ |
| 1685 | +#if 0 | |
| 1686 | + /* Make sure the check-in manifest of the resulting merge child does not | |
| 1687 | + ** include a +close tag referring to the leaf check-in on a private | |
| 1688 | + ** branch, so as not to generate a missing artifact reference on | |
| 1689 | + ** repository clones without that private branch. The merge command | |
| 1690 | + ** should have dropped the --integrate option, at this point. */ | |
| 1691 | + assert( !content_is_private(rid) ); | |
| 1692 | +#endif | |
| 1686 | 1693 | blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); |
| 1687 | 1694 | } |
| 1688 | 1695 | } |
| 1689 | 1696 | db_finalize(&q); |
| 1690 | 1697 | |
| @@ -2048,10 +2055,12 @@ | ||
| 2048 | 2055 | const char *zComment; /* Check-in comment */ |
| 2049 | 2056 | Stmt q; /* Various queries */ |
| 2050 | 2057 | char *zUuid; /* UUID of the new check-in */ |
| 2051 | 2058 | int useHash = 0; /* True to verify file status using hashing */ |
| 2052 | 2059 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 2060 | + int privateFlag = 0; /* True if the --private option is present */ | |
| 2061 | + int privateParent = 0; /* True if the parent check-in is private */ | |
| 2053 | 2062 | int isAMerge = 0; /* True if checking in a merge */ |
| 2054 | 2063 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 2055 | 2064 | int noPrompt = 0; /* True if skipping all prompts */ |
| 2056 | 2065 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 2057 | 2066 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -2085,14 +2094,20 @@ | ||
| 2085 | 2094 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2086 | 2095 | url_proxy_options(); |
| 2087 | 2096 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2088 | 2097 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2089 | 2098 | noSign = find_option("nosign",0,0)!=0; |
| 2099 | + privateFlag = find_option("private",0,0)!=0; | |
| 2090 | 2100 | forceDelta = find_option("delta",0,0)!=0; |
| 2091 | 2101 | forceBaseline = find_option("baseline",0,0)!=0; |
| 2092 | - if( forceDelta && forceBaseline ){ | |
| 2093 | - fossil_fatal("cannot use --delta and --baseline together"); | |
| 2102 | + if( forceDelta ){ | |
| 2103 | + if( forceBaseline ){ | |
| 2104 | + fossil_fatal("cannot use --delta and --baseline together"); | |
| 2105 | + } | |
| 2106 | + if( db_get_boolean("forbid-delta-manifests",0) ){ | |
| 2107 | + fossil_fatal("delta manifests are prohibited in this repository"); | |
| 2108 | + } | |
| 2094 | 2109 | } |
| 2095 | 2110 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2096 | 2111 | if( !dryRunFlag ){ |
| 2097 | 2112 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 2098 | 2113 | } |
| @@ -2117,17 +2132,10 @@ | ||
| 2117 | 2132 | sizeof(char*)*(nTag+2)); |
| 2118 | 2133 | sCiInfo.azTag[nTag++] = zTag; |
| 2119 | 2134 | sCiInfo.azTag[nTag] = 0; |
| 2120 | 2135 | } |
| 2121 | 2136 | zComFile = find_option("message-file", "M", 1); |
| 2122 | - if( find_option("private",0,0) ){ | |
| 2123 | - g.markPrivate = 1; | |
| 2124 | - if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; | |
| 2125 | - if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){ | |
| 2126 | - sCiInfo.zBrClr = "#fec084"; /* Orange */ | |
| 2127 | - } | |
| 2128 | - } | |
| 2129 | 2137 | sCiInfo.zDateOvrd = find_option("date-override",0,1); |
| 2130 | 2138 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2131 | 2139 | db_must_be_within_tree(); |
| 2132 | 2140 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2133 | 2141 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| @@ -2137,15 +2145,25 @@ | ||
| 2137 | 2145 | |
| 2138 | 2146 | /* Get the ID of the parent manifest artifact */ |
| 2139 | 2147 | vid = db_lget_int("checkout", 0); |
| 2140 | 2148 | if( vid==0 ){ |
| 2141 | 2149 | useCksum = 1; |
| 2142 | - if( sCiInfo.zBranch==0 ) { | |
| 2150 | + if( privateFlag==0 && sCiInfo.zBranch==0 ) { | |
| 2143 | 2151 | sCiInfo.zBranch=db_get("main-branch", 0); |
| 2144 | 2152 | } |
| 2145 | - }else if( content_is_private(vid) ){ | |
| 2146 | - g.markPrivate = 1; | |
| 2153 | + }else{ | |
| 2154 | + privateParent = content_is_private(vid); | |
| 2155 | + } | |
| 2156 | + | |
| 2157 | + /* Track the "private" status */ | |
| 2158 | + g.markPrivate = privateFlag || privateParent; | |
| 2159 | + if( privateFlag && !privateParent ){ | |
| 2160 | + /* Apply default branch name ("private") and color ("orange") if not | |
| 2161 | + ** specified otherwise on the command-line, and if the parent is not | |
| 2162 | + ** already private. */ | |
| 2163 | + if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; | |
| 2164 | + if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084"; | |
| 2147 | 2165 | } |
| 2148 | 2166 | |
| 2149 | 2167 | /* Do not allow the creation of a new branch using an existing open |
| 2150 | 2168 | ** branch name unless the --force flag is used */ |
| 2151 | 2169 | if( sCiInfo.zBranch!=0 |
| @@ -2168,11 +2186,14 @@ | ||
| 2168 | 2186 | ** manifest) can continue to use this repository, do not create a new |
| 2169 | 2187 | ** delta-manifest unless this repository already contains one or more |
| 2170 | 2188 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2171 | 2189 | ** by the --delta option. |
| 2172 | 2190 | */ |
| 2173 | - if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ | |
| 2191 | + if( !forceDelta | |
| 2192 | + && !db_get_boolean("seen-delta-manifest",0) | |
| 2193 | + && !db_get_boolean("forbid-delta-manifests",0) | |
| 2194 | + ){ | |
| 2174 | 2195 | forceBaseline = 1; |
| 2175 | 2196 | } |
| 2176 | 2197 | |
| 2177 | 2198 | /* |
| 2178 | 2199 | ** Autosync if autosync is enabled and this is not a private check-in. |
| @@ -2642,10 +2663,11 @@ | ||
| 2642 | 2663 | /* Commit */ |
| 2643 | 2664 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2644 | 2665 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2645 | 2666 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2646 | 2667 | if( dryRunFlag ){ |
| 2668 | + leaf_ambiguity_warning(nvid,nvid); | |
| 2647 | 2669 | db_end_transaction(1); |
| 2648 | 2670 | exit(1); |
| 2649 | 2671 | } |
| 2650 | 2672 | db_end_transaction(0); |
| 2651 | 2673 | |
| @@ -2664,7 +2686,9 @@ | ||
| 2664 | 2686 | int nTries = db_get_int("autosync-tries",1); |
| 2665 | 2687 | autosync_loop(syncFlags, nTries, 0); |
| 2666 | 2688 | } |
| 2667 | 2689 | if( count_nonbranch_children(vid)>1 ){ |
| 2668 | 2690 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 2691 | + }else{ | |
| 2692 | + leaf_ambiguity_warning(nvid,nvid); | |
| 2669 | 2693 | } |
| 2670 | 2694 | } |
| 2671 | 2695 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -801,11 +801,10 @@ | |
| 801 | } |
| 802 | if (nullSeparateFlag) |
| 803 | separator='\0'; |
| 804 | if( showAge ){ |
| 805 | fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator); |
| 806 | if (nullSeparateFlag) putchar('\0'); |
| 807 | }else{ |
| 808 | fossil_print("%s%s%c", type, zPathname, separator); |
| 809 | } |
| 810 | free(zFullName); |
| 811 | } |
| @@ -1681,10 +1680,18 @@ | |
| 1681 | while( db_step(&q)==SQLITE_ROW ){ |
| 1682 | const char *zIntegrateUuid = db_column_text(&q, 0); |
| 1683 | int rid = db_column_int(&q, 1); |
| 1684 | if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " |
| 1685 | " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ |
| 1686 | blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); |
| 1687 | } |
| 1688 | } |
| 1689 | db_finalize(&q); |
| 1690 | |
| @@ -2048,10 +2055,12 @@ | |
| 2048 | const char *zComment; /* Check-in comment */ |
| 2049 | Stmt q; /* Various queries */ |
| 2050 | char *zUuid; /* UUID of the new check-in */ |
| 2051 | int useHash = 0; /* True to verify file status using hashing */ |
| 2052 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 2053 | int isAMerge = 0; /* True if checking in a merge */ |
| 2054 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 2055 | int noPrompt = 0; /* True if skipping all prompts */ |
| 2056 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 2057 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -2085,14 +2094,20 @@ | |
| 2085 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2086 | url_proxy_options(); |
| 2087 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2088 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2089 | noSign = find_option("nosign",0,0)!=0; |
| 2090 | forceDelta = find_option("delta",0,0)!=0; |
| 2091 | forceBaseline = find_option("baseline",0,0)!=0; |
| 2092 | if( forceDelta && forceBaseline ){ |
| 2093 | fossil_fatal("cannot use --delta and --baseline together"); |
| 2094 | } |
| 2095 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2096 | if( !dryRunFlag ){ |
| 2097 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 2098 | } |
| @@ -2117,17 +2132,10 @@ | |
| 2117 | sizeof(char*)*(nTag+2)); |
| 2118 | sCiInfo.azTag[nTag++] = zTag; |
| 2119 | sCiInfo.azTag[nTag] = 0; |
| 2120 | } |
| 2121 | zComFile = find_option("message-file", "M", 1); |
| 2122 | if( find_option("private",0,0) ){ |
| 2123 | g.markPrivate = 1; |
| 2124 | if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; |
| 2125 | if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){ |
| 2126 | sCiInfo.zBrClr = "#fec084"; /* Orange */ |
| 2127 | } |
| 2128 | } |
| 2129 | sCiInfo.zDateOvrd = find_option("date-override",0,1); |
| 2130 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2131 | db_must_be_within_tree(); |
| 2132 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2133 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| @@ -2137,15 +2145,25 @@ | |
| 2137 | |
| 2138 | /* Get the ID of the parent manifest artifact */ |
| 2139 | vid = db_lget_int("checkout", 0); |
| 2140 | if( vid==0 ){ |
| 2141 | useCksum = 1; |
| 2142 | if( sCiInfo.zBranch==0 ) { |
| 2143 | sCiInfo.zBranch=db_get("main-branch", 0); |
| 2144 | } |
| 2145 | }else if( content_is_private(vid) ){ |
| 2146 | g.markPrivate = 1; |
| 2147 | } |
| 2148 | |
| 2149 | /* Do not allow the creation of a new branch using an existing open |
| 2150 | ** branch name unless the --force flag is used */ |
| 2151 | if( sCiInfo.zBranch!=0 |
| @@ -2168,11 +2186,14 @@ | |
| 2168 | ** manifest) can continue to use this repository, do not create a new |
| 2169 | ** delta-manifest unless this repository already contains one or more |
| 2170 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2171 | ** by the --delta option. |
| 2172 | */ |
| 2173 | if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ |
| 2174 | forceBaseline = 1; |
| 2175 | } |
| 2176 | |
| 2177 | /* |
| 2178 | ** Autosync if autosync is enabled and this is not a private check-in. |
| @@ -2642,10 +2663,11 @@ | |
| 2642 | /* Commit */ |
| 2643 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2644 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2645 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2646 | if( dryRunFlag ){ |
| 2647 | db_end_transaction(1); |
| 2648 | exit(1); |
| 2649 | } |
| 2650 | db_end_transaction(0); |
| 2651 | |
| @@ -2664,7 +2686,9 @@ | |
| 2664 | int nTries = db_get_int("autosync-tries",1); |
| 2665 | autosync_loop(syncFlags, nTries, 0); |
| 2666 | } |
| 2667 | if( count_nonbranch_children(vid)>1 ){ |
| 2668 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 2669 | } |
| 2670 | } |
| 2671 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -801,11 +801,10 @@ | |
| 801 | } |
| 802 | if (nullSeparateFlag) |
| 803 | separator='\0'; |
| 804 | if( showAge ){ |
| 805 | fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator); |
| 806 | }else{ |
| 807 | fossil_print("%s%s%c", type, zPathname, separator); |
| 808 | } |
| 809 | free(zFullName); |
| 810 | } |
| @@ -1681,10 +1680,18 @@ | |
| 1680 | while( db_step(&q)==SQLITE_ROW ){ |
| 1681 | const char *zIntegrateUuid = db_column_text(&q, 0); |
| 1682 | int rid = db_column_int(&q, 1); |
| 1683 | if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " |
| 1684 | " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ |
| 1685 | #if 0 |
| 1686 | /* Make sure the check-in manifest of the resulting merge child does not |
| 1687 | ** include a +close tag referring to the leaf check-in on a private |
| 1688 | ** branch, so as not to generate a missing artifact reference on |
| 1689 | ** repository clones without that private branch. The merge command |
| 1690 | ** should have dropped the --integrate option, at this point. */ |
| 1691 | assert( !content_is_private(rid) ); |
| 1692 | #endif |
| 1693 | blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); |
| 1694 | } |
| 1695 | } |
| 1696 | db_finalize(&q); |
| 1697 | |
| @@ -2048,10 +2055,12 @@ | |
| 2055 | const char *zComment; /* Check-in comment */ |
| 2056 | Stmt q; /* Various queries */ |
| 2057 | char *zUuid; /* UUID of the new check-in */ |
| 2058 | int useHash = 0; /* True to verify file status using hashing */ |
| 2059 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 2060 | int privateFlag = 0; /* True if the --private option is present */ |
| 2061 | int privateParent = 0; /* True if the parent check-in is private */ |
| 2062 | int isAMerge = 0; /* True if checking in a merge */ |
| 2063 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 2064 | int noPrompt = 0; /* True if skipping all prompts */ |
| 2065 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 2066 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -2085,14 +2094,20 @@ | |
| 2094 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2095 | url_proxy_options(); |
| 2096 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2097 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2098 | noSign = find_option("nosign",0,0)!=0; |
| 2099 | privateFlag = find_option("private",0,0)!=0; |
| 2100 | forceDelta = find_option("delta",0,0)!=0; |
| 2101 | forceBaseline = find_option("baseline",0,0)!=0; |
| 2102 | if( forceDelta ){ |
| 2103 | if( forceBaseline ){ |
| 2104 | fossil_fatal("cannot use --delta and --baseline together"); |
| 2105 | } |
| 2106 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 2107 | fossil_fatal("delta manifests are prohibited in this repository"); |
| 2108 | } |
| 2109 | } |
| 2110 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2111 | if( !dryRunFlag ){ |
| 2112 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 2113 | } |
| @@ -2117,17 +2132,10 @@ | |
| 2132 | sizeof(char*)*(nTag+2)); |
| 2133 | sCiInfo.azTag[nTag++] = zTag; |
| 2134 | sCiInfo.azTag[nTag] = 0; |
| 2135 | } |
| 2136 | zComFile = find_option("message-file", "M", 1); |
| 2137 | sCiInfo.zDateOvrd = find_option("date-override",0,1); |
| 2138 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2139 | db_must_be_within_tree(); |
| 2140 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2141 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| @@ -2137,15 +2145,25 @@ | |
| 2145 | |
| 2146 | /* Get the ID of the parent manifest artifact */ |
| 2147 | vid = db_lget_int("checkout", 0); |
| 2148 | if( vid==0 ){ |
| 2149 | useCksum = 1; |
| 2150 | if( privateFlag==0 && sCiInfo.zBranch==0 ) { |
| 2151 | sCiInfo.zBranch=db_get("main-branch", 0); |
| 2152 | } |
| 2153 | }else{ |
| 2154 | privateParent = content_is_private(vid); |
| 2155 | } |
| 2156 | |
| 2157 | /* Track the "private" status */ |
| 2158 | g.markPrivate = privateFlag || privateParent; |
| 2159 | if( privateFlag && !privateParent ){ |
| 2160 | /* Apply default branch name ("private") and color ("orange") if not |
| 2161 | ** specified otherwise on the command-line, and if the parent is not |
| 2162 | ** already private. */ |
| 2163 | if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; |
| 2164 | if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084"; |
| 2165 | } |
| 2166 | |
| 2167 | /* Do not allow the creation of a new branch using an existing open |
| 2168 | ** branch name unless the --force flag is used */ |
| 2169 | if( sCiInfo.zBranch!=0 |
| @@ -2168,11 +2186,14 @@ | |
| 2186 | ** manifest) can continue to use this repository, do not create a new |
| 2187 | ** delta-manifest unless this repository already contains one or more |
| 2188 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2189 | ** by the --delta option. |
| 2190 | */ |
| 2191 | if( !forceDelta |
| 2192 | && !db_get_boolean("seen-delta-manifest",0) |
| 2193 | && !db_get_boolean("forbid-delta-manifests",0) |
| 2194 | ){ |
| 2195 | forceBaseline = 1; |
| 2196 | } |
| 2197 | |
| 2198 | /* |
| 2199 | ** Autosync if autosync is enabled and this is not a private check-in. |
| @@ -2642,10 +2663,11 @@ | |
| 2663 | /* Commit */ |
| 2664 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2665 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2666 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2667 | if( dryRunFlag ){ |
| 2668 | leaf_ambiguity_warning(nvid,nvid); |
| 2669 | db_end_transaction(1); |
| 2670 | exit(1); |
| 2671 | } |
| 2672 | db_end_transaction(0); |
| 2673 | |
| @@ -2664,7 +2686,9 @@ | |
| 2686 | int nTries = db_get_int("autosync-tries",1); |
| 2687 | autosync_loop(syncFlags, nTries, 0); |
| 2688 | } |
| 2689 | if( count_nonbranch_children(vid)>1 ){ |
| 2690 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 2691 | }else{ |
| 2692 | leaf_ambiguity_warning(nvid,nvid); |
| 2693 | } |
| 2694 | } |
| 2695 |
+38
-14
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -801,11 +801,10 @@ | ||
| 801 | 801 | } |
| 802 | 802 | if (nullSeparateFlag) |
| 803 | 803 | separator='\0'; |
| 804 | 804 | if( showAge ){ |
| 805 | 805 | fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator); |
| 806 | - if (nullSeparateFlag) putchar('\0'); | |
| 807 | 806 | }else{ |
| 808 | 807 | fossil_print("%s%s%c", type, zPathname, separator); |
| 809 | 808 | } |
| 810 | 809 | free(zFullName); |
| 811 | 810 | } |
| @@ -1681,10 +1680,18 @@ | ||
| 1681 | 1680 | while( db_step(&q)==SQLITE_ROW ){ |
| 1682 | 1681 | const char *zIntegrateUuid = db_column_text(&q, 0); |
| 1683 | 1682 | int rid = db_column_int(&q, 1); |
| 1684 | 1683 | if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " |
| 1685 | 1684 | " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ |
| 1685 | +#if 0 | |
| 1686 | + /* Make sure the check-in manifest of the resulting merge child does not | |
| 1687 | + ** include a +close tag referring to the leaf check-in on a private | |
| 1688 | + ** branch, so as not to generate a missing artifact reference on | |
| 1689 | + ** repository clones without that private branch. The merge command | |
| 1690 | + ** should have dropped the --integrate option, at this point. */ | |
| 1691 | + assert( !content_is_private(rid) ); | |
| 1692 | +#endif | |
| 1686 | 1693 | blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); |
| 1687 | 1694 | } |
| 1688 | 1695 | } |
| 1689 | 1696 | db_finalize(&q); |
| 1690 | 1697 | |
| @@ -2048,10 +2055,12 @@ | ||
| 2048 | 2055 | const char *zComment; /* Check-in comment */ |
| 2049 | 2056 | Stmt q; /* Various queries */ |
| 2050 | 2057 | char *zUuid; /* UUID of the new check-in */ |
| 2051 | 2058 | int useHash = 0; /* True to verify file status using hashing */ |
| 2052 | 2059 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 2060 | + int privateFlag = 0; /* True if the --private option is present */ | |
| 2061 | + int privateParent = 0; /* True if the parent check-in is private */ | |
| 2053 | 2062 | int isAMerge = 0; /* True if checking in a merge */ |
| 2054 | 2063 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 2055 | 2064 | int noPrompt = 0; /* True if skipping all prompts */ |
| 2056 | 2065 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 2057 | 2066 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -2085,14 +2094,20 @@ | ||
| 2085 | 2094 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2086 | 2095 | url_proxy_options(); |
| 2087 | 2096 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2088 | 2097 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2089 | 2098 | noSign = find_option("nosign",0,0)!=0; |
| 2099 | + privateFlag = find_option("private",0,0)!=0; | |
| 2090 | 2100 | forceDelta = find_option("delta",0,0)!=0; |
| 2091 | 2101 | forceBaseline = find_option("baseline",0,0)!=0; |
| 2092 | - if( forceDelta && forceBaseline ){ | |
| 2093 | - fossil_fatal("cannot use --delta and --baseline together"); | |
| 2102 | + if( forceDelta ){ | |
| 2103 | + if( forceBaseline ){ | |
| 2104 | + fossil_fatal("cannot use --delta and --baseline together"); | |
| 2105 | + } | |
| 2106 | + if( db_get_boolean("forbid-delta-manifests",0) ){ | |
| 2107 | + fossil_fatal("delta manifests are prohibited in this repository"); | |
| 2108 | + } | |
| 2094 | 2109 | } |
| 2095 | 2110 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2096 | 2111 | if( !dryRunFlag ){ |
| 2097 | 2112 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 2098 | 2113 | } |
| @@ -2117,17 +2132,10 @@ | ||
| 2117 | 2132 | sizeof(char*)*(nTag+2)); |
| 2118 | 2133 | sCiInfo.azTag[nTag++] = zTag; |
| 2119 | 2134 | sCiInfo.azTag[nTag] = 0; |
| 2120 | 2135 | } |
| 2121 | 2136 | zComFile = find_option("message-file", "M", 1); |
| 2122 | - if( find_option("private",0,0) ){ | |
| 2123 | - g.markPrivate = 1; | |
| 2124 | - if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; | |
| 2125 | - if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){ | |
| 2126 | - sCiInfo.zBrClr = "#fec084"; /* Orange */ | |
| 2127 | - } | |
| 2128 | - } | |
| 2129 | 2137 | sCiInfo.zDateOvrd = find_option("date-override",0,1); |
| 2130 | 2138 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2131 | 2139 | db_must_be_within_tree(); |
| 2132 | 2140 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2133 | 2141 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| @@ -2137,15 +2145,25 @@ | ||
| 2137 | 2145 | |
| 2138 | 2146 | /* Get the ID of the parent manifest artifact */ |
| 2139 | 2147 | vid = db_lget_int("checkout", 0); |
| 2140 | 2148 | if( vid==0 ){ |
| 2141 | 2149 | useCksum = 1; |
| 2142 | - if( sCiInfo.zBranch==0 ) { | |
| 2150 | + if( privateFlag==0 && sCiInfo.zBranch==0 ) { | |
| 2143 | 2151 | sCiInfo.zBranch=db_get("main-branch", 0); |
| 2144 | 2152 | } |
| 2145 | - }else if( content_is_private(vid) ){ | |
| 2146 | - g.markPrivate = 1; | |
| 2153 | + }else{ | |
| 2154 | + privateParent = content_is_private(vid); | |
| 2155 | + } | |
| 2156 | + | |
| 2157 | + /* Track the "private" status */ | |
| 2158 | + g.markPrivate = privateFlag || privateParent; | |
| 2159 | + if( privateFlag && !privateParent ){ | |
| 2160 | + /* Apply default branch name ("private") and color ("orange") if not | |
| 2161 | + ** specified otherwise on the command-line, and if the parent is not | |
| 2162 | + ** already private. */ | |
| 2163 | + if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; | |
| 2164 | + if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084"; | |
| 2147 | 2165 | } |
| 2148 | 2166 | |
| 2149 | 2167 | /* Do not allow the creation of a new branch using an existing open |
| 2150 | 2168 | ** branch name unless the --force flag is used */ |
| 2151 | 2169 | if( sCiInfo.zBranch!=0 |
| @@ -2168,11 +2186,14 @@ | ||
| 2168 | 2186 | ** manifest) can continue to use this repository, do not create a new |
| 2169 | 2187 | ** delta-manifest unless this repository already contains one or more |
| 2170 | 2188 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2171 | 2189 | ** by the --delta option. |
| 2172 | 2190 | */ |
| 2173 | - if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ | |
| 2191 | + if( !forceDelta | |
| 2192 | + && !db_get_boolean("seen-delta-manifest",0) | |
| 2193 | + && !db_get_boolean("forbid-delta-manifests",0) | |
| 2194 | + ){ | |
| 2174 | 2195 | forceBaseline = 1; |
| 2175 | 2196 | } |
| 2176 | 2197 | |
| 2177 | 2198 | /* |
| 2178 | 2199 | ** Autosync if autosync is enabled and this is not a private check-in. |
| @@ -2642,10 +2663,11 @@ | ||
| 2642 | 2663 | /* Commit */ |
| 2643 | 2664 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2644 | 2665 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2645 | 2666 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2646 | 2667 | if( dryRunFlag ){ |
| 2668 | + leaf_ambiguity_warning(nvid,nvid); | |
| 2647 | 2669 | db_end_transaction(1); |
| 2648 | 2670 | exit(1); |
| 2649 | 2671 | } |
| 2650 | 2672 | db_end_transaction(0); |
| 2651 | 2673 | |
| @@ -2664,7 +2686,9 @@ | ||
| 2664 | 2686 | int nTries = db_get_int("autosync-tries",1); |
| 2665 | 2687 | autosync_loop(syncFlags, nTries, 0); |
| 2666 | 2688 | } |
| 2667 | 2689 | if( count_nonbranch_children(vid)>1 ){ |
| 2668 | 2690 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 2691 | + }else{ | |
| 2692 | + leaf_ambiguity_warning(nvid,nvid); | |
| 2669 | 2693 | } |
| 2670 | 2694 | } |
| 2671 | 2695 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -801,11 +801,10 @@ | |
| 801 | } |
| 802 | if (nullSeparateFlag) |
| 803 | separator='\0'; |
| 804 | if( showAge ){ |
| 805 | fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator); |
| 806 | if (nullSeparateFlag) putchar('\0'); |
| 807 | }else{ |
| 808 | fossil_print("%s%s%c", type, zPathname, separator); |
| 809 | } |
| 810 | free(zFullName); |
| 811 | } |
| @@ -1681,10 +1680,18 @@ | |
| 1681 | while( db_step(&q)==SQLITE_ROW ){ |
| 1682 | const char *zIntegrateUuid = db_column_text(&q, 0); |
| 1683 | int rid = db_column_int(&q, 1); |
| 1684 | if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " |
| 1685 | " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ |
| 1686 | blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); |
| 1687 | } |
| 1688 | } |
| 1689 | db_finalize(&q); |
| 1690 | |
| @@ -2048,10 +2055,12 @@ | |
| 2048 | const char *zComment; /* Check-in comment */ |
| 2049 | Stmt q; /* Various queries */ |
| 2050 | char *zUuid; /* UUID of the new check-in */ |
| 2051 | int useHash = 0; /* True to verify file status using hashing */ |
| 2052 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 2053 | int isAMerge = 0; /* True if checking in a merge */ |
| 2054 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 2055 | int noPrompt = 0; /* True if skipping all prompts */ |
| 2056 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 2057 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -2085,14 +2094,20 @@ | |
| 2085 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2086 | url_proxy_options(); |
| 2087 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2088 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2089 | noSign = find_option("nosign",0,0)!=0; |
| 2090 | forceDelta = find_option("delta",0,0)!=0; |
| 2091 | forceBaseline = find_option("baseline",0,0)!=0; |
| 2092 | if( forceDelta && forceBaseline ){ |
| 2093 | fossil_fatal("cannot use --delta and --baseline together"); |
| 2094 | } |
| 2095 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2096 | if( !dryRunFlag ){ |
| 2097 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 2098 | } |
| @@ -2117,17 +2132,10 @@ | |
| 2117 | sizeof(char*)*(nTag+2)); |
| 2118 | sCiInfo.azTag[nTag++] = zTag; |
| 2119 | sCiInfo.azTag[nTag] = 0; |
| 2120 | } |
| 2121 | zComFile = find_option("message-file", "M", 1); |
| 2122 | if( find_option("private",0,0) ){ |
| 2123 | g.markPrivate = 1; |
| 2124 | if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; |
| 2125 | if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){ |
| 2126 | sCiInfo.zBrClr = "#fec084"; /* Orange */ |
| 2127 | } |
| 2128 | } |
| 2129 | sCiInfo.zDateOvrd = find_option("date-override",0,1); |
| 2130 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2131 | db_must_be_within_tree(); |
| 2132 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2133 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| @@ -2137,15 +2145,25 @@ | |
| 2137 | |
| 2138 | /* Get the ID of the parent manifest artifact */ |
| 2139 | vid = db_lget_int("checkout", 0); |
| 2140 | if( vid==0 ){ |
| 2141 | useCksum = 1; |
| 2142 | if( sCiInfo.zBranch==0 ) { |
| 2143 | sCiInfo.zBranch=db_get("main-branch", 0); |
| 2144 | } |
| 2145 | }else if( content_is_private(vid) ){ |
| 2146 | g.markPrivate = 1; |
| 2147 | } |
| 2148 | |
| 2149 | /* Do not allow the creation of a new branch using an existing open |
| 2150 | ** branch name unless the --force flag is used */ |
| 2151 | if( sCiInfo.zBranch!=0 |
| @@ -2168,11 +2186,14 @@ | |
| 2168 | ** manifest) can continue to use this repository, do not create a new |
| 2169 | ** delta-manifest unless this repository already contains one or more |
| 2170 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2171 | ** by the --delta option. |
| 2172 | */ |
| 2173 | if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ |
| 2174 | forceBaseline = 1; |
| 2175 | } |
| 2176 | |
| 2177 | /* |
| 2178 | ** Autosync if autosync is enabled and this is not a private check-in. |
| @@ -2642,10 +2663,11 @@ | |
| 2642 | /* Commit */ |
| 2643 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2644 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2645 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2646 | if( dryRunFlag ){ |
| 2647 | db_end_transaction(1); |
| 2648 | exit(1); |
| 2649 | } |
| 2650 | db_end_transaction(0); |
| 2651 | |
| @@ -2664,7 +2686,9 @@ | |
| 2664 | int nTries = db_get_int("autosync-tries",1); |
| 2665 | autosync_loop(syncFlags, nTries, 0); |
| 2666 | } |
| 2667 | if( count_nonbranch_children(vid)>1 ){ |
| 2668 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 2669 | } |
| 2670 | } |
| 2671 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -801,11 +801,10 @@ | |
| 801 | } |
| 802 | if (nullSeparateFlag) |
| 803 | separator='\0'; |
| 804 | if( showAge ){ |
| 805 | fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator); |
| 806 | }else{ |
| 807 | fossil_print("%s%s%c", type, zPathname, separator); |
| 808 | } |
| 809 | free(zFullName); |
| 810 | } |
| @@ -1681,10 +1680,18 @@ | |
| 1680 | while( db_step(&q)==SQLITE_ROW ){ |
| 1681 | const char *zIntegrateUuid = db_column_text(&q, 0); |
| 1682 | int rid = db_column_int(&q, 1); |
| 1683 | if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " |
| 1684 | " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ |
| 1685 | #if 0 |
| 1686 | /* Make sure the check-in manifest of the resulting merge child does not |
| 1687 | ** include a +close tag referring to the leaf check-in on a private |
| 1688 | ** branch, so as not to generate a missing artifact reference on |
| 1689 | ** repository clones without that private branch. The merge command |
| 1690 | ** should have dropped the --integrate option, at this point. */ |
| 1691 | assert( !content_is_private(rid) ); |
| 1692 | #endif |
| 1693 | blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); |
| 1694 | } |
| 1695 | } |
| 1696 | db_finalize(&q); |
| 1697 | |
| @@ -2048,10 +2055,12 @@ | |
| 2055 | const char *zComment; /* Check-in comment */ |
| 2056 | Stmt q; /* Various queries */ |
| 2057 | char *zUuid; /* UUID of the new check-in */ |
| 2058 | int useHash = 0; /* True to verify file status using hashing */ |
| 2059 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 2060 | int privateFlag = 0; /* True if the --private option is present */ |
| 2061 | int privateParent = 0; /* True if the parent check-in is private */ |
| 2062 | int isAMerge = 0; /* True if checking in a merge */ |
| 2063 | int noWarningFlag = 0; /* True if skipping all warnings */ |
| 2064 | int noPrompt = 0; /* True if skipping all prompts */ |
| 2065 | int forceFlag = 0; /* Undocumented: Disables all checks */ |
| 2066 | int forceDelta = 0; /* Force a delta-manifest */ |
| @@ -2085,14 +2094,20 @@ | |
| 2094 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2095 | url_proxy_options(); |
| 2096 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2097 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| 2098 | noSign = find_option("nosign",0,0)!=0; |
| 2099 | privateFlag = find_option("private",0,0)!=0; |
| 2100 | forceDelta = find_option("delta",0,0)!=0; |
| 2101 | forceBaseline = find_option("baseline",0,0)!=0; |
| 2102 | if( forceDelta ){ |
| 2103 | if( forceBaseline ){ |
| 2104 | fossil_fatal("cannot use --delta and --baseline together"); |
| 2105 | } |
| 2106 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 2107 | fossil_fatal("delta manifests are prohibited in this repository"); |
| 2108 | } |
| 2109 | } |
| 2110 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 2111 | if( !dryRunFlag ){ |
| 2112 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 2113 | } |
| @@ -2117,17 +2132,10 @@ | |
| 2132 | sizeof(char*)*(nTag+2)); |
| 2133 | sCiInfo.azTag[nTag++] = zTag; |
| 2134 | sCiInfo.azTag[nTag] = 0; |
| 2135 | } |
| 2136 | zComFile = find_option("message-file", "M", 1); |
| 2137 | sCiInfo.zDateOvrd = find_option("date-override",0,1); |
| 2138 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2139 | db_must_be_within_tree(); |
| 2140 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2141 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| @@ -2137,15 +2145,25 @@ | |
| 2145 | |
| 2146 | /* Get the ID of the parent manifest artifact */ |
| 2147 | vid = db_lget_int("checkout", 0); |
| 2148 | if( vid==0 ){ |
| 2149 | useCksum = 1; |
| 2150 | if( privateFlag==0 && sCiInfo.zBranch==0 ) { |
| 2151 | sCiInfo.zBranch=db_get("main-branch", 0); |
| 2152 | } |
| 2153 | }else{ |
| 2154 | privateParent = content_is_private(vid); |
| 2155 | } |
| 2156 | |
| 2157 | /* Track the "private" status */ |
| 2158 | g.markPrivate = privateFlag || privateParent; |
| 2159 | if( privateFlag && !privateParent ){ |
| 2160 | /* Apply default branch name ("private") and color ("orange") if not |
| 2161 | ** specified otherwise on the command-line, and if the parent is not |
| 2162 | ** already private. */ |
| 2163 | if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; |
| 2164 | if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084"; |
| 2165 | } |
| 2166 | |
| 2167 | /* Do not allow the creation of a new branch using an existing open |
| 2168 | ** branch name unless the --force flag is used */ |
| 2169 | if( sCiInfo.zBranch!=0 |
| @@ -2168,11 +2186,14 @@ | |
| 2186 | ** manifest) can continue to use this repository, do not create a new |
| 2187 | ** delta-manifest unless this repository already contains one or more |
| 2188 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2189 | ** by the --delta option. |
| 2190 | */ |
| 2191 | if( !forceDelta |
| 2192 | && !db_get_boolean("seen-delta-manifest",0) |
| 2193 | && !db_get_boolean("forbid-delta-manifests",0) |
| 2194 | ){ |
| 2195 | forceBaseline = 1; |
| 2196 | } |
| 2197 | |
| 2198 | /* |
| 2199 | ** Autosync if autosync is enabled and this is not a private check-in. |
| @@ -2642,10 +2663,11 @@ | |
| 2663 | /* Commit */ |
| 2664 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2665 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2666 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2667 | if( dryRunFlag ){ |
| 2668 | leaf_ambiguity_warning(nvid,nvid); |
| 2669 | db_end_transaction(1); |
| 2670 | exit(1); |
| 2671 | } |
| 2672 | db_end_transaction(0); |
| 2673 | |
| @@ -2664,7 +2686,9 @@ | |
| 2686 | int nTries = db_get_int("autosync-tries",1); |
| 2687 | autosync_loop(syncFlags, nTries, 0); |
| 2688 | } |
| 2689 | if( count_nonbranch_children(vid)>1 ){ |
| 2690 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 2691 | }else{ |
| 2692 | leaf_ambiguity_warning(nvid,nvid); |
| 2693 | } |
| 2694 | } |
| 2695 |
+17
-31
| --- src/comformat.c | ||
| +++ src/comformat.c | ||
| @@ -19,16 +19,10 @@ | ||
| 19 | 19 | ** text on a TTY. |
| 20 | 20 | */ |
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "comformat.h" |
| 23 | 23 | #include <assert.h> |
| 24 | -#ifdef _WIN32 | |
| 25 | -# include <windows.h> | |
| 26 | -#else | |
| 27 | -# include <termios.h> | |
| 28 | -# include <sys/ioctl.h> | |
| 29 | -#endif | |
| 30 | 24 | |
| 31 | 25 | #if INTERFACE |
| 32 | 26 | #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */ |
| 33 | 27 | #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */ |
| 34 | 28 | #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ |
| @@ -67,35 +61,27 @@ | ||
| 67 | 61 | */ |
| 68 | 62 | static int comment_set_maxchars( |
| 69 | 63 | int indent, |
| 70 | 64 | int *pMaxChars |
| 71 | 65 | ){ |
| 72 | -#if defined(_WIN32) | |
| 73 | - CONSOLE_SCREEN_BUFFER_INFO csbi; | |
| 74 | - memset(&csbi, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO)); | |
| 75 | - if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ | |
| 76 | - *pMaxChars = csbi.srWindow.Right - csbi.srWindow.Left - indent; | |
| 77 | - return 1; | |
| 78 | - } | |
| 79 | - return 0; | |
| 80 | -#elif defined(TIOCGWINSZ) | |
| 81 | - struct winsize w; | |
| 82 | - memset(&w, 0, sizeof(struct winsize)); | |
| 83 | - if( ioctl(0, TIOCGWINSZ, &w)!=-1 ){ | |
| 84 | - *pMaxChars = w.ws_col - indent; | |
| 85 | - return 1; | |
| 86 | - } | |
| 87 | - return 0; | |
| 88 | -#else | |
| 89 | - /* | |
| 90 | - ** Fallback to using more-or-less the "legacy semantics" of hard-coding | |
| 91 | - ** the maximum line length to a value reasonable for the vast majority | |
| 92 | - ** of supported systems. | |
| 93 | - */ | |
| 94 | - *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent; | |
| 95 | - return -1; | |
| 96 | -#endif | |
| 66 | + struct TerminalSize ts; | |
| 67 | + if ( !terminal_get_size(&ts) ){ | |
| 68 | + return 0; | |
| 69 | + } | |
| 70 | + | |
| 71 | + if( ts.nColumns ){ | |
| 72 | + *pMaxChars = ts.nColumns - indent; | |
| 73 | + return 1; | |
| 74 | + }else{ | |
| 75 | + /* | |
| 76 | + ** Fallback to using more-or-less the "legacy semantics" of hard-coding | |
| 77 | + ** the maximum line length to a value reasonable for the vast majority | |
| 78 | + ** of supported systems. | |
| 79 | + */ | |
| 80 | + *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent; | |
| 81 | + return -1; | |
| 82 | + } | |
| 97 | 83 | } |
| 98 | 84 | |
| 99 | 85 | /* |
| 100 | 86 | ** This function checks the current line being printed against the original |
| 101 | 87 | ** comment text. Upon matching, it updates the provided character and line |
| 102 | 88 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -19,16 +19,10 @@ | |
| 19 | ** text on a TTY. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "comformat.h" |
| 23 | #include <assert.h> |
| 24 | #ifdef _WIN32 |
| 25 | # include <windows.h> |
| 26 | #else |
| 27 | # include <termios.h> |
| 28 | # include <sys/ioctl.h> |
| 29 | #endif |
| 30 | |
| 31 | #if INTERFACE |
| 32 | #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */ |
| 33 | #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */ |
| 34 | #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ |
| @@ -67,35 +61,27 @@ | |
| 67 | */ |
| 68 | static int comment_set_maxchars( |
| 69 | int indent, |
| 70 | int *pMaxChars |
| 71 | ){ |
| 72 | #if defined(_WIN32) |
| 73 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 74 | memset(&csbi, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO)); |
| 75 | if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ |
| 76 | *pMaxChars = csbi.srWindow.Right - csbi.srWindow.Left - indent; |
| 77 | return 1; |
| 78 | } |
| 79 | return 0; |
| 80 | #elif defined(TIOCGWINSZ) |
| 81 | struct winsize w; |
| 82 | memset(&w, 0, sizeof(struct winsize)); |
| 83 | if( ioctl(0, TIOCGWINSZ, &w)!=-1 ){ |
| 84 | *pMaxChars = w.ws_col - indent; |
| 85 | return 1; |
| 86 | } |
| 87 | return 0; |
| 88 | #else |
| 89 | /* |
| 90 | ** Fallback to using more-or-less the "legacy semantics" of hard-coding |
| 91 | ** the maximum line length to a value reasonable for the vast majority |
| 92 | ** of supported systems. |
| 93 | */ |
| 94 | *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent; |
| 95 | return -1; |
| 96 | #endif |
| 97 | } |
| 98 | |
| 99 | /* |
| 100 | ** This function checks the current line being printed against the original |
| 101 | ** comment text. Upon matching, it updates the provided character and line |
| 102 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -19,16 +19,10 @@ | |
| 19 | ** text on a TTY. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "comformat.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | #if INTERFACE |
| 26 | #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */ |
| 27 | #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */ |
| 28 | #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ |
| @@ -67,35 +61,27 @@ | |
| 61 | */ |
| 62 | static int comment_set_maxchars( |
| 63 | int indent, |
| 64 | int *pMaxChars |
| 65 | ){ |
| 66 | struct TerminalSize ts; |
| 67 | if ( !terminal_get_size(&ts) ){ |
| 68 | return 0; |
| 69 | } |
| 70 | |
| 71 | if( ts.nColumns ){ |
| 72 | *pMaxChars = ts.nColumns - indent; |
| 73 | return 1; |
| 74 | }else{ |
| 75 | /* |
| 76 | ** Fallback to using more-or-less the "legacy semantics" of hard-coding |
| 77 | ** the maximum line length to a value reasonable for the vast majority |
| 78 | ** of supported systems. |
| 79 | */ |
| 80 | *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent; |
| 81 | return -1; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | /* |
| 86 | ** This function checks the current line being printed against the original |
| 87 | ** comment text. Upon matching, it updates the provided character and line |
| 88 |
+1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -147,10 +147,11 @@ | ||
| 147 | 147 | { "parent-project-code", CONFIGSET_PROJ }, |
| 148 | 148 | { "parent-project-name", CONFIGSET_PROJ }, |
| 149 | 149 | { "hash-policy", CONFIGSET_PROJ }, |
| 150 | 150 | { "comment-format", CONFIGSET_PROJ }, |
| 151 | 151 | { "mimetypes", CONFIGSET_PROJ }, |
| 152 | + { "forbid-delta-manifests", CONFIGSET_PROJ }, | |
| 152 | 153 | |
| 153 | 154 | #ifdef FOSSIL_ENABLE_LEGACY_MV_RM |
| 154 | 155 | { "mv-rm-files", CONFIGSET_PROJ }, |
| 155 | 156 | #endif |
| 156 | 157 | |
| 157 | 158 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -147,10 +147,11 @@ | |
| 147 | { "parent-project-code", CONFIGSET_PROJ }, |
| 148 | { "parent-project-name", CONFIGSET_PROJ }, |
| 149 | { "hash-policy", CONFIGSET_PROJ }, |
| 150 | { "comment-format", CONFIGSET_PROJ }, |
| 151 | { "mimetypes", CONFIGSET_PROJ }, |
| 152 | |
| 153 | #ifdef FOSSIL_ENABLE_LEGACY_MV_RM |
| 154 | { "mv-rm-files", CONFIGSET_PROJ }, |
| 155 | #endif |
| 156 | |
| 157 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -147,10 +147,11 @@ | |
| 147 | { "parent-project-code", CONFIGSET_PROJ }, |
| 148 | { "parent-project-name", CONFIGSET_PROJ }, |
| 149 | { "hash-policy", CONFIGSET_PROJ }, |
| 150 | { "comment-format", CONFIGSET_PROJ }, |
| 151 | { "mimetypes", CONFIGSET_PROJ }, |
| 152 | { "forbid-delta-manifests", CONFIGSET_PROJ }, |
| 153 | |
| 154 | #ifdef FOSSIL_ENABLE_LEGACY_MV_RM |
| 155 | { "mv-rm-files", CONFIGSET_PROJ }, |
| 156 | #endif |
| 157 | |
| 158 |
+12
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -774,10 +774,22 @@ | ||
| 774 | 774 | void content_make_public(int rid){ |
| 775 | 775 | static Stmt s1; |
| 776 | 776 | db_static_prepare(&s1, |
| 777 | 777 | "DELETE FROM private WHERE rid=:rid" |
| 778 | 778 | ); |
| 779 | + db_bind_int(&s1, ":rid", rid); | |
| 780 | + db_exec(&s1); | |
| 781 | +} | |
| 782 | + | |
| 783 | +/* | |
| 784 | +** Make sure an artifact is private | |
| 785 | +*/ | |
| 786 | +void content_make_private(int rid){ | |
| 787 | + static Stmt s1; | |
| 788 | + db_static_prepare(&s1, | |
| 789 | + "INSERT OR IGNORE INTO private(rid) VALUES(:rid)" | |
| 790 | + ); | |
| 779 | 791 | db_bind_int(&s1, ":rid", rid); |
| 780 | 792 | db_exec(&s1); |
| 781 | 793 | } |
| 782 | 794 | |
| 783 | 795 | /* |
| 784 | 796 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -774,10 +774,22 @@ | |
| 774 | void content_make_public(int rid){ |
| 775 | static Stmt s1; |
| 776 | db_static_prepare(&s1, |
| 777 | "DELETE FROM private WHERE rid=:rid" |
| 778 | ); |
| 779 | db_bind_int(&s1, ":rid", rid); |
| 780 | db_exec(&s1); |
| 781 | } |
| 782 | |
| 783 | /* |
| 784 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -774,10 +774,22 @@ | |
| 774 | void content_make_public(int rid){ |
| 775 | static Stmt s1; |
| 776 | db_static_prepare(&s1, |
| 777 | "DELETE FROM private WHERE rid=:rid" |
| 778 | ); |
| 779 | db_bind_int(&s1, ":rid", rid); |
| 780 | db_exec(&s1); |
| 781 | } |
| 782 | |
| 783 | /* |
| 784 | ** Make sure an artifact is private |
| 785 | */ |
| 786 | void content_make_private(int rid){ |
| 787 | static Stmt s1; |
| 788 | db_static_prepare(&s1, |
| 789 | "INSERT OR IGNORE INTO private(rid) VALUES(:rid)" |
| 790 | ); |
| 791 | db_bind_int(&s1, ":rid", rid); |
| 792 | db_exec(&s1); |
| 793 | } |
| 794 | |
| 795 | /* |
| 796 |
M
src/db.c
+122
-61
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -18,11 +18,12 @@ | ||
| 18 | 18 | ** Code for interfacing to the various databases. |
| 19 | 19 | ** |
| 20 | 20 | ** There are three separate database files that fossil interacts |
| 21 | 21 | ** with: |
| 22 | 22 | ** |
| 23 | -** (1) The "user" database in ~/.fossil | |
| 23 | +** (1) The "configdb" database in ~/.fossil or ~/.config/fossil.db | |
| 24 | +** or in %LOCALAPPDATA%/_fossil | |
| 24 | 25 | ** |
| 25 | 26 | ** (2) The "repository" database |
| 26 | 27 | ** |
| 27 | 28 | ** (3) A local checkout database named "_FOSSIL_" or ".fslckout" |
| 28 | 29 | ** and located at the root of the local copy of the source tree. |
| @@ -1369,11 +1370,11 @@ | ||
| 1369 | 1370 | db_attach(zDbName, zLabel); |
| 1370 | 1371 | } |
| 1371 | 1372 | } |
| 1372 | 1373 | |
| 1373 | 1374 | /* |
| 1374 | -** Close the per-user database file in ~/.fossil | |
| 1375 | +** Close the per-user configuration database file | |
| 1375 | 1376 | */ |
| 1376 | 1377 | void db_close_config(){ |
| 1377 | 1378 | int iSlot = db_database_slot("configdb"); |
| 1378 | 1379 | if( iSlot>0 ){ |
| 1379 | 1380 | db_detach("configdb"); |
| @@ -1393,31 +1394,30 @@ | ||
| 1393 | 1394 | fossil_free(g.zConfigDbName); |
| 1394 | 1395 | g.zConfigDbName = 0; |
| 1395 | 1396 | } |
| 1396 | 1397 | |
| 1397 | 1398 | /* |
| 1398 | -** Open the user database in "~/.fossil". Create the database anew if | |
| 1399 | -** it does not already exist. | |
| 1399 | +** Compute the name of the configuration database. If unable to find the | |
| 1400 | +** database, return 0 if isOptional is true, or panic if isOptional is false. | |
| 1400 | 1401 | ** |
| 1401 | -** If the useAttach flag is 0 (the usual case) then the user database is | |
| 1402 | -** opened on a separate database connection g.dbConfig. This prevents | |
| 1403 | -** the ~/.fossil database from becoming locked on long check-in or sync | |
| 1404 | -** operations which hold an exclusive transaction. In a few cases, though, | |
| 1405 | -** it is convenient for the ~/.fossil to be attached to the main database | |
| 1406 | -** connection so that we can join between the various databases. In that | |
| 1407 | -** case, invoke this routine with useAttach as 1. | |
| 1402 | +** Space to hold the result comes from fossil_malloc(). | |
| 1408 | 1403 | */ |
| 1409 | -int db_open_config(int useAttach, int isOptional){ | |
| 1410 | - char *zDbName; | |
| 1411 | - char *zHome; | |
| 1412 | - if( g.zConfigDbName ){ | |
| 1413 | - int alreadyAttached = db_database_slot("configdb")>0; | |
| 1414 | - if( useAttach==alreadyAttached ) return 1; /* Already open. */ | |
| 1415 | - db_close_config(); | |
| 1416 | - } | |
| 1417 | - zHome = fossil_getenv("FOSSIL_HOME"); | |
| 1404 | +static char *db_configdb_name(int isOptional){ | |
| 1405 | + char *zHome; /* Home directory */ | |
| 1406 | + char *zDbName; /* Name of the database file */ | |
| 1407 | + | |
| 1408 | + | |
| 1409 | + /* On Windows, look for these directories, in order: | |
| 1410 | + ** | |
| 1411 | + ** FOSSIL_HOME | |
| 1412 | + ** LOCALAPPDATA | |
| 1413 | + ** APPDATA | |
| 1414 | + ** USERPROFILE | |
| 1415 | + ** HOMEDRIVE HOMEPATH | |
| 1416 | + */ | |
| 1418 | 1417 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1418 | + zHome = fossil_getenv("FOSSIL_HOME"); | |
| 1419 | 1419 | if( zHome==0 ){ |
| 1420 | 1420 | zHome = fossil_getenv("LOCALAPPDATA"); |
| 1421 | 1421 | if( zHome==0 ){ |
| 1422 | 1422 | zHome = fossil_getenv("APPDATA"); |
| 1423 | 1423 | if( zHome==0 ){ |
| @@ -1428,40 +1428,98 @@ | ||
| 1428 | 1428 | if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); |
| 1429 | 1429 | } |
| 1430 | 1430 | } |
| 1431 | 1431 | } |
| 1432 | 1432 | } |
| 1433 | - if( zHome==0 ){ | |
| 1434 | - if( isOptional ) return 0; | |
| 1435 | - fossil_panic("cannot locate home directory - please set the " | |
| 1436 | - "FOSSIL_HOME, LOCALAPPDATA, APPDATA, USERPROFILE, " | |
| 1437 | - "or HOMEDRIVE / HOMEPATH environment variables"); | |
| 1438 | - } | |
| 1439 | -#else | |
| 1440 | - if( zHome==0 ){ | |
| 1441 | - zHome = fossil_getenv("HOME"); | |
| 1442 | - } | |
| 1443 | - if( zHome==0 ){ | |
| 1444 | - if( isOptional ) return 0; | |
| 1445 | - fossil_panic("cannot locate home directory - please set the " | |
| 1446 | - "FOSSIL_HOME or HOME environment variables"); | |
| 1447 | - } | |
| 1448 | -#endif | |
| 1449 | - if( file_isdir(zHome, ExtFILE)!=1 ){ | |
| 1450 | - if( isOptional ) return 0; | |
| 1451 | - fossil_panic("invalid home directory: %s", zHome); | |
| 1452 | - } | |
| 1453 | -#if defined(_WIN32) || defined(__CYGWIN__) | |
| 1454 | - /* . filenames give some window systems problems and many apps problems */ | |
| 1455 | - zDbName = mprintf("%//_fossil", zHome); | |
| 1456 | -#else | |
| 1457 | - zDbName = mprintf("%s/.fossil", zHome); | |
| 1458 | -#endif | |
| 1459 | - if( file_size(zDbName, ExtFILE)<1024*3 ){ | |
| 1460 | - if( file_access(zHome, W_OK) ){ | |
| 1461 | - if( isOptional ) return 0; | |
| 1462 | - fossil_panic("home directory %s must be writeable", zHome); | |
| 1433 | + zDbName = mprintf("%//_fossil", zHome); | |
| 1434 | + fossil_free(zHome); | |
| 1435 | + return zDbName; | |
| 1436 | + | |
| 1437 | +#else /* if unix */ | |
| 1438 | + char *zXdgHome; | |
| 1439 | + | |
| 1440 | + /* For unix. a 5-step algorithm is used. | |
| 1441 | + ** See ../www/tech_overview.wiki for discussion. | |
| 1442 | + ** | |
| 1443 | + ** Step 1: If FOSSIL_HOME exists -> $FOSSIL_HOME/.fossil | |
| 1444 | + */ | |
| 1445 | + zHome = fossil_getenv("FOSSIL_HOME"); | |
| 1446 | + if( zHome!=0 ) return mprintf("%s/.fossil", zHome); | |
| 1447 | + | |
| 1448 | + /* Step 2: If HOME exists and file $HOME/.fossil exists -> $HOME/.fossil | |
| 1449 | + */ | |
| 1450 | + zHome = fossil_getenv("HOME"); | |
| 1451 | + if( zHome ){ | |
| 1452 | + zDbName = mprintf("%s/.fossil", zHome); | |
| 1453 | + if( file_size(zDbName, ExtFILE)>1024*3 ){ | |
| 1454 | + return zDbName; | |
| 1455 | + } | |
| 1456 | + fossil_free(zDbName); | |
| 1457 | + } | |
| 1458 | + | |
| 1459 | + /* Step 3: if XDG_CONFIG_HOME exists -> $XDG_CONFIG_HOME/fossil.db | |
| 1460 | + */ | |
| 1461 | + zXdgHome = fossil_getenv("XDG_CONFIG_HOME"); | |
| 1462 | + if( zXdgHome!=0 ){ | |
| 1463 | + return mprintf("%s/fossil.db", zXdgHome); | |
| 1464 | + } | |
| 1465 | + | |
| 1466 | + /* The HOME variable is required in order to continue. | |
| 1467 | + */ | |
| 1468 | + if( zHome==0 ){ | |
| 1469 | + if( isOptional ) return 0; | |
| 1470 | + fossil_panic("cannot locate home directory - please set one of the " | |
| 1471 | + "FOSSIL_HOME, XDG_CONFIG_HOME, or HOME environment " | |
| 1472 | + "variables"); | |
| 1473 | + } | |
| 1474 | + | |
| 1475 | + /* Step 4: If $HOME/.config is a directory -> $HOME/.config/fossil.db | |
| 1476 | + */ | |
| 1477 | + zXdgHome = mprintf("%s/.config", zHome); | |
| 1478 | + if( file_isdir(zXdgHome, ExtFILE)==1 ){ | |
| 1479 | + fossil_free(zXdgHome); | |
| 1480 | + return mprintf("%s/.config/fossil.db", zHome); | |
| 1481 | + } | |
| 1482 | + | |
| 1483 | + /* Step 5: Otherwise -> $HOME/.fossil | |
| 1484 | + */ | |
| 1485 | + return mprintf("%s/.fossil", zHome); | |
| 1486 | +#endif /* unix */ | |
| 1487 | +} | |
| 1488 | + | |
| 1489 | +/* | |
| 1490 | +** Open the configuration database. Create the database anew if | |
| 1491 | +** it does not already exist. | |
| 1492 | +** | |
| 1493 | +** If the useAttach flag is 0 (the usual case) then the configuration | |
| 1494 | +** database is opened on a separate database connection g.dbConfig. | |
| 1495 | +** This prevents the database from becoming locked on long check-in or sync | |
| 1496 | +** operations which hold an exclusive transaction. In a few cases, though, | |
| 1497 | +** it is convenient for the database to be attached to the main database | |
| 1498 | +** connection so that we can join between the various databases. In that | |
| 1499 | +** case, invoke this routine with useAttach as 1. | |
| 1500 | +*/ | |
| 1501 | +int db_open_config(int useAttach, int isOptional){ | |
| 1502 | + char *zDbName; | |
| 1503 | + if( g.zConfigDbName ){ | |
| 1504 | + int alreadyAttached = db_database_slot("configdb")>0; | |
| 1505 | + if( useAttach==alreadyAttached ) return 1; /* Already open. */ | |
| 1506 | + db_close_config(); | |
| 1507 | + } | |
| 1508 | + zDbName = db_configdb_name(isOptional); | |
| 1509 | + if( zDbName==0 ) return 0; | |
| 1510 | + if( file_size(zDbName, ExtFILE)<1024*3 ){ | |
| 1511 | + char *zHome = file_dirname(zDbName); | |
| 1512 | + int rc; | |
| 1513 | + if( file_isdir(zHome, ExtFILE)==0 ){ | |
| 1514 | + file_mkdir(zHome, ExtFILE, 0); | |
| 1515 | + } | |
| 1516 | + rc = file_access(zHome, W_OK); | |
| 1517 | + fossil_free(zHome); | |
| 1518 | + if( rc ){ | |
| 1519 | + if( isOptional ) return 0; | |
| 1520 | + fossil_panic("home directory \"%s\" must be writeable", zHome); | |
| 1463 | 1521 | } |
| 1464 | 1522 | db_init_database(zDbName, zConfigSchema, (char*)0); |
| 1465 | 1523 | } |
| 1466 | 1524 | if( file_access(zDbName, W_OK) ){ |
| 1467 | 1525 | if( isOptional ) return 0; |
| @@ -2310,11 +2368,11 @@ | ||
| 2310 | 2368 | ** SQL functions for debugging. |
| 2311 | 2369 | ** |
| 2312 | 2370 | ** The print() function writes its arguments on stdout, but only |
| 2313 | 2371 | ** if the -sqlprint command-line option is turned on. |
| 2314 | 2372 | */ |
| 2315 | -LOCAL void db_sql_print( | |
| 2373 | +void db_sql_print( | |
| 2316 | 2374 | sqlite3_context *context, |
| 2317 | 2375 | int argc, |
| 2318 | 2376 | sqlite3_value **argv |
| 2319 | 2377 | ){ |
| 2320 | 2378 | int i; |
| @@ -2506,16 +2564,16 @@ | ||
| 2506 | 2564 | return 0; |
| 2507 | 2565 | } |
| 2508 | 2566 | |
| 2509 | 2567 | /* |
| 2510 | 2568 | ** Swap the g.db and g.dbConfig connections so that the various db_* routines |
| 2511 | -** work on the ~/.fossil database instead of on the repository database. | |
| 2569 | +** work on the configuration database instead of on the repository database. | |
| 2512 | 2570 | ** Be sure to swap them back after doing the operation. |
| 2513 | 2571 | ** |
| 2514 | -** If the ~/.fossil database has already been opened as the main database or | |
| 2515 | -** is attached to the main database, no connection swaps are required so this | |
| 2516 | -** routine is a no-op. | |
| 2572 | +** If the configuration database has already been opened as the main database | |
| 2573 | +** or is attached to the main database, no connection swaps are required so | |
| 2574 | +** this routine is a no-op. | |
| 2517 | 2575 | */ |
| 2518 | 2576 | void db_swap_connections(void){ |
| 2519 | 2577 | /* |
| 2520 | 2578 | ** When swapping the main database connection with the config database |
| 2521 | 2579 | ** connection, the config database connection must be open (not simply |
| @@ -3498,10 +3556,14 @@ | ||
| 3498 | 3556 | /* |
| 3499 | 3557 | ** SETTING: pgp-command width=40 |
| 3500 | 3558 | ** Command used to clear-sign manifests at check-in. |
| 3501 | 3559 | ** Default value is "gpg --clearsign -o" |
| 3502 | 3560 | */ |
| 3561 | +/* | |
| 3562 | +** SETTING: forbid-delta-manifests boolean default=off | |
| 3563 | +** If enabled, new delta manifests are prohibited. | |
| 3564 | +*/ | |
| 3503 | 3565 | /* |
| 3504 | 3566 | ** SETTING: proxy width=32 default=off |
| 3505 | 3567 | ** URL of the HTTP proxy. If undefined or "off" then |
| 3506 | 3568 | ** the "http_proxy" environment variable is consulted. |
| 3507 | 3569 | ** If the http_proxy environment variable is undefined |
| @@ -3720,14 +3782,13 @@ | ||
| 3720 | 3782 | ** The "unset" command clears a setting. |
| 3721 | 3783 | ** |
| 3722 | 3784 | ** Settings can have both a "local" repository-only value and "global" value |
| 3723 | 3785 | ** that applies to all repositories. The local values are stored in the |
| 3724 | 3786 | ** "config" table of the repository and the global values are stored in the |
| 3725 | -** $HOME/.fossil file on unix or in the %LOCALAPPDATA%/_fossil file on Windows. | |
| 3726 | -** If both a local and a global value exists for a setting, the local value | |
| 3727 | -** takes precedence. This command normally operates on the local settings. | |
| 3728 | -** Use the --global option to change global settings. | |
| 3787 | +** configuration database. If both a local and a global value exists for a | |
| 3788 | +** setting, the local value takes precedence. This command normally operates | |
| 3789 | +** on the local settings. Use the --global option to change global settings. | |
| 3729 | 3790 | ** |
| 3730 | 3791 | ** Options: |
| 3731 | 3792 | ** --global set or unset the given property globally instead of |
| 3732 | 3793 | ** setting or unsetting it for the open repository only. |
| 3733 | 3794 | ** |
| @@ -3858,12 +3919,12 @@ | ||
| 3858 | 3919 | ** COMMAND: test-without-rowid |
| 3859 | 3920 | ** |
| 3860 | 3921 | ** Usage: %fossil test-without-rowid FILENAME... |
| 3861 | 3922 | ** |
| 3862 | 3923 | ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID |
| 3863 | -** optimization. FILENAME can also be the ~/.fossil file or a local | |
| 3864 | -** .fslckout or _FOSSIL_ file. | |
| 3924 | +** optimization. FILENAME can also be the configuration database file | |
| 3925 | +** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file. | |
| 3865 | 3926 | ** |
| 3866 | 3927 | ** The purpose of this command is for testing the WITHOUT ROWID capabilities |
| 3867 | 3928 | ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil. |
| 3868 | 3929 | ** |
| 3869 | 3930 | ** Options: |
| 3870 | 3931 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -18,11 +18,12 @@ | |
| 18 | ** Code for interfacing to the various databases. |
| 19 | ** |
| 20 | ** There are three separate database files that fossil interacts |
| 21 | ** with: |
| 22 | ** |
| 23 | ** (1) The "user" database in ~/.fossil |
| 24 | ** |
| 25 | ** (2) The "repository" database |
| 26 | ** |
| 27 | ** (3) A local checkout database named "_FOSSIL_" or ".fslckout" |
| 28 | ** and located at the root of the local copy of the source tree. |
| @@ -1369,11 +1370,11 @@ | |
| 1369 | db_attach(zDbName, zLabel); |
| 1370 | } |
| 1371 | } |
| 1372 | |
| 1373 | /* |
| 1374 | ** Close the per-user database file in ~/.fossil |
| 1375 | */ |
| 1376 | void db_close_config(){ |
| 1377 | int iSlot = db_database_slot("configdb"); |
| 1378 | if( iSlot>0 ){ |
| 1379 | db_detach("configdb"); |
| @@ -1393,31 +1394,30 @@ | |
| 1393 | fossil_free(g.zConfigDbName); |
| 1394 | g.zConfigDbName = 0; |
| 1395 | } |
| 1396 | |
| 1397 | /* |
| 1398 | ** Open the user database in "~/.fossil". Create the database anew if |
| 1399 | ** it does not already exist. |
| 1400 | ** |
| 1401 | ** If the useAttach flag is 0 (the usual case) then the user database is |
| 1402 | ** opened on a separate database connection g.dbConfig. This prevents |
| 1403 | ** the ~/.fossil database from becoming locked on long check-in or sync |
| 1404 | ** operations which hold an exclusive transaction. In a few cases, though, |
| 1405 | ** it is convenient for the ~/.fossil to be attached to the main database |
| 1406 | ** connection so that we can join between the various databases. In that |
| 1407 | ** case, invoke this routine with useAttach as 1. |
| 1408 | */ |
| 1409 | int db_open_config(int useAttach, int isOptional){ |
| 1410 | char *zDbName; |
| 1411 | char *zHome; |
| 1412 | if( g.zConfigDbName ){ |
| 1413 | int alreadyAttached = db_database_slot("configdb")>0; |
| 1414 | if( useAttach==alreadyAttached ) return 1; /* Already open. */ |
| 1415 | db_close_config(); |
| 1416 | } |
| 1417 | zHome = fossil_getenv("FOSSIL_HOME"); |
| 1418 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1419 | if( zHome==0 ){ |
| 1420 | zHome = fossil_getenv("LOCALAPPDATA"); |
| 1421 | if( zHome==0 ){ |
| 1422 | zHome = fossil_getenv("APPDATA"); |
| 1423 | if( zHome==0 ){ |
| @@ -1428,40 +1428,98 @@ | |
| 1428 | if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); |
| 1429 | } |
| 1430 | } |
| 1431 | } |
| 1432 | } |
| 1433 | if( zHome==0 ){ |
| 1434 | if( isOptional ) return 0; |
| 1435 | fossil_panic("cannot locate home directory - please set the " |
| 1436 | "FOSSIL_HOME, LOCALAPPDATA, APPDATA, USERPROFILE, " |
| 1437 | "or HOMEDRIVE / HOMEPATH environment variables"); |
| 1438 | } |
| 1439 | #else |
| 1440 | if( zHome==0 ){ |
| 1441 | zHome = fossil_getenv("HOME"); |
| 1442 | } |
| 1443 | if( zHome==0 ){ |
| 1444 | if( isOptional ) return 0; |
| 1445 | fossil_panic("cannot locate home directory - please set the " |
| 1446 | "FOSSIL_HOME or HOME environment variables"); |
| 1447 | } |
| 1448 | #endif |
| 1449 | if( file_isdir(zHome, ExtFILE)!=1 ){ |
| 1450 | if( isOptional ) return 0; |
| 1451 | fossil_panic("invalid home directory: %s", zHome); |
| 1452 | } |
| 1453 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1454 | /* . filenames give some window systems problems and many apps problems */ |
| 1455 | zDbName = mprintf("%//_fossil", zHome); |
| 1456 | #else |
| 1457 | zDbName = mprintf("%s/.fossil", zHome); |
| 1458 | #endif |
| 1459 | if( file_size(zDbName, ExtFILE)<1024*3 ){ |
| 1460 | if( file_access(zHome, W_OK) ){ |
| 1461 | if( isOptional ) return 0; |
| 1462 | fossil_panic("home directory %s must be writeable", zHome); |
| 1463 | } |
| 1464 | db_init_database(zDbName, zConfigSchema, (char*)0); |
| 1465 | } |
| 1466 | if( file_access(zDbName, W_OK) ){ |
| 1467 | if( isOptional ) return 0; |
| @@ -2310,11 +2368,11 @@ | |
| 2310 | ** SQL functions for debugging. |
| 2311 | ** |
| 2312 | ** The print() function writes its arguments on stdout, but only |
| 2313 | ** if the -sqlprint command-line option is turned on. |
| 2314 | */ |
| 2315 | LOCAL void db_sql_print( |
| 2316 | sqlite3_context *context, |
| 2317 | int argc, |
| 2318 | sqlite3_value **argv |
| 2319 | ){ |
| 2320 | int i; |
| @@ -2506,16 +2564,16 @@ | |
| 2506 | return 0; |
| 2507 | } |
| 2508 | |
| 2509 | /* |
| 2510 | ** Swap the g.db and g.dbConfig connections so that the various db_* routines |
| 2511 | ** work on the ~/.fossil database instead of on the repository database. |
| 2512 | ** Be sure to swap them back after doing the operation. |
| 2513 | ** |
| 2514 | ** If the ~/.fossil database has already been opened as the main database or |
| 2515 | ** is attached to the main database, no connection swaps are required so this |
| 2516 | ** routine is a no-op. |
| 2517 | */ |
| 2518 | void db_swap_connections(void){ |
| 2519 | /* |
| 2520 | ** When swapping the main database connection with the config database |
| 2521 | ** connection, the config database connection must be open (not simply |
| @@ -3498,10 +3556,14 @@ | |
| 3498 | /* |
| 3499 | ** SETTING: pgp-command width=40 |
| 3500 | ** Command used to clear-sign manifests at check-in. |
| 3501 | ** Default value is "gpg --clearsign -o" |
| 3502 | */ |
| 3503 | /* |
| 3504 | ** SETTING: proxy width=32 default=off |
| 3505 | ** URL of the HTTP proxy. If undefined or "off" then |
| 3506 | ** the "http_proxy" environment variable is consulted. |
| 3507 | ** If the http_proxy environment variable is undefined |
| @@ -3720,14 +3782,13 @@ | |
| 3720 | ** The "unset" command clears a setting. |
| 3721 | ** |
| 3722 | ** Settings can have both a "local" repository-only value and "global" value |
| 3723 | ** that applies to all repositories. The local values are stored in the |
| 3724 | ** "config" table of the repository and the global values are stored in the |
| 3725 | ** $HOME/.fossil file on unix or in the %LOCALAPPDATA%/_fossil file on Windows. |
| 3726 | ** If both a local and a global value exists for a setting, the local value |
| 3727 | ** takes precedence. This command normally operates on the local settings. |
| 3728 | ** Use the --global option to change global settings. |
| 3729 | ** |
| 3730 | ** Options: |
| 3731 | ** --global set or unset the given property globally instead of |
| 3732 | ** setting or unsetting it for the open repository only. |
| 3733 | ** |
| @@ -3858,12 +3919,12 @@ | |
| 3858 | ** COMMAND: test-without-rowid |
| 3859 | ** |
| 3860 | ** Usage: %fossil test-without-rowid FILENAME... |
| 3861 | ** |
| 3862 | ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID |
| 3863 | ** optimization. FILENAME can also be the ~/.fossil file or a local |
| 3864 | ** .fslckout or _FOSSIL_ file. |
| 3865 | ** |
| 3866 | ** The purpose of this command is for testing the WITHOUT ROWID capabilities |
| 3867 | ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil. |
| 3868 | ** |
| 3869 | ** Options: |
| 3870 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -18,11 +18,12 @@ | |
| 18 | ** Code for interfacing to the various databases. |
| 19 | ** |
| 20 | ** There are three separate database files that fossil interacts |
| 21 | ** with: |
| 22 | ** |
| 23 | ** (1) The "configdb" database in ~/.fossil or ~/.config/fossil.db |
| 24 | ** or in %LOCALAPPDATA%/_fossil |
| 25 | ** |
| 26 | ** (2) The "repository" database |
| 27 | ** |
| 28 | ** (3) A local checkout database named "_FOSSIL_" or ".fslckout" |
| 29 | ** and located at the root of the local copy of the source tree. |
| @@ -1369,11 +1370,11 @@ | |
| 1370 | db_attach(zDbName, zLabel); |
| 1371 | } |
| 1372 | } |
| 1373 | |
| 1374 | /* |
| 1375 | ** Close the per-user configuration database file |
| 1376 | */ |
| 1377 | void db_close_config(){ |
| 1378 | int iSlot = db_database_slot("configdb"); |
| 1379 | if( iSlot>0 ){ |
| 1380 | db_detach("configdb"); |
| @@ -1393,31 +1394,30 @@ | |
| 1394 | fossil_free(g.zConfigDbName); |
| 1395 | g.zConfigDbName = 0; |
| 1396 | } |
| 1397 | |
| 1398 | /* |
| 1399 | ** Compute the name of the configuration database. If unable to find the |
| 1400 | ** database, return 0 if isOptional is true, or panic if isOptional is false. |
| 1401 | ** |
| 1402 | ** Space to hold the result comes from fossil_malloc(). |
| 1403 | */ |
| 1404 | static char *db_configdb_name(int isOptional){ |
| 1405 | char *zHome; /* Home directory */ |
| 1406 | char *zDbName; /* Name of the database file */ |
| 1407 | |
| 1408 | |
| 1409 | /* On Windows, look for these directories, in order: |
| 1410 | ** |
| 1411 | ** FOSSIL_HOME |
| 1412 | ** LOCALAPPDATA |
| 1413 | ** APPDATA |
| 1414 | ** USERPROFILE |
| 1415 | ** HOMEDRIVE HOMEPATH |
| 1416 | */ |
| 1417 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1418 | zHome = fossil_getenv("FOSSIL_HOME"); |
| 1419 | if( zHome==0 ){ |
| 1420 | zHome = fossil_getenv("LOCALAPPDATA"); |
| 1421 | if( zHome==0 ){ |
| 1422 | zHome = fossil_getenv("APPDATA"); |
| 1423 | if( zHome==0 ){ |
| @@ -1428,40 +1428,98 @@ | |
| 1428 | if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); |
| 1429 | } |
| 1430 | } |
| 1431 | } |
| 1432 | } |
| 1433 | zDbName = mprintf("%//_fossil", zHome); |
| 1434 | fossil_free(zHome); |
| 1435 | return zDbName; |
| 1436 | |
| 1437 | #else /* if unix */ |
| 1438 | char *zXdgHome; |
| 1439 | |
| 1440 | /* For unix. a 5-step algorithm is used. |
| 1441 | ** See ../www/tech_overview.wiki for discussion. |
| 1442 | ** |
| 1443 | ** Step 1: If FOSSIL_HOME exists -> $FOSSIL_HOME/.fossil |
| 1444 | */ |
| 1445 | zHome = fossil_getenv("FOSSIL_HOME"); |
| 1446 | if( zHome!=0 ) return mprintf("%s/.fossil", zHome); |
| 1447 | |
| 1448 | /* Step 2: If HOME exists and file $HOME/.fossil exists -> $HOME/.fossil |
| 1449 | */ |
| 1450 | zHome = fossil_getenv("HOME"); |
| 1451 | if( zHome ){ |
| 1452 | zDbName = mprintf("%s/.fossil", zHome); |
| 1453 | if( file_size(zDbName, ExtFILE)>1024*3 ){ |
| 1454 | return zDbName; |
| 1455 | } |
| 1456 | fossil_free(zDbName); |
| 1457 | } |
| 1458 | |
| 1459 | /* Step 3: if XDG_CONFIG_HOME exists -> $XDG_CONFIG_HOME/fossil.db |
| 1460 | */ |
| 1461 | zXdgHome = fossil_getenv("XDG_CONFIG_HOME"); |
| 1462 | if( zXdgHome!=0 ){ |
| 1463 | return mprintf("%s/fossil.db", zXdgHome); |
| 1464 | } |
| 1465 | |
| 1466 | /* The HOME variable is required in order to continue. |
| 1467 | */ |
| 1468 | if( zHome==0 ){ |
| 1469 | if( isOptional ) return 0; |
| 1470 | fossil_panic("cannot locate home directory - please set one of the " |
| 1471 | "FOSSIL_HOME, XDG_CONFIG_HOME, or HOME environment " |
| 1472 | "variables"); |
| 1473 | } |
| 1474 | |
| 1475 | /* Step 4: If $HOME/.config is a directory -> $HOME/.config/fossil.db |
| 1476 | */ |
| 1477 | zXdgHome = mprintf("%s/.config", zHome); |
| 1478 | if( file_isdir(zXdgHome, ExtFILE)==1 ){ |
| 1479 | fossil_free(zXdgHome); |
| 1480 | return mprintf("%s/.config/fossil.db", zHome); |
| 1481 | } |
| 1482 | |
| 1483 | /* Step 5: Otherwise -> $HOME/.fossil |
| 1484 | */ |
| 1485 | return mprintf("%s/.fossil", zHome); |
| 1486 | #endif /* unix */ |
| 1487 | } |
| 1488 | |
| 1489 | /* |
| 1490 | ** Open the configuration database. Create the database anew if |
| 1491 | ** it does not already exist. |
| 1492 | ** |
| 1493 | ** If the useAttach flag is 0 (the usual case) then the configuration |
| 1494 | ** database is opened on a separate database connection g.dbConfig. |
| 1495 | ** This prevents the database from becoming locked on long check-in or sync |
| 1496 | ** operations which hold an exclusive transaction. In a few cases, though, |
| 1497 | ** it is convenient for the database to be attached to the main database |
| 1498 | ** connection so that we can join between the various databases. In that |
| 1499 | ** case, invoke this routine with useAttach as 1. |
| 1500 | */ |
| 1501 | int db_open_config(int useAttach, int isOptional){ |
| 1502 | char *zDbName; |
| 1503 | if( g.zConfigDbName ){ |
| 1504 | int alreadyAttached = db_database_slot("configdb")>0; |
| 1505 | if( useAttach==alreadyAttached ) return 1; /* Already open. */ |
| 1506 | db_close_config(); |
| 1507 | } |
| 1508 | zDbName = db_configdb_name(isOptional); |
| 1509 | if( zDbName==0 ) return 0; |
| 1510 | if( file_size(zDbName, ExtFILE)<1024*3 ){ |
| 1511 | char *zHome = file_dirname(zDbName); |
| 1512 | int rc; |
| 1513 | if( file_isdir(zHome, ExtFILE)==0 ){ |
| 1514 | file_mkdir(zHome, ExtFILE, 0); |
| 1515 | } |
| 1516 | rc = file_access(zHome, W_OK); |
| 1517 | fossil_free(zHome); |
| 1518 | if( rc ){ |
| 1519 | if( isOptional ) return 0; |
| 1520 | fossil_panic("home directory \"%s\" must be writeable", zHome); |
| 1521 | } |
| 1522 | db_init_database(zDbName, zConfigSchema, (char*)0); |
| 1523 | } |
| 1524 | if( file_access(zDbName, W_OK) ){ |
| 1525 | if( isOptional ) return 0; |
| @@ -2310,11 +2368,11 @@ | |
| 2368 | ** SQL functions for debugging. |
| 2369 | ** |
| 2370 | ** The print() function writes its arguments on stdout, but only |
| 2371 | ** if the -sqlprint command-line option is turned on. |
| 2372 | */ |
| 2373 | void db_sql_print( |
| 2374 | sqlite3_context *context, |
| 2375 | int argc, |
| 2376 | sqlite3_value **argv |
| 2377 | ){ |
| 2378 | int i; |
| @@ -2506,16 +2564,16 @@ | |
| 2564 | return 0; |
| 2565 | } |
| 2566 | |
| 2567 | /* |
| 2568 | ** Swap the g.db and g.dbConfig connections so that the various db_* routines |
| 2569 | ** work on the configuration database instead of on the repository database. |
| 2570 | ** Be sure to swap them back after doing the operation. |
| 2571 | ** |
| 2572 | ** If the configuration database has already been opened as the main database |
| 2573 | ** or is attached to the main database, no connection swaps are required so |
| 2574 | ** this routine is a no-op. |
| 2575 | */ |
| 2576 | void db_swap_connections(void){ |
| 2577 | /* |
| 2578 | ** When swapping the main database connection with the config database |
| 2579 | ** connection, the config database connection must be open (not simply |
| @@ -3498,10 +3556,14 @@ | |
| 3556 | /* |
| 3557 | ** SETTING: pgp-command width=40 |
| 3558 | ** Command used to clear-sign manifests at check-in. |
| 3559 | ** Default value is "gpg --clearsign -o" |
| 3560 | */ |
| 3561 | /* |
| 3562 | ** SETTING: forbid-delta-manifests boolean default=off |
| 3563 | ** If enabled, new delta manifests are prohibited. |
| 3564 | */ |
| 3565 | /* |
| 3566 | ** SETTING: proxy width=32 default=off |
| 3567 | ** URL of the HTTP proxy. If undefined or "off" then |
| 3568 | ** the "http_proxy" environment variable is consulted. |
| 3569 | ** If the http_proxy environment variable is undefined |
| @@ -3720,14 +3782,13 @@ | |
| 3782 | ** The "unset" command clears a setting. |
| 3783 | ** |
| 3784 | ** Settings can have both a "local" repository-only value and "global" value |
| 3785 | ** that applies to all repositories. The local values are stored in the |
| 3786 | ** "config" table of the repository and the global values are stored in the |
| 3787 | ** configuration database. If both a local and a global value exists for a |
| 3788 | ** setting, the local value takes precedence. This command normally operates |
| 3789 | ** on the local settings. Use the --global option to change global settings. |
| 3790 | ** |
| 3791 | ** Options: |
| 3792 | ** --global set or unset the given property globally instead of |
| 3793 | ** setting or unsetting it for the open repository only. |
| 3794 | ** |
| @@ -3858,12 +3919,12 @@ | |
| 3919 | ** COMMAND: test-without-rowid |
| 3920 | ** |
| 3921 | ** Usage: %fossil test-without-rowid FILENAME... |
| 3922 | ** |
| 3923 | ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID |
| 3924 | ** optimization. FILENAME can also be the configuration database file |
| 3925 | ** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file. |
| 3926 | ** |
| 3927 | ** The purpose of this command is for testing the WITHOUT ROWID capabilities |
| 3928 | ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil. |
| 3929 | ** |
| 3930 | ** Options: |
| 3931 |
+19
| --- src/default_css.txt | ||
| +++ src/default_css.txt | ||
| @@ -841,5 +841,24 @@ | ||
| 841 | 841 | } |
| 842 | 842 | .accordion_panel { |
| 843 | 843 | overflow: hidden; |
| 844 | 844 | transition: max-height 0.25s ease-out; |
| 845 | 845 | } |
| 846 | +#setup_skinedit_css_defaults { | |
| 847 | + max-width: 98%; | |
| 848 | + font-family: monospace; | |
| 849 | +// These are for the UL-based implementation: | |
| 850 | + column-width: auto; | |
| 851 | + column-count: 2; | |
| 852 | + padding-top: 1em; | |
| 853 | +} | |
| 854 | +// These are for the alternate table-based skinedit CSS list: | |
| 855 | +// #setup_skinedit_css_defaults > tbody > tr > td { | |
| 856 | +// font-family: monospace; | |
| 857 | +// white-space: pre-wrap; | |
| 858 | +// border: 1px solid black; | |
| 859 | +// vertical-align: top; | |
| 860 | +// } | |
| 861 | +// #setup_skinedit_css_defaults > tbody > tr > td:nth-of-type(2) > div { | |
| 862 | +// max-width: 30em; | |
| 863 | +// overflow: auto; | |
| 864 | +// } | |
| 846 | 865 |
| --- src/default_css.txt | |
| +++ src/default_css.txt | |
| @@ -841,5 +841,24 @@ | |
| 841 | } |
| 842 | .accordion_panel { |
| 843 | overflow: hidden; |
| 844 | transition: max-height 0.25s ease-out; |
| 845 | } |
| 846 |
| --- src/default_css.txt | |
| +++ src/default_css.txt | |
| @@ -841,5 +841,24 @@ | |
| 841 | } |
| 842 | .accordion_panel { |
| 843 | overflow: hidden; |
| 844 | transition: max-height 0.25s ease-out; |
| 845 | } |
| 846 | #setup_skinedit_css_defaults { |
| 847 | max-width: 98%; |
| 848 | font-family: monospace; |
| 849 | // These are for the UL-based implementation: |
| 850 | column-width: auto; |
| 851 | column-count: 2; |
| 852 | padding-top: 1em; |
| 853 | } |
| 854 | // These are for the alternate table-based skinedit CSS list: |
| 855 | // #setup_skinedit_css_defaults > tbody > tr > td { |
| 856 | // font-family: monospace; |
| 857 | // white-space: pre-wrap; |
| 858 | // border: 1px solid black; |
| 859 | // vertical-align: top; |
| 860 | // } |
| 861 | // #setup_skinedit_css_defaults > tbody > tr > td:nth-of-type(2) > div { |
| 862 | // max-width: 30em; |
| 863 | // overflow: auto; |
| 864 | // } |
| 865 |
+27
-2
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -2,11 +2,11 @@ | ||
| 2 | 2 | ** Copyright (c) 2007 D. Richard Hipp |
| 3 | 3 | ** |
| 4 | 4 | ** This program is free software; you can redistribute it and/or |
| 5 | 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | - | |
| 7 | +** | |
| 8 | 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | 9 | ** but without any warranty; without even the implied warranty of |
| 10 | 10 | ** merchantability or fitness for a particular purpose. |
| 11 | 11 | ** |
| 12 | 12 | ** Author contact information: |
| @@ -1826,14 +1826,39 @@ | ||
| 1826 | 1826 | } |
| 1827 | 1827 | |
| 1828 | 1828 | /* |
| 1829 | 1829 | ** Extract the width of columns for side-by-side diff. Supply an |
| 1830 | 1830 | ** appropriate default if no width is given. |
| 1831 | +** | |
| 1832 | +** Calculate the default automatically, based on terminal's current width: | |
| 1833 | +** term-width = 2*diff-col + diff-marker + 1 | |
| 1834 | +** diff-col = lineno + lmargin + text-width + rmargin | |
| 1835 | +** | |
| 1836 | +** text-width = (term-width - diff-marker - 1)/2 - lineno - lmargin - rmargin | |
| 1831 | 1837 | */ |
| 1832 | 1838 | int diff_width(u64 diffFlags){ |
| 1833 | 1839 | int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1); |
| 1834 | - if( w==0 ) w = 80; | |
| 1840 | + if( w==0 ){ | |
| 1841 | + static struct { | |
| 1842 | + unsigned int lineno, lmargin, text, rmargin, marker; | |
| 1843 | + } sbsW = { 5, 2, 0, 0, 3 }; | |
| 1844 | + const unsigned int wMin = 24, wMax = 132; | |
| 1845 | + unsigned int tw = terminal_get_width(80); | |
| 1846 | + unsigned int twMin = | |
| 1847 | + (wMin + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1; | |
| 1848 | + unsigned int twMax = | |
| 1849 | + (wMax + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1; | |
| 1850 | + | |
| 1851 | + if( tw<twMin ){ | |
| 1852 | + tw = twMin; | |
| 1853 | + }else if( tw>twMax ){ | |
| 1854 | + tw = twMax; | |
| 1855 | + } | |
| 1856 | + sbsW.text = | |
| 1857 | + (tw - sbsW.marker - 1)/2 - sbsW.lineno - sbsW.lmargin - sbsW.rmargin; | |
| 1858 | + w = sbsW.text; | |
| 1859 | + } | |
| 1835 | 1860 | return w; |
| 1836 | 1861 | } |
| 1837 | 1862 | |
| 1838 | 1863 | /* |
| 1839 | 1864 | ** Append the error message to pOut. |
| 1840 | 1865 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -2,11 +2,11 @@ | |
| 2 | ** Copyright (c) 2007 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but without any warranty; without even the implied warranty of |
| 10 | ** merchantability or fitness for a particular purpose. |
| 11 | ** |
| 12 | ** Author contact information: |
| @@ -1826,14 +1826,39 @@ | |
| 1826 | } |
| 1827 | |
| 1828 | /* |
| 1829 | ** Extract the width of columns for side-by-side diff. Supply an |
| 1830 | ** appropriate default if no width is given. |
| 1831 | */ |
| 1832 | int diff_width(u64 diffFlags){ |
| 1833 | int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1); |
| 1834 | if( w==0 ) w = 80; |
| 1835 | return w; |
| 1836 | } |
| 1837 | |
| 1838 | /* |
| 1839 | ** Append the error message to pOut. |
| 1840 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -2,11 +2,11 @@ | |
| 2 | ** Copyright (c) 2007 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but without any warranty; without even the implied warranty of |
| 10 | ** merchantability or fitness for a particular purpose. |
| 11 | ** |
| 12 | ** Author contact information: |
| @@ -1826,14 +1826,39 @@ | |
| 1826 | } |
| 1827 | |
| 1828 | /* |
| 1829 | ** Extract the width of columns for side-by-side diff. Supply an |
| 1830 | ** appropriate default if no width is given. |
| 1831 | ** |
| 1832 | ** Calculate the default automatically, based on terminal's current width: |
| 1833 | ** term-width = 2*diff-col + diff-marker + 1 |
| 1834 | ** diff-col = lineno + lmargin + text-width + rmargin |
| 1835 | ** |
| 1836 | ** text-width = (term-width - diff-marker - 1)/2 - lineno - lmargin - rmargin |
| 1837 | */ |
| 1838 | int diff_width(u64 diffFlags){ |
| 1839 | int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1); |
| 1840 | if( w==0 ){ |
| 1841 | static struct { |
| 1842 | unsigned int lineno, lmargin, text, rmargin, marker; |
| 1843 | } sbsW = { 5, 2, 0, 0, 3 }; |
| 1844 | const unsigned int wMin = 24, wMax = 132; |
| 1845 | unsigned int tw = terminal_get_width(80); |
| 1846 | unsigned int twMin = |
| 1847 | (wMin + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1; |
| 1848 | unsigned int twMax = |
| 1849 | (wMax + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1; |
| 1850 | |
| 1851 | if( tw<twMin ){ |
| 1852 | tw = twMin; |
| 1853 | }else if( tw>twMax ){ |
| 1854 | tw = twMax; |
| 1855 | } |
| 1856 | sbsW.text = |
| 1857 | (tw - sbsW.marker - 1)/2 - sbsW.lineno - sbsW.lmargin - sbsW.rmargin; |
| 1858 | w = sbsW.text; |
| 1859 | } |
| 1860 | return w; |
| 1861 | } |
| 1862 | |
| 1863 | /* |
| 1864 | ** Append the error message to pOut. |
| 1865 |
+43
-6
| --- src/etag.c | ||
| +++ src/etag.c | ||
| @@ -24,10 +24,12 @@ | ||
| 24 | 24 | ** (1) The mtime on the Fossil executable |
| 25 | 25 | ** (2) The last change to the CONFIG table |
| 26 | 26 | ** (3) The last change to the EVENT table |
| 27 | 27 | ** (4) The value of the display cookie |
| 28 | 28 | ** (5) A hash value supplied by the page generator |
| 29 | +** (6) The details of the request URI | |
| 30 | +** (7) The name user as determined by the login cookie | |
| 29 | 31 | ** |
| 30 | 32 | ** Item (1) is always included in the ETag. The other elements are |
| 31 | 33 | ** optional. Because (1) is always included as part of the ETag, all |
| 32 | 34 | ** outstanding ETags can be invalidated by touching the fossil executable. |
| 33 | 35 | ** |
| @@ -60,32 +62,50 @@ | ||
| 60 | 62 | */ |
| 61 | 63 | #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */ |
| 62 | 64 | #define ETAG_DATA 0x02 /* Output depends on the EVENT table */ |
| 63 | 65 | #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */ |
| 64 | 66 | #define ETAG_HASH 0x08 /* Output depends on a hash */ |
| 67 | +#define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */ | |
| 68 | + /* and the g.zLogin value */ | |
| 65 | 69 | #endif |
| 66 | 70 | |
| 67 | 71 | static char zETag[33]; /* The generated ETag */ |
| 68 | 72 | static int iMaxAge = 0; /* The max-age parameter in the reply */ |
| 69 | 73 | static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */ |
| 74 | + | |
| 75 | +/* | |
| 76 | +** Return a hash that changes every time the Fossil source code is | |
| 77 | +** rebuilt. | |
| 78 | +** | |
| 79 | +** The FOSSIL_BUILD_HASH string that is returned here gets computed by | |
| 80 | +** the mkversion utility program. The result is a hash of MANIFEST_UUID | |
| 81 | +** and the unix timestamp for when the mkversion utility program is run. | |
| 82 | +** | |
| 83 | +** During development rebuilds, if you need the source code id to change | |
| 84 | +** in order to invalidate caches, simply "touch" the "manifest" file in | |
| 85 | +** the top of the source directory prior to running "make" and a new | |
| 86 | +** FOSSIL_BUILD_HASH will be generated automatically. | |
| 87 | +*/ | |
| 88 | +const char *fossil_exe_id(void){ | |
| 89 | + return FOSSIL_BUILD_HASH; | |
| 90 | +} | |
| 70 | 91 | |
| 71 | 92 | /* |
| 72 | 93 | ** Generate an ETag |
| 73 | 94 | */ |
| 74 | 95 | void etag_check(unsigned eFlags, const char *zHash){ |
| 75 | - sqlite3_int64 mtime; | |
| 76 | 96 | const char *zIfNoneMatch; |
| 77 | 97 | char zBuf[50]; |
| 78 | 98 | assert( zETag[0]==0 ); /* Only call this routine once! */ |
| 79 | 99 | |
| 80 | 100 | iMaxAge = 86400; |
| 81 | 101 | md5sum_init(); |
| 82 | 102 | |
| 83 | - /* Always include the mtime of the executable as part of the hash */ | |
| 84 | - mtime = file_mtime(g.nameOfExe, ExtFILE); | |
| 85 | - sqlite3_snprintf(sizeof(zBuf),zBuf,"mtime: %lld\n", mtime); | |
| 86 | - md5sum_step_text(zBuf, -1); | |
| 103 | + /* Always include the executable ID as part of the hash */ | |
| 104 | + md5sum_step_text("exe-id: ", -1); | |
| 105 | + md5sum_step_text(fossil_exe_id(), -1); | |
| 106 | + md5sum_step_text("\n", 1); | |
| 87 | 107 | |
| 88 | 108 | if( (eFlags & ETAG_HASH)!=0 && zHash ){ |
| 89 | 109 | md5sum_step_text("hash: ", -1); |
| 90 | 110 | md5sum_step_text(zHash, -1); |
| 91 | 111 | md5sum_step_text("\n", 1); |
| @@ -98,11 +118,11 @@ | ||
| 98 | 118 | md5sum_step_text("\n", 1); |
| 99 | 119 | iMaxAge = 60; |
| 100 | 120 | }else if( eFlags & ETAG_CONFIG ){ |
| 101 | 121 | int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'"); |
| 102 | 122 | sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey); |
| 103 | - md5sum_step_text("data: ", -1); | |
| 123 | + md5sum_step_text("config: ", -1); | |
| 104 | 124 | md5sum_step_text(zBuf, -1); |
| 105 | 125 | md5sum_step_text("\n", 1); |
| 106 | 126 | iMaxAge = 3600; |
| 107 | 127 | } |
| 108 | 128 | |
| @@ -111,10 +131,27 @@ | ||
| 111 | 131 | md5sum_step_text("display-cookie: ", -1); |
| 112 | 132 | md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1); |
| 113 | 133 | md5sum_step_text("\n", 1); |
| 114 | 134 | iMaxAge = 0; |
| 115 | 135 | } |
| 136 | + | |
| 137 | + /* Output depends on PATH_INFO and QUERY_STRING */ | |
| 138 | + if( eFlags & ETAG_QUERY ){ | |
| 139 | + const char *zQS = P("QUERY_STRING"); | |
| 140 | + md5sum_step_text("query: ", -1); | |
| 141 | + md5sum_step_text(PD("PATH_INFO",""), -1); | |
| 142 | + if( zQS ){ | |
| 143 | + md5sum_step_text("?", 1); | |
| 144 | + md5sum_step_text(zQS, -1); | |
| 145 | + } | |
| 146 | + md5sum_step_text("\n",1); | |
| 147 | + if( g.zLogin ){ | |
| 148 | + md5sum_step_text("login: ", -1); | |
| 149 | + md5sum_step_text(g.zLogin, -1); | |
| 150 | + md5sum_step_text("\n", 1); | |
| 151 | + } | |
| 152 | + } | |
| 116 | 153 | |
| 117 | 154 | /* Generate the ETag */ |
| 118 | 155 | memcpy(zETag, md5sum_finish(0), 33); |
| 119 | 156 | |
| 120 | 157 | /* Check to see if the generated ETag matches If-None-Match and |
| 121 | 158 |
| --- src/etag.c | |
| +++ src/etag.c | |
| @@ -24,10 +24,12 @@ | |
| 24 | ** (1) The mtime on the Fossil executable |
| 25 | ** (2) The last change to the CONFIG table |
| 26 | ** (3) The last change to the EVENT table |
| 27 | ** (4) The value of the display cookie |
| 28 | ** (5) A hash value supplied by the page generator |
| 29 | ** |
| 30 | ** Item (1) is always included in the ETag. The other elements are |
| 31 | ** optional. Because (1) is always included as part of the ETag, all |
| 32 | ** outstanding ETags can be invalidated by touching the fossil executable. |
| 33 | ** |
| @@ -60,32 +62,50 @@ | |
| 60 | */ |
| 61 | #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */ |
| 62 | #define ETAG_DATA 0x02 /* Output depends on the EVENT table */ |
| 63 | #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */ |
| 64 | #define ETAG_HASH 0x08 /* Output depends on a hash */ |
| 65 | #endif |
| 66 | |
| 67 | static char zETag[33]; /* The generated ETag */ |
| 68 | static int iMaxAge = 0; /* The max-age parameter in the reply */ |
| 69 | static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */ |
| 70 | |
| 71 | /* |
| 72 | ** Generate an ETag |
| 73 | */ |
| 74 | void etag_check(unsigned eFlags, const char *zHash){ |
| 75 | sqlite3_int64 mtime; |
| 76 | const char *zIfNoneMatch; |
| 77 | char zBuf[50]; |
| 78 | assert( zETag[0]==0 ); /* Only call this routine once! */ |
| 79 | |
| 80 | iMaxAge = 86400; |
| 81 | md5sum_init(); |
| 82 | |
| 83 | /* Always include the mtime of the executable as part of the hash */ |
| 84 | mtime = file_mtime(g.nameOfExe, ExtFILE); |
| 85 | sqlite3_snprintf(sizeof(zBuf),zBuf,"mtime: %lld\n", mtime); |
| 86 | md5sum_step_text(zBuf, -1); |
| 87 | |
| 88 | if( (eFlags & ETAG_HASH)!=0 && zHash ){ |
| 89 | md5sum_step_text("hash: ", -1); |
| 90 | md5sum_step_text(zHash, -1); |
| 91 | md5sum_step_text("\n", 1); |
| @@ -98,11 +118,11 @@ | |
| 98 | md5sum_step_text("\n", 1); |
| 99 | iMaxAge = 60; |
| 100 | }else if( eFlags & ETAG_CONFIG ){ |
| 101 | int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'"); |
| 102 | sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey); |
| 103 | md5sum_step_text("data: ", -1); |
| 104 | md5sum_step_text(zBuf, -1); |
| 105 | md5sum_step_text("\n", 1); |
| 106 | iMaxAge = 3600; |
| 107 | } |
| 108 | |
| @@ -111,10 +131,27 @@ | |
| 111 | md5sum_step_text("display-cookie: ", -1); |
| 112 | md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1); |
| 113 | md5sum_step_text("\n", 1); |
| 114 | iMaxAge = 0; |
| 115 | } |
| 116 | |
| 117 | /* Generate the ETag */ |
| 118 | memcpy(zETag, md5sum_finish(0), 33); |
| 119 | |
| 120 | /* Check to see if the generated ETag matches If-None-Match and |
| 121 |
| --- src/etag.c | |
| +++ src/etag.c | |
| @@ -24,10 +24,12 @@ | |
| 24 | ** (1) The mtime on the Fossil executable |
| 25 | ** (2) The last change to the CONFIG table |
| 26 | ** (3) The last change to the EVENT table |
| 27 | ** (4) The value of the display cookie |
| 28 | ** (5) A hash value supplied by the page generator |
| 29 | ** (6) The details of the request URI |
| 30 | ** (7) The name user as determined by the login cookie |
| 31 | ** |
| 32 | ** Item (1) is always included in the ETag. The other elements are |
| 33 | ** optional. Because (1) is always included as part of the ETag, all |
| 34 | ** outstanding ETags can be invalidated by touching the fossil executable. |
| 35 | ** |
| @@ -60,32 +62,50 @@ | |
| 62 | */ |
| 63 | #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */ |
| 64 | #define ETAG_DATA 0x02 /* Output depends on the EVENT table */ |
| 65 | #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */ |
| 66 | #define ETAG_HASH 0x08 /* Output depends on a hash */ |
| 67 | #define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */ |
| 68 | /* and the g.zLogin value */ |
| 69 | #endif |
| 70 | |
| 71 | static char zETag[33]; /* The generated ETag */ |
| 72 | static int iMaxAge = 0; /* The max-age parameter in the reply */ |
| 73 | static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */ |
| 74 | |
| 75 | /* |
| 76 | ** Return a hash that changes every time the Fossil source code is |
| 77 | ** rebuilt. |
| 78 | ** |
| 79 | ** The FOSSIL_BUILD_HASH string that is returned here gets computed by |
| 80 | ** the mkversion utility program. The result is a hash of MANIFEST_UUID |
| 81 | ** and the unix timestamp for when the mkversion utility program is run. |
| 82 | ** |
| 83 | ** During development rebuilds, if you need the source code id to change |
| 84 | ** in order to invalidate caches, simply "touch" the "manifest" file in |
| 85 | ** the top of the source directory prior to running "make" and a new |
| 86 | ** FOSSIL_BUILD_HASH will be generated automatically. |
| 87 | */ |
| 88 | const char *fossil_exe_id(void){ |
| 89 | return FOSSIL_BUILD_HASH; |
| 90 | } |
| 91 | |
| 92 | /* |
| 93 | ** Generate an ETag |
| 94 | */ |
| 95 | void etag_check(unsigned eFlags, const char *zHash){ |
| 96 | const char *zIfNoneMatch; |
| 97 | char zBuf[50]; |
| 98 | assert( zETag[0]==0 ); /* Only call this routine once! */ |
| 99 | |
| 100 | iMaxAge = 86400; |
| 101 | md5sum_init(); |
| 102 | |
| 103 | /* Always include the executable ID as part of the hash */ |
| 104 | md5sum_step_text("exe-id: ", -1); |
| 105 | md5sum_step_text(fossil_exe_id(), -1); |
| 106 | md5sum_step_text("\n", 1); |
| 107 | |
| 108 | if( (eFlags & ETAG_HASH)!=0 && zHash ){ |
| 109 | md5sum_step_text("hash: ", -1); |
| 110 | md5sum_step_text(zHash, -1); |
| 111 | md5sum_step_text("\n", 1); |
| @@ -98,11 +118,11 @@ | |
| 118 | md5sum_step_text("\n", 1); |
| 119 | iMaxAge = 60; |
| 120 | }else if( eFlags & ETAG_CONFIG ){ |
| 121 | int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'"); |
| 122 | sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey); |
| 123 | md5sum_step_text("config: ", -1); |
| 124 | md5sum_step_text(zBuf, -1); |
| 125 | md5sum_step_text("\n", 1); |
| 126 | iMaxAge = 3600; |
| 127 | } |
| 128 | |
| @@ -111,10 +131,27 @@ | |
| 131 | md5sum_step_text("display-cookie: ", -1); |
| 132 | md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1); |
| 133 | md5sum_step_text("\n", 1); |
| 134 | iMaxAge = 0; |
| 135 | } |
| 136 | |
| 137 | /* Output depends on PATH_INFO and QUERY_STRING */ |
| 138 | if( eFlags & ETAG_QUERY ){ |
| 139 | const char *zQS = P("QUERY_STRING"); |
| 140 | md5sum_step_text("query: ", -1); |
| 141 | md5sum_step_text(PD("PATH_INFO",""), -1); |
| 142 | if( zQS ){ |
| 143 | md5sum_step_text("?", 1); |
| 144 | md5sum_step_text(zQS, -1); |
| 145 | } |
| 146 | md5sum_step_text("\n",1); |
| 147 | if( g.zLogin ){ |
| 148 | md5sum_step_text("login: ", -1); |
| 149 | md5sum_step_text(g.zLogin, -1); |
| 150 | md5sum_step_text("\n", 1); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | /* Generate the ETag */ |
| 155 | memcpy(zETag, md5sum_finish(0), 33); |
| 156 | |
| 157 | /* Check to see if the generated ETag matches If-None-Match and |
| 158 |
+101
-1
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -294,11 +294,11 @@ | ||
| 294 | 294 | ** |
| 295 | 295 | ** On windows, this routine returns only PERM_REG. |
| 296 | 296 | */ |
| 297 | 297 | int file_perm(const char *zFilename, int eFType){ |
| 298 | 298 | #if !defined(_WIN32) |
| 299 | - if( !getStat(zFilename, RepoFILE) ){ | |
| 299 | + if( !getStat(zFilename, eFType) ){ | |
| 300 | 300 | if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 ) |
| 301 | 301 | return PERM_EXE; |
| 302 | 302 | else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) ) |
| 303 | 303 | return PERM_LNK; |
| 304 | 304 | } |
| @@ -346,10 +346,50 @@ | ||
| 346 | 346 | rc = 2; /* It exists and is something else. */ |
| 347 | 347 | } |
| 348 | 348 | free(zFN); |
| 349 | 349 | return rc; |
| 350 | 350 | } |
| 351 | + | |
| 352 | +/* | |
| 353 | +** Return true (1) if zFilename seems like it seems like a valid | |
| 354 | +** repository database. | |
| 355 | +*/ | |
| 356 | +int file_is_repository(const char *zFilename){ | |
| 357 | + i64 sz; | |
| 358 | + sqlite3 *db = 0; | |
| 359 | + sqlite3_stmt *pStmt = 0; | |
| 360 | + int rc; | |
| 361 | + int i; | |
| 362 | + static const char *azReqTab[] = { | |
| 363 | + "blob", "delta", "rcvfrom", "user", "config" | |
| 364 | + }; | |
| 365 | + if( !file_isfile(zFilename, ExtFILE) ) return 0; | |
| 366 | + sz = file_size(zFilename, ExtFILE); | |
| 367 | + if( sz<35328 ) return 0; | |
| 368 | + if( sz%512!=0 ) return 0; | |
| 369 | + rc = sqlite3_open_v2(zFilename, &db, | |
| 370 | + SQLITE_OPEN_READWRITE, 0); | |
| 371 | + if( rc!=0 ) goto not_a_repo; | |
| 372 | + for(i=0; i<count(azReqTab); i++){ | |
| 373 | + if( sqlite3_table_column_metadata(db, "main", azReqTab[i],0,0,0,0,0,0) ){ | |
| 374 | + goto not_a_repo; | |
| 375 | + } | |
| 376 | + } | |
| 377 | + rc = sqlite3_prepare_v2(db, "SELECT 1 FROM config WHERE name='project-code'", | |
| 378 | + -1, &pStmt, 0); | |
| 379 | + if( rc ) goto not_a_repo; | |
| 380 | + rc = sqlite3_step(pStmt); | |
| 381 | + if( rc!=SQLITE_ROW ) goto not_a_repo; | |
| 382 | + sqlite3_finalize(pStmt); | |
| 383 | + sqlite3_close(db); | |
| 384 | + return 1; | |
| 385 | + | |
| 386 | +not_a_repo: | |
| 387 | + sqlite3_finalize(pStmt); | |
| 388 | + sqlite3_close(db); | |
| 389 | + return 0; | |
| 390 | +} | |
| 351 | 391 | |
| 352 | 392 | |
| 353 | 393 | /* |
| 354 | 394 | ** Wrapper around the access() system call. |
| 355 | 395 | */ |
| @@ -1099,10 +1139,69 @@ | ||
| 1099 | 1139 | } |
| 1100 | 1140 | #endif |
| 1101 | 1141 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1102 | 1142 | blob_size(pOut), slash)); |
| 1103 | 1143 | } |
| 1144 | + | |
| 1145 | +/* | |
| 1146 | +** The input is the name of an executable, such as one might | |
| 1147 | +** type on a command-line. This routine resolves that name into | |
| 1148 | +** a full pathname. The result is obtained from fossil_malloc() | |
| 1149 | +** and should be freed by the caller. | |
| 1150 | +** | |
| 1151 | +** This routine only works on unix. On Windows, simply return | |
| 1152 | +** a copy of the input. | |
| 1153 | +*/ | |
| 1154 | +char *file_fullexename(const char *zCmd){ | |
| 1155 | +#ifdef _WIN32 | |
| 1156 | + return fossil_strdup(zCmd); | |
| 1157 | +#else | |
| 1158 | + char *zPath; | |
| 1159 | + char *z; | |
| 1160 | + if( zCmd[0]=='/' ){ | |
| 1161 | + return fossil_strdup(zCmd); | |
| 1162 | + } | |
| 1163 | + if( strchr(zCmd,'/')!=0 ){ | |
| 1164 | + Blob out = BLOB_INITIALIZER; | |
| 1165 | + file_canonical_name(zCmd, &out, 0); | |
| 1166 | + z = fossil_strdup(blob_str(&out)); | |
| 1167 | + blob_reset(&out); | |
| 1168 | + return z; | |
| 1169 | + } | |
| 1170 | + zPath = fossil_getenv("PATH"); | |
| 1171 | + while( zPath && zPath[0] ){ | |
| 1172 | + int n; | |
| 1173 | + char *zColon; | |
| 1174 | + zColon = strchr(zPath, ':'); | |
| 1175 | + n = zColon ? (int)(zColon-zPath) : (int)strlen(zPath); | |
| 1176 | + z = mprintf("%.*s/%s", n, zPath, zCmd); | |
| 1177 | + if( file_isexe(z, ExtFILE) ){ | |
| 1178 | + return z; | |
| 1179 | + } | |
| 1180 | + fossil_free(z); | |
| 1181 | + if( zColon==0 ) break; | |
| 1182 | + zPath = zColon+1; | |
| 1183 | + } | |
| 1184 | + return fossil_strdup(zCmd); | |
| 1185 | +#endif | |
| 1186 | +} | |
| 1187 | + | |
| 1188 | +/* | |
| 1189 | +** COMMAND: test-which | |
| 1190 | +** | |
| 1191 | +** Usage: %fossil test-which ARGS... | |
| 1192 | +** | |
| 1193 | +** For each argument, search the PATH for the executable with the name | |
| 1194 | +** and print its full pathname. | |
| 1195 | +*/ | |
| 1196 | +void test_which_cmd(void){ | |
| 1197 | + int i; | |
| 1198 | + for(i=2; i<g.argc; i++){ | |
| 1199 | + char *z = file_fullexename(g.argv[i]); | |
| 1200 | + fossil_print("%z\n", z); | |
| 1201 | + } | |
| 1202 | +} | |
| 1104 | 1203 | |
| 1105 | 1204 | /* |
| 1106 | 1205 | ** Emits the effective or raw stat() information for the specified |
| 1107 | 1206 | ** file or directory, optionally preserving the trailing slash and |
| 1108 | 1207 | ** resetting the cached stat() information. |
| @@ -1167,10 +1266,11 @@ | ||
| 1167 | 1266 | fossil_print(" file_isfile(RepoFILE) = %d\n", file_isfile(zPath,RepoFILE)); |
| 1168 | 1267 | fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath)); |
| 1169 | 1268 | fossil_print(" file_islink = %d\n", file_islink(zPath)); |
| 1170 | 1269 | fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE)); |
| 1171 | 1270 | fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE)); |
| 1271 | + fossil_print(" file_is_repository = %d\n", file_is_repository(zPath)); | |
| 1172 | 1272 | if( reset ) resetStat(); |
| 1173 | 1273 | } |
| 1174 | 1274 | |
| 1175 | 1275 | /* |
| 1176 | 1276 | ** COMMAND: test-file-environment |
| 1177 | 1277 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | ** |
| 295 | ** On windows, this routine returns only PERM_REG. |
| 296 | */ |
| 297 | int file_perm(const char *zFilename, int eFType){ |
| 298 | #if !defined(_WIN32) |
| 299 | if( !getStat(zFilename, RepoFILE) ){ |
| 300 | if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 ) |
| 301 | return PERM_EXE; |
| 302 | else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) ) |
| 303 | return PERM_LNK; |
| 304 | } |
| @@ -346,10 +346,50 @@ | |
| 346 | rc = 2; /* It exists and is something else. */ |
| 347 | } |
| 348 | free(zFN); |
| 349 | return rc; |
| 350 | } |
| 351 | |
| 352 | |
| 353 | /* |
| 354 | ** Wrapper around the access() system call. |
| 355 | */ |
| @@ -1099,10 +1139,69 @@ | |
| 1099 | } |
| 1100 | #endif |
| 1101 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1102 | blob_size(pOut), slash)); |
| 1103 | } |
| 1104 | |
| 1105 | /* |
| 1106 | ** Emits the effective or raw stat() information for the specified |
| 1107 | ** file or directory, optionally preserving the trailing slash and |
| 1108 | ** resetting the cached stat() information. |
| @@ -1167,10 +1266,11 @@ | |
| 1167 | fossil_print(" file_isfile(RepoFILE) = %d\n", file_isfile(zPath,RepoFILE)); |
| 1168 | fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath)); |
| 1169 | fossil_print(" file_islink = %d\n", file_islink(zPath)); |
| 1170 | fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE)); |
| 1171 | fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE)); |
| 1172 | if( reset ) resetStat(); |
| 1173 | } |
| 1174 | |
| 1175 | /* |
| 1176 | ** COMMAND: test-file-environment |
| 1177 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | ** |
| 295 | ** On windows, this routine returns only PERM_REG. |
| 296 | */ |
| 297 | int file_perm(const char *zFilename, int eFType){ |
| 298 | #if !defined(_WIN32) |
| 299 | if( !getStat(zFilename, eFType) ){ |
| 300 | if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 ) |
| 301 | return PERM_EXE; |
| 302 | else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) ) |
| 303 | return PERM_LNK; |
| 304 | } |
| @@ -346,10 +346,50 @@ | |
| 346 | rc = 2; /* It exists and is something else. */ |
| 347 | } |
| 348 | free(zFN); |
| 349 | return rc; |
| 350 | } |
| 351 | |
| 352 | /* |
| 353 | ** Return true (1) if zFilename seems like it seems like a valid |
| 354 | ** repository database. |
| 355 | */ |
| 356 | int file_is_repository(const char *zFilename){ |
| 357 | i64 sz; |
| 358 | sqlite3 *db = 0; |
| 359 | sqlite3_stmt *pStmt = 0; |
| 360 | int rc; |
| 361 | int i; |
| 362 | static const char *azReqTab[] = { |
| 363 | "blob", "delta", "rcvfrom", "user", "config" |
| 364 | }; |
| 365 | if( !file_isfile(zFilename, ExtFILE) ) return 0; |
| 366 | sz = file_size(zFilename, ExtFILE); |
| 367 | if( sz<35328 ) return 0; |
| 368 | if( sz%512!=0 ) return 0; |
| 369 | rc = sqlite3_open_v2(zFilename, &db, |
| 370 | SQLITE_OPEN_READWRITE, 0); |
| 371 | if( rc!=0 ) goto not_a_repo; |
| 372 | for(i=0; i<count(azReqTab); i++){ |
| 373 | if( sqlite3_table_column_metadata(db, "main", azReqTab[i],0,0,0,0,0,0) ){ |
| 374 | goto not_a_repo; |
| 375 | } |
| 376 | } |
| 377 | rc = sqlite3_prepare_v2(db, "SELECT 1 FROM config WHERE name='project-code'", |
| 378 | -1, &pStmt, 0); |
| 379 | if( rc ) goto not_a_repo; |
| 380 | rc = sqlite3_step(pStmt); |
| 381 | if( rc!=SQLITE_ROW ) goto not_a_repo; |
| 382 | sqlite3_finalize(pStmt); |
| 383 | sqlite3_close(db); |
| 384 | return 1; |
| 385 | |
| 386 | not_a_repo: |
| 387 | sqlite3_finalize(pStmt); |
| 388 | sqlite3_close(db); |
| 389 | return 0; |
| 390 | } |
| 391 | |
| 392 | |
| 393 | /* |
| 394 | ** Wrapper around the access() system call. |
| 395 | */ |
| @@ -1099,10 +1139,69 @@ | |
| 1139 | } |
| 1140 | #endif |
| 1141 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1142 | blob_size(pOut), slash)); |
| 1143 | } |
| 1144 | |
| 1145 | /* |
| 1146 | ** The input is the name of an executable, such as one might |
| 1147 | ** type on a command-line. This routine resolves that name into |
| 1148 | ** a full pathname. The result is obtained from fossil_malloc() |
| 1149 | ** and should be freed by the caller. |
| 1150 | ** |
| 1151 | ** This routine only works on unix. On Windows, simply return |
| 1152 | ** a copy of the input. |
| 1153 | */ |
| 1154 | char *file_fullexename(const char *zCmd){ |
| 1155 | #ifdef _WIN32 |
| 1156 | return fossil_strdup(zCmd); |
| 1157 | #else |
| 1158 | char *zPath; |
| 1159 | char *z; |
| 1160 | if( zCmd[0]=='/' ){ |
| 1161 | return fossil_strdup(zCmd); |
| 1162 | } |
| 1163 | if( strchr(zCmd,'/')!=0 ){ |
| 1164 | Blob out = BLOB_INITIALIZER; |
| 1165 | file_canonical_name(zCmd, &out, 0); |
| 1166 | z = fossil_strdup(blob_str(&out)); |
| 1167 | blob_reset(&out); |
| 1168 | return z; |
| 1169 | } |
| 1170 | zPath = fossil_getenv("PATH"); |
| 1171 | while( zPath && zPath[0] ){ |
| 1172 | int n; |
| 1173 | char *zColon; |
| 1174 | zColon = strchr(zPath, ':'); |
| 1175 | n = zColon ? (int)(zColon-zPath) : (int)strlen(zPath); |
| 1176 | z = mprintf("%.*s/%s", n, zPath, zCmd); |
| 1177 | if( file_isexe(z, ExtFILE) ){ |
| 1178 | return z; |
| 1179 | } |
| 1180 | fossil_free(z); |
| 1181 | if( zColon==0 ) break; |
| 1182 | zPath = zColon+1; |
| 1183 | } |
| 1184 | return fossil_strdup(zCmd); |
| 1185 | #endif |
| 1186 | } |
| 1187 | |
| 1188 | /* |
| 1189 | ** COMMAND: test-which |
| 1190 | ** |
| 1191 | ** Usage: %fossil test-which ARGS... |
| 1192 | ** |
| 1193 | ** For each argument, search the PATH for the executable with the name |
| 1194 | ** and print its full pathname. |
| 1195 | */ |
| 1196 | void test_which_cmd(void){ |
| 1197 | int i; |
| 1198 | for(i=2; i<g.argc; i++){ |
| 1199 | char *z = file_fullexename(g.argv[i]); |
| 1200 | fossil_print("%z\n", z); |
| 1201 | } |
| 1202 | } |
| 1203 | |
| 1204 | /* |
| 1205 | ** Emits the effective or raw stat() information for the specified |
| 1206 | ** file or directory, optionally preserving the trailing slash and |
| 1207 | ** resetting the cached stat() information. |
| @@ -1167,10 +1266,11 @@ | |
| 1266 | fossil_print(" file_isfile(RepoFILE) = %d\n", file_isfile(zPath,RepoFILE)); |
| 1267 | fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath)); |
| 1268 | fossil_print(" file_islink = %d\n", file_islink(zPath)); |
| 1269 | fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE)); |
| 1270 | fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE)); |
| 1271 | fossil_print(" file_is_repository = %d\n", file_is_repository(zPath)); |
| 1272 | if( reset ) resetStat(); |
| 1273 | } |
| 1274 | |
| 1275 | /* |
| 1276 | ** COMMAND: test-file-environment |
| 1277 |
+15
-11
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | TAG_BRANCH, zFilename, filename_collation(), |
| 200 | 200 | iLimit, iOffset |
| 201 | 201 | ); |
| 202 | 202 | blob_zero(&line); |
| 203 | 203 | if( iBrief ){ |
| 204 | - fossil_print("History of %s\n", blob_str(&fname)); | |
| 204 | + fossil_print("History for %s\n", blob_str(&fname)); | |
| 205 | 205 | } |
| 206 | 206 | while( db_step(&q)==SQLITE_ROW ){ |
| 207 | 207 | const char *zFileUuid = db_column_text(&q, 0); |
| 208 | 208 | const char *zCiUuid = db_column_text(&q,1); |
| 209 | 209 | const char *zDate = db_column_text(&q, 2); |
| @@ -296,11 +296,11 @@ | ||
| 296 | 296 | ** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM" |
| 297 | 297 | ** (eastward). Either no timezone suffix or "Z" means UTC. |
| 298 | 298 | */ |
| 299 | 299 | void finfo_page(void){ |
| 300 | 300 | Stmt q; |
| 301 | - const char *zFilename; | |
| 301 | + const char *zFilename = PD("name",""); | |
| 302 | 302 | char zPrevDate[20]; |
| 303 | 303 | const char *zA; |
| 304 | 304 | const char *zB; |
| 305 | 305 | int n; |
| 306 | 306 | int baseCheckin; |
| @@ -321,11 +321,16 @@ | ||
| 321 | 321 | const char *zMark; /* Mark this version of the file */ |
| 322 | 322 | int selRid = 0; /* RID of the marked file version */ |
| 323 | 323 | |
| 324 | 324 | login_check_credentials(); |
| 325 | 325 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 326 | - style_header("File History"); | |
| 326 | + fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); | |
| 327 | + if( fnid==0 ){ | |
| 328 | + style_header("No such file"); | |
| 329 | + }else{ | |
| 330 | + style_header("History for %s", zFilename); | |
| 331 | + } | |
| 327 | 332 | login_anonymous_available(); |
| 328 | 333 | tmFlags = timeline_ss_submenu(); |
| 329 | 334 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| 330 | 335 | zStyle = "Columnar"; |
| 331 | 336 | }else if( tmFlags & TIMELINE_COMPACT ){ |
| @@ -340,13 +345,11 @@ | ||
| 340 | 345 | url_initialize(&url, "finfo"); |
| 341 | 346 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 342 | 347 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 343 | 348 | baseCheckin = name_to_rid_www("ci"); |
| 344 | 349 | zPrevDate[0] = 0; |
| 345 | - zFilename = PD("name",""); | |
| 346 | 350 | cookie_render(); |
| 347 | - fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); | |
| 348 | 351 | if( fnid==0 ){ |
| 349 | 352 | @ No such file: %h(zFilename) |
| 350 | 353 | style_footer(); |
| 351 | 354 | return; |
| 352 | 355 | } |
| @@ -435,12 +438,13 @@ | ||
| 435 | 438 | }else if( n>0 ){ |
| 436 | 439 | blob_appendf(&title, "First %d ancestors of file ", n); |
| 437 | 440 | }else{ |
| 438 | 441 | blob_appendf(&title, "Ancestors of file "); |
| 439 | 442 | } |
| 440 | - blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>", | |
| 441 | - zFilename, zFilename); | |
| 443 | + blob_appendf(&title,"%z%h</a>", | |
| 444 | + href("%R/file?name=%T&ci=%!S", zFilename, zUuid), | |
| 445 | + zFilename); | |
| 442 | 446 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 443 | 447 | blob_append(&title, origCheckin ? " between " : " from ", -1); |
| 444 | 448 | blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid); |
| 445 | 449 | if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); |
| 446 | 450 | fossil_free(zUuid); |
| @@ -449,12 +453,12 @@ | ||
| 449 | 453 | zLink = href("%R/info/%!S", zUuid); |
| 450 | 454 | blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid); |
| 451 | 455 | fossil_free(zUuid); |
| 452 | 456 | } |
| 453 | 457 | }else{ |
| 454 | - blob_appendf(&title, "History of "); | |
| 455 | - hyperlinked_path(zFilename, &title, 0, "tree", ""); | |
| 458 | + blob_appendf(&title, "History for "); | |
| 459 | + hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE); | |
| 456 | 460 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 457 | 461 | } |
| 458 | 462 | if( uBg ){ |
| 459 | 463 | blob_append(&title, " (color-coded by user)", -1); |
| 460 | 464 | } |
| @@ -524,11 +528,11 @@ | ||
| 524 | 528 | @ <tr class='timelineSelected'> |
| 525 | 529 | }else{ |
| 526 | 530 | @ <tr> |
| 527 | 531 | } |
| 528 | 532 | @ <td class="timelineTime">\ |
| 529 | - @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td> | |
| 533 | + @ %z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))%s(zTime)</a></td> | |
| 530 | 534 | @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div> |
| 531 | 535 | @ </td> |
| 532 | 536 | if( zBgClr && zBgClr[0] ){ |
| 533 | 537 | @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'> |
| 534 | 538 | }else{ |
| @@ -561,11 +565,11 @@ | ||
| 561 | 565 | cgi_printf("<span class='clutter' id='detail-%d'>",frid); |
| 562 | 566 | } |
| 563 | 567 | cgi_printf("<span class='timeline%sDetail'>", zStyle); |
| 564 | 568 | if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("("); |
| 565 | 569 | if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){ |
| 566 | - @ file: %z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a> | |
| 570 | + @ file: %z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))[%S(zUuid)]</a> | |
| 567 | 571 | if( fShowId ){ |
| 568 | 572 | int srcId = delta_source_rid(frid); |
| 569 | 573 | if( srcId>0 ){ |
| 570 | 574 | @ id: %d(frid)←%d(srcId) |
| 571 | 575 | }else{ |
| 572 | 576 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | TAG_BRANCH, zFilename, filename_collation(), |
| 200 | iLimit, iOffset |
| 201 | ); |
| 202 | blob_zero(&line); |
| 203 | if( iBrief ){ |
| 204 | fossil_print("History of %s\n", blob_str(&fname)); |
| 205 | } |
| 206 | while( db_step(&q)==SQLITE_ROW ){ |
| 207 | const char *zFileUuid = db_column_text(&q, 0); |
| 208 | const char *zCiUuid = db_column_text(&q,1); |
| 209 | const char *zDate = db_column_text(&q, 2); |
| @@ -296,11 +296,11 @@ | |
| 296 | ** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM" |
| 297 | ** (eastward). Either no timezone suffix or "Z" means UTC. |
| 298 | */ |
| 299 | void finfo_page(void){ |
| 300 | Stmt q; |
| 301 | const char *zFilename; |
| 302 | char zPrevDate[20]; |
| 303 | const char *zA; |
| 304 | const char *zB; |
| 305 | int n; |
| 306 | int baseCheckin; |
| @@ -321,11 +321,16 @@ | |
| 321 | const char *zMark; /* Mark this version of the file */ |
| 322 | int selRid = 0; /* RID of the marked file version */ |
| 323 | |
| 324 | login_check_credentials(); |
| 325 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 326 | style_header("File History"); |
| 327 | login_anonymous_available(); |
| 328 | tmFlags = timeline_ss_submenu(); |
| 329 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| 330 | zStyle = "Columnar"; |
| 331 | }else if( tmFlags & TIMELINE_COMPACT ){ |
| @@ -340,13 +345,11 @@ | |
| 340 | url_initialize(&url, "finfo"); |
| 341 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 342 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 343 | baseCheckin = name_to_rid_www("ci"); |
| 344 | zPrevDate[0] = 0; |
| 345 | zFilename = PD("name",""); |
| 346 | cookie_render(); |
| 347 | fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); |
| 348 | if( fnid==0 ){ |
| 349 | @ No such file: %h(zFilename) |
| 350 | style_footer(); |
| 351 | return; |
| 352 | } |
| @@ -435,12 +438,13 @@ | |
| 435 | }else if( n>0 ){ |
| 436 | blob_appendf(&title, "First %d ancestors of file ", n); |
| 437 | }else{ |
| 438 | blob_appendf(&title, "Ancestors of file "); |
| 439 | } |
| 440 | blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>", |
| 441 | zFilename, zFilename); |
| 442 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 443 | blob_append(&title, origCheckin ? " between " : " from ", -1); |
| 444 | blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid); |
| 445 | if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); |
| 446 | fossil_free(zUuid); |
| @@ -449,12 +453,12 @@ | |
| 449 | zLink = href("%R/info/%!S", zUuid); |
| 450 | blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid); |
| 451 | fossil_free(zUuid); |
| 452 | } |
| 453 | }else{ |
| 454 | blob_appendf(&title, "History of "); |
| 455 | hyperlinked_path(zFilename, &title, 0, "tree", ""); |
| 456 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 457 | } |
| 458 | if( uBg ){ |
| 459 | blob_append(&title, " (color-coded by user)", -1); |
| 460 | } |
| @@ -524,11 +528,11 @@ | |
| 524 | @ <tr class='timelineSelected'> |
| 525 | }else{ |
| 526 | @ <tr> |
| 527 | } |
| 528 | @ <td class="timelineTime">\ |
| 529 | @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td> |
| 530 | @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div> |
| 531 | @ </td> |
| 532 | if( zBgClr && zBgClr[0] ){ |
| 533 | @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'> |
| 534 | }else{ |
| @@ -561,11 +565,11 @@ | |
| 561 | cgi_printf("<span class='clutter' id='detail-%d'>",frid); |
| 562 | } |
| 563 | cgi_printf("<span class='timeline%sDetail'>", zStyle); |
| 564 | if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("("); |
| 565 | if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){ |
| 566 | @ file: %z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a> |
| 567 | if( fShowId ){ |
| 568 | int srcId = delta_source_rid(frid); |
| 569 | if( srcId>0 ){ |
| 570 | @ id: %d(frid)←%d(srcId) |
| 571 | }else{ |
| 572 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | TAG_BRANCH, zFilename, filename_collation(), |
| 200 | iLimit, iOffset |
| 201 | ); |
| 202 | blob_zero(&line); |
| 203 | if( iBrief ){ |
| 204 | fossil_print("History for %s\n", blob_str(&fname)); |
| 205 | } |
| 206 | while( db_step(&q)==SQLITE_ROW ){ |
| 207 | const char *zFileUuid = db_column_text(&q, 0); |
| 208 | const char *zCiUuid = db_column_text(&q,1); |
| 209 | const char *zDate = db_column_text(&q, 2); |
| @@ -296,11 +296,11 @@ | |
| 296 | ** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM" |
| 297 | ** (eastward). Either no timezone suffix or "Z" means UTC. |
| 298 | */ |
| 299 | void finfo_page(void){ |
| 300 | Stmt q; |
| 301 | const char *zFilename = PD("name",""); |
| 302 | char zPrevDate[20]; |
| 303 | const char *zA; |
| 304 | const char *zB; |
| 305 | int n; |
| 306 | int baseCheckin; |
| @@ -321,11 +321,16 @@ | |
| 321 | const char *zMark; /* Mark this version of the file */ |
| 322 | int selRid = 0; /* RID of the marked file version */ |
| 323 | |
| 324 | login_check_credentials(); |
| 325 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 326 | fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); |
| 327 | if( fnid==0 ){ |
| 328 | style_header("No such file"); |
| 329 | }else{ |
| 330 | style_header("History for %s", zFilename); |
| 331 | } |
| 332 | login_anonymous_available(); |
| 333 | tmFlags = timeline_ss_submenu(); |
| 334 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| 335 | zStyle = "Columnar"; |
| 336 | }else if( tmFlags & TIMELINE_COMPACT ){ |
| @@ -340,13 +345,11 @@ | |
| 345 | url_initialize(&url, "finfo"); |
| 346 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 347 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 348 | baseCheckin = name_to_rid_www("ci"); |
| 349 | zPrevDate[0] = 0; |
| 350 | cookie_render(); |
| 351 | if( fnid==0 ){ |
| 352 | @ No such file: %h(zFilename) |
| 353 | style_footer(); |
| 354 | return; |
| 355 | } |
| @@ -435,12 +438,13 @@ | |
| 438 | }else if( n>0 ){ |
| 439 | blob_appendf(&title, "First %d ancestors of file ", n); |
| 440 | }else{ |
| 441 | blob_appendf(&title, "Ancestors of file "); |
| 442 | } |
| 443 | blob_appendf(&title,"%z%h</a>", |
| 444 | href("%R/file?name=%T&ci=%!S", zFilename, zUuid), |
| 445 | zFilename); |
| 446 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 447 | blob_append(&title, origCheckin ? " between " : " from ", -1); |
| 448 | blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid); |
| 449 | if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); |
| 450 | fossil_free(zUuid); |
| @@ -449,12 +453,12 @@ | |
| 453 | zLink = href("%R/info/%!S", zUuid); |
| 454 | blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid); |
| 455 | fossil_free(zUuid); |
| 456 | } |
| 457 | }else{ |
| 458 | blob_appendf(&title, "History for "); |
| 459 | hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE); |
| 460 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 461 | } |
| 462 | if( uBg ){ |
| 463 | blob_append(&title, " (color-coded by user)", -1); |
| 464 | } |
| @@ -524,11 +528,11 @@ | |
| 528 | @ <tr class='timelineSelected'> |
| 529 | }else{ |
| 530 | @ <tr> |
| 531 | } |
| 532 | @ <td class="timelineTime">\ |
| 533 | @ %z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))%s(zTime)</a></td> |
| 534 | @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div> |
| 535 | @ </td> |
| 536 | if( zBgClr && zBgClr[0] ){ |
| 537 | @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'> |
| 538 | }else{ |
| @@ -561,11 +565,11 @@ | |
| 565 | cgi_printf("<span class='clutter' id='detail-%d'>",frid); |
| 566 | } |
| 567 | cgi_printf("<span class='timeline%sDetail'>", zStyle); |
| 568 | if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("("); |
| 569 | if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){ |
| 570 | @ file: %z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))[%S(zUuid)]</a> |
| 571 | if( fShowId ){ |
| 572 | int srcId = delta_source_rid(frid); |
| 573 | if( srcId>0 ){ |
| 574 | @ id: %d(frid)←%d(srcId) |
| 575 | }else{ |
| 576 |
+172
-19
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -156,10 +156,11 @@ | ||
| 156 | 156 | static ForumThread *forumthread_create(int froot, int computeHierarchy){ |
| 157 | 157 | ForumThread *pThread; |
| 158 | 158 | ForumEntry *pEntry; |
| 159 | 159 | Stmt q; |
| 160 | 160 | int sid = 1; |
| 161 | + Bag seen = Bag_INIT; | |
| 161 | 162 | pThread = fossil_malloc( sizeof(*pThread) ); |
| 162 | 163 | memset(pThread, 0, sizeof(*pThread)); |
| 163 | 164 | db_prepare(&q, |
| 164 | 165 | "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)" |
| 165 | 166 | " FROM forumpost" |
| @@ -175,18 +176,24 @@ | ||
| 175 | 176 | pEntry->zUuid = fossil_strdup(db_column_text(&q,3)); |
| 176 | 177 | pEntry->mfirt = pEntry->firt; |
| 177 | 178 | pEntry->sid = sid++; |
| 178 | 179 | pEntry->pPrev = pThread->pLast; |
| 179 | 180 | pEntry->pNext = 0; |
| 181 | + bag_insert(&seen, pEntry->fpid); | |
| 180 | 182 | if( pThread->pLast==0 ){ |
| 181 | 183 | pThread->pFirst = pEntry; |
| 182 | 184 | }else{ |
| 183 | 185 | pThread->pLast->pNext = pEntry; |
| 184 | 186 | } |
| 187 | + if( pEntry->firt && !bag_find(&seen,pEntry->firt) ){ | |
| 188 | + pEntry->firt = froot; | |
| 189 | + pEntry->mfirt = froot; | |
| 190 | + } | |
| 185 | 191 | pThread->pLast = pEntry; |
| 186 | 192 | } |
| 187 | 193 | db_finalize(&q); |
| 194 | + bag_clear(&seen); | |
| 188 | 195 | |
| 189 | 196 | /* Establish which entries are the latest edit. After this loop |
| 190 | 197 | ** completes, entries that have non-NULL pLeaf should not be |
| 191 | 198 | ** displayed. |
| 192 | 199 | */ |
| @@ -283,16 +290,18 @@ | ||
| 283 | 290 | } |
| 284 | 291 | fossil_print("fpid = %d\n", fpid); |
| 285 | 292 | fossil_print("froot = %d\n", froot); |
| 286 | 293 | pThread = forumthread_create(froot, 1); |
| 287 | 294 | fossil_print("Chronological:\n"); |
| 288 | - /* 123456789 123456789 123456789 123456789 123456789 123456789 */ | |
| 289 | - fossil_print(" fpid firt fprev mfirt pLeaf nReply\n"); | |
| 295 | + fossil_print( | |
| 296 | +/* 0 1 2 3 4 5 6 7 */ | |
| 297 | +/* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */ | |
| 298 | + " sid fpid firt fprev mfirt pLeaf nReply hash\n"); | |
| 290 | 299 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 291 | - fossil_print("%9d %9d %9d %9d %9d %9d\n", | |
| 300 | + fossil_print("%4d %9d %9d %9d %9d %9d %6d %8.8s\n", p->sid, | |
| 292 | 301 | p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0, |
| 293 | - p->nReply); | |
| 302 | + p->nReply, p->zUuid); | |
| 294 | 303 | } |
| 295 | 304 | fossil_print("\nDisplay\n"); |
| 296 | 305 | for(p=pThread->pDisplay; p; p=p->pDisplay){ |
| 297 | 306 | fossil_print("%*s", (p->nIndent-1)*3, ""); |
| 298 | 307 | if( p->pLeaf ){ |
| @@ -408,10 +417,11 @@ | ||
| 408 | 417 | Manifest *pPost; |
| 409 | 418 | int isPrivate; /* True for posts awaiting moderation */ |
| 410 | 419 | int sameUser; /* True if author is also the reader */ |
| 411 | 420 | const char *zUuid; |
| 412 | 421 | char *zDisplayName; /* The display name */ |
| 422 | + int sid; | |
| 413 | 423 | |
| 414 | 424 | pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); |
| 415 | 425 | if( pPost==0 ) continue; |
| 416 | 426 | if( p->fpid==target ){ |
| 417 | 427 | @ <div id="forum%d(p->fpid)" class="forumTime forumSel"> |
| @@ -423,20 +433,21 @@ | ||
| 423 | 433 | if( pPost->zThreadTitle ){ |
| 424 | 434 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 425 | 435 | } |
| 426 | 436 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 427 | 437 | zDisplayName = display_name_from_login(pPost->zUser); |
| 428 | - @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate) | |
| 438 | + sid = p->pEdit ? p->pEdit->sid : p->sid; | |
| 439 | + @ <h3 class='forumPostHdr'>(%d(sid)) By %h(zDisplayName) on %h(zDate) | |
| 429 | 440 | fossil_free(zDisplayName); |
| 430 | 441 | fossil_free(zDate); |
| 431 | 442 | if( p->pEdit ){ |
| 432 | 443 | @ edit of %z(href("%R/forumpost/%S?t=%c",p->pEdit->zUuid,cMode))\ |
| 433 | 444 | @ %d(p->pEdit->sid)</a> |
| 434 | 445 | } |
| 435 | 446 | if( g.perm.Debug ){ |
| 436 | 447 | @ <span class="debug">\ |
| 437 | - @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span> | |
| 448 | + @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> | |
| 438 | 449 | } |
| 439 | 450 | if( p->firt ){ |
| 440 | 451 | ForumEntry *pIrt = p->pPrev; |
| 441 | 452 | while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; |
| 442 | 453 | if( pIrt ){ |
| @@ -451,10 +462,125 @@ | ||
| 451 | 462 | zUuid = p->pLeaf->zUuid; |
| 452 | 463 | } |
| 453 | 464 | if( p->fpid!=target ){ |
| 454 | 465 | @ %z(href("%R/forumpost/%S?t=%c",zUuid,cMode))[link]</a> |
| 455 | 466 | } |
| 467 | + if( !bRawMode ){ | |
| 468 | + @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> | |
| 469 | + } | |
| 470 | + isPrivate = content_is_private(p->fpid); | |
| 471 | + sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; | |
| 472 | + @ </h3> | |
| 473 | + if( isPrivate && !g.perm.ModForum && !sameUser ){ | |
| 474 | + @ <p><span class="modpending">Awaiting Moderator Approval</span></p> | |
| 475 | + }else{ | |
| 476 | + const char *zMimetype; | |
| 477 | + if( bRawMode ){ | |
| 478 | + zMimetype = "text/plain"; | |
| 479 | + }else if( p->pLeaf!=0 ){ | |
| 480 | + zMimetype = "text/plain"; | |
| 481 | + }else{ | |
| 482 | + zMimetype = pPost->zMimetype; | |
| 483 | + } | |
| 484 | + forum_render(0, zMimetype, pPost->zWiki, 0, 1); | |
| 485 | + } | |
| 486 | + if( g.perm.WrForum && p->pLeaf==0 ){ | |
| 487 | + int sameUser = login_is_individual() | |
| 488 | + && fossil_strcmp(pPost->zUser, g.zLogin)==0; | |
| 489 | + @ <p><form action="%R/forumedit" method="POST"> | |
| 490 | + @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> | |
| 491 | + if( !isPrivate ){ | |
| 492 | + /* Reply and Edit are only available if the post has already | |
| 493 | + ** been approved */ | |
| 494 | + @ <input type="submit" name="reply" value="Reply"> | |
| 495 | + if( g.perm.Admin || sameUser ){ | |
| 496 | + @ <input type="submit" name="edit" value="Edit"> | |
| 497 | + @ <input type="submit" name="nullout" value="Delete"> | |
| 498 | + } | |
| 499 | + }else if( g.perm.ModForum ){ | |
| 500 | + /* Provide moderators with moderation buttons for posts that | |
| 501 | + ** are pending moderation */ | |
| 502 | + @ <input type="submit" name="approve" value="Approve"> | |
| 503 | + @ <input type="submit" name="reject" value="Reject"> | |
| 504 | + generateTrustControls(pPost); | |
| 505 | + }else if( sameUser ){ | |
| 506 | + /* A post that is pending moderation can be deleted by the | |
| 507 | + ** person who originally submitted the post */ | |
| 508 | + @ <input type="submit" name="reject" value="Delete"> | |
| 509 | + } | |
| 510 | + @ </form></p> | |
| 511 | + } | |
| 512 | + manifest_destroy(pPost); | |
| 513 | + @ </div> | |
| 514 | + } | |
| 515 | + | |
| 516 | + /* Undocumented "threadtable" query parameter causes thread table | |
| 517 | + ** to be displayed for debugging purposes. | |
| 518 | + */ | |
| 519 | + if( PB("threadtable") ){ | |
| 520 | + @ <hr> | |
| 521 | + @ <table border="1" cellpadding="3" cellspacing="0"> | |
| 522 | + @ <tr><th>sid<th>fpid<th>firt<th>fprev<th>mfirt<th>pLeaf<th>nReply<th>hash | |
| 523 | + for(p=pThread->pFirst; p; p=p->pNext){ | |
| 524 | + @ <tr><td>%d(p->sid)<td>%d(p->fpid)<td>%d(p->firt)\ | |
| 525 | + @ <td>%d(p->fprev)<td>%d(p->mfirt)\ | |
| 526 | + @ <td>%d(p->pLeaf?p->pLeaf->fpid:0)<td>%d(p->nReply)\ | |
| 527 | + @ <td>%S(p->zUuid)</tr> | |
| 528 | + } | |
| 529 | + @ </table> | |
| 530 | + } | |
| 531 | + | |
| 532 | + forumthread_delete(pThread); | |
| 533 | +} | |
| 534 | +/* | |
| 535 | +** Display all the edit history of post "target". | |
| 536 | +*/ | |
| 537 | +static void forum_display_history(int froot, int target, int bRawMode){ | |
| 538 | + ForumThread *pThread = forumthread_create(froot, 0); | |
| 539 | + ForumEntry *p; | |
| 540 | + int notAnon = login_is_individual(); | |
| 541 | + char cMode = bRawMode ? 'r' : 'c'; | |
| 542 | + ForumEntry *pLeaf = 0; | |
| 543 | + int cnt = 0; | |
| 544 | + for(p=pThread->pFirst; p; p=p->pNext){ | |
| 545 | + if( p->fpid==target ){ | |
| 546 | + pLeaf = p->pLeaf ? p->pLeaf : p; | |
| 547 | + break; | |
| 548 | + } | |
| 549 | + } | |
| 550 | + for(p=pThread->pFirst; p; p=p->pNext){ | |
| 551 | + char *zDate; | |
| 552 | + Manifest *pPost; | |
| 553 | + int isPrivate; /* True for posts awaiting moderation */ | |
| 554 | + int sameUser; /* True if author is also the reader */ | |
| 555 | + const char *zUuid; | |
| 556 | + char *zDisplayName; /* The display name */ | |
| 557 | + | |
| 558 | + if( p->fpid!=pLeaf->fpid && p->pLeaf!=pLeaf ) continue; | |
| 559 | + cnt++; | |
| 560 | + pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); | |
| 561 | + if( pPost==0 ) continue; | |
| 562 | + @ <div id="forum%d(p->fpid)" class="forumTime"> | |
| 563 | + zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); | |
| 564 | + zDisplayName = display_name_from_login(pPost->zUser); | |
| 565 | + @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate) | |
| 566 | + fossil_free(zDisplayName); | |
| 567 | + fossil_free(zDate); | |
| 568 | + if( g.perm.Debug ){ | |
| 569 | + @ <span class="debug">\ | |
| 570 | + @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> | |
| 571 | + } | |
| 572 | + if( p->firt && cnt==1 ){ | |
| 573 | + ForumEntry *pIrt = p->pPrev; | |
| 574 | + while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; | |
| 575 | + if( pIrt ){ | |
| 576 | + @ in reply to %z(href("%R/forumpost/%S?t=%c",pIrt->zUuid,cMode))\ | |
| 577 | + @ %d(pIrt->sid)</a> | |
| 578 | + } | |
| 579 | + } | |
| 580 | + zUuid = p->zUuid; | |
| 581 | + @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a> | |
| 456 | 582 | if( !bRawMode ){ |
| 457 | 583 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 458 | 584 | } |
| 459 | 585 | isPrivate = content_is_private(p->fpid); |
| 460 | 586 | sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| @@ -549,16 +675,16 @@ | ||
| 549 | 675 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 550 | 676 | } |
| 551 | 677 | zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate); |
| 552 | 678 | zDisplayName = display_name_from_login(pOPost->zUser); |
| 553 | 679 | @ <h3 class='forumPostHdr'>\ |
| 554 | - @ (%d(p->pLeaf?p->pLeaf->sid:p->sid)) By %h(zDisplayName) on %h(zDate) | |
| 680 | + @ (%d(p->sid)) By %h(zDisplayName) on %h(zDate) | |
| 555 | 681 | fossil_free(zDisplayName); |
| 556 | 682 | fossil_free(zDate); |
| 557 | 683 | if( g.perm.Debug ){ |
| 558 | 684 | @ <span class="debug">\ |
| 559 | - @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span> | |
| 685 | + @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> | |
| 560 | 686 | } |
| 561 | 687 | if( p->pLeaf ){ |
| 562 | 688 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 563 | 689 | if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){ |
| 564 | 690 | @ and edited on %h(zDate) |
| @@ -566,21 +692,23 @@ | ||
| 566 | 692 | @ as edited by %h(pPost->zUser) on %h(zDate) |
| 567 | 693 | } |
| 568 | 694 | fossil_free(zDate); |
| 569 | 695 | if( g.perm.Debug ){ |
| 570 | 696 | @ <span class="debug">\ |
| 571 | - @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(artifact)</a></span> | |
| 697 | + @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">\ | |
| 698 | + @ (artifact-%d(p->pLeaf->fpid))</a></span> | |
| 572 | 699 | } |
| 700 | + @ %z(href("%R/forumpost/%S?t=y",p->zUuid))[history]</a> | |
| 573 | 701 | manifest_destroy(pOPost); |
| 574 | 702 | } |
| 575 | 703 | if( fpid!=target ){ |
| 576 | 704 | @ %z(href("%R/forumpost/%S",zUuid))[link]</a> |
| 577 | 705 | } |
| 578 | 706 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 579 | 707 | if( p->firt ){ |
| 580 | 708 | ForumEntry *pIrt = p->pPrev; |
| 581 | - while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; | |
| 709 | + while( pIrt && pIrt->fpid!=p->mfirt ) pIrt = pIrt->pPrev; | |
| 582 | 710 | if( pIrt ){ |
| 583 | 711 | @ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\ |
| 584 | 712 | @ %d(pIrt->sid)</a> |
| 585 | 713 | } |
| 586 | 714 | } |
| @@ -637,10 +765,11 @@ | ||
| 637 | 765 | ** t=MODE Display mode. |
| 638 | 766 | ** 'c' for chronological |
| 639 | 767 | ** 'h' for hierarchical |
| 640 | 768 | ** 'a' for automatic |
| 641 | 769 | ** 'r' for raw |
| 770 | +** 'y' for history of post X only | |
| 642 | 771 | ** raw If present, show only the post specified and |
| 643 | 772 | ** show its original unformatted source text. |
| 644 | 773 | */ |
| 645 | 774 | void forumpost_page(void){ |
| 646 | 775 | forumthread_page(); |
| @@ -679,10 +808,12 @@ | ||
| 679 | 808 | ** t=MODE Display mode. MODE is... |
| 680 | 809 | ** 'c' for chronological, or |
| 681 | 810 | ** 'h' for hierarchical, or |
| 682 | 811 | ** 'a' for automatic, or |
| 683 | 812 | ** 'r' for raw. |
| 813 | +** raw Show only the post given by name= and show it unformatted | |
| 814 | +** hist Show only the edit history for the name= post | |
| 684 | 815 | */ |
| 685 | 816 | void forumthread_page(void){ |
| 686 | 817 | int fpid; |
| 687 | 818 | int froot; |
| 688 | 819 | const char *zName = P("name"); |
| @@ -710,11 +841,13 @@ | ||
| 710 | 841 | zMode = "c"; /* Default to chronological on mobile */ |
| 711 | 842 | }else{ |
| 712 | 843 | zMode = "h"; |
| 713 | 844 | } |
| 714 | 845 | } |
| 715 | - forumthread_page_header(froot, fpid); | |
| 846 | + if( zMode[0]!='y' ){ | |
| 847 | + forumthread_page_header(froot, fpid); | |
| 848 | + } | |
| 716 | 849 | if( bRaw && fpid ){ |
| 717 | 850 | Manifest *pPost; |
| 718 | 851 | pPost = manifest_get(fpid, CFTYPE_FORUM, 0); |
| 719 | 852 | if( pPost==0 ){ |
| 720 | 853 | @ <p>No such forum post: %h(zName) |
| @@ -735,10 +868,14 @@ | ||
| 735 | 868 | forum_display_chronological(froot, fpid, 0); |
| 736 | 869 | }else if( zMode[0]=='r' ){ |
| 737 | 870 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 738 | 871 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 739 | 872 | forum_display_chronological(froot, fpid, 1); |
| 873 | + }else if( zMode[0]=='y' ){ | |
| 874 | + style_header("Edit History Of A Forum Post"); | |
| 875 | + style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName); | |
| 876 | + forum_display_history(froot, fpid, 1); | |
| 740 | 877 | }else{ |
| 741 | 878 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 742 | 879 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 743 | 880 | forum_display_hierarchical(froot, fpid); |
| 744 | 881 | } |
| @@ -753,10 +890,19 @@ | ||
| 753 | 890 | if( P("domod") ) return 1; |
| 754 | 891 | if( g.perm.WrTForum ) return 0; |
| 755 | 892 | if( g.perm.ModForum ) return 0; |
| 756 | 893 | return 1; |
| 757 | 894 | } |
| 895 | + | |
| 896 | +/* | |
| 897 | +** Return true if the string is white-space only. | |
| 898 | +*/ | |
| 899 | +static int whitespace_only(const char *z){ | |
| 900 | + if( z==0 ) return 1; | |
| 901 | + while( z[0] && fossil_isspace(z[0]) ){ z++; } | |
| 902 | + return z[0]==0; | |
| 903 | +} | |
| 758 | 904 | |
| 759 | 905 | /* |
| 760 | 906 | ** Add a new Forum Post artifact to the repository. |
| 761 | 907 | ** |
| 762 | 908 | ** Return true if a redirect occurs. |
| @@ -773,12 +919,16 @@ | ||
| 773 | 919 | char *zI; |
| 774 | 920 | char *zG; |
| 775 | 921 | int iBasis; |
| 776 | 922 | Blob x, cksum, formatCheck, errMsg; |
| 777 | 923 | Manifest *pPost; |
| 924 | + int nContent = zContent ? (int)strlen(zContent) : 0; | |
| 778 | 925 | |
| 779 | 926 | schema_forum(); |
| 927 | + if( iEdit==0 && whitespace_only(zContent) ){ | |
| 928 | + return 0; | |
| 929 | + } | |
| 780 | 930 | if( iInReplyTo==0 && iEdit>0 ){ |
| 781 | 931 | iBasis = iEdit; |
| 782 | 932 | iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit); |
| 783 | 933 | }else{ |
| 784 | 934 | iBasis = iInReplyTo; |
| @@ -819,11 +969,11 @@ | ||
| 819 | 969 | }else{ |
| 820 | 970 | zUser = login_name(); |
| 821 | 971 | } |
| 822 | 972 | } |
| 823 | 973 | blob_appendf(&x, "U %F\n", zUser); |
| 824 | - blob_appendf(&x, "W %d\n%s\n", strlen(zContent), zContent); | |
| 974 | + blob_appendf(&x, "W %d\n%s\n", nContent, zContent); | |
| 825 | 975 | md5sum_blob(&x, &cksum); |
| 826 | 976 | blob_appendf(&x, "Z %b\n", &cksum); |
| 827 | 977 | blob_reset(&cksum); |
| 828 | 978 | |
| 829 | 979 | /* Verify that the artifact we are creating is well-formed */ |
| @@ -950,24 +1100,24 @@ | ||
| 950 | 1100 | login_check_credentials(); |
| 951 | 1101 | if( !g.perm.WrForum ){ |
| 952 | 1102 | login_needed(g.anon.WrForum); |
| 953 | 1103 | return; |
| 954 | 1104 | } |
| 955 | - if( P("submit") ){ | |
| 1105 | + if( P("submit") && cgi_csrf_safe(1) ){ | |
| 956 | 1106 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; |
| 957 | 1107 | } |
| 958 | - if( P("preview") ){ | |
| 1108 | + if( P("preview") && !whitespace_only(zContent) ){ | |
| 959 | 1109 | @ <h1>Preview:</h1> |
| 960 | 1110 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 961 | 1111 | } |
| 962 | 1112 | style_header("New Forum Thread"); |
| 963 | 1113 | @ <form action="%R/forume1" method="POST"> |
| 964 | 1114 | @ <h1>New Thread:</h1> |
| 965 | 1115 | forum_from_line(); |
| 966 | 1116 | forum_entry_widget(zTitle, zMimetype, zContent); |
| 967 | 1117 | @ <input type="submit" name="preview" value="Preview"> |
| 968 | - if( P("preview") ){ | |
| 1118 | + if( P("preview") && !whitespace_only(zContent) ){ | |
| 969 | 1119 | @ <input type="submit" name="submit" value="Submit"> |
| 970 | 1120 | }else{ |
| 971 | 1121 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 972 | 1122 | } |
| 973 | 1123 | if( g.perm.Debug ){ |
| @@ -1053,14 +1203,17 @@ | ||
| 1053 | 1203 | } |
| 1054 | 1204 | return; |
| 1055 | 1205 | } |
| 1056 | 1206 | } |
| 1057 | 1207 | isDelete = P("nullout")!=0; |
| 1058 | - if( P("submit") && isCsrfSafe ){ | |
| 1208 | + if( P("submit") | |
| 1209 | + && isCsrfSafe | |
| 1210 | + && (zContent = PDT("content",""))!=0 | |
| 1211 | + && (!whitespace_only(zContent) || isDelete) | |
| 1212 | + ){ | |
| 1059 | 1213 | int done = 1; |
| 1060 | 1214 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1061 | - const char *zContent = PDT("content",""); | |
| 1062 | 1215 | if( P("reply") ){ |
| 1063 | 1216 | done = forum_post(0, fpid, 0, 0, zMimetype, zContent); |
| 1064 | 1217 | }else if( P("edit") || isDelete ){ |
| 1065 | 1218 | done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); |
| 1066 | 1219 | }else{ |
| @@ -1124,11 +1277,11 @@ | ||
| 1124 | 1277 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1125 | 1278 | @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3> |
| 1126 | 1279 | fossil_free(zDisplayName); |
| 1127 | 1280 | fossil_free(zDate); |
| 1128 | 1281 | forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); |
| 1129 | - if( P("preview") ){ | |
| 1282 | + if( P("preview") && !whitespace_only(zContent) ){ | |
| 1130 | 1283 | @ <h2>Preview:</h2> |
| 1131 | 1284 | forum_render(0, zMimetype,zContent, "forumEdit", 1); |
| 1132 | 1285 | } |
| 1133 | 1286 | @ <h2>Enter Reply:</h2> |
| 1134 | 1287 | @ <form action="%R/forume2" method="POST"> |
| @@ -1139,11 +1292,11 @@ | ||
| 1139 | 1292 | } |
| 1140 | 1293 | if( !isDelete ){ |
| 1141 | 1294 | @ <input type="submit" name="preview" value="Preview"> |
| 1142 | 1295 | } |
| 1143 | 1296 | @ <input type="submit" name="cancel" value="Cancel"> |
| 1144 | - if( P("preview") || isDelete ){ | |
| 1297 | + if( (P("preview") && !whitespace_only(zContent)) || isDelete ){ | |
| 1145 | 1298 | @ <input type="submit" name="submit" value="Submit"> |
| 1146 | 1299 | } |
| 1147 | 1300 | if( g.perm.Debug ){ |
| 1148 | 1301 | /* For the test-forumnew page add these extra debugging controls */ |
| 1149 | 1302 | @ <div class="debug"> |
| 1150 | 1303 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -156,10 +156,11 @@ | |
| 156 | static ForumThread *forumthread_create(int froot, int computeHierarchy){ |
| 157 | ForumThread *pThread; |
| 158 | ForumEntry *pEntry; |
| 159 | Stmt q; |
| 160 | int sid = 1; |
| 161 | pThread = fossil_malloc( sizeof(*pThread) ); |
| 162 | memset(pThread, 0, sizeof(*pThread)); |
| 163 | db_prepare(&q, |
| 164 | "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)" |
| 165 | " FROM forumpost" |
| @@ -175,18 +176,24 @@ | |
| 175 | pEntry->zUuid = fossil_strdup(db_column_text(&q,3)); |
| 176 | pEntry->mfirt = pEntry->firt; |
| 177 | pEntry->sid = sid++; |
| 178 | pEntry->pPrev = pThread->pLast; |
| 179 | pEntry->pNext = 0; |
| 180 | if( pThread->pLast==0 ){ |
| 181 | pThread->pFirst = pEntry; |
| 182 | }else{ |
| 183 | pThread->pLast->pNext = pEntry; |
| 184 | } |
| 185 | pThread->pLast = pEntry; |
| 186 | } |
| 187 | db_finalize(&q); |
| 188 | |
| 189 | /* Establish which entries are the latest edit. After this loop |
| 190 | ** completes, entries that have non-NULL pLeaf should not be |
| 191 | ** displayed. |
| 192 | */ |
| @@ -283,16 +290,18 @@ | |
| 283 | } |
| 284 | fossil_print("fpid = %d\n", fpid); |
| 285 | fossil_print("froot = %d\n", froot); |
| 286 | pThread = forumthread_create(froot, 1); |
| 287 | fossil_print("Chronological:\n"); |
| 288 | /* 123456789 123456789 123456789 123456789 123456789 123456789 */ |
| 289 | fossil_print(" fpid firt fprev mfirt pLeaf nReply\n"); |
| 290 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 291 | fossil_print("%9d %9d %9d %9d %9d %9d\n", |
| 292 | p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0, |
| 293 | p->nReply); |
| 294 | } |
| 295 | fossil_print("\nDisplay\n"); |
| 296 | for(p=pThread->pDisplay; p; p=p->pDisplay){ |
| 297 | fossil_print("%*s", (p->nIndent-1)*3, ""); |
| 298 | if( p->pLeaf ){ |
| @@ -408,10 +417,11 @@ | |
| 408 | Manifest *pPost; |
| 409 | int isPrivate; /* True for posts awaiting moderation */ |
| 410 | int sameUser; /* True if author is also the reader */ |
| 411 | const char *zUuid; |
| 412 | char *zDisplayName; /* The display name */ |
| 413 | |
| 414 | pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); |
| 415 | if( pPost==0 ) continue; |
| 416 | if( p->fpid==target ){ |
| 417 | @ <div id="forum%d(p->fpid)" class="forumTime forumSel"> |
| @@ -423,20 +433,21 @@ | |
| 423 | if( pPost->zThreadTitle ){ |
| 424 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 425 | } |
| 426 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 427 | zDisplayName = display_name_from_login(pPost->zUser); |
| 428 | @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate) |
| 429 | fossil_free(zDisplayName); |
| 430 | fossil_free(zDate); |
| 431 | if( p->pEdit ){ |
| 432 | @ edit of %z(href("%R/forumpost/%S?t=%c",p->pEdit->zUuid,cMode))\ |
| 433 | @ %d(p->pEdit->sid)</a> |
| 434 | } |
| 435 | if( g.perm.Debug ){ |
| 436 | @ <span class="debug">\ |
| 437 | @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span> |
| 438 | } |
| 439 | if( p->firt ){ |
| 440 | ForumEntry *pIrt = p->pPrev; |
| 441 | while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; |
| 442 | if( pIrt ){ |
| @@ -451,10 +462,125 @@ | |
| 451 | zUuid = p->pLeaf->zUuid; |
| 452 | } |
| 453 | if( p->fpid!=target ){ |
| 454 | @ %z(href("%R/forumpost/%S?t=%c",zUuid,cMode))[link]</a> |
| 455 | } |
| 456 | if( !bRawMode ){ |
| 457 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 458 | } |
| 459 | isPrivate = content_is_private(p->fpid); |
| 460 | sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| @@ -549,16 +675,16 @@ | |
| 549 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 550 | } |
| 551 | zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate); |
| 552 | zDisplayName = display_name_from_login(pOPost->zUser); |
| 553 | @ <h3 class='forumPostHdr'>\ |
| 554 | @ (%d(p->pLeaf?p->pLeaf->sid:p->sid)) By %h(zDisplayName) on %h(zDate) |
| 555 | fossil_free(zDisplayName); |
| 556 | fossil_free(zDate); |
| 557 | if( g.perm.Debug ){ |
| 558 | @ <span class="debug">\ |
| 559 | @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span> |
| 560 | } |
| 561 | if( p->pLeaf ){ |
| 562 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 563 | if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){ |
| 564 | @ and edited on %h(zDate) |
| @@ -566,21 +692,23 @@ | |
| 566 | @ as edited by %h(pPost->zUser) on %h(zDate) |
| 567 | } |
| 568 | fossil_free(zDate); |
| 569 | if( g.perm.Debug ){ |
| 570 | @ <span class="debug">\ |
| 571 | @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(artifact)</a></span> |
| 572 | } |
| 573 | manifest_destroy(pOPost); |
| 574 | } |
| 575 | if( fpid!=target ){ |
| 576 | @ %z(href("%R/forumpost/%S",zUuid))[link]</a> |
| 577 | } |
| 578 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 579 | if( p->firt ){ |
| 580 | ForumEntry *pIrt = p->pPrev; |
| 581 | while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; |
| 582 | if( pIrt ){ |
| 583 | @ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\ |
| 584 | @ %d(pIrt->sid)</a> |
| 585 | } |
| 586 | } |
| @@ -637,10 +765,11 @@ | |
| 637 | ** t=MODE Display mode. |
| 638 | ** 'c' for chronological |
| 639 | ** 'h' for hierarchical |
| 640 | ** 'a' for automatic |
| 641 | ** 'r' for raw |
| 642 | ** raw If present, show only the post specified and |
| 643 | ** show its original unformatted source text. |
| 644 | */ |
| 645 | void forumpost_page(void){ |
| 646 | forumthread_page(); |
| @@ -679,10 +808,12 @@ | |
| 679 | ** t=MODE Display mode. MODE is... |
| 680 | ** 'c' for chronological, or |
| 681 | ** 'h' for hierarchical, or |
| 682 | ** 'a' for automatic, or |
| 683 | ** 'r' for raw. |
| 684 | */ |
| 685 | void forumthread_page(void){ |
| 686 | int fpid; |
| 687 | int froot; |
| 688 | const char *zName = P("name"); |
| @@ -710,11 +841,13 @@ | |
| 710 | zMode = "c"; /* Default to chronological on mobile */ |
| 711 | }else{ |
| 712 | zMode = "h"; |
| 713 | } |
| 714 | } |
| 715 | forumthread_page_header(froot, fpid); |
| 716 | if( bRaw && fpid ){ |
| 717 | Manifest *pPost; |
| 718 | pPost = manifest_get(fpid, CFTYPE_FORUM, 0); |
| 719 | if( pPost==0 ){ |
| 720 | @ <p>No such forum post: %h(zName) |
| @@ -735,10 +868,14 @@ | |
| 735 | forum_display_chronological(froot, fpid, 0); |
| 736 | }else if( zMode[0]=='r' ){ |
| 737 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 738 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 739 | forum_display_chronological(froot, fpid, 1); |
| 740 | }else{ |
| 741 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 742 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 743 | forum_display_hierarchical(froot, fpid); |
| 744 | } |
| @@ -753,10 +890,19 @@ | |
| 753 | if( P("domod") ) return 1; |
| 754 | if( g.perm.WrTForum ) return 0; |
| 755 | if( g.perm.ModForum ) return 0; |
| 756 | return 1; |
| 757 | } |
| 758 | |
| 759 | /* |
| 760 | ** Add a new Forum Post artifact to the repository. |
| 761 | ** |
| 762 | ** Return true if a redirect occurs. |
| @@ -773,12 +919,16 @@ | |
| 773 | char *zI; |
| 774 | char *zG; |
| 775 | int iBasis; |
| 776 | Blob x, cksum, formatCheck, errMsg; |
| 777 | Manifest *pPost; |
| 778 | |
| 779 | schema_forum(); |
| 780 | if( iInReplyTo==0 && iEdit>0 ){ |
| 781 | iBasis = iEdit; |
| 782 | iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit); |
| 783 | }else{ |
| 784 | iBasis = iInReplyTo; |
| @@ -819,11 +969,11 @@ | |
| 819 | }else{ |
| 820 | zUser = login_name(); |
| 821 | } |
| 822 | } |
| 823 | blob_appendf(&x, "U %F\n", zUser); |
| 824 | blob_appendf(&x, "W %d\n%s\n", strlen(zContent), zContent); |
| 825 | md5sum_blob(&x, &cksum); |
| 826 | blob_appendf(&x, "Z %b\n", &cksum); |
| 827 | blob_reset(&cksum); |
| 828 | |
| 829 | /* Verify that the artifact we are creating is well-formed */ |
| @@ -950,24 +1100,24 @@ | |
| 950 | login_check_credentials(); |
| 951 | if( !g.perm.WrForum ){ |
| 952 | login_needed(g.anon.WrForum); |
| 953 | return; |
| 954 | } |
| 955 | if( P("submit") ){ |
| 956 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; |
| 957 | } |
| 958 | if( P("preview") ){ |
| 959 | @ <h1>Preview:</h1> |
| 960 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 961 | } |
| 962 | style_header("New Forum Thread"); |
| 963 | @ <form action="%R/forume1" method="POST"> |
| 964 | @ <h1>New Thread:</h1> |
| 965 | forum_from_line(); |
| 966 | forum_entry_widget(zTitle, zMimetype, zContent); |
| 967 | @ <input type="submit" name="preview" value="Preview"> |
| 968 | if( P("preview") ){ |
| 969 | @ <input type="submit" name="submit" value="Submit"> |
| 970 | }else{ |
| 971 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 972 | } |
| 973 | if( g.perm.Debug ){ |
| @@ -1053,14 +1203,17 @@ | |
| 1053 | } |
| 1054 | return; |
| 1055 | } |
| 1056 | } |
| 1057 | isDelete = P("nullout")!=0; |
| 1058 | if( P("submit") && isCsrfSafe ){ |
| 1059 | int done = 1; |
| 1060 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1061 | const char *zContent = PDT("content",""); |
| 1062 | if( P("reply") ){ |
| 1063 | done = forum_post(0, fpid, 0, 0, zMimetype, zContent); |
| 1064 | }else if( P("edit") || isDelete ){ |
| 1065 | done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); |
| 1066 | }else{ |
| @@ -1124,11 +1277,11 @@ | |
| 1124 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1125 | @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3> |
| 1126 | fossil_free(zDisplayName); |
| 1127 | fossil_free(zDate); |
| 1128 | forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); |
| 1129 | if( P("preview") ){ |
| 1130 | @ <h2>Preview:</h2> |
| 1131 | forum_render(0, zMimetype,zContent, "forumEdit", 1); |
| 1132 | } |
| 1133 | @ <h2>Enter Reply:</h2> |
| 1134 | @ <form action="%R/forume2" method="POST"> |
| @@ -1139,11 +1292,11 @@ | |
| 1139 | } |
| 1140 | if( !isDelete ){ |
| 1141 | @ <input type="submit" name="preview" value="Preview"> |
| 1142 | } |
| 1143 | @ <input type="submit" name="cancel" value="Cancel"> |
| 1144 | if( P("preview") || isDelete ){ |
| 1145 | @ <input type="submit" name="submit" value="Submit"> |
| 1146 | } |
| 1147 | if( g.perm.Debug ){ |
| 1148 | /* For the test-forumnew page add these extra debugging controls */ |
| 1149 | @ <div class="debug"> |
| 1150 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -156,10 +156,11 @@ | |
| 156 | static ForumThread *forumthread_create(int froot, int computeHierarchy){ |
| 157 | ForumThread *pThread; |
| 158 | ForumEntry *pEntry; |
| 159 | Stmt q; |
| 160 | int sid = 1; |
| 161 | Bag seen = Bag_INIT; |
| 162 | pThread = fossil_malloc( sizeof(*pThread) ); |
| 163 | memset(pThread, 0, sizeof(*pThread)); |
| 164 | db_prepare(&q, |
| 165 | "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)" |
| 166 | " FROM forumpost" |
| @@ -175,18 +176,24 @@ | |
| 176 | pEntry->zUuid = fossil_strdup(db_column_text(&q,3)); |
| 177 | pEntry->mfirt = pEntry->firt; |
| 178 | pEntry->sid = sid++; |
| 179 | pEntry->pPrev = pThread->pLast; |
| 180 | pEntry->pNext = 0; |
| 181 | bag_insert(&seen, pEntry->fpid); |
| 182 | if( pThread->pLast==0 ){ |
| 183 | pThread->pFirst = pEntry; |
| 184 | }else{ |
| 185 | pThread->pLast->pNext = pEntry; |
| 186 | } |
| 187 | if( pEntry->firt && !bag_find(&seen,pEntry->firt) ){ |
| 188 | pEntry->firt = froot; |
| 189 | pEntry->mfirt = froot; |
| 190 | } |
| 191 | pThread->pLast = pEntry; |
| 192 | } |
| 193 | db_finalize(&q); |
| 194 | bag_clear(&seen); |
| 195 | |
| 196 | /* Establish which entries are the latest edit. After this loop |
| 197 | ** completes, entries that have non-NULL pLeaf should not be |
| 198 | ** displayed. |
| 199 | */ |
| @@ -283,16 +290,18 @@ | |
| 290 | } |
| 291 | fossil_print("fpid = %d\n", fpid); |
| 292 | fossil_print("froot = %d\n", froot); |
| 293 | pThread = forumthread_create(froot, 1); |
| 294 | fossil_print("Chronological:\n"); |
| 295 | fossil_print( |
| 296 | /* 0 1 2 3 4 5 6 7 */ |
| 297 | /* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */ |
| 298 | " sid fpid firt fprev mfirt pLeaf nReply hash\n"); |
| 299 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 300 | fossil_print("%4d %9d %9d %9d %9d %9d %6d %8.8s\n", p->sid, |
| 301 | p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0, |
| 302 | p->nReply, p->zUuid); |
| 303 | } |
| 304 | fossil_print("\nDisplay\n"); |
| 305 | for(p=pThread->pDisplay; p; p=p->pDisplay){ |
| 306 | fossil_print("%*s", (p->nIndent-1)*3, ""); |
| 307 | if( p->pLeaf ){ |
| @@ -408,10 +417,11 @@ | |
| 417 | Manifest *pPost; |
| 418 | int isPrivate; /* True for posts awaiting moderation */ |
| 419 | int sameUser; /* True if author is also the reader */ |
| 420 | const char *zUuid; |
| 421 | char *zDisplayName; /* The display name */ |
| 422 | int sid; |
| 423 | |
| 424 | pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); |
| 425 | if( pPost==0 ) continue; |
| 426 | if( p->fpid==target ){ |
| 427 | @ <div id="forum%d(p->fpid)" class="forumTime forumSel"> |
| @@ -423,20 +433,21 @@ | |
| 433 | if( pPost->zThreadTitle ){ |
| 434 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 435 | } |
| 436 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 437 | zDisplayName = display_name_from_login(pPost->zUser); |
| 438 | sid = p->pEdit ? p->pEdit->sid : p->sid; |
| 439 | @ <h3 class='forumPostHdr'>(%d(sid)) By %h(zDisplayName) on %h(zDate) |
| 440 | fossil_free(zDisplayName); |
| 441 | fossil_free(zDate); |
| 442 | if( p->pEdit ){ |
| 443 | @ edit of %z(href("%R/forumpost/%S?t=%c",p->pEdit->zUuid,cMode))\ |
| 444 | @ %d(p->pEdit->sid)</a> |
| 445 | } |
| 446 | if( g.perm.Debug ){ |
| 447 | @ <span class="debug">\ |
| 448 | @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> |
| 449 | } |
| 450 | if( p->firt ){ |
| 451 | ForumEntry *pIrt = p->pPrev; |
| 452 | while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; |
| 453 | if( pIrt ){ |
| @@ -451,10 +462,125 @@ | |
| 462 | zUuid = p->pLeaf->zUuid; |
| 463 | } |
| 464 | if( p->fpid!=target ){ |
| 465 | @ %z(href("%R/forumpost/%S?t=%c",zUuid,cMode))[link]</a> |
| 466 | } |
| 467 | if( !bRawMode ){ |
| 468 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 469 | } |
| 470 | isPrivate = content_is_private(p->fpid); |
| 471 | sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| 472 | @ </h3> |
| 473 | if( isPrivate && !g.perm.ModForum && !sameUser ){ |
| 474 | @ <p><span class="modpending">Awaiting Moderator Approval</span></p> |
| 475 | }else{ |
| 476 | const char *zMimetype; |
| 477 | if( bRawMode ){ |
| 478 | zMimetype = "text/plain"; |
| 479 | }else if( p->pLeaf!=0 ){ |
| 480 | zMimetype = "text/plain"; |
| 481 | }else{ |
| 482 | zMimetype = pPost->zMimetype; |
| 483 | } |
| 484 | forum_render(0, zMimetype, pPost->zWiki, 0, 1); |
| 485 | } |
| 486 | if( g.perm.WrForum && p->pLeaf==0 ){ |
| 487 | int sameUser = login_is_individual() |
| 488 | && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| 489 | @ <p><form action="%R/forumedit" method="POST"> |
| 490 | @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> |
| 491 | if( !isPrivate ){ |
| 492 | /* Reply and Edit are only available if the post has already |
| 493 | ** been approved */ |
| 494 | @ <input type="submit" name="reply" value="Reply"> |
| 495 | if( g.perm.Admin || sameUser ){ |
| 496 | @ <input type="submit" name="edit" value="Edit"> |
| 497 | @ <input type="submit" name="nullout" value="Delete"> |
| 498 | } |
| 499 | }else if( g.perm.ModForum ){ |
| 500 | /* Provide moderators with moderation buttons for posts that |
| 501 | ** are pending moderation */ |
| 502 | @ <input type="submit" name="approve" value="Approve"> |
| 503 | @ <input type="submit" name="reject" value="Reject"> |
| 504 | generateTrustControls(pPost); |
| 505 | }else if( sameUser ){ |
| 506 | /* A post that is pending moderation can be deleted by the |
| 507 | ** person who originally submitted the post */ |
| 508 | @ <input type="submit" name="reject" value="Delete"> |
| 509 | } |
| 510 | @ </form></p> |
| 511 | } |
| 512 | manifest_destroy(pPost); |
| 513 | @ </div> |
| 514 | } |
| 515 | |
| 516 | /* Undocumented "threadtable" query parameter causes thread table |
| 517 | ** to be displayed for debugging purposes. |
| 518 | */ |
| 519 | if( PB("threadtable") ){ |
| 520 | @ <hr> |
| 521 | @ <table border="1" cellpadding="3" cellspacing="0"> |
| 522 | @ <tr><th>sid<th>fpid<th>firt<th>fprev<th>mfirt<th>pLeaf<th>nReply<th>hash |
| 523 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 524 | @ <tr><td>%d(p->sid)<td>%d(p->fpid)<td>%d(p->firt)\ |
| 525 | @ <td>%d(p->fprev)<td>%d(p->mfirt)\ |
| 526 | @ <td>%d(p->pLeaf?p->pLeaf->fpid:0)<td>%d(p->nReply)\ |
| 527 | @ <td>%S(p->zUuid)</tr> |
| 528 | } |
| 529 | @ </table> |
| 530 | } |
| 531 | |
| 532 | forumthread_delete(pThread); |
| 533 | } |
| 534 | /* |
| 535 | ** Display all the edit history of post "target". |
| 536 | */ |
| 537 | static void forum_display_history(int froot, int target, int bRawMode){ |
| 538 | ForumThread *pThread = forumthread_create(froot, 0); |
| 539 | ForumEntry *p; |
| 540 | int notAnon = login_is_individual(); |
| 541 | char cMode = bRawMode ? 'r' : 'c'; |
| 542 | ForumEntry *pLeaf = 0; |
| 543 | int cnt = 0; |
| 544 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 545 | if( p->fpid==target ){ |
| 546 | pLeaf = p->pLeaf ? p->pLeaf : p; |
| 547 | break; |
| 548 | } |
| 549 | } |
| 550 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 551 | char *zDate; |
| 552 | Manifest *pPost; |
| 553 | int isPrivate; /* True for posts awaiting moderation */ |
| 554 | int sameUser; /* True if author is also the reader */ |
| 555 | const char *zUuid; |
| 556 | char *zDisplayName; /* The display name */ |
| 557 | |
| 558 | if( p->fpid!=pLeaf->fpid && p->pLeaf!=pLeaf ) continue; |
| 559 | cnt++; |
| 560 | pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); |
| 561 | if( pPost==0 ) continue; |
| 562 | @ <div id="forum%d(p->fpid)" class="forumTime"> |
| 563 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 564 | zDisplayName = display_name_from_login(pPost->zUser); |
| 565 | @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate) |
| 566 | fossil_free(zDisplayName); |
| 567 | fossil_free(zDate); |
| 568 | if( g.perm.Debug ){ |
| 569 | @ <span class="debug">\ |
| 570 | @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> |
| 571 | } |
| 572 | if( p->firt && cnt==1 ){ |
| 573 | ForumEntry *pIrt = p->pPrev; |
| 574 | while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; |
| 575 | if( pIrt ){ |
| 576 | @ in reply to %z(href("%R/forumpost/%S?t=%c",pIrt->zUuid,cMode))\ |
| 577 | @ %d(pIrt->sid)</a> |
| 578 | } |
| 579 | } |
| 580 | zUuid = p->zUuid; |
| 581 | @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a> |
| 582 | if( !bRawMode ){ |
| 583 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 584 | } |
| 585 | isPrivate = content_is_private(p->fpid); |
| 586 | sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| @@ -549,16 +675,16 @@ | |
| 675 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 676 | } |
| 677 | zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate); |
| 678 | zDisplayName = display_name_from_login(pOPost->zUser); |
| 679 | @ <h3 class='forumPostHdr'>\ |
| 680 | @ (%d(p->sid)) By %h(zDisplayName) on %h(zDate) |
| 681 | fossil_free(zDisplayName); |
| 682 | fossil_free(zDate); |
| 683 | if( g.perm.Debug ){ |
| 684 | @ <span class="debug">\ |
| 685 | @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> |
| 686 | } |
| 687 | if( p->pLeaf ){ |
| 688 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 689 | if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){ |
| 690 | @ and edited on %h(zDate) |
| @@ -566,21 +692,23 @@ | |
| 692 | @ as edited by %h(pPost->zUser) on %h(zDate) |
| 693 | } |
| 694 | fossil_free(zDate); |
| 695 | if( g.perm.Debug ){ |
| 696 | @ <span class="debug">\ |
| 697 | @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">\ |
| 698 | @ (artifact-%d(p->pLeaf->fpid))</a></span> |
| 699 | } |
| 700 | @ %z(href("%R/forumpost/%S?t=y",p->zUuid))[history]</a> |
| 701 | manifest_destroy(pOPost); |
| 702 | } |
| 703 | if( fpid!=target ){ |
| 704 | @ %z(href("%R/forumpost/%S",zUuid))[link]</a> |
| 705 | } |
| 706 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 707 | if( p->firt ){ |
| 708 | ForumEntry *pIrt = p->pPrev; |
| 709 | while( pIrt && pIrt->fpid!=p->mfirt ) pIrt = pIrt->pPrev; |
| 710 | if( pIrt ){ |
| 711 | @ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\ |
| 712 | @ %d(pIrt->sid)</a> |
| 713 | } |
| 714 | } |
| @@ -637,10 +765,11 @@ | |
| 765 | ** t=MODE Display mode. |
| 766 | ** 'c' for chronological |
| 767 | ** 'h' for hierarchical |
| 768 | ** 'a' for automatic |
| 769 | ** 'r' for raw |
| 770 | ** 'y' for history of post X only |
| 771 | ** raw If present, show only the post specified and |
| 772 | ** show its original unformatted source text. |
| 773 | */ |
| 774 | void forumpost_page(void){ |
| 775 | forumthread_page(); |
| @@ -679,10 +808,12 @@ | |
| 808 | ** t=MODE Display mode. MODE is... |
| 809 | ** 'c' for chronological, or |
| 810 | ** 'h' for hierarchical, or |
| 811 | ** 'a' for automatic, or |
| 812 | ** 'r' for raw. |
| 813 | ** raw Show only the post given by name= and show it unformatted |
| 814 | ** hist Show only the edit history for the name= post |
| 815 | */ |
| 816 | void forumthread_page(void){ |
| 817 | int fpid; |
| 818 | int froot; |
| 819 | const char *zName = P("name"); |
| @@ -710,11 +841,13 @@ | |
| 841 | zMode = "c"; /* Default to chronological on mobile */ |
| 842 | }else{ |
| 843 | zMode = "h"; |
| 844 | } |
| 845 | } |
| 846 | if( zMode[0]!='y' ){ |
| 847 | forumthread_page_header(froot, fpid); |
| 848 | } |
| 849 | if( bRaw && fpid ){ |
| 850 | Manifest *pPost; |
| 851 | pPost = manifest_get(fpid, CFTYPE_FORUM, 0); |
| 852 | if( pPost==0 ){ |
| 853 | @ <p>No such forum post: %h(zName) |
| @@ -735,10 +868,14 @@ | |
| 868 | forum_display_chronological(froot, fpid, 0); |
| 869 | }else if( zMode[0]=='r' ){ |
| 870 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 871 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 872 | forum_display_chronological(froot, fpid, 1); |
| 873 | }else if( zMode[0]=='y' ){ |
| 874 | style_header("Edit History Of A Forum Post"); |
| 875 | style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName); |
| 876 | forum_display_history(froot, fpid, 1); |
| 877 | }else{ |
| 878 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 879 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 880 | forum_display_hierarchical(froot, fpid); |
| 881 | } |
| @@ -753,10 +890,19 @@ | |
| 890 | if( P("domod") ) return 1; |
| 891 | if( g.perm.WrTForum ) return 0; |
| 892 | if( g.perm.ModForum ) return 0; |
| 893 | return 1; |
| 894 | } |
| 895 | |
| 896 | /* |
| 897 | ** Return true if the string is white-space only. |
| 898 | */ |
| 899 | static int whitespace_only(const char *z){ |
| 900 | if( z==0 ) return 1; |
| 901 | while( z[0] && fossil_isspace(z[0]) ){ z++; } |
| 902 | return z[0]==0; |
| 903 | } |
| 904 | |
| 905 | /* |
| 906 | ** Add a new Forum Post artifact to the repository. |
| 907 | ** |
| 908 | ** Return true if a redirect occurs. |
| @@ -773,12 +919,16 @@ | |
| 919 | char *zI; |
| 920 | char *zG; |
| 921 | int iBasis; |
| 922 | Blob x, cksum, formatCheck, errMsg; |
| 923 | Manifest *pPost; |
| 924 | int nContent = zContent ? (int)strlen(zContent) : 0; |
| 925 | |
| 926 | schema_forum(); |
| 927 | if( iEdit==0 && whitespace_only(zContent) ){ |
| 928 | return 0; |
| 929 | } |
| 930 | if( iInReplyTo==0 && iEdit>0 ){ |
| 931 | iBasis = iEdit; |
| 932 | iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit); |
| 933 | }else{ |
| 934 | iBasis = iInReplyTo; |
| @@ -819,11 +969,11 @@ | |
| 969 | }else{ |
| 970 | zUser = login_name(); |
| 971 | } |
| 972 | } |
| 973 | blob_appendf(&x, "U %F\n", zUser); |
| 974 | blob_appendf(&x, "W %d\n%s\n", nContent, zContent); |
| 975 | md5sum_blob(&x, &cksum); |
| 976 | blob_appendf(&x, "Z %b\n", &cksum); |
| 977 | blob_reset(&cksum); |
| 978 | |
| 979 | /* Verify that the artifact we are creating is well-formed */ |
| @@ -950,24 +1100,24 @@ | |
| 1100 | login_check_credentials(); |
| 1101 | if( !g.perm.WrForum ){ |
| 1102 | login_needed(g.anon.WrForum); |
| 1103 | return; |
| 1104 | } |
| 1105 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1106 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; |
| 1107 | } |
| 1108 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1109 | @ <h1>Preview:</h1> |
| 1110 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 1111 | } |
| 1112 | style_header("New Forum Thread"); |
| 1113 | @ <form action="%R/forume1" method="POST"> |
| 1114 | @ <h1>New Thread:</h1> |
| 1115 | forum_from_line(); |
| 1116 | forum_entry_widget(zTitle, zMimetype, zContent); |
| 1117 | @ <input type="submit" name="preview" value="Preview"> |
| 1118 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1119 | @ <input type="submit" name="submit" value="Submit"> |
| 1120 | }else{ |
| 1121 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1122 | } |
| 1123 | if( g.perm.Debug ){ |
| @@ -1053,14 +1203,17 @@ | |
| 1203 | } |
| 1204 | return; |
| 1205 | } |
| 1206 | } |
| 1207 | isDelete = P("nullout")!=0; |
| 1208 | if( P("submit") |
| 1209 | && isCsrfSafe |
| 1210 | && (zContent = PDT("content",""))!=0 |
| 1211 | && (!whitespace_only(zContent) || isDelete) |
| 1212 | ){ |
| 1213 | int done = 1; |
| 1214 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1215 | if( P("reply") ){ |
| 1216 | done = forum_post(0, fpid, 0, 0, zMimetype, zContent); |
| 1217 | }else if( P("edit") || isDelete ){ |
| 1218 | done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); |
| 1219 | }else{ |
| @@ -1124,11 +1277,11 @@ | |
| 1277 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1278 | @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3> |
| 1279 | fossil_free(zDisplayName); |
| 1280 | fossil_free(zDate); |
| 1281 | forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); |
| 1282 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1283 | @ <h2>Preview:</h2> |
| 1284 | forum_render(0, zMimetype,zContent, "forumEdit", 1); |
| 1285 | } |
| 1286 | @ <h2>Enter Reply:</h2> |
| 1287 | @ <form action="%R/forume2" method="POST"> |
| @@ -1139,11 +1292,11 @@ | |
| 1292 | } |
| 1293 | if( !isDelete ){ |
| 1294 | @ <input type="submit" name="preview" value="Preview"> |
| 1295 | } |
| 1296 | @ <input type="submit" name="cancel" value="Cancel"> |
| 1297 | if( (P("preview") && !whitespace_only(zContent)) || isDelete ){ |
| 1298 | @ <input type="submit" name="submit" value="Submit"> |
| 1299 | } |
| 1300 | if( g.perm.Debug ){ |
| 1301 | /* For the test-forumnew page add these extra debugging controls */ |
| 1302 | @ <div class="debug"> |
| 1303 |
+5
-1
| --- src/fshell.c | ||
| +++ src/fshell.c | ||
| @@ -60,11 +60,15 @@ | ||
| 60 | 60 | pid_t childPid; |
| 61 | 61 | char *zLine = 0; |
| 62 | 62 | char *zPrompt = 0; |
| 63 | 63 | fDebug = find_option("debug", 0, 0)!=0; |
| 64 | 64 | db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); |
| 65 | - zPrompt = mprintf("fossil (%z)> ", db_get("project-name","no repo")); | |
| 65 | + if(g.zRepositoryName!=0){ | |
| 66 | + zPrompt = mprintf("fossil (%z)> ", db_get("project-name","unnamed")); | |
| 67 | + }else{ | |
| 68 | + zPrompt = mprintf("fossil (no repo)> "); | |
| 69 | + } | |
| 66 | 70 | db_close(0); |
| 67 | 71 | sqlite3_shutdown(); |
| 68 | 72 | linenoiseSetMultiLine(1); |
| 69 | 73 | while( (free(zLine), zLine = linenoise(zPrompt)) ){ |
| 70 | 74 | /* Remember shell history within the current session */ |
| 71 | 75 |
| --- src/fshell.c | |
| +++ src/fshell.c | |
| @@ -60,11 +60,15 @@ | |
| 60 | pid_t childPid; |
| 61 | char *zLine = 0; |
| 62 | char *zPrompt = 0; |
| 63 | fDebug = find_option("debug", 0, 0)!=0; |
| 64 | db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); |
| 65 | zPrompt = mprintf("fossil (%z)> ", db_get("project-name","no repo")); |
| 66 | db_close(0); |
| 67 | sqlite3_shutdown(); |
| 68 | linenoiseSetMultiLine(1); |
| 69 | while( (free(zLine), zLine = linenoise(zPrompt)) ){ |
| 70 | /* Remember shell history within the current session */ |
| 71 |
| --- src/fshell.c | |
| +++ src/fshell.c | |
| @@ -60,11 +60,15 @@ | |
| 60 | pid_t childPid; |
| 61 | char *zLine = 0; |
| 62 | char *zPrompt = 0; |
| 63 | fDebug = find_option("debug", 0, 0)!=0; |
| 64 | db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); |
| 65 | if(g.zRepositoryName!=0){ |
| 66 | zPrompt = mprintf("fossil (%z)> ", db_get("project-name","unnamed")); |
| 67 | }else{ |
| 68 | zPrompt = mprintf("fossil (no repo)> "); |
| 69 | } |
| 70 | db_close(0); |
| 71 | sqlite3_shutdown(); |
| 72 | linenoiseSetMultiLine(1); |
| 73 | while( (free(zLine), zLine = linenoise(zPrompt)) ){ |
| 74 | /* Remember shell history within the current session */ |
| 75 |
+3
-1
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -534,17 +534,19 @@ | ||
| 534 | 534 | pParent->idxTop = pRow->idxTop; |
| 535 | 535 | } |
| 536 | 536 | } |
| 537 | 537 | |
| 538 | 538 | if( tmFlags & TIMELINE_FILLGAPS ){ |
| 539 | - /* If a node has no pChild and there is a node higher up in the graph | |
| 539 | + /* If a node has no pChild in the graph | |
| 540 | + ** and there is a node higher up in the graph | |
| 540 | 541 | ** that is in the same branch and has no in-graph parent, then |
| 541 | 542 | ** make the lower node a step-child of the upper node. This will |
| 542 | 543 | ** be represented on the graph by a thick dotted line without an arrowhead. |
| 543 | 544 | */ |
| 544 | 545 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 545 | 546 | if( pRow->pChild ) continue; |
| 547 | + if( pRow->isLeaf ) continue; | |
| 546 | 548 | for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){ |
| 547 | 549 | if( pLoop->nParent>0 |
| 548 | 550 | && pLoop->zBranch==pRow->zBranch |
| 549 | 551 | && hashFind(p,pLoop->aParent[0])==0 |
| 550 | 552 | ){ |
| 551 | 553 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -534,17 +534,19 @@ | |
| 534 | pParent->idxTop = pRow->idxTop; |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | if( tmFlags & TIMELINE_FILLGAPS ){ |
| 539 | /* If a node has no pChild and there is a node higher up in the graph |
| 540 | ** that is in the same branch and has no in-graph parent, then |
| 541 | ** make the lower node a step-child of the upper node. This will |
| 542 | ** be represented on the graph by a thick dotted line without an arrowhead. |
| 543 | */ |
| 544 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 545 | if( pRow->pChild ) continue; |
| 546 | for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){ |
| 547 | if( pLoop->nParent>0 |
| 548 | && pLoop->zBranch==pRow->zBranch |
| 549 | && hashFind(p,pLoop->aParent[0])==0 |
| 550 | ){ |
| 551 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -534,17 +534,19 @@ | |
| 534 | pParent->idxTop = pRow->idxTop; |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | if( tmFlags & TIMELINE_FILLGAPS ){ |
| 539 | /* If a node has no pChild in the graph |
| 540 | ** and there is a node higher up in the graph |
| 541 | ** that is in the same branch and has no in-graph parent, then |
| 542 | ** make the lower node a step-child of the upper node. This will |
| 543 | ** be represented on the graph by a thick dotted line without an arrowhead. |
| 544 | */ |
| 545 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 546 | if( pRow->pChild ) continue; |
| 547 | if( pRow->isLeaf ) continue; |
| 548 | for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){ |
| 549 | if( pLoop->nParent>0 |
| 550 | && pLoop->zBranch==pRow->zBranch |
| 551 | && hashFind(p,pLoop->aParent[0])==0 |
| 552 | ){ |
| 553 |
+228
-121
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -30,10 +30,11 @@ | ||
| 30 | 30 | #ifdef FOSSIL_ENABLE_SSL |
| 31 | 31 | |
| 32 | 32 | #include <openssl/bio.h> |
| 33 | 33 | #include <openssl/ssl.h> |
| 34 | 34 | #include <openssl/err.h> |
| 35 | +#include <openssl/x509.h> | |
| 35 | 36 | |
| 36 | 37 | #include "http_ssl.h" |
| 37 | 38 | #include <assert.h> |
| 38 | 39 | #include <sys/types.h> |
| 39 | 40 | |
| @@ -45,11 +46,15 @@ | ||
| 45 | 46 | static int sslIsInit = 0; /* True after global initialization */ |
| 46 | 47 | static BIO *iBio = 0; /* OpenSSL I/O abstraction */ |
| 47 | 48 | static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */ |
| 48 | 49 | static SSL_CTX *sslCtx; /* SSL context */ |
| 49 | 50 | static SSL *ssl; |
| 50 | - | |
| 51 | +static struct { /* Accept this SSL cert for this session only */ | |
| 52 | + char *zHost; /* Subject or host name */ | |
| 53 | + char *zHash; /* SHA2-256 hash of the cert */ | |
| 54 | +} sException; | |
| 55 | +static int sslNoCertVerify = 0; /* Do not verify SSL certs */ | |
| 51 | 56 | |
| 52 | 57 | /* |
| 53 | 58 | ** Clear the SSL error message |
| 54 | 59 | */ |
| 55 | 60 | static void ssl_clear_errmsg(void){ |
| @@ -184,11 +189,12 @@ | ||
| 184 | 189 | Blob snd, reply; |
| 185 | 190 | int done=0,end=0; |
| 186 | 191 | blob_zero(&snd); |
| 187 | 192 | blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname, |
| 188 | 193 | pUrlData->proxyOrigPort); |
| 189 | - blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort); | |
| 194 | + blob_appendf(&snd, "Host: %s:%d\r\n", | |
| 195 | + pUrlData->hostname, pUrlData->proxyOrigPort); | |
| 190 | 196 | if( pUrlData->proxyAuth ){ |
| 191 | 197 | blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth); |
| 192 | 198 | } |
| 193 | 199 | blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1); |
| 194 | 200 | blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent()); |
| @@ -222,46 +228,45 @@ | ||
| 222 | 228 | }while(!done); |
| 223 | 229 | sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc); |
| 224 | 230 | blob_reset(&reply); |
| 225 | 231 | return rc; |
| 226 | 232 | } |
| 233 | + | |
| 234 | +/* | |
| 235 | +** Invoke this routine to disable SSL cert verification. After | |
| 236 | +** this call is made, any SSL cert that the server provides will | |
| 237 | +** be accepted. Communication will still be encrypted, but the | |
| 238 | +** client has no way of knowing whether it is talking to the | |
| 239 | +** real server or a man-in-the-middle imposter. | |
| 240 | +*/ | |
| 241 | +void ssl_disable_cert_verification(void){ | |
| 242 | + sslNoCertVerify = 1; | |
| 243 | +} | |
| 227 | 244 | |
| 228 | 245 | /* |
| 229 | 246 | ** Open an SSL connection. The identify of the server is determined |
| 230 | 247 | ** as follows: |
| 231 | 248 | ** |
| 232 | -** g.url.name Name of the server. Ex: www.fossil-scm.org | |
| 249 | +** pUrlData->name Name of the server. Ex: www.fossil-scm.org | |
| 250 | +** g.url.name Name of the proxy server, if proxying. | |
| 233 | 251 | ** pUrlData->port TCP/IP port to use. Ex: 80 |
| 234 | 252 | ** |
| 235 | 253 | ** Return the number of errors. |
| 236 | 254 | */ |
| 237 | 255 | int ssl_open(UrlData *pUrlData){ |
| 238 | 256 | X509 *cert; |
| 239 | - int hasSavedCertificate = 0; | |
| 240 | - int trusted = 0; | |
| 241 | - unsigned long e; | |
| 242 | 257 | |
| 243 | 258 | ssl_global_init(); |
| 244 | - | |
| 245 | - /* Get certificate for current server from global config and | |
| 246 | - * (if we have it in config) add it to certificate store. | |
| 247 | - */ | |
| 248 | - cert = ssl_get_certificate(pUrlData, &trusted); | |
| 249 | - if ( cert!=NULL ){ | |
| 250 | - X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); | |
| 251 | - X509_free(cert); | |
| 252 | - hasSavedCertificate = 1; | |
| 253 | - } | |
| 254 | - | |
| 255 | 259 | if( pUrlData->useProxy ){ |
| 256 | 260 | int rc; |
| 257 | 261 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| 258 | 262 | BIO *sBio = BIO_new_connect(connStr); |
| 259 | 263 | free(connStr); |
| 260 | 264 | if( BIO_do_connect(sBio)<=0 ){ |
| 261 | 265 | ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", |
| 262 | - pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error())); | |
| 266 | + pUrlData->name, pUrlData->port, | |
| 267 | + ERR_reason_error_string(ERR_get_error())); | |
| 263 | 268 | ssl_close(); |
| 264 | 269 | return 1; |
| 265 | 270 | } |
| 266 | 271 | rc = establish_proxy_tunnel(pUrlData, sBio); |
| 267 | 272 | if( rc<200||rc>299 ){ |
| @@ -282,11 +287,13 @@ | ||
| 282 | 287 | return 1; |
| 283 | 288 | } |
| 284 | 289 | BIO_get_ssl(iBio, &ssl); |
| 285 | 290 | |
| 286 | 291 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 287 | - if( !SSL_set_tlsext_host_name(ssl, (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) ){ | |
| 292 | + if( !SSL_set_tlsext_host_name(ssl, | |
| 293 | + (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) | |
| 294 | + ){ | |
| 288 | 295 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 289 | 296 | "continuing without it.\n"); |
| 290 | 297 | } |
| 291 | 298 | #endif |
| 292 | 299 | |
| @@ -296,11 +303,12 @@ | ||
| 296 | 303 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 297 | 304 | BIO_set_conn_hostname(iBio, connStr); |
| 298 | 305 | free(connStr); |
| 299 | 306 | if( BIO_do_connect(iBio)<=0 ){ |
| 300 | 307 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 301 | - pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error())); | |
| 308 | + pUrlData->name, pUrlData->port, | |
| 309 | + ERR_reason_error_string(ERR_get_error())); | |
| 302 | 310 | ssl_close(); |
| 303 | 311 | return 1; |
| 304 | 312 | } |
| 305 | 313 | } |
| 306 | 314 | |
| @@ -319,81 +327,78 @@ | ||
| 319 | 327 | ssl_set_errmsg("No SSL certificate was presented by the peer"); |
| 320 | 328 | ssl_close(); |
| 321 | 329 | return 1; |
| 322 | 330 | } |
| 323 | 331 | |
| 324 | - if( trusted<=0 && (e = SSL_get_verify_result(ssl)) != X509_V_OK ){ | |
| 332 | + if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){ | |
| 325 | 333 | char *desc, *prompt; |
| 326 | - const char *warning = ""; | |
| 327 | 334 | Blob ans; |
| 328 | 335 | char cReply; |
| 329 | 336 | BIO *mem; |
| 330 | 337 | unsigned char md[32]; |
| 331 | - unsigned int mdLength = 31; | |
| 332 | - | |
| 333 | - mem = BIO_new(BIO_s_mem()); | |
| 334 | - X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE); | |
| 335 | - BIO_puts(mem, "\n\nIssued By:\n\n"); | |
| 336 | - X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE); | |
| 337 | - BIO_puts(mem, "\n\nSHA1 Fingerprint:\n\n "); | |
| 338 | - if(X509_digest(cert, EVP_sha1(), md, &mdLength)){ | |
| 339 | - int j; | |
| 340 | - for( j = 0; j < mdLength; ++j ) { | |
| 341 | - BIO_printf(mem, " %02x", md[j]); | |
| 342 | - } | |
| 343 | - } | |
| 344 | - BIO_write(mem, "", 1); /* nul-terminate mem buffer */ | |
| 345 | - BIO_get_mem_data(mem, &desc); | |
| 346 | - | |
| 347 | - if( hasSavedCertificate ){ | |
| 348 | - warning = "WARNING: Certificate doesn't match the " | |
| 349 | - "saved certificate for this host!"; | |
| 350 | - } | |
| 351 | - prompt = mprintf("\nSSL verification failed: %s\n" | |
| 352 | - "Certificate received: \n\n%s\n\n%s\n" | |
| 353 | - "Either:\n" | |
| 354 | - " * verify the certificate is correct using the " | |
| 355 | - "SHA1 fingerprint above\n" | |
| 356 | - " * use the global ssl-ca-location setting to specify your CA root\n" | |
| 357 | - " certificates list\n\n" | |
| 358 | - "If you are not expecting this message, answer no and " | |
| 359 | - "contact your server\nadministrator.\n\n" | |
| 360 | - "Accept certificate for host %s (a=always/y/N)? ", | |
| 361 | - X509_verify_cert_error_string(e), desc, warning, | |
| 362 | - pUrlData->useProxy?pUrlData->hostname:pUrlData->name); | |
| 363 | - BIO_free(mem); | |
| 364 | - | |
| 365 | - prompt_user(prompt, &ans); | |
| 366 | - free(prompt); | |
| 367 | - cReply = blob_str(&ans)[0]; | |
| 368 | - blob_reset(&ans); | |
| 369 | - if( cReply!='y' && cReply!='Y' && cReply!='a' && cReply!='A') { | |
| 370 | - X509_free(cert); | |
| 371 | - ssl_set_errmsg("SSL certificate declined"); | |
| 372 | - ssl_close(); | |
| 373 | - return 1; | |
| 374 | - } | |
| 375 | - if( cReply=='a' || cReply=='A') { | |
| 376 | - if ( trusted==0 ){ | |
| 377 | - prompt_user("\nSave this certificate as fully trusted (a=always/N)? ", | |
| 378 | - &ans); | |
| 379 | - cReply = blob_str(&ans)[0]; | |
| 380 | - trusted = ( cReply=='a' || cReply=='A' ); | |
| 381 | - blob_reset(&ans); | |
| 382 | - } | |
| 383 | - ssl_save_certificate(pUrlData, cert, trusted); | |
| 338 | + char zHash[32*2+1]; | |
| 339 | + unsigned int mdLength = (int)sizeof(md); | |
| 340 | + | |
| 341 | + memset(md, 0, sizeof(md)); | |
| 342 | + zHash[0] = 0; | |
| 343 | + if( X509_digest(cert, EVP_sha256(), md, &mdLength) ){ | |
| 344 | + int j; | |
| 345 | + for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){ | |
| 346 | + zHash[j*2] = "0123456789abcdef"[md[j]>>4]; | |
| 347 | + zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf]; | |
| 348 | + } | |
| 349 | + zHash[j*2] = 0; | |
| 350 | + } | |
| 351 | + | |
| 352 | + if( ssl_certificate_exception_exists(pUrlData, zHash) ){ | |
| 353 | + /* Ignore the failure because an exception exists */ | |
| 354 | + ssl_one_time_exception(pUrlData, zHash); | |
| 355 | + }else{ | |
| 356 | + /* Tell the user about the failure and ask what to do */ | |
| 357 | + mem = BIO_new(BIO_s_mem()); | |
| 358 | + BIO_puts(mem, " subject: "); | |
| 359 | + X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0, XN_FLAG_ONELINE); | |
| 360 | + BIO_puts(mem, "\n issuer: "); | |
| 361 | + X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE); | |
| 362 | + BIO_printf(mem, "\n sha256: %s", zHash); | |
| 363 | + BIO_get_mem_data(mem, &desc); | |
| 364 | + | |
| 365 | + prompt = mprintf("Unable to verify SSL cert from %s\n%s\n" | |
| 366 | + "accept this cert and continue (y/N)? ", | |
| 367 | + pUrlData->name, desc); | |
| 368 | + BIO_free(mem); | |
| 369 | + | |
| 370 | + prompt_user(prompt, &ans); | |
| 371 | + free(prompt); | |
| 372 | + cReply = blob_str(&ans)[0]; | |
| 373 | + blob_reset(&ans); | |
| 374 | + if( cReply!='y' && cReply!='Y' ){ | |
| 375 | + X509_free(cert); | |
| 376 | + ssl_set_errmsg("SSL cert declined"); | |
| 377 | + ssl_close(); | |
| 378 | + return 1; | |
| 379 | + } | |
| 380 | + ssl_one_time_exception(pUrlData, zHash); | |
| 381 | + prompt_user("remember this exception (y/N)? ", &ans); | |
| 382 | + cReply = blob_str(&ans)[0]; | |
| 383 | + if( cReply=='y' || cReply=='Y') { | |
| 384 | + ssl_remember_certificate_exception(pUrlData, zHash); | |
| 385 | + } | |
| 386 | + blob_reset(&ans); | |
| 384 | 387 | } |
| 385 | 388 | } |
| 386 | 389 | |
| 387 | 390 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 388 | 391 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| 389 | 392 | ** if any files are received from the server. |
| 390 | 393 | */ |
| 391 | 394 | { |
| 392 | - /* As soon as libressl implements BIO_ADDR_hostname_string/BIO_get_conn_address. | |
| 393 | - * check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable */ | |
| 394 | - #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ | |
| 395 | + /* As soon as libressl implements | |
| 396 | + ** BIO_ADDR_hostname_string/BIO_get_conn_address. | |
| 397 | + ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable | |
| 398 | + */ | |
| 399 | +#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ | |
| 395 | 400 | && !defined(LIBRESSL_VERSION_NUMBER) |
| 396 | 401 | char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1); |
| 397 | 402 | g.zIpAddr = mprintf("%s", ip); |
| 398 | 403 | OPENSSL_free(ip); |
| 399 | 404 | #else |
| @@ -407,58 +412,53 @@ | ||
| 407 | 412 | X509_free(cert); |
| 408 | 413 | return 0; |
| 409 | 414 | } |
| 410 | 415 | |
| 411 | 416 | /* |
| 412 | -** Save certificate to global config. | |
| 417 | +** Remember that the cert with the given hash is a acceptable for | |
| 418 | +** use with pUrlData->name. | |
| 419 | +*/ | |
| 420 | +LOCAL void ssl_remember_certificate_exception( | |
| 421 | + UrlData *pUrlData, | |
| 422 | + const char *zHash | |
| 423 | +){ | |
| 424 | + char *zName = mprintf("cert:%s", pUrlData->name); | |
| 425 | + db_set(zName, zHash, 1); | |
| 426 | + fossil_free(zName); | |
| 427 | +} | |
| 428 | + | |
| 429 | +/* | |
| 430 | +** Return true if the there exists a certificate exception for | |
| 431 | +** pUrlData->name that matches the hash. | |
| 413 | 432 | */ |
| 414 | -void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){ | |
| 415 | - BIO *mem; | |
| 416 | - char *zCert, *zHost; | |
| 417 | - | |
| 418 | - mem = BIO_new(BIO_s_mem()); | |
| 419 | - PEM_write_bio_X509(mem, cert); | |
| 420 | - BIO_write(mem, "", 1); /* nul-terminate mem buffer */ | |
| 421 | - BIO_get_mem_data(mem, &zCert); | |
| 422 | - zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name); | |
| 423 | - db_set(zHost, zCert, 1); | |
| 424 | - free(zHost); | |
| 425 | - zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name); | |
| 426 | - db_set_int(zHost, trusted, 1); | |
| 427 | - free(zHost); | |
| 428 | - BIO_free(mem); | |
| 433 | +LOCAL int ssl_certificate_exception_exists( | |
| 434 | + UrlData *pUrlData, | |
| 435 | + const char *zHash | |
| 436 | +){ | |
| 437 | + char *zName, *zValue; | |
| 438 | + if( fossil_strcmp(sException.zHost,pUrlData->name)==0 | |
| 439 | + && fossil_strcmp(sException.zHash,zHash)==0 | |
| 440 | + ){ | |
| 441 | + return 1; | |
| 442 | + } | |
| 443 | + zName = mprintf("cert:%s", pUrlData->name); | |
| 444 | + zValue = db_get(zName,0); | |
| 445 | + fossil_free(zName); | |
| 446 | + return zValue!=0 && strcmp(zHash,zValue)==0; | |
| 429 | 447 | } |
| 430 | 448 | |
| 431 | 449 | /* |
| 432 | -** Get certificate for pUrlData->urlName from global config. | |
| 433 | -** Return NULL if no certificate found. | |
| 450 | +** Remember zHash as an acceptable certificate for this session only. | |
| 434 | 451 | */ |
| 435 | -X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){ | |
| 436 | - char *zHost, *zCert; | |
| 437 | - BIO *mem; | |
| 438 | - X509 *cert; | |
| 439 | - | |
| 440 | - zHost = mprintf("cert:%s", | |
| 441 | - pUrlData->useProxy ? pUrlData->hostname : pUrlData->name); | |
| 442 | - zCert = db_get(zHost, NULL); | |
| 443 | - free(zHost); | |
| 444 | - if ( zCert==NULL ) | |
| 445 | - return NULL; | |
| 446 | - | |
| 447 | - if ( pTrusted!=0 ){ | |
| 448 | - zHost = mprintf("trusted:%s", | |
| 449 | - pUrlData->useProxy ? pUrlData->hostname : pUrlData->name); | |
| 450 | - *pTrusted = db_get_int(zHost, 0); | |
| 451 | - free(zHost); | |
| 452 | - } | |
| 453 | - | |
| 454 | - mem = BIO_new(BIO_s_mem()); | |
| 455 | - BIO_puts(mem, zCert); | |
| 456 | - cert = PEM_read_bio_X509(mem, NULL, 0, NULL); | |
| 457 | - free(zCert); | |
| 458 | - BIO_free(mem); | |
| 459 | - return cert; | |
| 452 | +LOCAL void ssl_one_time_exception( | |
| 453 | + UrlData *pUrlData, | |
| 454 | + const char *zHash | |
| 455 | +){ | |
| 456 | + fossil_free(sException.zHost); | |
| 457 | + sException.zHost = fossil_strdup(pUrlData->name); | |
| 458 | + fossil_free(sException.zHash); | |
| 459 | + sException.zHash = fossil_strdup(zHash); | |
| 460 | 460 | } |
| 461 | 461 | |
| 462 | 462 | /* |
| 463 | 463 | ** Send content out over the SSL connection. |
| 464 | 464 | */ |
| @@ -498,5 +498,112 @@ | ||
| 498 | 498 | } |
| 499 | 499 | return total; |
| 500 | 500 | } |
| 501 | 501 | |
| 502 | 502 | #endif /* FOSSIL_ENABLE_SSL */ |
| 503 | + | |
| 504 | +/* | |
| 505 | +** COMMAND: tls-config* | |
| 506 | +** | |
| 507 | +** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...] | |
| 508 | +** | |
| 509 | +** This command is used to view or modify the TLS (Transport Layer | |
| 510 | +** Security) configuration for Fossil. TLS (formerly SSL) is the | |
| 511 | +** encryption technology used for secure HTTPS transport. | |
| 512 | +** | |
| 513 | +** Sub-commands: | |
| 514 | +** | |
| 515 | +** show Show the TLS configuration | |
| 516 | +** | |
| 517 | +** remove-exception DOMAIN... Remove TLS cert exceptions | |
| 518 | +** for the domains listed. Or if | |
| 519 | +** the --all option is specified, | |
| 520 | +** remove all TLS cert exceptions. | |
| 521 | +*/ | |
| 522 | +void test_tlsconfig_info(void){ | |
| 523 | + const char *zCmd; | |
| 524 | + size_t nCmd; | |
| 525 | + int nHit = 0; | |
| 526 | +#if !defined(FOSSIL_ENABLE_SSL) | |
| 527 | + fossil_print("TLS disabled in this build\n"); | |
| 528 | +#else | |
| 529 | + db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); | |
| 530 | + db_open_config(1,0); | |
| 531 | + zCmd = g.argc>=3 ? g.argv[2] : "show"; | |
| 532 | + nCmd = strlen(zCmd); | |
| 533 | + if( strncmp("show",zCmd,nCmd)==0 ){ | |
| 534 | + const char *zName, *zValue; | |
| 535 | + size_t nName; | |
| 536 | + Stmt q; | |
| 537 | + fossil_print("OpenSSL-version: %s\n", SSLeay_version(SSLEAY_VERSION)); | |
| 538 | + fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); | |
| 539 | + fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); | |
| 540 | + zName = X509_get_default_cert_file_env(); | |
| 541 | + zValue = fossil_getenv(zName); | |
| 542 | + if( zValue==0 ) zValue = ""; | |
| 543 | + nName = strlen(zName); | |
| 544 | + fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue); | |
| 545 | + zName = X509_get_default_cert_dir_env(); | |
| 546 | + zValue = fossil_getenv(zName); | |
| 547 | + if( zValue==0 ) zValue = ""; | |
| 548 | + nName = strlen(zName); | |
| 549 | + fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue); | |
| 550 | + nHit++; | |
| 551 | + fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location","")); | |
| 552 | + fossil_print("ssl-identity: %s\n", db_get("ssl-identity","")); | |
| 553 | + db_prepare(&q, | |
| 554 | + "SELECT name FROM global_config" | |
| 555 | + " WHERE name GLOB 'cert:*'" | |
| 556 | + "UNION ALL " | |
| 557 | + "SELECT name FROM config" | |
| 558 | + " WHERE name GLOB 'cert:*'" | |
| 559 | + " ORDER BY name" | |
| 560 | + ); | |
| 561 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 562 | + fossil_print("exception: %s\n", db_column_text(&q,0)+5); | |
| 563 | + } | |
| 564 | + db_finalize(&q); | |
| 565 | + }else | |
| 566 | + if( strncmp("remove-exception",zCmd,nCmd)==0 ){ | |
| 567 | + int i; | |
| 568 | + Blob sql; | |
| 569 | + char *zSep = "("; | |
| 570 | + db_begin_transaction(); | |
| 571 | + blob_init(&sql, 0, 0); | |
| 572 | + if( g.argc==4 && find_option("all",0,0)!=0 ){ | |
| 573 | + blob_append_sql(&sql, | |
| 574 | + "DELETE FROM global_config WHERE name GLOB 'cert:*';\n" | |
| 575 | + "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n" | |
| 576 | + "DELETE FROM config WHERE name GLOB 'cert:*';\n" | |
| 577 | + "DELETE FROM config WHERE name GLOB 'trusted:*';\n" | |
| 578 | + ); | |
| 579 | + }else{ | |
| 580 | + if( g.argc<4 ){ | |
| 581 | + usage("remove-exception DOMAIN-NAME ..."); | |
| 582 | + } | |
| 583 | + blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN "); | |
| 584 | + for(i=3; i<g.argc; i++){ | |
| 585 | + blob_append_sql(&sql,"%s'cert:%q','trust:%q'", | |
| 586 | + zSep/*safe-for-%s*/, g.argv[i], g.argv[i]); | |
| 587 | + zSep = ","; | |
| 588 | + } | |
| 589 | + blob_append_sql(&sql,");\n"); | |
| 590 | + zSep = "("; | |
| 591 | + blob_append_sql(&sql,"DELETE FROM config WHERE name IN "); | |
| 592 | + for(i=3; i<g.argc; i++){ | |
| 593 | + blob_append_sql(&sql,"%s'cert:%q','trusted:%q'", | |
| 594 | + zSep/*safe-for-%s*/, g.argv[i], g.argv[i]); | |
| 595 | + zSep = ","; | |
| 596 | + } | |
| 597 | + blob_append_sql(&sql,");"); | |
| 598 | + } | |
| 599 | + db_exec_sql(blob_str(&sql)); | |
| 600 | + db_commit_transaction(); | |
| 601 | + blob_reset(&sql); | |
| 602 | + }else | |
| 603 | + /*default*/{ | |
| 604 | + fossil_fatal("unknown sub-command \"%s\".\nshould be one of:" | |
| 605 | + " remove-exception show", | |
| 606 | + zCmd); | |
| 607 | + } | |
| 608 | +#endif | |
| 609 | +} | |
| 503 | 610 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -30,10 +30,11 @@ | |
| 30 | #ifdef FOSSIL_ENABLE_SSL |
| 31 | |
| 32 | #include <openssl/bio.h> |
| 33 | #include <openssl/ssl.h> |
| 34 | #include <openssl/err.h> |
| 35 | |
| 36 | #include "http_ssl.h" |
| 37 | #include <assert.h> |
| 38 | #include <sys/types.h> |
| 39 | |
| @@ -45,11 +46,15 @@ | |
| 45 | static int sslIsInit = 0; /* True after global initialization */ |
| 46 | static BIO *iBio = 0; /* OpenSSL I/O abstraction */ |
| 47 | static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */ |
| 48 | static SSL_CTX *sslCtx; /* SSL context */ |
| 49 | static SSL *ssl; |
| 50 | |
| 51 | |
| 52 | /* |
| 53 | ** Clear the SSL error message |
| 54 | */ |
| 55 | static void ssl_clear_errmsg(void){ |
| @@ -184,11 +189,12 @@ | |
| 184 | Blob snd, reply; |
| 185 | int done=0,end=0; |
| 186 | blob_zero(&snd); |
| 187 | blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname, |
| 188 | pUrlData->proxyOrigPort); |
| 189 | blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort); |
| 190 | if( pUrlData->proxyAuth ){ |
| 191 | blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth); |
| 192 | } |
| 193 | blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1); |
| 194 | blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent()); |
| @@ -222,46 +228,45 @@ | |
| 222 | }while(!done); |
| 223 | sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc); |
| 224 | blob_reset(&reply); |
| 225 | return rc; |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | ** Open an SSL connection. The identify of the server is determined |
| 230 | ** as follows: |
| 231 | ** |
| 232 | ** g.url.name Name of the server. Ex: www.fossil-scm.org |
| 233 | ** pUrlData->port TCP/IP port to use. Ex: 80 |
| 234 | ** |
| 235 | ** Return the number of errors. |
| 236 | */ |
| 237 | int ssl_open(UrlData *pUrlData){ |
| 238 | X509 *cert; |
| 239 | int hasSavedCertificate = 0; |
| 240 | int trusted = 0; |
| 241 | unsigned long e; |
| 242 | |
| 243 | ssl_global_init(); |
| 244 | |
| 245 | /* Get certificate for current server from global config and |
| 246 | * (if we have it in config) add it to certificate store. |
| 247 | */ |
| 248 | cert = ssl_get_certificate(pUrlData, &trusted); |
| 249 | if ( cert!=NULL ){ |
| 250 | X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); |
| 251 | X509_free(cert); |
| 252 | hasSavedCertificate = 1; |
| 253 | } |
| 254 | |
| 255 | if( pUrlData->useProxy ){ |
| 256 | int rc; |
| 257 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| 258 | BIO *sBio = BIO_new_connect(connStr); |
| 259 | free(connStr); |
| 260 | if( BIO_do_connect(sBio)<=0 ){ |
| 261 | ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", |
| 262 | pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error())); |
| 263 | ssl_close(); |
| 264 | return 1; |
| 265 | } |
| 266 | rc = establish_proxy_tunnel(pUrlData, sBio); |
| 267 | if( rc<200||rc>299 ){ |
| @@ -282,11 +287,13 @@ | |
| 282 | return 1; |
| 283 | } |
| 284 | BIO_get_ssl(iBio, &ssl); |
| 285 | |
| 286 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 287 | if( !SSL_set_tlsext_host_name(ssl, (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) ){ |
| 288 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 289 | "continuing without it.\n"); |
| 290 | } |
| 291 | #endif |
| 292 | |
| @@ -296,11 +303,12 @@ | |
| 296 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 297 | BIO_set_conn_hostname(iBio, connStr); |
| 298 | free(connStr); |
| 299 | if( BIO_do_connect(iBio)<=0 ){ |
| 300 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 301 | pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error())); |
| 302 | ssl_close(); |
| 303 | return 1; |
| 304 | } |
| 305 | } |
| 306 | |
| @@ -319,81 +327,78 @@ | |
| 319 | ssl_set_errmsg("No SSL certificate was presented by the peer"); |
| 320 | ssl_close(); |
| 321 | return 1; |
| 322 | } |
| 323 | |
| 324 | if( trusted<=0 && (e = SSL_get_verify_result(ssl)) != X509_V_OK ){ |
| 325 | char *desc, *prompt; |
| 326 | const char *warning = ""; |
| 327 | Blob ans; |
| 328 | char cReply; |
| 329 | BIO *mem; |
| 330 | unsigned char md[32]; |
| 331 | unsigned int mdLength = 31; |
| 332 | |
| 333 | mem = BIO_new(BIO_s_mem()); |
| 334 | X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE); |
| 335 | BIO_puts(mem, "\n\nIssued By:\n\n"); |
| 336 | X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE); |
| 337 | BIO_puts(mem, "\n\nSHA1 Fingerprint:\n\n "); |
| 338 | if(X509_digest(cert, EVP_sha1(), md, &mdLength)){ |
| 339 | int j; |
| 340 | for( j = 0; j < mdLength; ++j ) { |
| 341 | BIO_printf(mem, " %02x", md[j]); |
| 342 | } |
| 343 | } |
| 344 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 345 | BIO_get_mem_data(mem, &desc); |
| 346 | |
| 347 | if( hasSavedCertificate ){ |
| 348 | warning = "WARNING: Certificate doesn't match the " |
| 349 | "saved certificate for this host!"; |
| 350 | } |
| 351 | prompt = mprintf("\nSSL verification failed: %s\n" |
| 352 | "Certificate received: \n\n%s\n\n%s\n" |
| 353 | "Either:\n" |
| 354 | " * verify the certificate is correct using the " |
| 355 | "SHA1 fingerprint above\n" |
| 356 | " * use the global ssl-ca-location setting to specify your CA root\n" |
| 357 | " certificates list\n\n" |
| 358 | "If you are not expecting this message, answer no and " |
| 359 | "contact your server\nadministrator.\n\n" |
| 360 | "Accept certificate for host %s (a=always/y/N)? ", |
| 361 | X509_verify_cert_error_string(e), desc, warning, |
| 362 | pUrlData->useProxy?pUrlData->hostname:pUrlData->name); |
| 363 | BIO_free(mem); |
| 364 | |
| 365 | prompt_user(prompt, &ans); |
| 366 | free(prompt); |
| 367 | cReply = blob_str(&ans)[0]; |
| 368 | blob_reset(&ans); |
| 369 | if( cReply!='y' && cReply!='Y' && cReply!='a' && cReply!='A') { |
| 370 | X509_free(cert); |
| 371 | ssl_set_errmsg("SSL certificate declined"); |
| 372 | ssl_close(); |
| 373 | return 1; |
| 374 | } |
| 375 | if( cReply=='a' || cReply=='A') { |
| 376 | if ( trusted==0 ){ |
| 377 | prompt_user("\nSave this certificate as fully trusted (a=always/N)? ", |
| 378 | &ans); |
| 379 | cReply = blob_str(&ans)[0]; |
| 380 | trusted = ( cReply=='a' || cReply=='A' ); |
| 381 | blob_reset(&ans); |
| 382 | } |
| 383 | ssl_save_certificate(pUrlData, cert, trusted); |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 388 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| 389 | ** if any files are received from the server. |
| 390 | */ |
| 391 | { |
| 392 | /* As soon as libressl implements BIO_ADDR_hostname_string/BIO_get_conn_address. |
| 393 | * check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable */ |
| 394 | #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ |
| 395 | && !defined(LIBRESSL_VERSION_NUMBER) |
| 396 | char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1); |
| 397 | g.zIpAddr = mprintf("%s", ip); |
| 398 | OPENSSL_free(ip); |
| 399 | #else |
| @@ -407,58 +412,53 @@ | |
| 407 | X509_free(cert); |
| 408 | return 0; |
| 409 | } |
| 410 | |
| 411 | /* |
| 412 | ** Save certificate to global config. |
| 413 | */ |
| 414 | void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){ |
| 415 | BIO *mem; |
| 416 | char *zCert, *zHost; |
| 417 | |
| 418 | mem = BIO_new(BIO_s_mem()); |
| 419 | PEM_write_bio_X509(mem, cert); |
| 420 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 421 | BIO_get_mem_data(mem, &zCert); |
| 422 | zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name); |
| 423 | db_set(zHost, zCert, 1); |
| 424 | free(zHost); |
| 425 | zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name); |
| 426 | db_set_int(zHost, trusted, 1); |
| 427 | free(zHost); |
| 428 | BIO_free(mem); |
| 429 | } |
| 430 | |
| 431 | /* |
| 432 | ** Get certificate for pUrlData->urlName from global config. |
| 433 | ** Return NULL if no certificate found. |
| 434 | */ |
| 435 | X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){ |
| 436 | char *zHost, *zCert; |
| 437 | BIO *mem; |
| 438 | X509 *cert; |
| 439 | |
| 440 | zHost = mprintf("cert:%s", |
| 441 | pUrlData->useProxy ? pUrlData->hostname : pUrlData->name); |
| 442 | zCert = db_get(zHost, NULL); |
| 443 | free(zHost); |
| 444 | if ( zCert==NULL ) |
| 445 | return NULL; |
| 446 | |
| 447 | if ( pTrusted!=0 ){ |
| 448 | zHost = mprintf("trusted:%s", |
| 449 | pUrlData->useProxy ? pUrlData->hostname : pUrlData->name); |
| 450 | *pTrusted = db_get_int(zHost, 0); |
| 451 | free(zHost); |
| 452 | } |
| 453 | |
| 454 | mem = BIO_new(BIO_s_mem()); |
| 455 | BIO_puts(mem, zCert); |
| 456 | cert = PEM_read_bio_X509(mem, NULL, 0, NULL); |
| 457 | free(zCert); |
| 458 | BIO_free(mem); |
| 459 | return cert; |
| 460 | } |
| 461 | |
| 462 | /* |
| 463 | ** Send content out over the SSL connection. |
| 464 | */ |
| @@ -498,5 +498,112 @@ | |
| 498 | } |
| 499 | return total; |
| 500 | } |
| 501 | |
| 502 | #endif /* FOSSIL_ENABLE_SSL */ |
| 503 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -30,10 +30,11 @@ | |
| 30 | #ifdef FOSSIL_ENABLE_SSL |
| 31 | |
| 32 | #include <openssl/bio.h> |
| 33 | #include <openssl/ssl.h> |
| 34 | #include <openssl/err.h> |
| 35 | #include <openssl/x509.h> |
| 36 | |
| 37 | #include "http_ssl.h" |
| 38 | #include <assert.h> |
| 39 | #include <sys/types.h> |
| 40 | |
| @@ -45,11 +46,15 @@ | |
| 46 | static int sslIsInit = 0; /* True after global initialization */ |
| 47 | static BIO *iBio = 0; /* OpenSSL I/O abstraction */ |
| 48 | static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */ |
| 49 | static SSL_CTX *sslCtx; /* SSL context */ |
| 50 | static SSL *ssl; |
| 51 | static struct { /* Accept this SSL cert for this session only */ |
| 52 | char *zHost; /* Subject or host name */ |
| 53 | char *zHash; /* SHA2-256 hash of the cert */ |
| 54 | } sException; |
| 55 | static int sslNoCertVerify = 0; /* Do not verify SSL certs */ |
| 56 | |
| 57 | /* |
| 58 | ** Clear the SSL error message |
| 59 | */ |
| 60 | static void ssl_clear_errmsg(void){ |
| @@ -184,11 +189,12 @@ | |
| 189 | Blob snd, reply; |
| 190 | int done=0,end=0; |
| 191 | blob_zero(&snd); |
| 192 | blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname, |
| 193 | pUrlData->proxyOrigPort); |
| 194 | blob_appendf(&snd, "Host: %s:%d\r\n", |
| 195 | pUrlData->hostname, pUrlData->proxyOrigPort); |
| 196 | if( pUrlData->proxyAuth ){ |
| 197 | blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth); |
| 198 | } |
| 199 | blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1); |
| 200 | blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent()); |
| @@ -222,46 +228,45 @@ | |
| 228 | }while(!done); |
| 229 | sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc); |
| 230 | blob_reset(&reply); |
| 231 | return rc; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | ** Invoke this routine to disable SSL cert verification. After |
| 236 | ** this call is made, any SSL cert that the server provides will |
| 237 | ** be accepted. Communication will still be encrypted, but the |
| 238 | ** client has no way of knowing whether it is talking to the |
| 239 | ** real server or a man-in-the-middle imposter. |
| 240 | */ |
| 241 | void ssl_disable_cert_verification(void){ |
| 242 | sslNoCertVerify = 1; |
| 243 | } |
| 244 | |
| 245 | /* |
| 246 | ** Open an SSL connection. The identify of the server is determined |
| 247 | ** as follows: |
| 248 | ** |
| 249 | ** pUrlData->name Name of the server. Ex: www.fossil-scm.org |
| 250 | ** g.url.name Name of the proxy server, if proxying. |
| 251 | ** pUrlData->port TCP/IP port to use. Ex: 80 |
| 252 | ** |
| 253 | ** Return the number of errors. |
| 254 | */ |
| 255 | int ssl_open(UrlData *pUrlData){ |
| 256 | X509 *cert; |
| 257 | |
| 258 | ssl_global_init(); |
| 259 | if( pUrlData->useProxy ){ |
| 260 | int rc; |
| 261 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| 262 | BIO *sBio = BIO_new_connect(connStr); |
| 263 | free(connStr); |
| 264 | if( BIO_do_connect(sBio)<=0 ){ |
| 265 | ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", |
| 266 | pUrlData->name, pUrlData->port, |
| 267 | ERR_reason_error_string(ERR_get_error())); |
| 268 | ssl_close(); |
| 269 | return 1; |
| 270 | } |
| 271 | rc = establish_proxy_tunnel(pUrlData, sBio); |
| 272 | if( rc<200||rc>299 ){ |
| @@ -282,11 +287,13 @@ | |
| 287 | return 1; |
| 288 | } |
| 289 | BIO_get_ssl(iBio, &ssl); |
| 290 | |
| 291 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 292 | if( !SSL_set_tlsext_host_name(ssl, |
| 293 | (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) |
| 294 | ){ |
| 295 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 296 | "continuing without it.\n"); |
| 297 | } |
| 298 | #endif |
| 299 | |
| @@ -296,11 +303,12 @@ | |
| 303 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 304 | BIO_set_conn_hostname(iBio, connStr); |
| 305 | free(connStr); |
| 306 | if( BIO_do_connect(iBio)<=0 ){ |
| 307 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 308 | pUrlData->name, pUrlData->port, |
| 309 | ERR_reason_error_string(ERR_get_error())); |
| 310 | ssl_close(); |
| 311 | return 1; |
| 312 | } |
| 313 | } |
| 314 | |
| @@ -319,81 +327,78 @@ | |
| 327 | ssl_set_errmsg("No SSL certificate was presented by the peer"); |
| 328 | ssl_close(); |
| 329 | return 1; |
| 330 | } |
| 331 | |
| 332 | if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){ |
| 333 | char *desc, *prompt; |
| 334 | Blob ans; |
| 335 | char cReply; |
| 336 | BIO *mem; |
| 337 | unsigned char md[32]; |
| 338 | char zHash[32*2+1]; |
| 339 | unsigned int mdLength = (int)sizeof(md); |
| 340 | |
| 341 | memset(md, 0, sizeof(md)); |
| 342 | zHash[0] = 0; |
| 343 | if( X509_digest(cert, EVP_sha256(), md, &mdLength) ){ |
| 344 | int j; |
| 345 | for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){ |
| 346 | zHash[j*2] = "0123456789abcdef"[md[j]>>4]; |
| 347 | zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf]; |
| 348 | } |
| 349 | zHash[j*2] = 0; |
| 350 | } |
| 351 | |
| 352 | if( ssl_certificate_exception_exists(pUrlData, zHash) ){ |
| 353 | /* Ignore the failure because an exception exists */ |
| 354 | ssl_one_time_exception(pUrlData, zHash); |
| 355 | }else{ |
| 356 | /* Tell the user about the failure and ask what to do */ |
| 357 | mem = BIO_new(BIO_s_mem()); |
| 358 | BIO_puts(mem, " subject: "); |
| 359 | X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0, XN_FLAG_ONELINE); |
| 360 | BIO_puts(mem, "\n issuer: "); |
| 361 | X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE); |
| 362 | BIO_printf(mem, "\n sha256: %s", zHash); |
| 363 | BIO_get_mem_data(mem, &desc); |
| 364 | |
| 365 | prompt = mprintf("Unable to verify SSL cert from %s\n%s\n" |
| 366 | "accept this cert and continue (y/N)? ", |
| 367 | pUrlData->name, desc); |
| 368 | BIO_free(mem); |
| 369 | |
| 370 | prompt_user(prompt, &ans); |
| 371 | free(prompt); |
| 372 | cReply = blob_str(&ans)[0]; |
| 373 | blob_reset(&ans); |
| 374 | if( cReply!='y' && cReply!='Y' ){ |
| 375 | X509_free(cert); |
| 376 | ssl_set_errmsg("SSL cert declined"); |
| 377 | ssl_close(); |
| 378 | return 1; |
| 379 | } |
| 380 | ssl_one_time_exception(pUrlData, zHash); |
| 381 | prompt_user("remember this exception (y/N)? ", &ans); |
| 382 | cReply = blob_str(&ans)[0]; |
| 383 | if( cReply=='y' || cReply=='Y') { |
| 384 | ssl_remember_certificate_exception(pUrlData, zHash); |
| 385 | } |
| 386 | blob_reset(&ans); |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 391 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| 392 | ** if any files are received from the server. |
| 393 | */ |
| 394 | { |
| 395 | /* As soon as libressl implements |
| 396 | ** BIO_ADDR_hostname_string/BIO_get_conn_address. |
| 397 | ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable |
| 398 | */ |
| 399 | #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ |
| 400 | && !defined(LIBRESSL_VERSION_NUMBER) |
| 401 | char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1); |
| 402 | g.zIpAddr = mprintf("%s", ip); |
| 403 | OPENSSL_free(ip); |
| 404 | #else |
| @@ -407,58 +412,53 @@ | |
| 412 | X509_free(cert); |
| 413 | return 0; |
| 414 | } |
| 415 | |
| 416 | /* |
| 417 | ** Remember that the cert with the given hash is a acceptable for |
| 418 | ** use with pUrlData->name. |
| 419 | */ |
| 420 | LOCAL void ssl_remember_certificate_exception( |
| 421 | UrlData *pUrlData, |
| 422 | const char *zHash |
| 423 | ){ |
| 424 | char *zName = mprintf("cert:%s", pUrlData->name); |
| 425 | db_set(zName, zHash, 1); |
| 426 | fossil_free(zName); |
| 427 | } |
| 428 | |
| 429 | /* |
| 430 | ** Return true if the there exists a certificate exception for |
| 431 | ** pUrlData->name that matches the hash. |
| 432 | */ |
| 433 | LOCAL int ssl_certificate_exception_exists( |
| 434 | UrlData *pUrlData, |
| 435 | const char *zHash |
| 436 | ){ |
| 437 | char *zName, *zValue; |
| 438 | if( fossil_strcmp(sException.zHost,pUrlData->name)==0 |
| 439 | && fossil_strcmp(sException.zHash,zHash)==0 |
| 440 | ){ |
| 441 | return 1; |
| 442 | } |
| 443 | zName = mprintf("cert:%s", pUrlData->name); |
| 444 | zValue = db_get(zName,0); |
| 445 | fossil_free(zName); |
| 446 | return zValue!=0 && strcmp(zHash,zValue)==0; |
| 447 | } |
| 448 | |
| 449 | /* |
| 450 | ** Remember zHash as an acceptable certificate for this session only. |
| 451 | */ |
| 452 | LOCAL void ssl_one_time_exception( |
| 453 | UrlData *pUrlData, |
| 454 | const char *zHash |
| 455 | ){ |
| 456 | fossil_free(sException.zHost); |
| 457 | sException.zHost = fossil_strdup(pUrlData->name); |
| 458 | fossil_free(sException.zHash); |
| 459 | sException.zHash = fossil_strdup(zHash); |
| 460 | } |
| 461 | |
| 462 | /* |
| 463 | ** Send content out over the SSL connection. |
| 464 | */ |
| @@ -498,5 +498,112 @@ | |
| 498 | } |
| 499 | return total; |
| 500 | } |
| 501 | |
| 502 | #endif /* FOSSIL_ENABLE_SSL */ |
| 503 | |
| 504 | /* |
| 505 | ** COMMAND: tls-config* |
| 506 | ** |
| 507 | ** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 508 | ** |
| 509 | ** This command is used to view or modify the TLS (Transport Layer |
| 510 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 511 | ** encryption technology used for secure HTTPS transport. |
| 512 | ** |
| 513 | ** Sub-commands: |
| 514 | ** |
| 515 | ** show Show the TLS configuration |
| 516 | ** |
| 517 | ** remove-exception DOMAIN... Remove TLS cert exceptions |
| 518 | ** for the domains listed. Or if |
| 519 | ** the --all option is specified, |
| 520 | ** remove all TLS cert exceptions. |
| 521 | */ |
| 522 | void test_tlsconfig_info(void){ |
| 523 | const char *zCmd; |
| 524 | size_t nCmd; |
| 525 | int nHit = 0; |
| 526 | #if !defined(FOSSIL_ENABLE_SSL) |
| 527 | fossil_print("TLS disabled in this build\n"); |
| 528 | #else |
| 529 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 530 | db_open_config(1,0); |
| 531 | zCmd = g.argc>=3 ? g.argv[2] : "show"; |
| 532 | nCmd = strlen(zCmd); |
| 533 | if( strncmp("show",zCmd,nCmd)==0 ){ |
| 534 | const char *zName, *zValue; |
| 535 | size_t nName; |
| 536 | Stmt q; |
| 537 | fossil_print("OpenSSL-version: %s\n", SSLeay_version(SSLEAY_VERSION)); |
| 538 | fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); |
| 539 | fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); |
| 540 | zName = X509_get_default_cert_file_env(); |
| 541 | zValue = fossil_getenv(zName); |
| 542 | if( zValue==0 ) zValue = ""; |
| 543 | nName = strlen(zName); |
| 544 | fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue); |
| 545 | zName = X509_get_default_cert_dir_env(); |
| 546 | zValue = fossil_getenv(zName); |
| 547 | if( zValue==0 ) zValue = ""; |
| 548 | nName = strlen(zName); |
| 549 | fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue); |
| 550 | nHit++; |
| 551 | fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location","")); |
| 552 | fossil_print("ssl-identity: %s\n", db_get("ssl-identity","")); |
| 553 | db_prepare(&q, |
| 554 | "SELECT name FROM global_config" |
| 555 | " WHERE name GLOB 'cert:*'" |
| 556 | "UNION ALL " |
| 557 | "SELECT name FROM config" |
| 558 | " WHERE name GLOB 'cert:*'" |
| 559 | " ORDER BY name" |
| 560 | ); |
| 561 | while( db_step(&q)==SQLITE_ROW ){ |
| 562 | fossil_print("exception: %s\n", db_column_text(&q,0)+5); |
| 563 | } |
| 564 | db_finalize(&q); |
| 565 | }else |
| 566 | if( strncmp("remove-exception",zCmd,nCmd)==0 ){ |
| 567 | int i; |
| 568 | Blob sql; |
| 569 | char *zSep = "("; |
| 570 | db_begin_transaction(); |
| 571 | blob_init(&sql, 0, 0); |
| 572 | if( g.argc==4 && find_option("all",0,0)!=0 ){ |
| 573 | blob_append_sql(&sql, |
| 574 | "DELETE FROM global_config WHERE name GLOB 'cert:*';\n" |
| 575 | "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n" |
| 576 | "DELETE FROM config WHERE name GLOB 'cert:*';\n" |
| 577 | "DELETE FROM config WHERE name GLOB 'trusted:*';\n" |
| 578 | ); |
| 579 | }else{ |
| 580 | if( g.argc<4 ){ |
| 581 | usage("remove-exception DOMAIN-NAME ..."); |
| 582 | } |
| 583 | blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN "); |
| 584 | for(i=3; i<g.argc; i++){ |
| 585 | blob_append_sql(&sql,"%s'cert:%q','trust:%q'", |
| 586 | zSep/*safe-for-%s*/, g.argv[i], g.argv[i]); |
| 587 | zSep = ","; |
| 588 | } |
| 589 | blob_append_sql(&sql,");\n"); |
| 590 | zSep = "("; |
| 591 | blob_append_sql(&sql,"DELETE FROM config WHERE name IN "); |
| 592 | for(i=3; i<g.argc; i++){ |
| 593 | blob_append_sql(&sql,"%s'cert:%q','trusted:%q'", |
| 594 | zSep/*safe-for-%s*/, g.argv[i], g.argv[i]); |
| 595 | zSep = ","; |
| 596 | } |
| 597 | blob_append_sql(&sql,");"); |
| 598 | } |
| 599 | db_exec_sql(blob_str(&sql)); |
| 600 | db_commit_transaction(); |
| 601 | blob_reset(&sql); |
| 602 | }else |
| 603 | /*default*/{ |
| 604 | fossil_fatal("unknown sub-command \"%s\".\nshould be one of:" |
| 605 | " remove-exception show", |
| 606 | zCmd); |
| 607 | } |
| 608 | #endif |
| 609 | } |
| 610 |
+220
-203
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -167,11 +167,10 @@ | ||
| 167 | 167 | fossil_print("derived-from: %s %s\n", zParentCode, |
| 168 | 168 | db_get("parent-project-name","")); |
| 169 | 169 | } |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | - | |
| 173 | 172 | /* |
| 174 | 173 | ** COMMAND: info |
| 175 | 174 | ** |
| 176 | 175 | ** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS? |
| 177 | 176 | ** |
| @@ -215,33 +214,54 @@ | ||
| 215 | 214 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 216 | 215 | showParentProject(); |
| 217 | 216 | extraRepoInfo(); |
| 218 | 217 | return; |
| 219 | 218 | } |
| 220 | - db_find_and_open_repository(0,0); | |
| 219 | + db_find_and_open_repository(OPEN_OK_NOT_FOUND,0); | |
| 221 | 220 | verify_all_options(); |
| 222 | 221 | if( g.argc==2 ){ |
| 223 | 222 | int vid; |
| 224 | - /* 012345678901234 */ | |
| 225 | - db_record_repository_filename(0); | |
| 226 | - fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); | |
| 223 | + if( g.repositoryOpen ){ | |
| 224 | + db_record_repository_filename(0); | |
| 225 | + fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); | |
| 226 | + }else{ | |
| 227 | + db_open_config(0,1); | |
| 228 | + } | |
| 227 | 229 | if( g.localOpen ){ |
| 228 | 230 | fossil_print("repository: %s\n", db_repository_filename()); |
| 229 | 231 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 230 | 232 | } |
| 231 | - if( verboseFlag ) extraRepoInfo(); | |
| 233 | + if( verboseFlag && g.repositoryOpen ){ | |
| 234 | + extraRepoInfo(); | |
| 235 | + } | |
| 232 | 236 | if( g.zConfigDbName ){ |
| 233 | 237 | fossil_print("config-db: %s\n", g.zConfigDbName); |
| 234 | 238 | } |
| 235 | - fossil_print("project-code: %s\n", db_get("project-code", "")); | |
| 236 | - showParentProject(); | |
| 237 | - vid = g.localOpen ? db_lget_int("checkout", 0) : 0; | |
| 238 | - if( vid ){ | |
| 239 | - show_common_info(vid, "checkout:", 1, 1); | |
| 240 | - } | |
| 241 | - fossil_print("check-ins: %d\n", | |
| 239 | + if( g.repositoryOpen ){ | |
| 240 | + fossil_print("project-code: %s\n", db_get("project-code", "")); | |
| 241 | + showParentProject(); | |
| 242 | + vid = g.localOpen ? db_lget_int("checkout", 0) : 0; | |
| 243 | + if( vid ){ | |
| 244 | + show_common_info(vid, "checkout:", 1, 1); | |
| 245 | + } | |
| 246 | + fossil_print("check-ins: %d\n", | |
| 242 | 247 | db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/")); |
| 248 | + } | |
| 249 | + if( verboseFlag || !g.repositoryOpen ){ | |
| 250 | + Blob vx; | |
| 251 | + char *z; | |
| 252 | + fossil_version_blob(&vx, 0); | |
| 253 | + z = strstr(blob_str(&vx), "version"); | |
| 254 | + if( z ){ | |
| 255 | + z += 8; | |
| 256 | + }else{ | |
| 257 | + z = blob_str(&vx); | |
| 258 | + } | |
| 259 | + fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); | |
| 260 | + fossil_print("version: %s", z); | |
| 261 | + blob_reset(&vx); | |
| 262 | + } | |
| 243 | 263 | }else{ |
| 244 | 264 | int rid; |
| 245 | 265 | rid = name_to_rid(g.argv[2]); |
| 246 | 266 | if( rid==0 ){ |
| 247 | 267 | fossil_fatal("no such object: %s", g.argv[2]); |
| @@ -302,75 +322,10 @@ | ||
| 302 | 322 | |TIMELINE_CHPICK, |
| 303 | 323 | 0, 0, 0, rid, rid2, 0); |
| 304 | 324 | db_finalize(&q); |
| 305 | 325 | } |
| 306 | 326 | |
| 307 | -/* | |
| 308 | -** Show a graph all wiki, tickets, and check-ins that refer to object zUuid. | |
| 309 | -** | |
| 310 | -** If zLabel is not NULL and the graph is not empty, then output zLabel as | |
| 311 | -** a prefix to the graph. | |
| 312 | -*/ | |
| 313 | -void render_backlink_graph(const char *zUuid, const char *zLabel){ | |
| 314 | - Blob sql; | |
| 315 | - Stmt q; | |
| 316 | - char *zGlob; | |
| 317 | - zGlob = mprintf("%.5s*", zUuid); | |
| 318 | - db_multi_exec( | |
| 319 | - "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" | |
| 320 | - "DELETE FROM ok;" | |
| 321 | - "INSERT OR IGNORE INTO ok" | |
| 322 | - " SELECT srcid FROM backlink" | |
| 323 | - " WHERE target GLOB %Q" | |
| 324 | - " AND %Q GLOB (target || '*');", | |
| 325 | - zGlob, zUuid | |
| 326 | - ); | |
| 327 | - if( !db_exists("SELECT 1 FROM ok") ) return; | |
| 328 | - if( zLabel ) cgi_printf("%s", zLabel); | |
| 329 | - blob_zero(&sql); | |
| 330 | - blob_append(&sql, timeline_query_for_www(), -1); | |
| 331 | - blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); | |
| 332 | - db_prepare(&q, "%s", blob_sql_text(&sql)); | |
| 333 | - www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, | |
| 334 | - 0, 0, 0, 0, 0, 0); | |
| 335 | - db_finalize(&q); | |
| 336 | -} | |
| 337 | - | |
| 338 | -/* | |
| 339 | -** WEBPAGE: test-backlinks | |
| 340 | -** | |
| 341 | -** Show a timeline of all check-ins and other events that have entries | |
| 342 | -** in the backlink table. This is used for testing the rendering | |
| 343 | -** of the "References" section of the /info page. | |
| 344 | -*/ | |
| 345 | -void backlink_timeline_page(void){ | |
| 346 | - Blob sql; | |
| 347 | - Stmt q; | |
| 348 | - | |
| 349 | - login_check_credentials(); | |
| 350 | - if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){ | |
| 351 | - login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); | |
| 352 | - return; | |
| 353 | - } | |
| 354 | - style_header("Backlink Timeline (Internal Testing Use)"); | |
| 355 | - db_multi_exec( | |
| 356 | - "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" | |
| 357 | - "DELETE FROM ok;" | |
| 358 | - "INSERT OR IGNORE INTO ok" | |
| 359 | - " SELECT blob.rid FROM backlink, blob" | |
| 360 | - " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')" | |
| 361 | - ); | |
| 362 | - blob_zero(&sql); | |
| 363 | - blob_append(&sql, timeline_query_for_www(), -1); | |
| 364 | - blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); | |
| 365 | - db_prepare(&q, "%s", blob_sql_text(&sql)); | |
| 366 | - www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, | |
| 367 | - 0, 0, 0, 0, 0, 0); | |
| 368 | - db_finalize(&q); | |
| 369 | - style_footer(); | |
| 370 | -} | |
| 371 | - | |
| 372 | 327 | |
| 373 | 328 | /* |
| 374 | 329 | ** Append the difference between artifacts to the output |
| 375 | 330 | */ |
| 376 | 331 | static void append_diff( |
| @@ -849,21 +804,23 @@ | ||
| 849 | 804 | @ (<a href="%R/rcvfrom?rcvid=%d(rcvid)">Rcvid %d(rcvid)</a>)</td></tr> |
| 850 | 805 | } |
| 851 | 806 | db_finalize(&q2); |
| 852 | 807 | } |
| 853 | 808 | |
| 854 | - /* Only show links to read wiki pages if the users can read wiki | |
| 809 | + /* Only show links to edit wiki pages if the users can read wiki | |
| 855 | 810 | ** and if the wiki pages already exist */ |
| 856 | - if( g.perm.RdWiki | |
| 811 | + if( g.perm.WrWiki | |
| 812 | + && g.perm.RdWiki | |
| 813 | + && g.perm.Write | |
| 857 | 814 | && ((okWiki = wiki_tagid2("checkin",zUuid))!=0 || |
| 858 | 815 | blob_size(&wiki_read_links)>0) |
| 859 | 816 | && db_get_boolean("wiki-about",1) |
| 860 | 817 | ){ |
| 861 | 818 | const char *zLinks = blob_str(&wiki_read_links); |
| 862 | - @ <tr><th>Wiki:</th><td>\ | |
| 819 | + @ <tr><th>Edit Wiki:</th><td>\ | |
| 863 | 820 | if( okWiki ){ |
| 864 | - @ %z(href("%R/wiki?name=checkin/%s",zUuid))this checkin</a>\ | |
| 821 | + @ %z(href("%R/wikiedit?name=checkin/%s",zUuid))this checkin</a>\ | |
| 865 | 822 | }else if( zLinks[0] ){ |
| 866 | 823 | zLinks += 3; |
| 867 | 824 | } |
| 868 | 825 | @ %s(zLinks)</td></tr> |
| 869 | 826 | } |
| @@ -1383,33 +1340,22 @@ | ||
| 1383 | 1340 | |
| 1384 | 1341 | /* |
| 1385 | 1342 | ** Possible flags for the second parameter to |
| 1386 | 1343 | ** object_description() |
| 1387 | 1344 | */ |
| 1388 | -#define OBJDESC_DETAIL 0x0001 /* more detail */ | |
| 1345 | +#define OBJDESC_DETAIL 0x0001 /* Show more detail */ | |
| 1389 | 1346 | #define OBJDESC_BASE 0x0002 /* Set <base> using this object */ |
| 1390 | 1347 | #endif |
| 1391 | 1348 | |
| 1392 | 1349 | /* |
| 1393 | 1350 | ** Write a description of an object to the www reply. |
| 1394 | -** | |
| 1395 | -** If the object is a file then mention: | |
| 1396 | -** | |
| 1397 | -** * It's artifact ID | |
| 1398 | -** * All its filenames | |
| 1399 | -** * The check-in it was part of, with times and users | |
| 1400 | -** | |
| 1401 | -** If the object is a manifest, then mention: | |
| 1402 | -** | |
| 1403 | -** * It's artifact ID | |
| 1404 | -** * date of check-in | |
| 1405 | -** * Comment & user | |
| 1406 | 1351 | */ |
| 1407 | 1352 | int object_description( |
| 1408 | - int rid, /* The artifact ID */ | |
| 1353 | + int rid, /* The artifact ID for the object to describe */ | |
| 1409 | 1354 | u32 objdescFlags, /* Flags to control display */ |
| 1410 | - Blob *pDownloadName /* Fill with an appropriate download name */ | |
| 1355 | + const char *zFileName, /* For file objects, use this name. Can be NULL */ | |
| 1356 | + Blob *pDownloadName /* Fill with a good download name. Can be NULL */ | |
| 1411 | 1357 | ){ |
| 1412 | 1358 | Stmt q; |
| 1413 | 1359 | int cnt = 0; |
| 1414 | 1360 | int nWiki = 0; |
| 1415 | 1361 | int objType = 0; |
| @@ -1444,10 +1390,11 @@ | ||
| 1444 | 1390 | const char *zVers = db_column_text(&q, 4); |
| 1445 | 1391 | int mPerm = db_column_int(&q, 5); |
| 1446 | 1392 | const char *zBr = db_column_text(&q, 6); |
| 1447 | 1393 | int szFile = db_column_int(&q,7); |
| 1448 | 1394 | int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0; |
| 1395 | + if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue; | |
| 1449 | 1396 | if( sameFilename && !showDetail ){ |
| 1450 | 1397 | if( cnt==1 ){ |
| 1451 | 1398 | @ %z(href("%R/whatis/%!S",zUuid))[more...]</a> |
| 1452 | 1399 | } |
| 1453 | 1400 | cnt++; |
| @@ -1714,12 +1661,12 @@ | ||
| 1714 | 1661 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1715 | 1662 | cookie_link_parameter("diff","diff","2"); |
| 1716 | 1663 | diffType = atoi(PD("diff","2")); |
| 1717 | 1664 | cookie_render(); |
| 1718 | 1665 | if( P("from") && P("to") ){ |
| 1719 | - v1 = artifact_from_ci_and_filename(0, "from"); | |
| 1720 | - v2 = artifact_from_ci_and_filename(0, "to"); | |
| 1666 | + v1 = artifact_from_ci_and_filename("from"); | |
| 1667 | + v2 = artifact_from_ci_and_filename("to"); | |
| 1721 | 1668 | }else{ |
| 1722 | 1669 | Stmt q; |
| 1723 | 1670 | v1 = name_to_rid_www("v1"); |
| 1724 | 1671 | v2 = name_to_rid_www("v2"); |
| 1725 | 1672 | |
| @@ -1790,13 +1737,13 @@ | ||
| 1790 | 1737 | @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To |
| 1791 | 1738 | @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2> |
| 1792 | 1739 | }else{ |
| 1793 | 1740 | @ <h2>Differences From |
| 1794 | 1741 | @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2> |
| 1795 | - object_description(v1, objdescFlags, 0); | |
| 1742 | + object_description(v1, objdescFlags,0, 0); | |
| 1796 | 1743 | @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2> |
| 1797 | - object_description(v2, objdescFlags, 0); | |
| 1744 | + object_description(v2, objdescFlags,0, 0); | |
| 1798 | 1745 | } |
| 1799 | 1746 | if( pRe ){ |
| 1800 | 1747 | @ <b>Only differences that match regular expression "%h(zRe)" |
| 1801 | 1748 | @ are shown.</b> |
| 1802 | 1749 | } |
| @@ -1816,12 +1763,12 @@ | ||
| 1816 | 1763 | */ |
| 1817 | 1764 | void rawartifact_page(void){ |
| 1818 | 1765 | int rid = 0; |
| 1819 | 1766 | char *zUuid; |
| 1820 | 1767 | |
| 1821 | - if( P("ci") && P("filename") ){ | |
| 1822 | - rid = artifact_from_ci_and_filename(0, 0); | |
| 1768 | + if( P("ci") ){ | |
| 1769 | + rid = artifact_from_ci_and_filename(0); | |
| 1823 | 1770 | } |
| 1824 | 1771 | if( rid==0 ){ |
| 1825 | 1772 | rid = name_to_rid_www("name"); |
| 1826 | 1773 | } |
| 1827 | 1774 | login_check_credentials(); |
| @@ -1980,11 +1927,11 @@ | ||
| 1980 | 1927 | }else{ |
| 1981 | 1928 | @ :</h2> |
| 1982 | 1929 | } |
| 1983 | 1930 | blob_zero(&downloadName); |
| 1984 | 1931 | if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; |
| 1985 | - object_description(rid, objdescFlags, &downloadName); | |
| 1932 | + object_description(rid, objdescFlags, 0, &downloadName); | |
| 1986 | 1933 | style_submenu_element("Download", "%s/raw/%T?name=%s", |
| 1987 | 1934 | g.zTop, blob_str(&downloadName), zUuid); |
| 1988 | 1935 | @ <hr /> |
| 1989 | 1936 | content_get(rid, &content); |
| 1990 | 1937 | @ <blockquote><pre> |
| @@ -1995,59 +1942,53 @@ | ||
| 1995 | 1942 | |
| 1996 | 1943 | /* |
| 1997 | 1944 | ** Look for "ci" and "filename" query parameters. If found, try to |
| 1998 | 1945 | ** use them to extract the record ID of an artifact for the file. |
| 1999 | 1946 | ** |
| 2000 | -** Also look for "fn" as an alias for "filename". If either "filename" | |
| 2001 | -** or "fn" is present but "ci" is missing, use "tip" as a default value | |
| 2002 | -** for "ci". | |
| 2003 | -** | |
| 2004 | -** If zNameParam is not NULL, this use that parameter as the filename | |
| 2005 | -** rather than "fn" or "filename". | |
| 2006 | -** | |
| 2007 | -** If pUrl is not NULL, then record the "ci" and "filename" values in | |
| 2008 | -** pUrl. | |
| 2009 | -** | |
| 2010 | -** At least one of pUrl or zNameParam must be NULL. | |
| 1947 | +** Also look for "fn" and "name" as an aliases for "filename". If any | |
| 1948 | +** "filename" or "fn" or "name" are present but "ci" is missing, then | |
| 1949 | +** use "tip" as the default value for "ci". | |
| 1950 | +** | |
| 1951 | +** If zNameParam is not NULL, then use that parameter as the filename | |
| 1952 | +** rather than "fn" or "filename" or "name". the zNameParam is used | |
| 1953 | +** for the from= and to= query parameters of /fdiff. | |
| 2011 | 1954 | */ |
| 2012 | -int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){ | |
| 1955 | +int artifact_from_ci_and_filename(const char *zNameParam){ | |
| 2013 | 1956 | const char *zFilename; |
| 2014 | 1957 | const char *zCI; |
| 2015 | 1958 | int cirid; |
| 2016 | 1959 | Manifest *pManifest; |
| 2017 | 1960 | ManifestFile *pFile; |
| 1961 | + int rid = 0; | |
| 2018 | 1962 | |
| 2019 | 1963 | if( zNameParam ){ |
| 2020 | 1964 | zFilename = P(zNameParam); |
| 2021 | 1965 | }else{ |
| 2022 | 1966 | zFilename = P("filename"); |
| 2023 | 1967 | if( zFilename==0 ){ |
| 2024 | 1968 | zFilename = P("fn"); |
| 2025 | 1969 | } |
| 1970 | + if( zFilename==0 ){ | |
| 1971 | + zFilename = P("name"); | |
| 1972 | + } | |
| 2026 | 1973 | } |
| 2027 | 1974 | if( zFilename==0 ) return 0; |
| 2028 | 1975 | |
| 2029 | - zCI = P("ci"); | |
| 2030 | - cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci"); | |
| 1976 | + zCI = PD("ci", "tip"); | |
| 1977 | + cirid = name_to_typed_rid(zCI, "ci"); | |
| 2031 | 1978 | if( cirid<=0 ) return 0; |
| 2032 | 1979 | pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0); |
| 2033 | 1980 | if( pManifest==0 ) return 0; |
| 2034 | 1981 | manifest_file_rewind(pManifest); |
| 2035 | 1982 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 2036 | 1983 | if( fossil_strcmp(zFilename, pFile->zName)==0 ){ |
| 2037 | - int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); | |
| 2038 | - manifest_destroy(pManifest); | |
| 2039 | - if( pUrl ){ | |
| 2040 | - assert( zNameParam==0 ); | |
| 2041 | - url_add_parameter(pUrl, "fn", zFilename); | |
| 2042 | - if( zCI ) url_add_parameter(pUrl, "ci", zCI); | |
| 2043 | - } | |
| 2044 | - return rid; | |
| 1984 | + rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); | |
| 1985 | + break; | |
| 2045 | 1986 | } |
| 2046 | 1987 | } |
| 2047 | 1988 | manifest_destroy(pManifest); |
| 2048 | - return 0; | |
| 1989 | + return rid; | |
| 2049 | 1990 | } |
| 2050 | 1991 | |
| 2051 | 1992 | /* |
| 2052 | 1993 | ** The "z" argument is a string that contains the text of a source code |
| 2053 | 1994 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| @@ -2149,21 +2090,31 @@ | ||
| 2149 | 2090 | ** ln=N - highlight line number N |
| 2150 | 2091 | ** ln=M-N - highlight lines M through N inclusive |
| 2151 | 2092 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2152 | 2093 | ** verbose - show more detail in the description |
| 2153 | 2094 | ** download - redirect to the download (artifact page only) |
| 2154 | -** name=SHA1HASH - Provide the SHA1HASH as a query parameter | |
| 2155 | -** filename=NAME - Show information for content file NAME | |
| 2156 | -** fn=NAME - "fn" is shorthand for "filename" | |
| 2157 | -** ci=VERSION - The specific check-in to use for "filename=". | |
| 2095 | +** name=NAME - filename or hash as a query parameter | |
| 2096 | +** filename=NAME - alternative spelling for "name=" | |
| 2097 | +** fn=NAME - alternative spelling for "name=" | |
| 2098 | +** ci=VERSION - The specific check-in to use with "name=" to | |
| 2099 | +** identify the file. | |
| 2158 | 2100 | ** |
| 2159 | 2101 | ** The /artifact page show the complete content of a file |
| 2160 | -** identified by HASH as preformatted text. The | |
| 2161 | -** /whatis page shows only a description of the file. The /file | |
| 2162 | -** page shows the most recent version of the file or directory | |
| 2163 | -** called NAME, or a list of the top-level directory if NAME is | |
| 2164 | -** omitted. | |
| 2102 | +** identified by HASH. The /whatis page shows only a description | |
| 2103 | +** of how the artifact is used. The /file page shows the most recent | |
| 2104 | +** version of the file or directory called NAME, or a list of the | |
| 2105 | +** top-level directory if NAME is omitted. | |
| 2106 | +** | |
| 2107 | +** For /artifact and /whatis, the name= query parameter can refer to | |
| 2108 | +** either the name of a file, or an artifact hash. If the ci= query | |
| 2109 | +** parameter is also present, then name= must refer to a file name. | |
| 2110 | +** If ci= is omitted, then the hash interpretation is preferred but | |
| 2111 | +** if name= cannot be understood as a hash, a default "tip" value is | |
| 2112 | +** used for ci=. | |
| 2113 | +** | |
| 2114 | +** For /file, name= can only be interpreted as a filename. As before, | |
| 2115 | +** a default value of "tip" is used for ci= if ci= is omitted. | |
| 2165 | 2116 | */ |
| 2166 | 2117 | void artifact_page(void){ |
| 2167 | 2118 | int rid = 0; |
| 2168 | 2119 | Blob content; |
| 2169 | 2120 | const char *zMime; |
| @@ -2170,95 +2121,146 @@ | ||
| 2170 | 2121 | Blob downloadName; |
| 2171 | 2122 | int renderAsWiki = 0; |
| 2172 | 2123 | int renderAsHtml = 0; |
| 2173 | 2124 | int objType; |
| 2174 | 2125 | int asText; |
| 2175 | - const char *zUuid; | |
| 2126 | + const char *zUuid = 0; | |
| 2176 | 2127 | u32 objdescFlags = OBJDESC_BASE; |
| 2177 | 2128 | int descOnly = fossil_strcmp(g.zPath,"whatis")==0; |
| 2178 | 2129 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2179 | 2130 | const char *zLn = P("ln"); |
| 2180 | 2131 | const char *zName = P("name"); |
| 2181 | - HQuery url; | |
| 2182 | - | |
| 2183 | - url_initialize(&url, g.zPath); | |
| 2184 | - rid = artifact_from_ci_and_filename(&url, 0); | |
| 2185 | - if( rid==0 ){ | |
| 2186 | - url_add_parameter(&url, "name", zName); | |
| 2187 | - if( isFile ){ | |
| 2188 | - /* Do a top-level directory listing in /file mode if no argument | |
| 2189 | - ** specified */ | |
| 2190 | - if( zName==0 || zName[0]==0 ){ | |
| 2191 | - if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2192 | - page_tree(); | |
| 2193 | - return; | |
| 2194 | - } | |
| 2195 | - /* Look for a single file with the given name */ | |
| 2196 | - rid = db_int(0, | |
| 2197 | - "SELECT fid FROM filename, mlink, event" | |
| 2198 | - " WHERE name=%Q" | |
| 2199 | - " AND mlink.fnid=filename.fnid" | |
| 2200 | - " AND event.objid=mlink.mid" | |
| 2201 | - " ORDER BY event.mtime DESC LIMIT 1", | |
| 2202 | - zName | |
| 2203 | - ); | |
| 2204 | - /* If no file called NAME exists, instead look for a directory | |
| 2205 | - ** with that name, and do a directory listing */ | |
| 2206 | - if( rid==0 ){ | |
| 2207 | - int nName = (int)strlen(zName); | |
| 2208 | - if( nName && zName[nName-1]=='/' ) nName--; | |
| 2209 | - if( db_exists( | |
| 2210 | - "SELECT 1 FROM filename" | |
| 2211 | - " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", | |
| 2212 | - nName, zName, nName+1, nName, zName | |
| 2213 | - ) ){ | |
| 2214 | - if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2215 | - page_tree(); | |
| 2216 | - return; | |
| 2217 | - } | |
| 2218 | - } | |
| 2219 | - /* If no file or directory called NAME: issue an error */ | |
| 2220 | - if( rid==0 ){ | |
| 2221 | - style_header("No such file"); | |
| 2222 | - @ File '%h(zName)' does not exist in this repository. | |
| 2223 | - style_footer(); | |
| 2224 | - return; | |
| 2225 | - } | |
| 2226 | - }else{ | |
| 2227 | - rid = name_to_rid_www("name"); | |
| 2228 | - } | |
| 2229 | - } | |
| 2230 | - | |
| 2231 | - login_check_credentials(); | |
| 2232 | - if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 2233 | - if( rid==0 ){ | |
| 2234 | - style_header("No such artifact"); | |
| 2235 | - @ Artifact '%h(zName)' does not exist in this repository. | |
| 2236 | - style_footer(); | |
| 2237 | - return; | |
| 2238 | - } | |
| 2132 | + const char *zCI = P("ci"); | |
| 2133 | + HQuery url; | |
| 2134 | + char *zCIUuid = 0; | |
| 2135 | + int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */ | |
| 2136 | + int isBranchCI = 0; /* ci= refers to a branch name */ | |
| 2137 | + char *zHeader = 0; | |
| 2138 | + | |
| 2139 | + login_check_credentials(); | |
| 2140 | + if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 2141 | + | |
| 2142 | + /* Capture and normalize the name= and ci= query parameters */ | |
| 2143 | + if( zName==0 ){ | |
| 2144 | + zName = P("filename"); | |
| 2145 | + if( zName==0 ){ | |
| 2146 | + zName = P("fn"); | |
| 2147 | + } | |
| 2148 | + } | |
| 2149 | + if( zCI && strlen(zCI)==0 ){ zCI = 0; } | |
| 2150 | + if( zCI | |
| 2151 | + && name_to_uuid2(zCI, "ci", &zCIUuid) | |
| 2152 | + && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0 | |
| 2153 | + ){ | |
| 2154 | + isSymbolicCI = 1; | |
| 2155 | + isBranchCI = branch_includes_uuid(zCI, zCIUuid); | |
| 2156 | + } | |
| 2157 | + | |
| 2158 | + /* The name= query parameter (or at least one of its alternative | |
| 2159 | + ** spellings) is required. Except for /file, show a top-level | |
| 2160 | + ** directory listing if name= is omitted. | |
| 2161 | + */ | |
| 2162 | + if( zName==0 ){ | |
| 2163 | + if( isFile ){ | |
| 2164 | + if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2165 | + page_tree(); | |
| 2166 | + return; | |
| 2167 | + } | |
| 2168 | + style_header("Missing name= query parameter"); | |
| 2169 | + @ The name= query parameter is missing | |
| 2170 | + style_footer(); | |
| 2171 | + return; | |
| 2172 | + } | |
| 2173 | + | |
| 2174 | + url_initialize(&url, g.zPath); | |
| 2175 | + url_add_parameter(&url, "name", zName); | |
| 2176 | + url_add_parameter(&url, "ci", zCI); | |
| 2177 | + | |
| 2178 | + if( zCI==0 && !isFile ){ | |
| 2179 | + /* If there is no ci= query parameter, then prefer to interpret | |
| 2180 | + ** name= as a hash for /artifact and /whatis. But for not for /file. | |
| 2181 | + ** For /file, a name= without a ci= while prefer to use the default | |
| 2182 | + ** "tip" value for ci=. */ | |
| 2183 | + rid = name_to_rid(zName); | |
| 2184 | + } | |
| 2185 | + if( rid==0 ){ | |
| 2186 | + rid = artifact_from_ci_and_filename(0); | |
| 2187 | + } | |
| 2188 | + | |
| 2189 | + if( rid==0 ){ /* Artifact not found */ | |
| 2190 | + if( isFile ){ | |
| 2191 | + /* For /file, also check to see if name= refers to a directory, | |
| 2192 | + ** and if so, do a listing for that directory */ | |
| 2193 | + int nName = (int)strlen(zName); | |
| 2194 | + if( nName && zName[nName-1]=='/' ) nName--; | |
| 2195 | + if( db_exists( | |
| 2196 | + "SELECT 1 FROM filename" | |
| 2197 | + " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", | |
| 2198 | + nName, zName, nName+1, nName, zName | |
| 2199 | + ) ){ | |
| 2200 | + if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); | |
| 2201 | + page_tree(); | |
| 2202 | + return; | |
| 2203 | + } | |
| 2204 | + style_header("No such file"); | |
| 2205 | + @ File '%h(zName)' does not exist in this repository. | |
| 2206 | + }else{ | |
| 2207 | + style_header("No such artifact"); | |
| 2208 | + @ Artifact '%h(zName)' does not exist in this repository. | |
| 2209 | + } | |
| 2210 | + style_footer(); | |
| 2211 | + return; | |
| 2212 | + } | |
| 2213 | + | |
| 2239 | 2214 | if( descOnly || P("verbose")!=0 ){ |
| 2240 | 2215 | url_add_parameter(&url, "verbose", "1"); |
| 2241 | 2216 | objdescFlags |= OBJDESC_DETAIL; |
| 2242 | 2217 | } |
| 2243 | 2218 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2219 | + | |
| 2220 | + asText = P("txt")!=0; | |
| 2244 | 2221 | if( isFile ){ |
| 2245 | - @ <h2>Latest version of file '%h(zName)':</h2> | |
| 2222 | + if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ | |
| 2223 | + zCI = "tip"; | |
| 2224 | + @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a> | |
| 2225 | + @ from the %z(href("%R/info/tip"))latest check-in</a></h2> | |
| 2226 | + }else{ | |
| 2227 | + const char *zPath; | |
| 2228 | + Blob path; | |
| 2229 | + blob_zero(&path); | |
| 2230 | + hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO); | |
| 2231 | + zPath = blob_str(&path); | |
| 2232 | + @ <h2>File %s(zPath) \ | |
| 2233 | + if( isBranchCI ){ | |
| 2234 | + @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> | |
| 2235 | + }else if( isSymbolicCI ){ | |
| 2236 | + @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2> | |
| 2237 | + }else{ | |
| 2238 | + @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2> | |
| 2239 | + } | |
| 2240 | + blob_reset(&path); | |
| 2241 | + } | |
| 2246 | 2242 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2243 | + style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", | |
| 2244 | + zName, zCI); | |
| 2245 | + style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", | |
| 2246 | + zName, zCI); | |
| 2247 | + blob_init(&downloadName, zName, -1); | |
| 2248 | + objType = OBJTYPE_CONTENT; | |
| 2247 | 2249 | }else{ |
| 2248 | 2250 | @ <h2>Artifact |
| 2249 | 2251 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| 2250 | 2252 | if( g.perm.Setup ){ |
| 2251 | 2253 | @ (%d(rid)):</h2> |
| 2252 | 2254 | }else{ |
| 2253 | 2255 | @ :</h2> |
| 2254 | 2256 | } |
| 2257 | + blob_zero(&downloadName); | |
| 2258 | + if( asText ) objdescFlags &= ~OBJDESC_BASE; | |
| 2259 | + objType = object_description(rid, objdescFlags, | |
| 2260 | + (isFile?zName:0), &downloadName); | |
| 2255 | 2261 | } |
| 2256 | - blob_zero(&downloadName); | |
| 2257 | - asText = P("txt")!=0; | |
| 2258 | - if( asText ) objdescFlags &= ~OBJDESC_BASE; | |
| 2259 | - objType = object_description(rid, objdescFlags, &downloadName); | |
| 2260 | 2262 | if( !descOnly && P("download")!=0 ){ |
| 2261 | 2263 | cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName), |
| 2262 | 2264 | db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid)); |
| 2263 | 2265 | /*NOTREACHED*/ |
| 2264 | 2266 | } |
| @@ -2269,13 +2271,28 @@ | ||
| 2269 | 2271 | g.zTop, zUuid); |
| 2270 | 2272 | }else{ |
| 2271 | 2273 | style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); |
| 2272 | 2274 | } |
| 2273 | 2275 | } |
| 2274 | - style_header("%s", isFile ? "File Content" : | |
| 2275 | - descOnly ? "Artifact Description" : "Artifact Content"); | |
| 2276 | - if( g.perm.Admin ){ | |
| 2276 | + | |
| 2277 | + if( isFile ){ | |
| 2278 | + if( isSymbolicCI ){ | |
| 2279 | + zHeader = mprintf("%s at %s", file_tail(zName), zCI); | |
| 2280 | + }else if( zCI ){ | |
| 2281 | + zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); | |
| 2282 | + }else{ | |
| 2283 | + zHeader = mprintf("%s", file_tail(zName)); | |
| 2284 | + } | |
| 2285 | + }else if( descOnly ){ | |
| 2286 | + zHeader = mprintf("Artifact Description [%S]", zUuid); | |
| 2287 | + }else{ | |
| 2288 | + zHeader = mprintf("Artifact [%S]", zUuid); | |
| 2289 | + } | |
| 2290 | + style_header("%s", zHeader); | |
| 2291 | + fossil_free(zCIUuid); | |
| 2292 | + fossil_free(zHeader); | |
| 2293 | + if( !isFile && g.perm.Admin ){ | |
| 2277 | 2294 | Stmt q; |
| 2278 | 2295 | db_prepare(&q, |
| 2279 | 2296 | "SELECT coalesce(user.login,rcvfrom.uid)," |
| 2280 | 2297 | " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr" |
| 2281 | 2298 | " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid" |
| 2282 | 2299 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -167,11 +167,10 @@ | |
| 167 | fossil_print("derived-from: %s %s\n", zParentCode, |
| 168 | db_get("parent-project-name","")); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | |
| 173 | /* |
| 174 | ** COMMAND: info |
| 175 | ** |
| 176 | ** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS? |
| 177 | ** |
| @@ -215,33 +214,54 @@ | |
| 215 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 216 | showParentProject(); |
| 217 | extraRepoInfo(); |
| 218 | return; |
| 219 | } |
| 220 | db_find_and_open_repository(0,0); |
| 221 | verify_all_options(); |
| 222 | if( g.argc==2 ){ |
| 223 | int vid; |
| 224 | /* 012345678901234 */ |
| 225 | db_record_repository_filename(0); |
| 226 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| 227 | if( g.localOpen ){ |
| 228 | fossil_print("repository: %s\n", db_repository_filename()); |
| 229 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 230 | } |
| 231 | if( verboseFlag ) extraRepoInfo(); |
| 232 | if( g.zConfigDbName ){ |
| 233 | fossil_print("config-db: %s\n", g.zConfigDbName); |
| 234 | } |
| 235 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 236 | showParentProject(); |
| 237 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 238 | if( vid ){ |
| 239 | show_common_info(vid, "checkout:", 1, 1); |
| 240 | } |
| 241 | fossil_print("check-ins: %d\n", |
| 242 | db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/")); |
| 243 | }else{ |
| 244 | int rid; |
| 245 | rid = name_to_rid(g.argv[2]); |
| 246 | if( rid==0 ){ |
| 247 | fossil_fatal("no such object: %s", g.argv[2]); |
| @@ -302,75 +322,10 @@ | |
| 302 | |TIMELINE_CHPICK, |
| 303 | 0, 0, 0, rid, rid2, 0); |
| 304 | db_finalize(&q); |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | ** Show a graph all wiki, tickets, and check-ins that refer to object zUuid. |
| 309 | ** |
| 310 | ** If zLabel is not NULL and the graph is not empty, then output zLabel as |
| 311 | ** a prefix to the graph. |
| 312 | */ |
| 313 | void render_backlink_graph(const char *zUuid, const char *zLabel){ |
| 314 | Blob sql; |
| 315 | Stmt q; |
| 316 | char *zGlob; |
| 317 | zGlob = mprintf("%.5s*", zUuid); |
| 318 | db_multi_exec( |
| 319 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" |
| 320 | "DELETE FROM ok;" |
| 321 | "INSERT OR IGNORE INTO ok" |
| 322 | " SELECT srcid FROM backlink" |
| 323 | " WHERE target GLOB %Q" |
| 324 | " AND %Q GLOB (target || '*');", |
| 325 | zGlob, zUuid |
| 326 | ); |
| 327 | if( !db_exists("SELECT 1 FROM ok") ) return; |
| 328 | if( zLabel ) cgi_printf("%s", zLabel); |
| 329 | blob_zero(&sql); |
| 330 | blob_append(&sql, timeline_query_for_www(), -1); |
| 331 | blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); |
| 332 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 333 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 334 | 0, 0, 0, 0, 0, 0); |
| 335 | db_finalize(&q); |
| 336 | } |
| 337 | |
| 338 | /* |
| 339 | ** WEBPAGE: test-backlinks |
| 340 | ** |
| 341 | ** Show a timeline of all check-ins and other events that have entries |
| 342 | ** in the backlink table. This is used for testing the rendering |
| 343 | ** of the "References" section of the /info page. |
| 344 | */ |
| 345 | void backlink_timeline_page(void){ |
| 346 | Blob sql; |
| 347 | Stmt q; |
| 348 | |
| 349 | login_check_credentials(); |
| 350 | if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){ |
| 351 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 352 | return; |
| 353 | } |
| 354 | style_header("Backlink Timeline (Internal Testing Use)"); |
| 355 | db_multi_exec( |
| 356 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" |
| 357 | "DELETE FROM ok;" |
| 358 | "INSERT OR IGNORE INTO ok" |
| 359 | " SELECT blob.rid FROM backlink, blob" |
| 360 | " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')" |
| 361 | ); |
| 362 | blob_zero(&sql); |
| 363 | blob_append(&sql, timeline_query_for_www(), -1); |
| 364 | blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); |
| 365 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 366 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 367 | 0, 0, 0, 0, 0, 0); |
| 368 | db_finalize(&q); |
| 369 | style_footer(); |
| 370 | } |
| 371 | |
| 372 | |
| 373 | /* |
| 374 | ** Append the difference between artifacts to the output |
| 375 | */ |
| 376 | static void append_diff( |
| @@ -849,21 +804,23 @@ | |
| 849 | @ (<a href="%R/rcvfrom?rcvid=%d(rcvid)">Rcvid %d(rcvid)</a>)</td></tr> |
| 850 | } |
| 851 | db_finalize(&q2); |
| 852 | } |
| 853 | |
| 854 | /* Only show links to read wiki pages if the users can read wiki |
| 855 | ** and if the wiki pages already exist */ |
| 856 | if( g.perm.RdWiki |
| 857 | && ((okWiki = wiki_tagid2("checkin",zUuid))!=0 || |
| 858 | blob_size(&wiki_read_links)>0) |
| 859 | && db_get_boolean("wiki-about",1) |
| 860 | ){ |
| 861 | const char *zLinks = blob_str(&wiki_read_links); |
| 862 | @ <tr><th>Wiki:</th><td>\ |
| 863 | if( okWiki ){ |
| 864 | @ %z(href("%R/wiki?name=checkin/%s",zUuid))this checkin</a>\ |
| 865 | }else if( zLinks[0] ){ |
| 866 | zLinks += 3; |
| 867 | } |
| 868 | @ %s(zLinks)</td></tr> |
| 869 | } |
| @@ -1383,33 +1340,22 @@ | |
| 1383 | |
| 1384 | /* |
| 1385 | ** Possible flags for the second parameter to |
| 1386 | ** object_description() |
| 1387 | */ |
| 1388 | #define OBJDESC_DETAIL 0x0001 /* more detail */ |
| 1389 | #define OBJDESC_BASE 0x0002 /* Set <base> using this object */ |
| 1390 | #endif |
| 1391 | |
| 1392 | /* |
| 1393 | ** Write a description of an object to the www reply. |
| 1394 | ** |
| 1395 | ** If the object is a file then mention: |
| 1396 | ** |
| 1397 | ** * It's artifact ID |
| 1398 | ** * All its filenames |
| 1399 | ** * The check-in it was part of, with times and users |
| 1400 | ** |
| 1401 | ** If the object is a manifest, then mention: |
| 1402 | ** |
| 1403 | ** * It's artifact ID |
| 1404 | ** * date of check-in |
| 1405 | ** * Comment & user |
| 1406 | */ |
| 1407 | int object_description( |
| 1408 | int rid, /* The artifact ID */ |
| 1409 | u32 objdescFlags, /* Flags to control display */ |
| 1410 | Blob *pDownloadName /* Fill with an appropriate download name */ |
| 1411 | ){ |
| 1412 | Stmt q; |
| 1413 | int cnt = 0; |
| 1414 | int nWiki = 0; |
| 1415 | int objType = 0; |
| @@ -1444,10 +1390,11 @@ | |
| 1444 | const char *zVers = db_column_text(&q, 4); |
| 1445 | int mPerm = db_column_int(&q, 5); |
| 1446 | const char *zBr = db_column_text(&q, 6); |
| 1447 | int szFile = db_column_int(&q,7); |
| 1448 | int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0; |
| 1449 | if( sameFilename && !showDetail ){ |
| 1450 | if( cnt==1 ){ |
| 1451 | @ %z(href("%R/whatis/%!S",zUuid))[more...]</a> |
| 1452 | } |
| 1453 | cnt++; |
| @@ -1714,12 +1661,12 @@ | |
| 1714 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1715 | cookie_link_parameter("diff","diff","2"); |
| 1716 | diffType = atoi(PD("diff","2")); |
| 1717 | cookie_render(); |
| 1718 | if( P("from") && P("to") ){ |
| 1719 | v1 = artifact_from_ci_and_filename(0, "from"); |
| 1720 | v2 = artifact_from_ci_and_filename(0, "to"); |
| 1721 | }else{ |
| 1722 | Stmt q; |
| 1723 | v1 = name_to_rid_www("v1"); |
| 1724 | v2 = name_to_rid_www("v2"); |
| 1725 | |
| @@ -1790,13 +1737,13 @@ | |
| 1790 | @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To |
| 1791 | @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2> |
| 1792 | }else{ |
| 1793 | @ <h2>Differences From |
| 1794 | @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2> |
| 1795 | object_description(v1, objdescFlags, 0); |
| 1796 | @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2> |
| 1797 | object_description(v2, objdescFlags, 0); |
| 1798 | } |
| 1799 | if( pRe ){ |
| 1800 | @ <b>Only differences that match regular expression "%h(zRe)" |
| 1801 | @ are shown.</b> |
| 1802 | } |
| @@ -1816,12 +1763,12 @@ | |
| 1816 | */ |
| 1817 | void rawartifact_page(void){ |
| 1818 | int rid = 0; |
| 1819 | char *zUuid; |
| 1820 | |
| 1821 | if( P("ci") && P("filename") ){ |
| 1822 | rid = artifact_from_ci_and_filename(0, 0); |
| 1823 | } |
| 1824 | if( rid==0 ){ |
| 1825 | rid = name_to_rid_www("name"); |
| 1826 | } |
| 1827 | login_check_credentials(); |
| @@ -1980,11 +1927,11 @@ | |
| 1980 | }else{ |
| 1981 | @ :</h2> |
| 1982 | } |
| 1983 | blob_zero(&downloadName); |
| 1984 | if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; |
| 1985 | object_description(rid, objdescFlags, &downloadName); |
| 1986 | style_submenu_element("Download", "%s/raw/%T?name=%s", |
| 1987 | g.zTop, blob_str(&downloadName), zUuid); |
| 1988 | @ <hr /> |
| 1989 | content_get(rid, &content); |
| 1990 | @ <blockquote><pre> |
| @@ -1995,59 +1942,53 @@ | |
| 1995 | |
| 1996 | /* |
| 1997 | ** Look for "ci" and "filename" query parameters. If found, try to |
| 1998 | ** use them to extract the record ID of an artifact for the file. |
| 1999 | ** |
| 2000 | ** Also look for "fn" as an alias for "filename". If either "filename" |
| 2001 | ** or "fn" is present but "ci" is missing, use "tip" as a default value |
| 2002 | ** for "ci". |
| 2003 | ** |
| 2004 | ** If zNameParam is not NULL, this use that parameter as the filename |
| 2005 | ** rather than "fn" or "filename". |
| 2006 | ** |
| 2007 | ** If pUrl is not NULL, then record the "ci" and "filename" values in |
| 2008 | ** pUrl. |
| 2009 | ** |
| 2010 | ** At least one of pUrl or zNameParam must be NULL. |
| 2011 | */ |
| 2012 | int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){ |
| 2013 | const char *zFilename; |
| 2014 | const char *zCI; |
| 2015 | int cirid; |
| 2016 | Manifest *pManifest; |
| 2017 | ManifestFile *pFile; |
| 2018 | |
| 2019 | if( zNameParam ){ |
| 2020 | zFilename = P(zNameParam); |
| 2021 | }else{ |
| 2022 | zFilename = P("filename"); |
| 2023 | if( zFilename==0 ){ |
| 2024 | zFilename = P("fn"); |
| 2025 | } |
| 2026 | } |
| 2027 | if( zFilename==0 ) return 0; |
| 2028 | |
| 2029 | zCI = P("ci"); |
| 2030 | cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci"); |
| 2031 | if( cirid<=0 ) return 0; |
| 2032 | pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0); |
| 2033 | if( pManifest==0 ) return 0; |
| 2034 | manifest_file_rewind(pManifest); |
| 2035 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 2036 | if( fossil_strcmp(zFilename, pFile->zName)==0 ){ |
| 2037 | int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); |
| 2038 | manifest_destroy(pManifest); |
| 2039 | if( pUrl ){ |
| 2040 | assert( zNameParam==0 ); |
| 2041 | url_add_parameter(pUrl, "fn", zFilename); |
| 2042 | if( zCI ) url_add_parameter(pUrl, "ci", zCI); |
| 2043 | } |
| 2044 | return rid; |
| 2045 | } |
| 2046 | } |
| 2047 | manifest_destroy(pManifest); |
| 2048 | return 0; |
| 2049 | } |
| 2050 | |
| 2051 | /* |
| 2052 | ** The "z" argument is a string that contains the text of a source code |
| 2053 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| @@ -2149,21 +2090,31 @@ | |
| 2149 | ** ln=N - highlight line number N |
| 2150 | ** ln=M-N - highlight lines M through N inclusive |
| 2151 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2152 | ** verbose - show more detail in the description |
| 2153 | ** download - redirect to the download (artifact page only) |
| 2154 | ** name=SHA1HASH - Provide the SHA1HASH as a query parameter |
| 2155 | ** filename=NAME - Show information for content file NAME |
| 2156 | ** fn=NAME - "fn" is shorthand for "filename" |
| 2157 | ** ci=VERSION - The specific check-in to use for "filename=". |
| 2158 | ** |
| 2159 | ** The /artifact page show the complete content of a file |
| 2160 | ** identified by HASH as preformatted text. The |
| 2161 | ** /whatis page shows only a description of the file. The /file |
| 2162 | ** page shows the most recent version of the file or directory |
| 2163 | ** called NAME, or a list of the top-level directory if NAME is |
| 2164 | ** omitted. |
| 2165 | */ |
| 2166 | void artifact_page(void){ |
| 2167 | int rid = 0; |
| 2168 | Blob content; |
| 2169 | const char *zMime; |
| @@ -2170,95 +2121,146 @@ | |
| 2170 | Blob downloadName; |
| 2171 | int renderAsWiki = 0; |
| 2172 | int renderAsHtml = 0; |
| 2173 | int objType; |
| 2174 | int asText; |
| 2175 | const char *zUuid; |
| 2176 | u32 objdescFlags = OBJDESC_BASE; |
| 2177 | int descOnly = fossil_strcmp(g.zPath,"whatis")==0; |
| 2178 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2179 | const char *zLn = P("ln"); |
| 2180 | const char *zName = P("name"); |
| 2181 | HQuery url; |
| 2182 | |
| 2183 | url_initialize(&url, g.zPath); |
| 2184 | rid = artifact_from_ci_and_filename(&url, 0); |
| 2185 | if( rid==0 ){ |
| 2186 | url_add_parameter(&url, "name", zName); |
| 2187 | if( isFile ){ |
| 2188 | /* Do a top-level directory listing in /file mode if no argument |
| 2189 | ** specified */ |
| 2190 | if( zName==0 || zName[0]==0 ){ |
| 2191 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2192 | page_tree(); |
| 2193 | return; |
| 2194 | } |
| 2195 | /* Look for a single file with the given name */ |
| 2196 | rid = db_int(0, |
| 2197 | "SELECT fid FROM filename, mlink, event" |
| 2198 | " WHERE name=%Q" |
| 2199 | " AND mlink.fnid=filename.fnid" |
| 2200 | " AND event.objid=mlink.mid" |
| 2201 | " ORDER BY event.mtime DESC LIMIT 1", |
| 2202 | zName |
| 2203 | ); |
| 2204 | /* If no file called NAME exists, instead look for a directory |
| 2205 | ** with that name, and do a directory listing */ |
| 2206 | if( rid==0 ){ |
| 2207 | int nName = (int)strlen(zName); |
| 2208 | if( nName && zName[nName-1]=='/' ) nName--; |
| 2209 | if( db_exists( |
| 2210 | "SELECT 1 FROM filename" |
| 2211 | " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", |
| 2212 | nName, zName, nName+1, nName, zName |
| 2213 | ) ){ |
| 2214 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2215 | page_tree(); |
| 2216 | return; |
| 2217 | } |
| 2218 | } |
| 2219 | /* If no file or directory called NAME: issue an error */ |
| 2220 | if( rid==0 ){ |
| 2221 | style_header("No such file"); |
| 2222 | @ File '%h(zName)' does not exist in this repository. |
| 2223 | style_footer(); |
| 2224 | return; |
| 2225 | } |
| 2226 | }else{ |
| 2227 | rid = name_to_rid_www("name"); |
| 2228 | } |
| 2229 | } |
| 2230 | |
| 2231 | login_check_credentials(); |
| 2232 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2233 | if( rid==0 ){ |
| 2234 | style_header("No such artifact"); |
| 2235 | @ Artifact '%h(zName)' does not exist in this repository. |
| 2236 | style_footer(); |
| 2237 | return; |
| 2238 | } |
| 2239 | if( descOnly || P("verbose")!=0 ){ |
| 2240 | url_add_parameter(&url, "verbose", "1"); |
| 2241 | objdescFlags |= OBJDESC_DETAIL; |
| 2242 | } |
| 2243 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2244 | if( isFile ){ |
| 2245 | @ <h2>Latest version of file '%h(zName)':</h2> |
| 2246 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2247 | }else{ |
| 2248 | @ <h2>Artifact |
| 2249 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| 2250 | if( g.perm.Setup ){ |
| 2251 | @ (%d(rid)):</h2> |
| 2252 | }else{ |
| 2253 | @ :</h2> |
| 2254 | } |
| 2255 | } |
| 2256 | blob_zero(&downloadName); |
| 2257 | asText = P("txt")!=0; |
| 2258 | if( asText ) objdescFlags &= ~OBJDESC_BASE; |
| 2259 | objType = object_description(rid, objdescFlags, &downloadName); |
| 2260 | if( !descOnly && P("download")!=0 ){ |
| 2261 | cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName), |
| 2262 | db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid)); |
| 2263 | /*NOTREACHED*/ |
| 2264 | } |
| @@ -2269,13 +2271,28 @@ | |
| 2269 | g.zTop, zUuid); |
| 2270 | }else{ |
| 2271 | style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); |
| 2272 | } |
| 2273 | } |
| 2274 | style_header("%s", isFile ? "File Content" : |
| 2275 | descOnly ? "Artifact Description" : "Artifact Content"); |
| 2276 | if( g.perm.Admin ){ |
| 2277 | Stmt q; |
| 2278 | db_prepare(&q, |
| 2279 | "SELECT coalesce(user.login,rcvfrom.uid)," |
| 2280 | " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr" |
| 2281 | " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid" |
| 2282 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -167,11 +167,10 @@ | |
| 167 | fossil_print("derived-from: %s %s\n", zParentCode, |
| 168 | db_get("parent-project-name","")); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | /* |
| 173 | ** COMMAND: info |
| 174 | ** |
| 175 | ** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS? |
| 176 | ** |
| @@ -215,33 +214,54 @@ | |
| 214 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 215 | showParentProject(); |
| 216 | extraRepoInfo(); |
| 217 | return; |
| 218 | } |
| 219 | db_find_and_open_repository(OPEN_OK_NOT_FOUND,0); |
| 220 | verify_all_options(); |
| 221 | if( g.argc==2 ){ |
| 222 | int vid; |
| 223 | if( g.repositoryOpen ){ |
| 224 | db_record_repository_filename(0); |
| 225 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| 226 | }else{ |
| 227 | db_open_config(0,1); |
| 228 | } |
| 229 | if( g.localOpen ){ |
| 230 | fossil_print("repository: %s\n", db_repository_filename()); |
| 231 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 232 | } |
| 233 | if( verboseFlag && g.repositoryOpen ){ |
| 234 | extraRepoInfo(); |
| 235 | } |
| 236 | if( g.zConfigDbName ){ |
| 237 | fossil_print("config-db: %s\n", g.zConfigDbName); |
| 238 | } |
| 239 | if( g.repositoryOpen ){ |
| 240 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 241 | showParentProject(); |
| 242 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 243 | if( vid ){ |
| 244 | show_common_info(vid, "checkout:", 1, 1); |
| 245 | } |
| 246 | fossil_print("check-ins: %d\n", |
| 247 | db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/")); |
| 248 | } |
| 249 | if( verboseFlag || !g.repositoryOpen ){ |
| 250 | Blob vx; |
| 251 | char *z; |
| 252 | fossil_version_blob(&vx, 0); |
| 253 | z = strstr(blob_str(&vx), "version"); |
| 254 | if( z ){ |
| 255 | z += 8; |
| 256 | }else{ |
| 257 | z = blob_str(&vx); |
| 258 | } |
| 259 | fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); |
| 260 | fossil_print("version: %s", z); |
| 261 | blob_reset(&vx); |
| 262 | } |
| 263 | }else{ |
| 264 | int rid; |
| 265 | rid = name_to_rid(g.argv[2]); |
| 266 | if( rid==0 ){ |
| 267 | fossil_fatal("no such object: %s", g.argv[2]); |
| @@ -302,75 +322,10 @@ | |
| 322 | |TIMELINE_CHPICK, |
| 323 | 0, 0, 0, rid, rid2, 0); |
| 324 | db_finalize(&q); |
| 325 | } |
| 326 | |
| 327 | |
| 328 | /* |
| 329 | ** Append the difference between artifacts to the output |
| 330 | */ |
| 331 | static void append_diff( |
| @@ -849,21 +804,23 @@ | |
| 804 | @ (<a href="%R/rcvfrom?rcvid=%d(rcvid)">Rcvid %d(rcvid)</a>)</td></tr> |
| 805 | } |
| 806 | db_finalize(&q2); |
| 807 | } |
| 808 | |
| 809 | /* Only show links to edit wiki pages if the users can read wiki |
| 810 | ** and if the wiki pages already exist */ |
| 811 | if( g.perm.WrWiki |
| 812 | && g.perm.RdWiki |
| 813 | && g.perm.Write |
| 814 | && ((okWiki = wiki_tagid2("checkin",zUuid))!=0 || |
| 815 | blob_size(&wiki_read_links)>0) |
| 816 | && db_get_boolean("wiki-about",1) |
| 817 | ){ |
| 818 | const char *zLinks = blob_str(&wiki_read_links); |
| 819 | @ <tr><th>Edit Wiki:</th><td>\ |
| 820 | if( okWiki ){ |
| 821 | @ %z(href("%R/wikiedit?name=checkin/%s",zUuid))this checkin</a>\ |
| 822 | }else if( zLinks[0] ){ |
| 823 | zLinks += 3; |
| 824 | } |
| 825 | @ %s(zLinks)</td></tr> |
| 826 | } |
| @@ -1383,33 +1340,22 @@ | |
| 1340 | |
| 1341 | /* |
| 1342 | ** Possible flags for the second parameter to |
| 1343 | ** object_description() |
| 1344 | */ |
| 1345 | #define OBJDESC_DETAIL 0x0001 /* Show more detail */ |
| 1346 | #define OBJDESC_BASE 0x0002 /* Set <base> using this object */ |
| 1347 | #endif |
| 1348 | |
| 1349 | /* |
| 1350 | ** Write a description of an object to the www reply. |
| 1351 | */ |
| 1352 | int object_description( |
| 1353 | int rid, /* The artifact ID for the object to describe */ |
| 1354 | u32 objdescFlags, /* Flags to control display */ |
| 1355 | const char *zFileName, /* For file objects, use this name. Can be NULL */ |
| 1356 | Blob *pDownloadName /* Fill with a good download name. Can be NULL */ |
| 1357 | ){ |
| 1358 | Stmt q; |
| 1359 | int cnt = 0; |
| 1360 | int nWiki = 0; |
| 1361 | int objType = 0; |
| @@ -1444,10 +1390,11 @@ | |
| 1390 | const char *zVers = db_column_text(&q, 4); |
| 1391 | int mPerm = db_column_int(&q, 5); |
| 1392 | const char *zBr = db_column_text(&q, 6); |
| 1393 | int szFile = db_column_int(&q,7); |
| 1394 | int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0; |
| 1395 | if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue; |
| 1396 | if( sameFilename && !showDetail ){ |
| 1397 | if( cnt==1 ){ |
| 1398 | @ %z(href("%R/whatis/%!S",zUuid))[more...]</a> |
| 1399 | } |
| 1400 | cnt++; |
| @@ -1714,12 +1661,12 @@ | |
| 1661 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1662 | cookie_link_parameter("diff","diff","2"); |
| 1663 | diffType = atoi(PD("diff","2")); |
| 1664 | cookie_render(); |
| 1665 | if( P("from") && P("to") ){ |
| 1666 | v1 = artifact_from_ci_and_filename("from"); |
| 1667 | v2 = artifact_from_ci_and_filename("to"); |
| 1668 | }else{ |
| 1669 | Stmt q; |
| 1670 | v1 = name_to_rid_www("v1"); |
| 1671 | v2 = name_to_rid_www("v2"); |
| 1672 | |
| @@ -1790,13 +1737,13 @@ | |
| 1737 | @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To |
| 1738 | @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2> |
| 1739 | }else{ |
| 1740 | @ <h2>Differences From |
| 1741 | @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2> |
| 1742 | object_description(v1, objdescFlags,0, 0); |
| 1743 | @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2> |
| 1744 | object_description(v2, objdescFlags,0, 0); |
| 1745 | } |
| 1746 | if( pRe ){ |
| 1747 | @ <b>Only differences that match regular expression "%h(zRe)" |
| 1748 | @ are shown.</b> |
| 1749 | } |
| @@ -1816,12 +1763,12 @@ | |
| 1763 | */ |
| 1764 | void rawartifact_page(void){ |
| 1765 | int rid = 0; |
| 1766 | char *zUuid; |
| 1767 | |
| 1768 | if( P("ci") ){ |
| 1769 | rid = artifact_from_ci_and_filename(0); |
| 1770 | } |
| 1771 | if( rid==0 ){ |
| 1772 | rid = name_to_rid_www("name"); |
| 1773 | } |
| 1774 | login_check_credentials(); |
| @@ -1980,11 +1927,11 @@ | |
| 1927 | }else{ |
| 1928 | @ :</h2> |
| 1929 | } |
| 1930 | blob_zero(&downloadName); |
| 1931 | if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; |
| 1932 | object_description(rid, objdescFlags, 0, &downloadName); |
| 1933 | style_submenu_element("Download", "%s/raw/%T?name=%s", |
| 1934 | g.zTop, blob_str(&downloadName), zUuid); |
| 1935 | @ <hr /> |
| 1936 | content_get(rid, &content); |
| 1937 | @ <blockquote><pre> |
| @@ -1995,59 +1942,53 @@ | |
| 1942 | |
| 1943 | /* |
| 1944 | ** Look for "ci" and "filename" query parameters. If found, try to |
| 1945 | ** use them to extract the record ID of an artifact for the file. |
| 1946 | ** |
| 1947 | ** Also look for "fn" and "name" as an aliases for "filename". If any |
| 1948 | ** "filename" or "fn" or "name" are present but "ci" is missing, then |
| 1949 | ** use "tip" as the default value for "ci". |
| 1950 | ** |
| 1951 | ** If zNameParam is not NULL, then use that parameter as the filename |
| 1952 | ** rather than "fn" or "filename" or "name". the zNameParam is used |
| 1953 | ** for the from= and to= query parameters of /fdiff. |
| 1954 | */ |
| 1955 | int artifact_from_ci_and_filename(const char *zNameParam){ |
| 1956 | const char *zFilename; |
| 1957 | const char *zCI; |
| 1958 | int cirid; |
| 1959 | Manifest *pManifest; |
| 1960 | ManifestFile *pFile; |
| 1961 | int rid = 0; |
| 1962 | |
| 1963 | if( zNameParam ){ |
| 1964 | zFilename = P(zNameParam); |
| 1965 | }else{ |
| 1966 | zFilename = P("filename"); |
| 1967 | if( zFilename==0 ){ |
| 1968 | zFilename = P("fn"); |
| 1969 | } |
| 1970 | if( zFilename==0 ){ |
| 1971 | zFilename = P("name"); |
| 1972 | } |
| 1973 | } |
| 1974 | if( zFilename==0 ) return 0; |
| 1975 | |
| 1976 | zCI = PD("ci", "tip"); |
| 1977 | cirid = name_to_typed_rid(zCI, "ci"); |
| 1978 | if( cirid<=0 ) return 0; |
| 1979 | pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0); |
| 1980 | if( pManifest==0 ) return 0; |
| 1981 | manifest_file_rewind(pManifest); |
| 1982 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 1983 | if( fossil_strcmp(zFilename, pFile->zName)==0 ){ |
| 1984 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); |
| 1985 | break; |
| 1986 | } |
| 1987 | } |
| 1988 | manifest_destroy(pManifest); |
| 1989 | return rid; |
| 1990 | } |
| 1991 | |
| 1992 | /* |
| 1993 | ** The "z" argument is a string that contains the text of a source code |
| 1994 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| @@ -2149,21 +2090,31 @@ | |
| 2090 | ** ln=N - highlight line number N |
| 2091 | ** ln=M-N - highlight lines M through N inclusive |
| 2092 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2093 | ** verbose - show more detail in the description |
| 2094 | ** download - redirect to the download (artifact page only) |
| 2095 | ** name=NAME - filename or hash as a query parameter |
| 2096 | ** filename=NAME - alternative spelling for "name=" |
| 2097 | ** fn=NAME - alternative spelling for "name=" |
| 2098 | ** ci=VERSION - The specific check-in to use with "name=" to |
| 2099 | ** identify the file. |
| 2100 | ** |
| 2101 | ** The /artifact page show the complete content of a file |
| 2102 | ** identified by HASH. The /whatis page shows only a description |
| 2103 | ** of how the artifact is used. The /file page shows the most recent |
| 2104 | ** version of the file or directory called NAME, or a list of the |
| 2105 | ** top-level directory if NAME is omitted. |
| 2106 | ** |
| 2107 | ** For /artifact and /whatis, the name= query parameter can refer to |
| 2108 | ** either the name of a file, or an artifact hash. If the ci= query |
| 2109 | ** parameter is also present, then name= must refer to a file name. |
| 2110 | ** If ci= is omitted, then the hash interpretation is preferred but |
| 2111 | ** if name= cannot be understood as a hash, a default "tip" value is |
| 2112 | ** used for ci=. |
| 2113 | ** |
| 2114 | ** For /file, name= can only be interpreted as a filename. As before, |
| 2115 | ** a default value of "tip" is used for ci= if ci= is omitted. |
| 2116 | */ |
| 2117 | void artifact_page(void){ |
| 2118 | int rid = 0; |
| 2119 | Blob content; |
| 2120 | const char *zMime; |
| @@ -2170,95 +2121,146 @@ | |
| 2121 | Blob downloadName; |
| 2122 | int renderAsWiki = 0; |
| 2123 | int renderAsHtml = 0; |
| 2124 | int objType; |
| 2125 | int asText; |
| 2126 | const char *zUuid = 0; |
| 2127 | u32 objdescFlags = OBJDESC_BASE; |
| 2128 | int descOnly = fossil_strcmp(g.zPath,"whatis")==0; |
| 2129 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2130 | const char *zLn = P("ln"); |
| 2131 | const char *zName = P("name"); |
| 2132 | const char *zCI = P("ci"); |
| 2133 | HQuery url; |
| 2134 | char *zCIUuid = 0; |
| 2135 | int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */ |
| 2136 | int isBranchCI = 0; /* ci= refers to a branch name */ |
| 2137 | char *zHeader = 0; |
| 2138 | |
| 2139 | login_check_credentials(); |
| 2140 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2141 | |
| 2142 | /* Capture and normalize the name= and ci= query parameters */ |
| 2143 | if( zName==0 ){ |
| 2144 | zName = P("filename"); |
| 2145 | if( zName==0 ){ |
| 2146 | zName = P("fn"); |
| 2147 | } |
| 2148 | } |
| 2149 | if( zCI && strlen(zCI)==0 ){ zCI = 0; } |
| 2150 | if( zCI |
| 2151 | && name_to_uuid2(zCI, "ci", &zCIUuid) |
| 2152 | && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0 |
| 2153 | ){ |
| 2154 | isSymbolicCI = 1; |
| 2155 | isBranchCI = branch_includes_uuid(zCI, zCIUuid); |
| 2156 | } |
| 2157 | |
| 2158 | /* The name= query parameter (or at least one of its alternative |
| 2159 | ** spellings) is required. Except for /file, show a top-level |
| 2160 | ** directory listing if name= is omitted. |
| 2161 | */ |
| 2162 | if( zName==0 ){ |
| 2163 | if( isFile ){ |
| 2164 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2165 | page_tree(); |
| 2166 | return; |
| 2167 | } |
| 2168 | style_header("Missing name= query parameter"); |
| 2169 | @ The name= query parameter is missing |
| 2170 | style_footer(); |
| 2171 | return; |
| 2172 | } |
| 2173 | |
| 2174 | url_initialize(&url, g.zPath); |
| 2175 | url_add_parameter(&url, "name", zName); |
| 2176 | url_add_parameter(&url, "ci", zCI); |
| 2177 | |
| 2178 | if( zCI==0 && !isFile ){ |
| 2179 | /* If there is no ci= query parameter, then prefer to interpret |
| 2180 | ** name= as a hash for /artifact and /whatis. But for not for /file. |
| 2181 | ** For /file, a name= without a ci= while prefer to use the default |
| 2182 | ** "tip" value for ci=. */ |
| 2183 | rid = name_to_rid(zName); |
| 2184 | } |
| 2185 | if( rid==0 ){ |
| 2186 | rid = artifact_from_ci_and_filename(0); |
| 2187 | } |
| 2188 | |
| 2189 | if( rid==0 ){ /* Artifact not found */ |
| 2190 | if( isFile ){ |
| 2191 | /* For /file, also check to see if name= refers to a directory, |
| 2192 | ** and if so, do a listing for that directory */ |
| 2193 | int nName = (int)strlen(zName); |
| 2194 | if( nName && zName[nName-1]=='/' ) nName--; |
| 2195 | if( db_exists( |
| 2196 | "SELECT 1 FROM filename" |
| 2197 | " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", |
| 2198 | nName, zName, nName+1, nName, zName |
| 2199 | ) ){ |
| 2200 | if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); |
| 2201 | page_tree(); |
| 2202 | return; |
| 2203 | } |
| 2204 | style_header("No such file"); |
| 2205 | @ File '%h(zName)' does not exist in this repository. |
| 2206 | }else{ |
| 2207 | style_header("No such artifact"); |
| 2208 | @ Artifact '%h(zName)' does not exist in this repository. |
| 2209 | } |
| 2210 | style_footer(); |
| 2211 | return; |
| 2212 | } |
| 2213 | |
| 2214 | if( descOnly || P("verbose")!=0 ){ |
| 2215 | url_add_parameter(&url, "verbose", "1"); |
| 2216 | objdescFlags |= OBJDESC_DETAIL; |
| 2217 | } |
| 2218 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2219 | |
| 2220 | asText = P("txt")!=0; |
| 2221 | if( isFile ){ |
| 2222 | if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ |
| 2223 | zCI = "tip"; |
| 2224 | @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a> |
| 2225 | @ from the %z(href("%R/info/tip"))latest check-in</a></h2> |
| 2226 | }else{ |
| 2227 | const char *zPath; |
| 2228 | Blob path; |
| 2229 | blob_zero(&path); |
| 2230 | hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO); |
| 2231 | zPath = blob_str(&path); |
| 2232 | @ <h2>File %s(zPath) \ |
| 2233 | if( isBranchCI ){ |
| 2234 | @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> |
| 2235 | }else if( isSymbolicCI ){ |
| 2236 | @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2> |
| 2237 | }else{ |
| 2238 | @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2> |
| 2239 | } |
| 2240 | blob_reset(&path); |
| 2241 | } |
| 2242 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2243 | style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", |
| 2244 | zName, zCI); |
| 2245 | style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", |
| 2246 | zName, zCI); |
| 2247 | blob_init(&downloadName, zName, -1); |
| 2248 | objType = OBJTYPE_CONTENT; |
| 2249 | }else{ |
| 2250 | @ <h2>Artifact |
| 2251 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| 2252 | if( g.perm.Setup ){ |
| 2253 | @ (%d(rid)):</h2> |
| 2254 | }else{ |
| 2255 | @ :</h2> |
| 2256 | } |
| 2257 | blob_zero(&downloadName); |
| 2258 | if( asText ) objdescFlags &= ~OBJDESC_BASE; |
| 2259 | objType = object_description(rid, objdescFlags, |
| 2260 | (isFile?zName:0), &downloadName); |
| 2261 | } |
| 2262 | if( !descOnly && P("download")!=0 ){ |
| 2263 | cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName), |
| 2264 | db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid)); |
| 2265 | /*NOTREACHED*/ |
| 2266 | } |
| @@ -2269,13 +2271,28 @@ | |
| 2271 | g.zTop, zUuid); |
| 2272 | }else{ |
| 2273 | style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); |
| 2274 | } |
| 2275 | } |
| 2276 | |
| 2277 | if( isFile ){ |
| 2278 | if( isSymbolicCI ){ |
| 2279 | zHeader = mprintf("%s at %s", file_tail(zName), zCI); |
| 2280 | }else if( zCI ){ |
| 2281 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2282 | }else{ |
| 2283 | zHeader = mprintf("%s", file_tail(zName)); |
| 2284 | } |
| 2285 | }else if( descOnly ){ |
| 2286 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2287 | }else{ |
| 2288 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2289 | } |
| 2290 | style_header("%s", zHeader); |
| 2291 | fossil_free(zCIUuid); |
| 2292 | fossil_free(zHeader); |
| 2293 | if( !isFile && g.perm.Admin ){ |
| 2294 | Stmt q; |
| 2295 | db_prepare(&q, |
| 2296 | "SELECT coalesce(user.login,rcvfrom.uid)," |
| 2297 | " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr" |
| 2298 | " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid" |
| 2299 |
+1
-4
| --- src/json_branch.c | ||
| +++ src/json_branch.c | ||
| @@ -266,13 +266,10 @@ | ||
| 266 | 266 | if( zColor!=0 ){ |
| 267 | 267 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 268 | 268 | } |
| 269 | 269 | blob_appendf(&branch, "T *branch * %F\n", zBranch); |
| 270 | 270 | blob_appendf(&branch, "T *sym-%F *\n", zBranch); |
| 271 | - if( zOpt->isPrivate ){ | |
| 272 | - blob_appendf(&branch, "T +private *\n"); | |
| 273 | - } | |
| 274 | 271 | |
| 275 | 272 | /* Cancel all other symbolic tags */ |
| 276 | 273 | db_prepare(&q, |
| 277 | 274 | "SELECT tagname FROM tagxref, tag" |
| 278 | 275 | " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" |
| @@ -287,11 +284,11 @@ | ||
| 287 | 284 | |
| 288 | 285 | blob_appendf(&branch, "U %F\n", g.zLogin); |
| 289 | 286 | md5sum_blob(&branch, &mcksum); |
| 290 | 287 | blob_appendf(&branch, "Z %b\n", &mcksum); |
| 291 | 288 | |
| 292 | - brid = content_put(&branch); | |
| 289 | + brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate); | |
| 293 | 290 | if( brid==0 ){ |
| 294 | 291 | fossil_panic("Problem committing manifest: %s", g.zErrMsg); |
| 295 | 292 | } |
| 296 | 293 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 297 | 294 | if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ |
| 298 | 295 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -266,13 +266,10 @@ | |
| 266 | if( zColor!=0 ){ |
| 267 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 268 | } |
| 269 | blob_appendf(&branch, "T *branch * %F\n", zBranch); |
| 270 | blob_appendf(&branch, "T *sym-%F *\n", zBranch); |
| 271 | if( zOpt->isPrivate ){ |
| 272 | blob_appendf(&branch, "T +private *\n"); |
| 273 | } |
| 274 | |
| 275 | /* Cancel all other symbolic tags */ |
| 276 | db_prepare(&q, |
| 277 | "SELECT tagname FROM tagxref, tag" |
| 278 | " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" |
| @@ -287,11 +284,11 @@ | |
| 287 | |
| 288 | blob_appendf(&branch, "U %F\n", g.zLogin); |
| 289 | md5sum_blob(&branch, &mcksum); |
| 290 | blob_appendf(&branch, "Z %b\n", &mcksum); |
| 291 | |
| 292 | brid = content_put(&branch); |
| 293 | if( brid==0 ){ |
| 294 | fossil_panic("Problem committing manifest: %s", g.zErrMsg); |
| 295 | } |
| 296 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 297 | if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ |
| 298 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -266,13 +266,10 @@ | |
| 266 | if( zColor!=0 ){ |
| 267 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 268 | } |
| 269 | blob_appendf(&branch, "T *branch * %F\n", zBranch); |
| 270 | blob_appendf(&branch, "T *sym-%F *\n", zBranch); |
| 271 | |
| 272 | /* Cancel all other symbolic tags */ |
| 273 | db_prepare(&q, |
| 274 | "SELECT tagname FROM tagxref, tag" |
| 275 | " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" |
| @@ -287,11 +284,11 @@ | |
| 284 | |
| 285 | blob_appendf(&branch, "U %F\n", g.zLogin); |
| 286 | md5sum_blob(&branch, &mcksum); |
| 287 | blob_appendf(&branch, "Z %b\n", &mcksum); |
| 288 | |
| 289 | brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate); |
| 290 | if( brid==0 ){ |
| 291 | fossil_panic("Problem committing manifest: %s", g.zErrMsg); |
| 292 | } |
| 293 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 294 | if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ |
| 295 |
+62
-14
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -481,11 +481,11 @@ | ||
| 481 | 481 | int login_self_register_available(const char *zNeeded){ |
| 482 | 482 | CapabilityString *pCap; |
| 483 | 483 | int rc; |
| 484 | 484 | if( !db_get_boolean("self-register",0) ) return 0; |
| 485 | 485 | if( zNeeded==0 ) return 1; |
| 486 | - pCap = capability_add(0, db_get("default-perms", 0)); | |
| 486 | + pCap = capability_add(0, db_get("default-perms", "u")); | |
| 487 | 487 | capability_expand(pCap); |
| 488 | 488 | rc = capability_has_any(pCap, zNeeded); |
| 489 | 489 | capability_free(pCap); |
| 490 | 490 | return rc; |
| 491 | 491 | } |
| @@ -1128,11 +1128,11 @@ | ||
| 1128 | 1128 | if( zPublicPages!=0 ){ |
| 1129 | 1129 | Glob *pGlob = glob_create(zPublicPages); |
| 1130 | 1130 | const char *zUri = PD("REQUEST_URI",""); |
| 1131 | 1131 | zUri += (int)strlen(g.zTop); |
| 1132 | 1132 | if( glob_match(pGlob, zUri) ){ |
| 1133 | - login_set_capabilities(db_get("default-perms", 0), 0); | |
| 1133 | + login_set_capabilities(db_get("default-perms", "u"), 0); | |
| 1134 | 1134 | } |
| 1135 | 1135 | glob_free(pGlob); |
| 1136 | 1136 | } |
| 1137 | 1137 | } |
| 1138 | 1138 | |
| @@ -1459,10 +1459,38 @@ | ||
| 1459 | 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | 1460 | zUserID, zUserID, zUserID |
| 1461 | 1461 | ); |
| 1462 | 1462 | return rc; |
| 1463 | 1463 | } |
| 1464 | + | |
| 1465 | +/* | |
| 1466 | +** Check an email address and confirm that it is valid for self-registration. | |
| 1467 | +** The email address is known already to be well-formed. Return true | |
| 1468 | +** if the email address is on the allowed list. | |
| 1469 | +** | |
| 1470 | +** The default behavior is that any valid email address is accepted. | |
| 1471 | +** But if the "auth-sub-email" setting exists and is not empty, then | |
| 1472 | +** it is a comma-separated list of GLOB patterns for email addresses | |
| 1473 | +** that are authorized to self-register. | |
| 1474 | +*/ | |
| 1475 | +int authorized_subscription_email(const char *zEAddr){ | |
| 1476 | + char *zGlob = db_get("auth-sub-email",0); | |
| 1477 | + Glob *pGlob; | |
| 1478 | + char *zAddr; | |
| 1479 | + int rc; | |
| 1480 | + | |
| 1481 | + if( zGlob==0 || zGlob[0]==0 ) return 1; | |
| 1482 | + zGlob = fossil_strtolwr(fossil_strdup(zGlob)); | |
| 1483 | + pGlob = glob_create(zGlob); | |
| 1484 | + fossil_free(zGlob); | |
| 1485 | + | |
| 1486 | + zAddr = fossil_strtolwr(fossil_strdup(zEAddr)); | |
| 1487 | + rc = glob_match(pGlob, zAddr); | |
| 1488 | + fossil_free(zAddr); | |
| 1489 | + glob_free(pGlob); | |
| 1490 | + return rc!=0; | |
| 1491 | +} | |
| 1464 | 1492 | |
| 1465 | 1493 | /* |
| 1466 | 1494 | ** WEBPAGE: register |
| 1467 | 1495 | ** |
| 1468 | 1496 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1471,13 +1499,14 @@ | ||
| 1471 | 1499 | void register_page(void){ |
| 1472 | 1500 | const char *zUserID, *zPasswd, *zConfirm, *zEAddr; |
| 1473 | 1501 | const char *zDName; |
| 1474 | 1502 | unsigned int uSeed; |
| 1475 | 1503 | const char *zDecoded; |
| 1476 | - char *zCaptcha; | |
| 1477 | 1504 | int iErrLine = -1; |
| 1478 | 1505 | const char *zErr = 0; |
| 1506 | + int captchaIsCorrect = 0; /* True on a correct captcha */ | |
| 1507 | + char *zCaptcha = ""; /* Value of the captcha text */ | |
| 1479 | 1508 | char *zPerms; /* Permissions for the default user */ |
| 1480 | 1509 | int canDoAlerts = 0; /* True if receiving email alerts is possible */ |
| 1481 | 1510 | int doAlerts = 0; /* True if subscription is wanted too */ |
| 1482 | 1511 | if( !db_get_boolean("self-register", 0) ){ |
| 1483 | 1512 | style_header("Registration not possible"); |
| @@ -1484,11 +1513,11 @@ | ||
| 1484 | 1513 | @ <p>This project does not allow user self-registration. Please contact the |
| 1485 | 1514 | @ project administrator to obtain an account.</p> |
| 1486 | 1515 | style_footer(); |
| 1487 | 1516 | return; |
| 1488 | 1517 | } |
| 1489 | - zPerms = db_get("default-perms", 0); | |
| 1518 | + zPerms = db_get("default-perms", "u"); | |
| 1490 | 1519 | |
| 1491 | 1520 | /* Prompt the user for email alerts if this repository is configured for |
| 1492 | 1521 | ** email alerts and if the default permissions include "7" */ |
| 1493 | 1522 | canDoAlerts = alert_tables_exist() && db_int(0, |
| 1494 | 1523 | "SELECT fullcap(%Q) GLOB '*7*'", zPerms |
| @@ -1503,11 +1532,11 @@ | ||
| 1503 | 1532 | |
| 1504 | 1533 | /* Verify user imputs */ |
| 1505 | 1534 | if( P("new")==0 || !cgi_csrf_safe(1) ){ |
| 1506 | 1535 | /* This is not a valid form submission. Fall through into |
| 1507 | 1536 | ** the form display */ |
| 1508 | - }else if( !captcha_is_correct(1) ){ | |
| 1537 | + }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ | |
| 1509 | 1538 | iErrLine = 6; |
| 1510 | 1539 | zErr = "Incorrect CAPTCHA"; |
| 1511 | 1540 | }else if( strlen(zUserID)<6 ){ |
| 1512 | 1541 | iErrLine = 1; |
| 1513 | 1542 | zErr = "User ID too short. Must be at least 6 characters."; |
| @@ -1521,10 +1550,13 @@ | ||
| 1521 | 1550 | iErrLine = 3; |
| 1522 | 1551 | zErr = "Required"; |
| 1523 | 1552 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1524 | 1553 | iErrLine = 3; |
| 1525 | 1554 | zErr = "Not a valid email address"; |
| 1555 | + }else if( authorized_subscription_email(zEAddr)==0 ){ | |
| 1556 | + iErrLine = 3; | |
| 1557 | + zErr = "Not an authorized email address"; | |
| 1526 | 1558 | }else if( strlen(zPasswd)<6 ){ |
| 1527 | 1559 | iErrLine = 4; |
| 1528 | 1560 | zErr = "Password must be at least 6 characters long"; |
| 1529 | 1561 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1530 | 1562 | iErrLine = 5; |
| @@ -1548,16 +1580,24 @@ | ||
| 1548 | 1580 | /* If all of the tests above have passed, that means that the submitted |
| 1549 | 1581 | ** form contains valid data and we can proceed to create the new login */ |
| 1550 | 1582 | Blob sql; |
| 1551 | 1583 | int uid; |
| 1552 | 1584 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 1585 | + const char *zStartPerms = zPerms; | |
| 1586 | + if( db_get_boolean("selfreg-verify",0) ){ | |
| 1587 | + /* If email verification is required for self-registration, initalize | |
| 1588 | + ** the new user capabilities to just "7" (Sign up for email). The | |
| 1589 | + ** full "default-perms" permissions will be added when they click | |
| 1590 | + ** the verification link on the email they are sent. */ | |
| 1591 | + zStartPerms = "7"; | |
| 1592 | + } | |
| 1553 | 1593 | blob_init(&sql, 0, 0); |
| 1554 | 1594 | blob_append_sql(&sql, |
| 1555 | 1595 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1556 | 1596 | "VALUES(%Q,%Q,%Q," |
| 1557 | 1597 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1558 | - zUserID, zPass, zPerms, zDName, zEAddr, g.zIpAddr); | |
| 1598 | + zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); | |
| 1559 | 1599 | fossil_free(zPass); |
| 1560 | 1600 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1561 | 1601 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1562 | 1602 | login_set_user_cookie(zUserID, uid, NULL); |
| 1563 | 1603 | if( doAlerts ){ |
| @@ -1567,16 +1607,20 @@ | ||
| 1567 | 1607 | sqlite3_int64 id; /* New subscriber Id */ |
| 1568 | 1608 | const char *zCode; /* New subscriber code (in hex) */ |
| 1569 | 1609 | const char *zGoto = P("g"); |
| 1570 | 1610 | int nsub = 0; |
| 1571 | 1611 | char ssub[20]; |
| 1612 | + CapabilityString *pCap; | |
| 1613 | + pCap = capability_add(0, zPerms); | |
| 1614 | + capability_expand(pCap); | |
| 1572 | 1615 | ssub[nsub++] = 'a'; |
| 1573 | - if( g.perm.Read ) ssub[nsub++] = 'c'; | |
| 1574 | - if( g.perm.RdForum ) ssub[nsub++] = 'f'; | |
| 1575 | - if( g.perm.RdTkt ) ssub[nsub++] = 't'; | |
| 1576 | - if( g.perm.RdWiki ) ssub[nsub++] = 'w'; | |
| 1616 | + if( capability_has_any(pCap,"o") ) ssub[nsub++] = 'c'; | |
| 1617 | + if( capability_has_any(pCap,"2") ) ssub[nsub++] = 'f'; | |
| 1618 | + if( capability_has_any(pCap,"r") ) ssub[nsub++] = 't'; | |
| 1619 | + if( capability_has_any(pCap,"j") ) ssub[nsub++] = 'w'; | |
| 1577 | 1620 | ssub[nsub] = 0; |
| 1621 | + capability_free(pCap); | |
| 1578 | 1622 | /* Also add the user to the subscriber table. */ |
| 1579 | 1623 | db_multi_exec( |
| 1580 | 1624 | "INSERT INTO subscriber(semail,suname," |
| 1581 | 1625 | " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" |
| 1582 | 1626 | " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)" |
| @@ -1616,12 +1660,11 @@ | ||
| 1616 | 1660 | @ <blockquote><pre> |
| 1617 | 1661 | @ %h(pSender->zErr) |
| 1618 | 1662 | @ </pre></blockquote> |
| 1619 | 1663 | }else{ |
| 1620 | 1664 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1621 | - @ hyperlink that you must click on in order to activate your | |
| 1622 | - @ subscription.</p> | |
| 1665 | + @ hyperlink that you must click to activate your account.</p> | |
| 1623 | 1666 | } |
| 1624 | 1667 | alert_sender_free(pSender); |
| 1625 | 1668 | if( zGoto ){ |
| 1626 | 1669 | @ <p><a href='%h(zGoto)'>Continue</a> |
| 1627 | 1670 | } |
| @@ -1630,11 +1673,15 @@ | ||
| 1630 | 1673 | } |
| 1631 | 1674 | redirect_to_g(); |
| 1632 | 1675 | } |
| 1633 | 1676 | |
| 1634 | 1677 | /* Prepare the captcha. */ |
| 1635 | - uSeed = captcha_seed(); | |
| 1678 | + if( captchaIsCorrect ){ | |
| 1679 | + uSeed = strtoul(P("captchaseed"),0,10); | |
| 1680 | + }else{ | |
| 1681 | + uSeed = captcha_seed(); | |
| 1682 | + } | |
| 1636 | 1683 | zDecoded = captcha_decode(uSeed); |
| 1637 | 1684 | zCaptcha = captcha_render(zDecoded); |
| 1638 | 1685 | |
| 1639 | 1686 | style_header("Register"); |
| 1640 | 1687 | /* Print out the registration form. */ |
| @@ -1689,11 +1736,12 @@ | ||
| 1689 | 1736 | if( iErrLine==5 ){ |
| 1690 | 1737 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1691 | 1738 | } |
| 1692 | 1739 | @ <tr> |
| 1693 | 1740 | @ <td class="form_label" align="right">Captcha:</td> |
| 1694 | - @ <td><input type="text" name="captcha" value="" size="30"> | |
| 1741 | + @ <td><input type="text" name="captcha" \ | |
| 1742 | + @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> | |
| 1695 | 1743 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1696 | 1744 | @ </td> |
| 1697 | 1745 | @ </tr> |
| 1698 | 1746 | if( iErrLine==6 ){ |
| 1699 | 1747 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1700 | 1748 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -481,11 +481,11 @@ | |
| 481 | int login_self_register_available(const char *zNeeded){ |
| 482 | CapabilityString *pCap; |
| 483 | int rc; |
| 484 | if( !db_get_boolean("self-register",0) ) return 0; |
| 485 | if( zNeeded==0 ) return 1; |
| 486 | pCap = capability_add(0, db_get("default-perms", 0)); |
| 487 | capability_expand(pCap); |
| 488 | rc = capability_has_any(pCap, zNeeded); |
| 489 | capability_free(pCap); |
| 490 | return rc; |
| 491 | } |
| @@ -1128,11 +1128,11 @@ | |
| 1128 | if( zPublicPages!=0 ){ |
| 1129 | Glob *pGlob = glob_create(zPublicPages); |
| 1130 | const char *zUri = PD("REQUEST_URI",""); |
| 1131 | zUri += (int)strlen(g.zTop); |
| 1132 | if( glob_match(pGlob, zUri) ){ |
| 1133 | login_set_capabilities(db_get("default-perms", 0), 0); |
| 1134 | } |
| 1135 | glob_free(pGlob); |
| 1136 | } |
| 1137 | } |
| 1138 | |
| @@ -1459,10 +1459,38 @@ | |
| 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | zUserID, zUserID, zUserID |
| 1461 | ); |
| 1462 | return rc; |
| 1463 | } |
| 1464 | |
| 1465 | /* |
| 1466 | ** WEBPAGE: register |
| 1467 | ** |
| 1468 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1471,13 +1499,14 @@ | |
| 1471 | void register_page(void){ |
| 1472 | const char *zUserID, *zPasswd, *zConfirm, *zEAddr; |
| 1473 | const char *zDName; |
| 1474 | unsigned int uSeed; |
| 1475 | const char *zDecoded; |
| 1476 | char *zCaptcha; |
| 1477 | int iErrLine = -1; |
| 1478 | const char *zErr = 0; |
| 1479 | char *zPerms; /* Permissions for the default user */ |
| 1480 | int canDoAlerts = 0; /* True if receiving email alerts is possible */ |
| 1481 | int doAlerts = 0; /* True if subscription is wanted too */ |
| 1482 | if( !db_get_boolean("self-register", 0) ){ |
| 1483 | style_header("Registration not possible"); |
| @@ -1484,11 +1513,11 @@ | |
| 1484 | @ <p>This project does not allow user self-registration. Please contact the |
| 1485 | @ project administrator to obtain an account.</p> |
| 1486 | style_footer(); |
| 1487 | return; |
| 1488 | } |
| 1489 | zPerms = db_get("default-perms", 0); |
| 1490 | |
| 1491 | /* Prompt the user for email alerts if this repository is configured for |
| 1492 | ** email alerts and if the default permissions include "7" */ |
| 1493 | canDoAlerts = alert_tables_exist() && db_int(0, |
| 1494 | "SELECT fullcap(%Q) GLOB '*7*'", zPerms |
| @@ -1503,11 +1532,11 @@ | |
| 1503 | |
| 1504 | /* Verify user imputs */ |
| 1505 | if( P("new")==0 || !cgi_csrf_safe(1) ){ |
| 1506 | /* This is not a valid form submission. Fall through into |
| 1507 | ** the form display */ |
| 1508 | }else if( !captcha_is_correct(1) ){ |
| 1509 | iErrLine = 6; |
| 1510 | zErr = "Incorrect CAPTCHA"; |
| 1511 | }else if( strlen(zUserID)<6 ){ |
| 1512 | iErrLine = 1; |
| 1513 | zErr = "User ID too short. Must be at least 6 characters."; |
| @@ -1521,10 +1550,13 @@ | |
| 1521 | iErrLine = 3; |
| 1522 | zErr = "Required"; |
| 1523 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1524 | iErrLine = 3; |
| 1525 | zErr = "Not a valid email address"; |
| 1526 | }else if( strlen(zPasswd)<6 ){ |
| 1527 | iErrLine = 4; |
| 1528 | zErr = "Password must be at least 6 characters long"; |
| 1529 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1530 | iErrLine = 5; |
| @@ -1548,16 +1580,24 @@ | |
| 1548 | /* If all of the tests above have passed, that means that the submitted |
| 1549 | ** form contains valid data and we can proceed to create the new login */ |
| 1550 | Blob sql; |
| 1551 | int uid; |
| 1552 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 1553 | blob_init(&sql, 0, 0); |
| 1554 | blob_append_sql(&sql, |
| 1555 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1556 | "VALUES(%Q,%Q,%Q," |
| 1557 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1558 | zUserID, zPass, zPerms, zDName, zEAddr, g.zIpAddr); |
| 1559 | fossil_free(zPass); |
| 1560 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1561 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1562 | login_set_user_cookie(zUserID, uid, NULL); |
| 1563 | if( doAlerts ){ |
| @@ -1567,16 +1607,20 @@ | |
| 1567 | sqlite3_int64 id; /* New subscriber Id */ |
| 1568 | const char *zCode; /* New subscriber code (in hex) */ |
| 1569 | const char *zGoto = P("g"); |
| 1570 | int nsub = 0; |
| 1571 | char ssub[20]; |
| 1572 | ssub[nsub++] = 'a'; |
| 1573 | if( g.perm.Read ) ssub[nsub++] = 'c'; |
| 1574 | if( g.perm.RdForum ) ssub[nsub++] = 'f'; |
| 1575 | if( g.perm.RdTkt ) ssub[nsub++] = 't'; |
| 1576 | if( g.perm.RdWiki ) ssub[nsub++] = 'w'; |
| 1577 | ssub[nsub] = 0; |
| 1578 | /* Also add the user to the subscriber table. */ |
| 1579 | db_multi_exec( |
| 1580 | "INSERT INTO subscriber(semail,suname," |
| 1581 | " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" |
| 1582 | " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)" |
| @@ -1616,12 +1660,11 @@ | |
| 1616 | @ <blockquote><pre> |
| 1617 | @ %h(pSender->zErr) |
| 1618 | @ </pre></blockquote> |
| 1619 | }else{ |
| 1620 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1621 | @ hyperlink that you must click on in order to activate your |
| 1622 | @ subscription.</p> |
| 1623 | } |
| 1624 | alert_sender_free(pSender); |
| 1625 | if( zGoto ){ |
| 1626 | @ <p><a href='%h(zGoto)'>Continue</a> |
| 1627 | } |
| @@ -1630,11 +1673,15 @@ | |
| 1630 | } |
| 1631 | redirect_to_g(); |
| 1632 | } |
| 1633 | |
| 1634 | /* Prepare the captcha. */ |
| 1635 | uSeed = captcha_seed(); |
| 1636 | zDecoded = captcha_decode(uSeed); |
| 1637 | zCaptcha = captcha_render(zDecoded); |
| 1638 | |
| 1639 | style_header("Register"); |
| 1640 | /* Print out the registration form. */ |
| @@ -1689,11 +1736,12 @@ | |
| 1689 | if( iErrLine==5 ){ |
| 1690 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1691 | } |
| 1692 | @ <tr> |
| 1693 | @ <td class="form_label" align="right">Captcha:</td> |
| 1694 | @ <td><input type="text" name="captcha" value="" size="30"> |
| 1695 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1696 | @ </td> |
| 1697 | @ </tr> |
| 1698 | if( iErrLine==6 ){ |
| 1699 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1700 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -481,11 +481,11 @@ | |
| 481 | int login_self_register_available(const char *zNeeded){ |
| 482 | CapabilityString *pCap; |
| 483 | int rc; |
| 484 | if( !db_get_boolean("self-register",0) ) return 0; |
| 485 | if( zNeeded==0 ) return 1; |
| 486 | pCap = capability_add(0, db_get("default-perms", "u")); |
| 487 | capability_expand(pCap); |
| 488 | rc = capability_has_any(pCap, zNeeded); |
| 489 | capability_free(pCap); |
| 490 | return rc; |
| 491 | } |
| @@ -1128,11 +1128,11 @@ | |
| 1128 | if( zPublicPages!=0 ){ |
| 1129 | Glob *pGlob = glob_create(zPublicPages); |
| 1130 | const char *zUri = PD("REQUEST_URI",""); |
| 1131 | zUri += (int)strlen(g.zTop); |
| 1132 | if( glob_match(pGlob, zUri) ){ |
| 1133 | login_set_capabilities(db_get("default-perms", "u"), 0); |
| 1134 | } |
| 1135 | glob_free(pGlob); |
| 1136 | } |
| 1137 | } |
| 1138 | |
| @@ -1459,10 +1459,38 @@ | |
| 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | zUserID, zUserID, zUserID |
| 1461 | ); |
| 1462 | return rc; |
| 1463 | } |
| 1464 | |
| 1465 | /* |
| 1466 | ** Check an email address and confirm that it is valid for self-registration. |
| 1467 | ** The email address is known already to be well-formed. Return true |
| 1468 | ** if the email address is on the allowed list. |
| 1469 | ** |
| 1470 | ** The default behavior is that any valid email address is accepted. |
| 1471 | ** But if the "auth-sub-email" setting exists and is not empty, then |
| 1472 | ** it is a comma-separated list of GLOB patterns for email addresses |
| 1473 | ** that are authorized to self-register. |
| 1474 | */ |
| 1475 | int authorized_subscription_email(const char *zEAddr){ |
| 1476 | char *zGlob = db_get("auth-sub-email",0); |
| 1477 | Glob *pGlob; |
| 1478 | char *zAddr; |
| 1479 | int rc; |
| 1480 | |
| 1481 | if( zGlob==0 || zGlob[0]==0 ) return 1; |
| 1482 | zGlob = fossil_strtolwr(fossil_strdup(zGlob)); |
| 1483 | pGlob = glob_create(zGlob); |
| 1484 | fossil_free(zGlob); |
| 1485 | |
| 1486 | zAddr = fossil_strtolwr(fossil_strdup(zEAddr)); |
| 1487 | rc = glob_match(pGlob, zAddr); |
| 1488 | fossil_free(zAddr); |
| 1489 | glob_free(pGlob); |
| 1490 | return rc!=0; |
| 1491 | } |
| 1492 | |
| 1493 | /* |
| 1494 | ** WEBPAGE: register |
| 1495 | ** |
| 1496 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1471,13 +1499,14 @@ | |
| 1499 | void register_page(void){ |
| 1500 | const char *zUserID, *zPasswd, *zConfirm, *zEAddr; |
| 1501 | const char *zDName; |
| 1502 | unsigned int uSeed; |
| 1503 | const char *zDecoded; |
| 1504 | int iErrLine = -1; |
| 1505 | const char *zErr = 0; |
| 1506 | int captchaIsCorrect = 0; /* True on a correct captcha */ |
| 1507 | char *zCaptcha = ""; /* Value of the captcha text */ |
| 1508 | char *zPerms; /* Permissions for the default user */ |
| 1509 | int canDoAlerts = 0; /* True if receiving email alerts is possible */ |
| 1510 | int doAlerts = 0; /* True if subscription is wanted too */ |
| 1511 | if( !db_get_boolean("self-register", 0) ){ |
| 1512 | style_header("Registration not possible"); |
| @@ -1484,11 +1513,11 @@ | |
| 1513 | @ <p>This project does not allow user self-registration. Please contact the |
| 1514 | @ project administrator to obtain an account.</p> |
| 1515 | style_footer(); |
| 1516 | return; |
| 1517 | } |
| 1518 | zPerms = db_get("default-perms", "u"); |
| 1519 | |
| 1520 | /* Prompt the user for email alerts if this repository is configured for |
| 1521 | ** email alerts and if the default permissions include "7" */ |
| 1522 | canDoAlerts = alert_tables_exist() && db_int(0, |
| 1523 | "SELECT fullcap(%Q) GLOB '*7*'", zPerms |
| @@ -1503,11 +1532,11 @@ | |
| 1532 | |
| 1533 | /* Verify user imputs */ |
| 1534 | if( P("new")==0 || !cgi_csrf_safe(1) ){ |
| 1535 | /* This is not a valid form submission. Fall through into |
| 1536 | ** the form display */ |
| 1537 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 1538 | iErrLine = 6; |
| 1539 | zErr = "Incorrect CAPTCHA"; |
| 1540 | }else if( strlen(zUserID)<6 ){ |
| 1541 | iErrLine = 1; |
| 1542 | zErr = "User ID too short. Must be at least 6 characters."; |
| @@ -1521,10 +1550,13 @@ | |
| 1550 | iErrLine = 3; |
| 1551 | zErr = "Required"; |
| 1552 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1553 | iErrLine = 3; |
| 1554 | zErr = "Not a valid email address"; |
| 1555 | }else if( authorized_subscription_email(zEAddr)==0 ){ |
| 1556 | iErrLine = 3; |
| 1557 | zErr = "Not an authorized email address"; |
| 1558 | }else if( strlen(zPasswd)<6 ){ |
| 1559 | iErrLine = 4; |
| 1560 | zErr = "Password must be at least 6 characters long"; |
| 1561 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1562 | iErrLine = 5; |
| @@ -1548,16 +1580,24 @@ | |
| 1580 | /* If all of the tests above have passed, that means that the submitted |
| 1581 | ** form contains valid data and we can proceed to create the new login */ |
| 1582 | Blob sql; |
| 1583 | int uid; |
| 1584 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 1585 | const char *zStartPerms = zPerms; |
| 1586 | if( db_get_boolean("selfreg-verify",0) ){ |
| 1587 | /* If email verification is required for self-registration, initalize |
| 1588 | ** the new user capabilities to just "7" (Sign up for email). The |
| 1589 | ** full "default-perms" permissions will be added when they click |
| 1590 | ** the verification link on the email they are sent. */ |
| 1591 | zStartPerms = "7"; |
| 1592 | } |
| 1593 | blob_init(&sql, 0, 0); |
| 1594 | blob_append_sql(&sql, |
| 1595 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1596 | "VALUES(%Q,%Q,%Q," |
| 1597 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1598 | zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); |
| 1599 | fossil_free(zPass); |
| 1600 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1601 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1602 | login_set_user_cookie(zUserID, uid, NULL); |
| 1603 | if( doAlerts ){ |
| @@ -1567,16 +1607,20 @@ | |
| 1607 | sqlite3_int64 id; /* New subscriber Id */ |
| 1608 | const char *zCode; /* New subscriber code (in hex) */ |
| 1609 | const char *zGoto = P("g"); |
| 1610 | int nsub = 0; |
| 1611 | char ssub[20]; |
| 1612 | CapabilityString *pCap; |
| 1613 | pCap = capability_add(0, zPerms); |
| 1614 | capability_expand(pCap); |
| 1615 | ssub[nsub++] = 'a'; |
| 1616 | if( capability_has_any(pCap,"o") ) ssub[nsub++] = 'c'; |
| 1617 | if( capability_has_any(pCap,"2") ) ssub[nsub++] = 'f'; |
| 1618 | if( capability_has_any(pCap,"r") ) ssub[nsub++] = 't'; |
| 1619 | if( capability_has_any(pCap,"j") ) ssub[nsub++] = 'w'; |
| 1620 | ssub[nsub] = 0; |
| 1621 | capability_free(pCap); |
| 1622 | /* Also add the user to the subscriber table. */ |
| 1623 | db_multi_exec( |
| 1624 | "INSERT INTO subscriber(semail,suname," |
| 1625 | " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" |
| 1626 | " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)" |
| @@ -1616,12 +1660,11 @@ | |
| 1660 | @ <blockquote><pre> |
| 1661 | @ %h(pSender->zErr) |
| 1662 | @ </pre></blockquote> |
| 1663 | }else{ |
| 1664 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1665 | @ hyperlink that you must click to activate your account.</p> |
| 1666 | } |
| 1667 | alert_sender_free(pSender); |
| 1668 | if( zGoto ){ |
| 1669 | @ <p><a href='%h(zGoto)'>Continue</a> |
| 1670 | } |
| @@ -1630,11 +1673,15 @@ | |
| 1673 | } |
| 1674 | redirect_to_g(); |
| 1675 | } |
| 1676 | |
| 1677 | /* Prepare the captcha. */ |
| 1678 | if( captchaIsCorrect ){ |
| 1679 | uSeed = strtoul(P("captchaseed"),0,10); |
| 1680 | }else{ |
| 1681 | uSeed = captcha_seed(); |
| 1682 | } |
| 1683 | zDecoded = captcha_decode(uSeed); |
| 1684 | zCaptcha = captcha_render(zDecoded); |
| 1685 | |
| 1686 | style_header("Register"); |
| 1687 | /* Print out the registration form. */ |
| @@ -1689,11 +1736,12 @@ | |
| 1736 | if( iErrLine==5 ){ |
| 1737 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1738 | } |
| 1739 | @ <tr> |
| 1740 | @ <td class="form_label" align="right">Captcha:</td> |
| 1741 | @ <td><input type="text" name="captcha" \ |
| 1742 | @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> |
| 1743 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1744 | @ </td> |
| 1745 | @ </tr> |
| 1746 | if( iErrLine==6 ){ |
| 1747 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1748 |
+37
-6
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -782,11 +782,21 @@ | ||
| 782 | 782 | nNewArgc = g.argc+1; |
| 783 | 783 | zNewArgv[i+1] = 0; |
| 784 | 784 | } |
| 785 | 785 | g.argc = nNewArgc; |
| 786 | 786 | g.argv = zNewArgv; |
| 787 | - } | |
| 787 | +#if 0 | |
| 788 | + }else if( g.argc==2 && file_is_repository(g.argv[1]) ){ | |
| 789 | + char **zNewArgv = fossil_malloc( sizeof(char*)*4 ); | |
| 790 | + zNewArgv[0] = g.argv[0]; | |
| 791 | + zNewArgv[1] = "ui"; | |
| 792 | + zNewArgv[2] = g.argv[1]; | |
| 793 | + zNewArgv[3] = 0; | |
| 794 | + g.argc = 3; | |
| 795 | + g.argv = zNewArgv; | |
| 796 | +#endif | |
| 797 | + } | |
| 788 | 798 | zCmdName = g.argv[1]; |
| 789 | 799 | } |
| 790 | 800 | #ifndef _WIN32 |
| 791 | 801 | /* There is a bug in stunnel4 in which it sometimes starts up client |
| 792 | 802 | ** processes without first opening file descriptor 2 (standard error). |
| @@ -812,10 +822,25 @@ | ||
| 812 | 822 | } |
| 813 | 823 | } |
| 814 | 824 | #endif |
| 815 | 825 | g.zCmdName = zCmdName; |
| 816 | 826 | rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); |
| 827 | + if( rc==1 && g.argc==2 && file_is_repository(g.argv[1]) ){ | |
| 828 | + /* If the command-line is "fossil ABC" and "ABC" is no a valid command, | |
| 829 | + ** but "ABC" is the name of a repository file, make the command be | |
| 830 | + ** "fossil ui ABC" instead. | |
| 831 | + */ | |
| 832 | + char **zNewArgv = fossil_malloc( sizeof(char*)*4 ); | |
| 833 | + zNewArgv[0] = g.argv[0]; | |
| 834 | + zNewArgv[1] = "ui"; | |
| 835 | + zNewArgv[2] = g.argv[1]; | |
| 836 | + zNewArgv[3] = 0; | |
| 837 | + g.argc = 3; | |
| 838 | + g.argv = zNewArgv; | |
| 839 | + g.zCmdName = zCmdName = "ui"; | |
| 840 | + rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); | |
| 841 | + } | |
| 817 | 842 | if( rc==1 ){ |
| 818 | 843 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 819 | 844 | if( !g.isHTTP && !g.fNoThHook ){ |
| 820 | 845 | rc = Th_CommandHook(zCmdName, 0); |
| 821 | 846 | }else{ |
| @@ -1091,11 +1116,11 @@ | ||
| 1091 | 1116 | /* |
| 1092 | 1117 | ** This function populates a blob with version information. It is used by |
| 1093 | 1118 | ** the "version" command and "test-version" web page. It assumes the blob |
| 1094 | 1119 | ** passed to it is uninitialized; otherwise, it will leak memory. |
| 1095 | 1120 | */ |
| 1096 | -static void get_version_blob( | |
| 1121 | +void fossil_version_blob( | |
| 1097 | 1122 | Blob *pOut, /* Write the manifest here */ |
| 1098 | 1123 | int bVerbose /* Non-zero for full information. */ |
| 1099 | 1124 | ){ |
| 1100 | 1125 | #if defined(FOSSIL_ENABLE_TCL) |
| 1101 | 1126 | int rc; |
| @@ -1226,11 +1251,11 @@ | ||
| 1226 | 1251 | Blob versionInfo; |
| 1227 | 1252 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 1228 | 1253 | |
| 1229 | 1254 | /* We should be done with options.. */ |
| 1230 | 1255 | verify_all_options(); |
| 1231 | - get_version_blob(&versionInfo, verboseFlag); | |
| 1256 | + fossil_version_blob(&versionInfo, verboseFlag); | |
| 1232 | 1257 | fossil_print("%s", blob_str(&versionInfo)); |
| 1233 | 1258 | } |
| 1234 | 1259 | |
| 1235 | 1260 | |
| 1236 | 1261 | /* |
| @@ -1249,11 +1274,11 @@ | ||
| 1249 | 1274 | login_check_credentials(); |
| 1250 | 1275 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1251 | 1276 | verboseFlag = PD("verbose", 0) != 0; |
| 1252 | 1277 | style_header("Version Information"); |
| 1253 | 1278 | style_submenu_element("Stat", "stat"); |
| 1254 | - get_version_blob(&versionInfo, verboseFlag); | |
| 1279 | + fossil_version_blob(&versionInfo, verboseFlag); | |
| 1255 | 1280 | @ <pre> |
| 1256 | 1281 | @ %h(blob_str(&versionInfo)) |
| 1257 | 1282 | @ </pre> |
| 1258 | 1283 | style_footer(); |
| 1259 | 1284 | } |
| @@ -2498,18 +2523,24 @@ | ||
| 2498 | 2523 | ** |
| 2499 | 2524 | ** Works like the http command but gives setup permission to all users. |
| 2500 | 2525 | ** |
| 2501 | 2526 | ** Options: |
| 2502 | 2527 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2528 | +** --usercap CAP user capability string. (Default: "sx") | |
| 2503 | 2529 | ** |
| 2504 | 2530 | */ |
| 2505 | 2531 | void cmd_test_http(void){ |
| 2506 | 2532 | const char *zIpAddr; /* IP address of remote client */ |
| 2533 | + const char *zUserCap; | |
| 2507 | 2534 | |
| 2508 | 2535 | Th_InitTraceLog(); |
| 2509 | - login_set_capabilities("sx", 0); | |
| 2510 | - g.useLocalauth = 1; | |
| 2536 | + zUserCap = find_option("usercap",0,1); | |
| 2537 | + if( zUserCap==0 ){ | |
| 2538 | + g.useLocalauth = 1; | |
| 2539 | + zUserCap = "sx"; | |
| 2540 | + } | |
| 2541 | + login_set_capabilities(zUserCap, 0); | |
| 2511 | 2542 | g.httpIn = stdin; |
| 2512 | 2543 | g.httpOut = stdout; |
| 2513 | 2544 | fossil_binary_mode(g.httpOut); |
| 2514 | 2545 | fossil_binary_mode(g.httpIn); |
| 2515 | 2546 | g.zExtRoot = find_option("extroot",0,1); |
| 2516 | 2547 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -782,11 +782,21 @@ | |
| 782 | nNewArgc = g.argc+1; |
| 783 | zNewArgv[i+1] = 0; |
| 784 | } |
| 785 | g.argc = nNewArgc; |
| 786 | g.argv = zNewArgv; |
| 787 | } |
| 788 | zCmdName = g.argv[1]; |
| 789 | } |
| 790 | #ifndef _WIN32 |
| 791 | /* There is a bug in stunnel4 in which it sometimes starts up client |
| 792 | ** processes without first opening file descriptor 2 (standard error). |
| @@ -812,10 +822,25 @@ | |
| 812 | } |
| 813 | } |
| 814 | #endif |
| 815 | g.zCmdName = zCmdName; |
| 816 | rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); |
| 817 | if( rc==1 ){ |
| 818 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 819 | if( !g.isHTTP && !g.fNoThHook ){ |
| 820 | rc = Th_CommandHook(zCmdName, 0); |
| 821 | }else{ |
| @@ -1091,11 +1116,11 @@ | |
| 1091 | /* |
| 1092 | ** This function populates a blob with version information. It is used by |
| 1093 | ** the "version" command and "test-version" web page. It assumes the blob |
| 1094 | ** passed to it is uninitialized; otherwise, it will leak memory. |
| 1095 | */ |
| 1096 | static void get_version_blob( |
| 1097 | Blob *pOut, /* Write the manifest here */ |
| 1098 | int bVerbose /* Non-zero for full information. */ |
| 1099 | ){ |
| 1100 | #if defined(FOSSIL_ENABLE_TCL) |
| 1101 | int rc; |
| @@ -1226,11 +1251,11 @@ | |
| 1226 | Blob versionInfo; |
| 1227 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 1228 | |
| 1229 | /* We should be done with options.. */ |
| 1230 | verify_all_options(); |
| 1231 | get_version_blob(&versionInfo, verboseFlag); |
| 1232 | fossil_print("%s", blob_str(&versionInfo)); |
| 1233 | } |
| 1234 | |
| 1235 | |
| 1236 | /* |
| @@ -1249,11 +1274,11 @@ | |
| 1249 | login_check_credentials(); |
| 1250 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1251 | verboseFlag = PD("verbose", 0) != 0; |
| 1252 | style_header("Version Information"); |
| 1253 | style_submenu_element("Stat", "stat"); |
| 1254 | get_version_blob(&versionInfo, verboseFlag); |
| 1255 | @ <pre> |
| 1256 | @ %h(blob_str(&versionInfo)) |
| 1257 | @ </pre> |
| 1258 | style_footer(); |
| 1259 | } |
| @@ -2498,18 +2523,24 @@ | |
| 2498 | ** |
| 2499 | ** Works like the http command but gives setup permission to all users. |
| 2500 | ** |
| 2501 | ** Options: |
| 2502 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2503 | ** |
| 2504 | */ |
| 2505 | void cmd_test_http(void){ |
| 2506 | const char *zIpAddr; /* IP address of remote client */ |
| 2507 | |
| 2508 | Th_InitTraceLog(); |
| 2509 | login_set_capabilities("sx", 0); |
| 2510 | g.useLocalauth = 1; |
| 2511 | g.httpIn = stdin; |
| 2512 | g.httpOut = stdout; |
| 2513 | fossil_binary_mode(g.httpOut); |
| 2514 | fossil_binary_mode(g.httpIn); |
| 2515 | g.zExtRoot = find_option("extroot",0,1); |
| 2516 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -782,11 +782,21 @@ | |
| 782 | nNewArgc = g.argc+1; |
| 783 | zNewArgv[i+1] = 0; |
| 784 | } |
| 785 | g.argc = nNewArgc; |
| 786 | g.argv = zNewArgv; |
| 787 | #if 0 |
| 788 | }else if( g.argc==2 && file_is_repository(g.argv[1]) ){ |
| 789 | char **zNewArgv = fossil_malloc( sizeof(char*)*4 ); |
| 790 | zNewArgv[0] = g.argv[0]; |
| 791 | zNewArgv[1] = "ui"; |
| 792 | zNewArgv[2] = g.argv[1]; |
| 793 | zNewArgv[3] = 0; |
| 794 | g.argc = 3; |
| 795 | g.argv = zNewArgv; |
| 796 | #endif |
| 797 | } |
| 798 | zCmdName = g.argv[1]; |
| 799 | } |
| 800 | #ifndef _WIN32 |
| 801 | /* There is a bug in stunnel4 in which it sometimes starts up client |
| 802 | ** processes without first opening file descriptor 2 (standard error). |
| @@ -812,10 +822,25 @@ | |
| 822 | } |
| 823 | } |
| 824 | #endif |
| 825 | g.zCmdName = zCmdName; |
| 826 | rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); |
| 827 | if( rc==1 && g.argc==2 && file_is_repository(g.argv[1]) ){ |
| 828 | /* If the command-line is "fossil ABC" and "ABC" is no a valid command, |
| 829 | ** but "ABC" is the name of a repository file, make the command be |
| 830 | ** "fossil ui ABC" instead. |
| 831 | */ |
| 832 | char **zNewArgv = fossil_malloc( sizeof(char*)*4 ); |
| 833 | zNewArgv[0] = g.argv[0]; |
| 834 | zNewArgv[1] = "ui"; |
| 835 | zNewArgv[2] = g.argv[1]; |
| 836 | zNewArgv[3] = 0; |
| 837 | g.argc = 3; |
| 838 | g.argv = zNewArgv; |
| 839 | g.zCmdName = zCmdName = "ui"; |
| 840 | rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); |
| 841 | } |
| 842 | if( rc==1 ){ |
| 843 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 844 | if( !g.isHTTP && !g.fNoThHook ){ |
| 845 | rc = Th_CommandHook(zCmdName, 0); |
| 846 | }else{ |
| @@ -1091,11 +1116,11 @@ | |
| 1116 | /* |
| 1117 | ** This function populates a blob with version information. It is used by |
| 1118 | ** the "version" command and "test-version" web page. It assumes the blob |
| 1119 | ** passed to it is uninitialized; otherwise, it will leak memory. |
| 1120 | */ |
| 1121 | void fossil_version_blob( |
| 1122 | Blob *pOut, /* Write the manifest here */ |
| 1123 | int bVerbose /* Non-zero for full information. */ |
| 1124 | ){ |
| 1125 | #if defined(FOSSIL_ENABLE_TCL) |
| 1126 | int rc; |
| @@ -1226,11 +1251,11 @@ | |
| 1251 | Blob versionInfo; |
| 1252 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 1253 | |
| 1254 | /* We should be done with options.. */ |
| 1255 | verify_all_options(); |
| 1256 | fossil_version_blob(&versionInfo, verboseFlag); |
| 1257 | fossil_print("%s", blob_str(&versionInfo)); |
| 1258 | } |
| 1259 | |
| 1260 | |
| 1261 | /* |
| @@ -1249,11 +1274,11 @@ | |
| 1274 | login_check_credentials(); |
| 1275 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1276 | verboseFlag = PD("verbose", 0) != 0; |
| 1277 | style_header("Version Information"); |
| 1278 | style_submenu_element("Stat", "stat"); |
| 1279 | fossil_version_blob(&versionInfo, verboseFlag); |
| 1280 | @ <pre> |
| 1281 | @ %h(blob_str(&versionInfo)) |
| 1282 | @ </pre> |
| 1283 | style_footer(); |
| 1284 | } |
| @@ -2498,18 +2523,24 @@ | |
| 2523 | ** |
| 2524 | ** Works like the http command but gives setup permission to all users. |
| 2525 | ** |
| 2526 | ** Options: |
| 2527 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2528 | ** --usercap CAP user capability string. (Default: "sx") |
| 2529 | ** |
| 2530 | */ |
| 2531 | void cmd_test_http(void){ |
| 2532 | const char *zIpAddr; /* IP address of remote client */ |
| 2533 | const char *zUserCap; |
| 2534 | |
| 2535 | Th_InitTraceLog(); |
| 2536 | zUserCap = find_option("usercap",0,1); |
| 2537 | if( zUserCap==0 ){ |
| 2538 | g.useLocalauth = 1; |
| 2539 | zUserCap = "sx"; |
| 2540 | } |
| 2541 | login_set_capabilities(zUserCap, 0); |
| 2542 | g.httpIn = stdin; |
| 2543 | g.httpOut = stdout; |
| 2544 | fossil_binary_mode(g.httpOut); |
| 2545 | fossil_binary_mode(g.httpIn); |
| 2546 | g.zExtRoot = find_option("extroot",0,1); |
| 2547 |
+24
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -18,10 +18,11 @@ | ||
| 18 | 18 | SRC = \ |
| 19 | 19 | $(SRCDIR)/add.c \ |
| 20 | 20 | $(SRCDIR)/alerts.c \ |
| 21 | 21 | $(SRCDIR)/allrepo.c \ |
| 22 | 22 | $(SRCDIR)/attach.c \ |
| 23 | + $(SRCDIR)/backlink.c \ | |
| 23 | 24 | $(SRCDIR)/backoffice.c \ |
| 24 | 25 | $(SRCDIR)/bag.c \ |
| 25 | 26 | $(SRCDIR)/bisect.c \ |
| 26 | 27 | $(SRCDIR)/blob.c \ |
| 27 | 28 | $(SRCDIR)/branch.c \ |
| @@ -130,10 +131,11 @@ | ||
| 130 | 131 | $(SRCDIR)/statrep.c \ |
| 131 | 132 | $(SRCDIR)/style.c \ |
| 132 | 133 | $(SRCDIR)/sync.c \ |
| 133 | 134 | $(SRCDIR)/tag.c \ |
| 134 | 135 | $(SRCDIR)/tar.c \ |
| 136 | + $(SRCDIR)/terminal.c \ | |
| 135 | 137 | $(SRCDIR)/th_main.c \ |
| 136 | 138 | $(SRCDIR)/timeline.c \ |
| 137 | 139 | $(SRCDIR)/tkt.c \ |
| 138 | 140 | $(SRCDIR)/tktsetup.c \ |
| 139 | 141 | $(SRCDIR)/undo.c \ |
| @@ -250,10 +252,11 @@ | ||
| 250 | 252 | TRANS_SRC = \ |
| 251 | 253 | $(OBJDIR)/add_.c \ |
| 252 | 254 | $(OBJDIR)/alerts_.c \ |
| 253 | 255 | $(OBJDIR)/allrepo_.c \ |
| 254 | 256 | $(OBJDIR)/attach_.c \ |
| 257 | + $(OBJDIR)/backlink_.c \ | |
| 255 | 258 | $(OBJDIR)/backoffice_.c \ |
| 256 | 259 | $(OBJDIR)/bag_.c \ |
| 257 | 260 | $(OBJDIR)/bisect_.c \ |
| 258 | 261 | $(OBJDIR)/blob_.c \ |
| 259 | 262 | $(OBJDIR)/branch_.c \ |
| @@ -362,10 +365,11 @@ | ||
| 362 | 365 | $(OBJDIR)/statrep_.c \ |
| 363 | 366 | $(OBJDIR)/style_.c \ |
| 364 | 367 | $(OBJDIR)/sync_.c \ |
| 365 | 368 | $(OBJDIR)/tag_.c \ |
| 366 | 369 | $(OBJDIR)/tar_.c \ |
| 370 | + $(OBJDIR)/terminal_.c \ | |
| 367 | 371 | $(OBJDIR)/th_main_.c \ |
| 368 | 372 | $(OBJDIR)/timeline_.c \ |
| 369 | 373 | $(OBJDIR)/tkt_.c \ |
| 370 | 374 | $(OBJDIR)/tktsetup_.c \ |
| 371 | 375 | $(OBJDIR)/undo_.c \ |
| @@ -391,10 +395,11 @@ | ||
| 391 | 395 | OBJ = \ |
| 392 | 396 | $(OBJDIR)/add.o \ |
| 393 | 397 | $(OBJDIR)/alerts.o \ |
| 394 | 398 | $(OBJDIR)/allrepo.o \ |
| 395 | 399 | $(OBJDIR)/attach.o \ |
| 400 | + $(OBJDIR)/backlink.o \ | |
| 396 | 401 | $(OBJDIR)/backoffice.o \ |
| 397 | 402 | $(OBJDIR)/bag.o \ |
| 398 | 403 | $(OBJDIR)/bisect.o \ |
| 399 | 404 | $(OBJDIR)/blob.o \ |
| 400 | 405 | $(OBJDIR)/branch.o \ |
| @@ -503,10 +508,11 @@ | ||
| 503 | 508 | $(OBJDIR)/statrep.o \ |
| 504 | 509 | $(OBJDIR)/style.o \ |
| 505 | 510 | $(OBJDIR)/sync.o \ |
| 506 | 511 | $(OBJDIR)/tag.o \ |
| 507 | 512 | $(OBJDIR)/tar.o \ |
| 513 | + $(OBJDIR)/terminal.o \ | |
| 508 | 514 | $(OBJDIR)/th_main.o \ |
| 509 | 515 | $(OBJDIR)/timeline.o \ |
| 510 | 516 | $(OBJDIR)/tkt.o \ |
| 511 | 517 | $(OBJDIR)/tktsetup.o \ |
| 512 | 518 | $(OBJDIR)/undo.o \ |
| @@ -727,10 +733,11 @@ | ||
| 727 | 733 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h |
| 728 | 734 | $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 729 | 735 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 730 | 736 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 731 | 737 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 738 | + $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ | |
| 732 | 739 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 733 | 740 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 734 | 741 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 735 | 742 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 736 | 743 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -839,10 +846,11 @@ | ||
| 839 | 846 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 840 | 847 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 841 | 848 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 842 | 849 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 843 | 850 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 851 | + $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ | |
| 844 | 852 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 845 | 853 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 846 | 854 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 847 | 855 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 848 | 856 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -900,10 +908,18 @@ | ||
| 900 | 908 | |
| 901 | 909 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 902 | 910 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 903 | 911 | |
| 904 | 912 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 913 | + | |
| 914 | +$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(OBJDIR)/translate | |
| 915 | + $(OBJDIR)/translate $(SRCDIR)/backlink.c >$@ | |
| 916 | + | |
| 917 | +$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h | |
| 918 | + $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c | |
| 919 | + | |
| 920 | +$(OBJDIR)/backlink.h: $(OBJDIR)/headers | |
| 905 | 921 | |
| 906 | 922 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate |
| 907 | 923 | $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@ |
| 908 | 924 | |
| 909 | 925 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -1796,10 +1812,18 @@ | ||
| 1796 | 1812 | |
| 1797 | 1813 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 1798 | 1814 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 1799 | 1815 | |
| 1800 | 1816 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 1817 | + | |
| 1818 | +$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(OBJDIR)/translate | |
| 1819 | + $(OBJDIR)/translate $(SRCDIR)/terminal.c >$@ | |
| 1820 | + | |
| 1821 | +$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h | |
| 1822 | + $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c | |
| 1823 | + | |
| 1824 | +$(OBJDIR)/terminal.h: $(OBJDIR)/headers | |
| 1801 | 1825 | |
| 1802 | 1826 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(OBJDIR)/translate |
| 1803 | 1827 | $(OBJDIR)/translate $(SRCDIR)/th_main.c >$@ |
| 1804 | 1828 | |
| 1805 | 1829 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 1806 | 1830 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -18,10 +18,11 @@ | |
| 18 | SRC = \ |
| 19 | $(SRCDIR)/add.c \ |
| 20 | $(SRCDIR)/alerts.c \ |
| 21 | $(SRCDIR)/allrepo.c \ |
| 22 | $(SRCDIR)/attach.c \ |
| 23 | $(SRCDIR)/backoffice.c \ |
| 24 | $(SRCDIR)/bag.c \ |
| 25 | $(SRCDIR)/bisect.c \ |
| 26 | $(SRCDIR)/blob.c \ |
| 27 | $(SRCDIR)/branch.c \ |
| @@ -130,10 +131,11 @@ | |
| 130 | $(SRCDIR)/statrep.c \ |
| 131 | $(SRCDIR)/style.c \ |
| 132 | $(SRCDIR)/sync.c \ |
| 133 | $(SRCDIR)/tag.c \ |
| 134 | $(SRCDIR)/tar.c \ |
| 135 | $(SRCDIR)/th_main.c \ |
| 136 | $(SRCDIR)/timeline.c \ |
| 137 | $(SRCDIR)/tkt.c \ |
| 138 | $(SRCDIR)/tktsetup.c \ |
| 139 | $(SRCDIR)/undo.c \ |
| @@ -250,10 +252,11 @@ | |
| 250 | TRANS_SRC = \ |
| 251 | $(OBJDIR)/add_.c \ |
| 252 | $(OBJDIR)/alerts_.c \ |
| 253 | $(OBJDIR)/allrepo_.c \ |
| 254 | $(OBJDIR)/attach_.c \ |
| 255 | $(OBJDIR)/backoffice_.c \ |
| 256 | $(OBJDIR)/bag_.c \ |
| 257 | $(OBJDIR)/bisect_.c \ |
| 258 | $(OBJDIR)/blob_.c \ |
| 259 | $(OBJDIR)/branch_.c \ |
| @@ -362,10 +365,11 @@ | |
| 362 | $(OBJDIR)/statrep_.c \ |
| 363 | $(OBJDIR)/style_.c \ |
| 364 | $(OBJDIR)/sync_.c \ |
| 365 | $(OBJDIR)/tag_.c \ |
| 366 | $(OBJDIR)/tar_.c \ |
| 367 | $(OBJDIR)/th_main_.c \ |
| 368 | $(OBJDIR)/timeline_.c \ |
| 369 | $(OBJDIR)/tkt_.c \ |
| 370 | $(OBJDIR)/tktsetup_.c \ |
| 371 | $(OBJDIR)/undo_.c \ |
| @@ -391,10 +395,11 @@ | |
| 391 | OBJ = \ |
| 392 | $(OBJDIR)/add.o \ |
| 393 | $(OBJDIR)/alerts.o \ |
| 394 | $(OBJDIR)/allrepo.o \ |
| 395 | $(OBJDIR)/attach.o \ |
| 396 | $(OBJDIR)/backoffice.o \ |
| 397 | $(OBJDIR)/bag.o \ |
| 398 | $(OBJDIR)/bisect.o \ |
| 399 | $(OBJDIR)/blob.o \ |
| 400 | $(OBJDIR)/branch.o \ |
| @@ -503,10 +508,11 @@ | |
| 503 | $(OBJDIR)/statrep.o \ |
| 504 | $(OBJDIR)/style.o \ |
| 505 | $(OBJDIR)/sync.o \ |
| 506 | $(OBJDIR)/tag.o \ |
| 507 | $(OBJDIR)/tar.o \ |
| 508 | $(OBJDIR)/th_main.o \ |
| 509 | $(OBJDIR)/timeline.o \ |
| 510 | $(OBJDIR)/tkt.o \ |
| 511 | $(OBJDIR)/tktsetup.o \ |
| 512 | $(OBJDIR)/undo.o \ |
| @@ -727,10 +733,11 @@ | |
| 727 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h |
| 728 | $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 729 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 730 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 731 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 732 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 733 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 734 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 735 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 736 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -839,10 +846,11 @@ | |
| 839 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 840 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 841 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 842 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 843 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 844 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 845 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 846 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 847 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 848 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -900,10 +908,18 @@ | |
| 900 | |
| 901 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 902 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 903 | |
| 904 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 905 | |
| 906 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate |
| 907 | $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@ |
| 908 | |
| 909 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -1796,10 +1812,18 @@ | |
| 1796 | |
| 1797 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 1798 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 1799 | |
| 1800 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 1801 | |
| 1802 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(OBJDIR)/translate |
| 1803 | $(OBJDIR)/translate $(SRCDIR)/th_main.c >$@ |
| 1804 | |
| 1805 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 1806 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -18,10 +18,11 @@ | |
| 18 | SRC = \ |
| 19 | $(SRCDIR)/add.c \ |
| 20 | $(SRCDIR)/alerts.c \ |
| 21 | $(SRCDIR)/allrepo.c \ |
| 22 | $(SRCDIR)/attach.c \ |
| 23 | $(SRCDIR)/backlink.c \ |
| 24 | $(SRCDIR)/backoffice.c \ |
| 25 | $(SRCDIR)/bag.c \ |
| 26 | $(SRCDIR)/bisect.c \ |
| 27 | $(SRCDIR)/blob.c \ |
| 28 | $(SRCDIR)/branch.c \ |
| @@ -130,10 +131,11 @@ | |
| 131 | $(SRCDIR)/statrep.c \ |
| 132 | $(SRCDIR)/style.c \ |
| 133 | $(SRCDIR)/sync.c \ |
| 134 | $(SRCDIR)/tag.c \ |
| 135 | $(SRCDIR)/tar.c \ |
| 136 | $(SRCDIR)/terminal.c \ |
| 137 | $(SRCDIR)/th_main.c \ |
| 138 | $(SRCDIR)/timeline.c \ |
| 139 | $(SRCDIR)/tkt.c \ |
| 140 | $(SRCDIR)/tktsetup.c \ |
| 141 | $(SRCDIR)/undo.c \ |
| @@ -250,10 +252,11 @@ | |
| 252 | TRANS_SRC = \ |
| 253 | $(OBJDIR)/add_.c \ |
| 254 | $(OBJDIR)/alerts_.c \ |
| 255 | $(OBJDIR)/allrepo_.c \ |
| 256 | $(OBJDIR)/attach_.c \ |
| 257 | $(OBJDIR)/backlink_.c \ |
| 258 | $(OBJDIR)/backoffice_.c \ |
| 259 | $(OBJDIR)/bag_.c \ |
| 260 | $(OBJDIR)/bisect_.c \ |
| 261 | $(OBJDIR)/blob_.c \ |
| 262 | $(OBJDIR)/branch_.c \ |
| @@ -362,10 +365,11 @@ | |
| 365 | $(OBJDIR)/statrep_.c \ |
| 366 | $(OBJDIR)/style_.c \ |
| 367 | $(OBJDIR)/sync_.c \ |
| 368 | $(OBJDIR)/tag_.c \ |
| 369 | $(OBJDIR)/tar_.c \ |
| 370 | $(OBJDIR)/terminal_.c \ |
| 371 | $(OBJDIR)/th_main_.c \ |
| 372 | $(OBJDIR)/timeline_.c \ |
| 373 | $(OBJDIR)/tkt_.c \ |
| 374 | $(OBJDIR)/tktsetup_.c \ |
| 375 | $(OBJDIR)/undo_.c \ |
| @@ -391,10 +395,11 @@ | |
| 395 | OBJ = \ |
| 396 | $(OBJDIR)/add.o \ |
| 397 | $(OBJDIR)/alerts.o \ |
| 398 | $(OBJDIR)/allrepo.o \ |
| 399 | $(OBJDIR)/attach.o \ |
| 400 | $(OBJDIR)/backlink.o \ |
| 401 | $(OBJDIR)/backoffice.o \ |
| 402 | $(OBJDIR)/bag.o \ |
| 403 | $(OBJDIR)/bisect.o \ |
| 404 | $(OBJDIR)/blob.o \ |
| 405 | $(OBJDIR)/branch.o \ |
| @@ -503,10 +508,11 @@ | |
| 508 | $(OBJDIR)/statrep.o \ |
| 509 | $(OBJDIR)/style.o \ |
| 510 | $(OBJDIR)/sync.o \ |
| 511 | $(OBJDIR)/tag.o \ |
| 512 | $(OBJDIR)/tar.o \ |
| 513 | $(OBJDIR)/terminal.o \ |
| 514 | $(OBJDIR)/th_main.o \ |
| 515 | $(OBJDIR)/timeline.o \ |
| 516 | $(OBJDIR)/tkt.o \ |
| 517 | $(OBJDIR)/tktsetup.o \ |
| 518 | $(OBJDIR)/undo.o \ |
| @@ -727,10 +733,11 @@ | |
| 733 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h |
| 734 | $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 735 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 736 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 737 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 738 | $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ |
| 739 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 740 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 741 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 742 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 743 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -839,10 +846,11 @@ | |
| 846 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 847 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 848 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 849 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 850 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 851 | $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ |
| 852 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 853 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 854 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 855 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 856 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -900,10 +908,18 @@ | |
| 908 | |
| 909 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 910 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 911 | |
| 912 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 913 | |
| 914 | $(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(OBJDIR)/translate |
| 915 | $(OBJDIR)/translate $(SRCDIR)/backlink.c >$@ |
| 916 | |
| 917 | $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h |
| 918 | $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c |
| 919 | |
| 920 | $(OBJDIR)/backlink.h: $(OBJDIR)/headers |
| 921 | |
| 922 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate |
| 923 | $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@ |
| 924 | |
| 925 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -1796,10 +1812,18 @@ | |
| 1812 | |
| 1813 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 1814 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 1815 | |
| 1816 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 1817 | |
| 1818 | $(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(OBJDIR)/translate |
| 1819 | $(OBJDIR)/translate $(SRCDIR)/terminal.c >$@ |
| 1820 | |
| 1821 | $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h |
| 1822 | $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c |
| 1823 | |
| 1824 | $(OBJDIR)/terminal.h: $(OBJDIR)/headers |
| 1825 | |
| 1826 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(OBJDIR)/translate |
| 1827 | $(OBJDIR)/translate $(SRCDIR)/th_main.c >$@ |
| 1828 | |
| 1829 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 1830 |
+4
-2
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -29,10 +29,11 @@ | ||
| 29 | 29 | set src { |
| 30 | 30 | add |
| 31 | 31 | alerts |
| 32 | 32 | allrepo |
| 33 | 33 | attach |
| 34 | + backlink | |
| 34 | 35 | backoffice |
| 35 | 36 | bag |
| 36 | 37 | bisect |
| 37 | 38 | blob |
| 38 | 39 | branch |
| @@ -140,10 +141,11 @@ | ||
| 140 | 141 | statrep |
| 141 | 142 | style |
| 142 | 143 | sync |
| 143 | 144 | tag |
| 144 | 145 | tar |
| 146 | + terminal | |
| 145 | 147 | th_main |
| 146 | 148 | timeline |
| 147 | 149 | tkt |
| 148 | 150 | tktsetup |
| 149 | 151 | undo |
| @@ -713,11 +715,11 @@ | ||
| 713 | 715 | #### The directories where the OpenSSL include and library files are located. |
| 714 | 716 | # The recommended usage here is to use the Sysinternals junction tool |
| 715 | 717 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 716 | 718 | # Fossil source code directory and the target OpenSSL source directory. |
| 717 | 719 | # |
| 718 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f | |
| 720 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g | |
| 719 | 721 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 720 | 722 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 721 | 723 | |
| 722 | 724 | #### Either the directory where the Tcl library is installed or the Tcl |
| 723 | 725 | # source code directory resides (depending on the value of the macro |
| @@ -1570,11 +1572,11 @@ | ||
| 1570 | 1572 | !ifndef USE_SEE |
| 1571 | 1573 | USE_SEE = 0 |
| 1572 | 1574 | !endif |
| 1573 | 1575 | |
| 1574 | 1576 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 1575 | -SSLDIR = $(B)\compat\openssl-1.1.1f | |
| 1577 | +SSLDIR = $(B)\compat\openssl-1.1.1g | |
| 1576 | 1578 | SSLINCDIR = $(SSLDIR)\include |
| 1577 | 1579 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 1578 | 1580 | SSLLIBDIR = $(SSLDIR) |
| 1579 | 1581 | !else |
| 1580 | 1582 | SSLLIBDIR = $(SSLDIR) |
| 1581 | 1583 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -29,10 +29,11 @@ | |
| 29 | set src { |
| 30 | add |
| 31 | alerts |
| 32 | allrepo |
| 33 | attach |
| 34 | backoffice |
| 35 | bag |
| 36 | bisect |
| 37 | blob |
| 38 | branch |
| @@ -140,10 +141,11 @@ | |
| 140 | statrep |
| 141 | style |
| 142 | sync |
| 143 | tag |
| 144 | tar |
| 145 | th_main |
| 146 | timeline |
| 147 | tkt |
| 148 | tktsetup |
| 149 | undo |
| @@ -713,11 +715,11 @@ | |
| 713 | #### The directories where the OpenSSL include and library files are located. |
| 714 | # The recommended usage here is to use the Sysinternals junction tool |
| 715 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 716 | # Fossil source code directory and the target OpenSSL source directory. |
| 717 | # |
| 718 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f |
| 719 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 720 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 721 | |
| 722 | #### Either the directory where the Tcl library is installed or the Tcl |
| 723 | # source code directory resides (depending on the value of the macro |
| @@ -1570,11 +1572,11 @@ | |
| 1570 | !ifndef USE_SEE |
| 1571 | USE_SEE = 0 |
| 1572 | !endif |
| 1573 | |
| 1574 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 1575 | SSLDIR = $(B)\compat\openssl-1.1.1f |
| 1576 | SSLINCDIR = $(SSLDIR)\include |
| 1577 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 1578 | SSLLIBDIR = $(SSLDIR) |
| 1579 | !else |
| 1580 | SSLLIBDIR = $(SSLDIR) |
| 1581 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -29,10 +29,11 @@ | |
| 29 | set src { |
| 30 | add |
| 31 | alerts |
| 32 | allrepo |
| 33 | attach |
| 34 | backlink |
| 35 | backoffice |
| 36 | bag |
| 37 | bisect |
| 38 | blob |
| 39 | branch |
| @@ -140,10 +141,11 @@ | |
| 141 | statrep |
| 142 | style |
| 143 | sync |
| 144 | tag |
| 145 | tar |
| 146 | terminal |
| 147 | th_main |
| 148 | timeline |
| 149 | tkt |
| 150 | tktsetup |
| 151 | undo |
| @@ -713,11 +715,11 @@ | |
| 715 | #### The directories where the OpenSSL include and library files are located. |
| 716 | # The recommended usage here is to use the Sysinternals junction tool |
| 717 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 718 | # Fossil source code directory and the target OpenSSL source directory. |
| 719 | # |
| 720 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g |
| 721 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 722 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 723 | |
| 724 | #### Either the directory where the Tcl library is installed or the Tcl |
| 725 | # source code directory resides (depending on the value of the macro |
| @@ -1570,11 +1572,11 @@ | |
| 1572 | !ifndef USE_SEE |
| 1573 | USE_SEE = 0 |
| 1574 | !endif |
| 1575 | |
| 1576 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 1577 | SSLDIR = $(B)\compat\openssl-1.1.1g |
| 1578 | SSLINCDIR = $(SSLDIR)\include |
| 1579 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 1580 | SSLLIBDIR = $(SSLDIR) |
| 1581 | !else |
| 1582 | SSLLIBDIR = $(SSLDIR) |
| 1583 |
+60
-13
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -1828,19 +1828,30 @@ | ||
| 1828 | 1828 | void manifest_crosslink_begin(void){ |
| 1829 | 1829 | assert( manifest_crosslink_busy==0 ); |
| 1830 | 1830 | manifest_crosslink_busy = 1; |
| 1831 | 1831 | db_begin_transaction(); |
| 1832 | 1832 | db_multi_exec( |
| 1833 | - "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);" | |
| 1833 | + "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;" | |
| 1834 | 1834 | "CREATE TEMP TABLE time_fudge(" |
| 1835 | 1835 | " mid INTEGER PRIMARY KEY," /* The rid of a manifest */ |
| 1836 | 1836 | " m1 REAL," /* The timestamp on mid */ |
| 1837 | 1837 | " cid INTEGER," /* A child or mid */ |
| 1838 | 1838 | " m2 REAL" /* Timestamp on the child */ |
| 1839 | 1839 | ");" |
| 1840 | 1840 | ); |
| 1841 | 1841 | } |
| 1842 | + | |
| 1843 | +/* | |
| 1844 | +** Add a new entry to the pending_xlink table. | |
| 1845 | +*/ | |
| 1846 | +static void add_pending_crosslink(char cType, const char *zId){ | |
| 1847 | + assert( manifest_crosslink_busy==1 ); | |
| 1848 | + db_multi_exec( | |
| 1849 | + "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')", | |
| 1850 | + cType, zId | |
| 1851 | + ); | |
| 1852 | +} | |
| 1842 | 1853 | |
| 1843 | 1854 | #if INTERFACE |
| 1844 | 1855 | /* Timestamps might be adjusted slightly to ensure that check-ins appear |
| 1845 | 1856 | ** on the timeline in chronological order. This is the maximum amount |
| 1846 | 1857 | ** of the adjustment window, in days. |
| @@ -1879,20 +1890,28 @@ | ||
| 1879 | 1890 | int rid = db_column_int(&q,0); |
| 1880 | 1891 | const char *zValue = db_column_text(&q,1); |
| 1881 | 1892 | manifest_reparent_checkin(rid, zValue); |
| 1882 | 1893 | } |
| 1883 | 1894 | db_finalize(&q); |
| 1884 | - db_prepare(&q, "SELECT uuid FROM pending_tkt"); | |
| 1895 | + db_prepare(&q, "SELECT id FROM pending_xlink"); | |
| 1885 | 1896 | while( db_step(&q)==SQLITE_ROW ){ |
| 1886 | - const char *zUuid = db_column_text(&q, 0); | |
| 1887 | - ticket_rebuild_entry(zUuid); | |
| 1888 | - if( permitHooks && rc==TH_OK ){ | |
| 1889 | - rc = xfer_run_script(zScript, zUuid, 0); | |
| 1897 | + const char *zId = db_column_text(&q, 0); | |
| 1898 | + char cType; | |
| 1899 | + if( zId==0 || zId[0]==0 ) continue; | |
| 1900 | + cType = zId[0]; | |
| 1901 | + zId++; | |
| 1902 | + if( cType=='t' ){ | |
| 1903 | + ticket_rebuild_entry(zId); | |
| 1904 | + if( permitHooks && rc==TH_OK ){ | |
| 1905 | + rc = xfer_run_script(zScript, zId, 0); | |
| 1906 | + } | |
| 1907 | + }else if( cType=='w' ){ | |
| 1908 | + backlink_wiki_refresh(zId); | |
| 1890 | 1909 | } |
| 1891 | 1910 | } |
| 1892 | 1911 | db_finalize(&q); |
| 1893 | - db_multi_exec("DROP TABLE pending_tkt"); | |
| 1912 | + db_multi_exec("DROP TABLE pending_xlink"); | |
| 1894 | 1913 | |
| 1895 | 1914 | /* If multiple check-ins happen close together in time, adjust their |
| 1896 | 1915 | ** times by a few milliseconds to make sure they appear in chronological |
| 1897 | 1916 | ** order. |
| 1898 | 1917 | */ |
| @@ -2162,11 +2181,11 @@ | ||
| 2162 | 2181 | TAG_USER, rid, |
| 2163 | 2182 | TAG_COMMENT, rid, p->rDate |
| 2164 | 2183 | ); |
| 2165 | 2184 | zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" |
| 2166 | 2185 | " WHERE rowid=last_insert_rowid()"); |
| 2167 | - wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE); | |
| 2186 | + backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1); | |
| 2168 | 2187 | fossil_free(zCom); |
| 2169 | 2188 | |
| 2170 | 2189 | /* If this is a delta-manifest, record the fact that this repository |
| 2171 | 2190 | ** contains delta manifests, to free the "commit" logic to generate |
| 2172 | 2191 | ** new delta manifests. |
| @@ -2227,10 +2246,11 @@ | ||
| 2227 | 2246 | if( p->type==CFTYPE_WIKI ){ |
| 2228 | 2247 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2229 | 2248 | int tagid = tag_findid(zTag, 1); |
| 2230 | 2249 | int prior; |
| 2231 | 2250 | char *zComment; |
| 2251 | + const char *zPrefix; | |
| 2232 | 2252 | int nWiki; |
| 2233 | 2253 | char zLength[40]; |
| 2234 | 2254 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2235 | 2255 | nWiki = strlen(p->zWiki); |
| 2236 | 2256 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| @@ -2243,16 +2263,44 @@ | ||
| 2243 | 2263 | tagid, p->rDate |
| 2244 | 2264 | ); |
| 2245 | 2265 | if( prior ){ |
| 2246 | 2266 | content_deltify(prior, &rid, 1, 0); |
| 2247 | 2267 | } |
| 2248 | - if( nWiki>0 ){ | |
| 2249 | - zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); | |
| 2268 | + if( nWiki<=0 ){ | |
| 2269 | + zPrefix = "Deleted"; | |
| 2270 | + }else if( !prior ){ | |
| 2271 | + zPrefix = "Added"; | |
| 2250 | 2272 | }else{ |
| 2251 | - zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); | |
| 2273 | + zPrefix = "Changes to"; | |
| 2274 | + } | |
| 2275 | + switch( wiki_page_type(p->zWikiTitle) ){ | |
| 2276 | + case WIKITYPE_CHECKIN: { | |
| 2277 | + zComment = mprintf("%s wiki for check-in [%S]", zPrefix, | |
| 2278 | + p->zWikiTitle+8); | |
| 2279 | + break; | |
| 2280 | + } | |
| 2281 | + case WIKITYPE_BRANCH: { | |
| 2282 | + zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]", | |
| 2283 | + zPrefix, p->zWikiTitle+7, p->zWikiTitle+7); | |
| 2284 | + break; | |
| 2285 | + } | |
| 2286 | + case WIKITYPE_TAG: { | |
| 2287 | + zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]", | |
| 2288 | + zPrefix, p->zWikiTitle+4, p->zWikiTitle+4); | |
| 2289 | + break; | |
| 2290 | + } | |
| 2291 | + default: { | |
| 2292 | + zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle); | |
| 2293 | + break; | |
| 2294 | + } | |
| 2252 | 2295 | } |
| 2253 | 2296 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2297 | + if( manifest_crosslink_busy ){ | |
| 2298 | + add_pending_crosslink('w',p->zWikiTitle); | |
| 2299 | + }else{ | |
| 2300 | + backlink_wiki_refresh(p->zWikiTitle); | |
| 2301 | + } | |
| 2254 | 2302 | db_multi_exec( |
| 2255 | 2303 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 2256 | 2304 | " bgcolor,euser,ecomment)" |
| 2257 | 2305 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 2258 | 2306 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| @@ -2349,12 +2397,11 @@ | ||
| 2349 | 2397 | Stmt qatt; |
| 2350 | 2398 | assert( manifest_crosslink_busy==1 ); |
| 2351 | 2399 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 2352 | 2400 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 2353 | 2401 | fossil_free(zTag); |
| 2354 | - db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", | |
| 2355 | - p->zTicketUuid); | |
| 2402 | + add_pending_crosslink('t',p->zTicketUuid); | |
| 2356 | 2403 | /* Locate and update comment for any attachments */ |
| 2357 | 2404 | db_prepare(&qatt, |
| 2358 | 2405 | "SELECT attachid, src, target, filename FROM attachment" |
| 2359 | 2406 | " WHERE target=%Q", |
| 2360 | 2407 | p->zTicketUuid |
| 2361 | 2408 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -1828,19 +1828,30 @@ | |
| 1828 | void manifest_crosslink_begin(void){ |
| 1829 | assert( manifest_crosslink_busy==0 ); |
| 1830 | manifest_crosslink_busy = 1; |
| 1831 | db_begin_transaction(); |
| 1832 | db_multi_exec( |
| 1833 | "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);" |
| 1834 | "CREATE TEMP TABLE time_fudge(" |
| 1835 | " mid INTEGER PRIMARY KEY," /* The rid of a manifest */ |
| 1836 | " m1 REAL," /* The timestamp on mid */ |
| 1837 | " cid INTEGER," /* A child or mid */ |
| 1838 | " m2 REAL" /* Timestamp on the child */ |
| 1839 | ");" |
| 1840 | ); |
| 1841 | } |
| 1842 | |
| 1843 | #if INTERFACE |
| 1844 | /* Timestamps might be adjusted slightly to ensure that check-ins appear |
| 1845 | ** on the timeline in chronological order. This is the maximum amount |
| 1846 | ** of the adjustment window, in days. |
| @@ -1879,20 +1890,28 @@ | |
| 1879 | int rid = db_column_int(&q,0); |
| 1880 | const char *zValue = db_column_text(&q,1); |
| 1881 | manifest_reparent_checkin(rid, zValue); |
| 1882 | } |
| 1883 | db_finalize(&q); |
| 1884 | db_prepare(&q, "SELECT uuid FROM pending_tkt"); |
| 1885 | while( db_step(&q)==SQLITE_ROW ){ |
| 1886 | const char *zUuid = db_column_text(&q, 0); |
| 1887 | ticket_rebuild_entry(zUuid); |
| 1888 | if( permitHooks && rc==TH_OK ){ |
| 1889 | rc = xfer_run_script(zScript, zUuid, 0); |
| 1890 | } |
| 1891 | } |
| 1892 | db_finalize(&q); |
| 1893 | db_multi_exec("DROP TABLE pending_tkt"); |
| 1894 | |
| 1895 | /* If multiple check-ins happen close together in time, adjust their |
| 1896 | ** times by a few milliseconds to make sure they appear in chronological |
| 1897 | ** order. |
| 1898 | */ |
| @@ -2162,11 +2181,11 @@ | |
| 2162 | TAG_USER, rid, |
| 2163 | TAG_COMMENT, rid, p->rDate |
| 2164 | ); |
| 2165 | zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" |
| 2166 | " WHERE rowid=last_insert_rowid()"); |
| 2167 | wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE); |
| 2168 | fossil_free(zCom); |
| 2169 | |
| 2170 | /* If this is a delta-manifest, record the fact that this repository |
| 2171 | ** contains delta manifests, to free the "commit" logic to generate |
| 2172 | ** new delta manifests. |
| @@ -2227,10 +2246,11 @@ | |
| 2227 | if( p->type==CFTYPE_WIKI ){ |
| 2228 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2229 | int tagid = tag_findid(zTag, 1); |
| 2230 | int prior; |
| 2231 | char *zComment; |
| 2232 | int nWiki; |
| 2233 | char zLength[40]; |
| 2234 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2235 | nWiki = strlen(p->zWiki); |
| 2236 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| @@ -2243,16 +2263,44 @@ | |
| 2243 | tagid, p->rDate |
| 2244 | ); |
| 2245 | if( prior ){ |
| 2246 | content_deltify(prior, &rid, 1, 0); |
| 2247 | } |
| 2248 | if( nWiki>0 ){ |
| 2249 | zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); |
| 2250 | }else{ |
| 2251 | zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); |
| 2252 | } |
| 2253 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2254 | db_multi_exec( |
| 2255 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 2256 | " bgcolor,euser,ecomment)" |
| 2257 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 2258 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| @@ -2349,12 +2397,11 @@ | |
| 2349 | Stmt qatt; |
| 2350 | assert( manifest_crosslink_busy==1 ); |
| 2351 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 2352 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 2353 | fossil_free(zTag); |
| 2354 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| 2355 | p->zTicketUuid); |
| 2356 | /* Locate and update comment for any attachments */ |
| 2357 | db_prepare(&qatt, |
| 2358 | "SELECT attachid, src, target, filename FROM attachment" |
| 2359 | " WHERE target=%Q", |
| 2360 | p->zTicketUuid |
| 2361 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -1828,19 +1828,30 @@ | |
| 1828 | void manifest_crosslink_begin(void){ |
| 1829 | assert( manifest_crosslink_busy==0 ); |
| 1830 | manifest_crosslink_busy = 1; |
| 1831 | db_begin_transaction(); |
| 1832 | db_multi_exec( |
| 1833 | "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;" |
| 1834 | "CREATE TEMP TABLE time_fudge(" |
| 1835 | " mid INTEGER PRIMARY KEY," /* The rid of a manifest */ |
| 1836 | " m1 REAL," /* The timestamp on mid */ |
| 1837 | " cid INTEGER," /* A child or mid */ |
| 1838 | " m2 REAL" /* Timestamp on the child */ |
| 1839 | ");" |
| 1840 | ); |
| 1841 | } |
| 1842 | |
| 1843 | /* |
| 1844 | ** Add a new entry to the pending_xlink table. |
| 1845 | */ |
| 1846 | static void add_pending_crosslink(char cType, const char *zId){ |
| 1847 | assert( manifest_crosslink_busy==1 ); |
| 1848 | db_multi_exec( |
| 1849 | "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')", |
| 1850 | cType, zId |
| 1851 | ); |
| 1852 | } |
| 1853 | |
| 1854 | #if INTERFACE |
| 1855 | /* Timestamps might be adjusted slightly to ensure that check-ins appear |
| 1856 | ** on the timeline in chronological order. This is the maximum amount |
| 1857 | ** of the adjustment window, in days. |
| @@ -1879,20 +1890,28 @@ | |
| 1890 | int rid = db_column_int(&q,0); |
| 1891 | const char *zValue = db_column_text(&q,1); |
| 1892 | manifest_reparent_checkin(rid, zValue); |
| 1893 | } |
| 1894 | db_finalize(&q); |
| 1895 | db_prepare(&q, "SELECT id FROM pending_xlink"); |
| 1896 | while( db_step(&q)==SQLITE_ROW ){ |
| 1897 | const char *zId = db_column_text(&q, 0); |
| 1898 | char cType; |
| 1899 | if( zId==0 || zId[0]==0 ) continue; |
| 1900 | cType = zId[0]; |
| 1901 | zId++; |
| 1902 | if( cType=='t' ){ |
| 1903 | ticket_rebuild_entry(zId); |
| 1904 | if( permitHooks && rc==TH_OK ){ |
| 1905 | rc = xfer_run_script(zScript, zId, 0); |
| 1906 | } |
| 1907 | }else if( cType=='w' ){ |
| 1908 | backlink_wiki_refresh(zId); |
| 1909 | } |
| 1910 | } |
| 1911 | db_finalize(&q); |
| 1912 | db_multi_exec("DROP TABLE pending_xlink"); |
| 1913 | |
| 1914 | /* If multiple check-ins happen close together in time, adjust their |
| 1915 | ** times by a few milliseconds to make sure they appear in chronological |
| 1916 | ** order. |
| 1917 | */ |
| @@ -2162,11 +2181,11 @@ | |
| 2181 | TAG_USER, rid, |
| 2182 | TAG_COMMENT, rid, p->rDate |
| 2183 | ); |
| 2184 | zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" |
| 2185 | " WHERE rowid=last_insert_rowid()"); |
| 2186 | backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1); |
| 2187 | fossil_free(zCom); |
| 2188 | |
| 2189 | /* If this is a delta-manifest, record the fact that this repository |
| 2190 | ** contains delta manifests, to free the "commit" logic to generate |
| 2191 | ** new delta manifests. |
| @@ -2227,10 +2246,11 @@ | |
| 2246 | if( p->type==CFTYPE_WIKI ){ |
| 2247 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2248 | int tagid = tag_findid(zTag, 1); |
| 2249 | int prior; |
| 2250 | char *zComment; |
| 2251 | const char *zPrefix; |
| 2252 | int nWiki; |
| 2253 | char zLength[40]; |
| 2254 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2255 | nWiki = strlen(p->zWiki); |
| 2256 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| @@ -2243,16 +2263,44 @@ | |
| 2263 | tagid, p->rDate |
| 2264 | ); |
| 2265 | if( prior ){ |
| 2266 | content_deltify(prior, &rid, 1, 0); |
| 2267 | } |
| 2268 | if( nWiki<=0 ){ |
| 2269 | zPrefix = "Deleted"; |
| 2270 | }else if( !prior ){ |
| 2271 | zPrefix = "Added"; |
| 2272 | }else{ |
| 2273 | zPrefix = "Changes to"; |
| 2274 | } |
| 2275 | switch( wiki_page_type(p->zWikiTitle) ){ |
| 2276 | case WIKITYPE_CHECKIN: { |
| 2277 | zComment = mprintf("%s wiki for check-in [%S]", zPrefix, |
| 2278 | p->zWikiTitle+8); |
| 2279 | break; |
| 2280 | } |
| 2281 | case WIKITYPE_BRANCH: { |
| 2282 | zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]", |
| 2283 | zPrefix, p->zWikiTitle+7, p->zWikiTitle+7); |
| 2284 | break; |
| 2285 | } |
| 2286 | case WIKITYPE_TAG: { |
| 2287 | zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]", |
| 2288 | zPrefix, p->zWikiTitle+4, p->zWikiTitle+4); |
| 2289 | break; |
| 2290 | } |
| 2291 | default: { |
| 2292 | zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle); |
| 2293 | break; |
| 2294 | } |
| 2295 | } |
| 2296 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2297 | if( manifest_crosslink_busy ){ |
| 2298 | add_pending_crosslink('w',p->zWikiTitle); |
| 2299 | }else{ |
| 2300 | backlink_wiki_refresh(p->zWikiTitle); |
| 2301 | } |
| 2302 | db_multi_exec( |
| 2303 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 2304 | " bgcolor,euser,ecomment)" |
| 2305 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 2306 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| @@ -2349,12 +2397,11 @@ | |
| 2397 | Stmt qatt; |
| 2398 | assert( manifest_crosslink_busy==1 ); |
| 2399 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 2400 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 2401 | fossil_free(zTag); |
| 2402 | add_pending_crosslink('t',p->zTicketUuid); |
| 2403 | /* Locate and update comment for any attachments */ |
| 2404 | db_prepare(&qatt, |
| 2405 | "SELECT attachid, src, target, filename FROM attachment" |
| 2406 | " WHERE target=%Q", |
| 2407 | p->zTicketUuid |
| 2408 |
+7
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -405,10 +405,17 @@ | ||
| 405 | 405 | return; |
| 406 | 406 | } |
| 407 | 407 | if( integrateFlag && !is_a_leaf(mid)){ |
| 408 | 408 | fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]); |
| 409 | 409 | integrateFlag = 0; |
| 410 | + } | |
| 411 | + if( integrateFlag && content_is_private(mid) ){ | |
| 412 | + fossil_warning( | |
| 413 | + "ignoring --integrate: %s is on a private branch" | |
| 414 | + "\n Use \"fossil amend --close\" (after commit) to close the leaf.", | |
| 415 | + g.argv[2]); | |
| 416 | + integrateFlag = 0; | |
| 410 | 417 | } |
| 411 | 418 | if( verboseFlag ){ |
| 412 | 419 | print_checkin_description(mid, 12, |
| 413 | 420 | integrateFlag ? "integrate:" : "merge-from:"); |
| 414 | 421 | print_checkin_description(pid, 12, "baseline:"); |
| 415 | 422 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -405,10 +405,17 @@ | |
| 405 | return; |
| 406 | } |
| 407 | if( integrateFlag && !is_a_leaf(mid)){ |
| 408 | fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]); |
| 409 | integrateFlag = 0; |
| 410 | } |
| 411 | if( verboseFlag ){ |
| 412 | print_checkin_description(mid, 12, |
| 413 | integrateFlag ? "integrate:" : "merge-from:"); |
| 414 | print_checkin_description(pid, 12, "baseline:"); |
| 415 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -405,10 +405,17 @@ | |
| 405 | return; |
| 406 | } |
| 407 | if( integrateFlag && !is_a_leaf(mid)){ |
| 408 | fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]); |
| 409 | integrateFlag = 0; |
| 410 | } |
| 411 | if( integrateFlag && content_is_private(mid) ){ |
| 412 | fossil_warning( |
| 413 | "ignoring --integrate: %s is on a private branch" |
| 414 | "\n Use \"fossil amend --close\" (after commit) to close the leaf.", |
| 415 | g.argv[2]); |
| 416 | integrateFlag = 0; |
| 417 | } |
| 418 | if( verboseFlag ){ |
| 419 | print_checkin_description(mid, 12, |
| 420 | integrateFlag ? "integrate:" : "merge-from:"); |
| 421 | print_checkin_description(pid, 12, "baseline:"); |
| 422 |
+1
-1
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -139,11 +139,11 @@ | ||
| 139 | 139 | ** Text of boundary markers for merge conflicts. |
| 140 | 140 | */ |
| 141 | 141 | static const char *const mergeMarker[] = { |
| 142 | 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", |
| 144 | - "======= COMMON ANCESTOR content follows ============================\n", | |
| 144 | + "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n", | |
| 145 | 145 | "======= MERGED IN content follows ==================================\n", |
| 146 | 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" |
| 147 | 147 | }; |
| 148 | 148 | |
| 149 | 149 | |
| 150 | 150 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -139,11 +139,11 @@ | |
| 139 | ** Text of boundary markers for merge conflicts. |
| 140 | */ |
| 141 | static const char *const mergeMarker[] = { |
| 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", |
| 144 | "======= COMMON ANCESTOR content follows ============================\n", |
| 145 | "======= MERGED IN content follows ==================================\n", |
| 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" |
| 147 | }; |
| 148 | |
| 149 | |
| 150 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -139,11 +139,11 @@ | |
| 139 | ** Text of boundary markers for merge conflicts. |
| 140 | */ |
| 141 | static const char *const mergeMarker[] = { |
| 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", |
| 144 | "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n", |
| 145 | "======= MERGED IN content follows ==================================\n", |
| 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" |
| 147 | }; |
| 148 | |
| 149 | |
| 150 |
+57
-3
| --- src/mkversion.c | ||
| +++ src/mkversion.c | ||
| @@ -8,27 +8,60 @@ | ||
| 8 | 8 | ** Note that the manifest.uuid and manifest files are generated by Fossil. |
| 9 | 9 | */ |
| 10 | 10 | #include <stdio.h> |
| 11 | 11 | #include <string.h> |
| 12 | 12 | #include <stdlib.h> |
| 13 | +#include <ctype.h> | |
| 14 | +#include <time.h> | |
| 13 | 15 | |
| 14 | 16 | static FILE *open_for_reading(const char *zFilename){ |
| 15 | 17 | FILE *f = fopen(zFilename, "r"); |
| 16 | 18 | if( f==0 ){ |
| 17 | 19 | fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); |
| 18 | 20 | exit(1); |
| 19 | 21 | } |
| 20 | 22 | return f; |
| 21 | 23 | } |
| 24 | + | |
| 25 | +/* | |
| 26 | +** Given an arbitrary-length input string key zIn, generate | |
| 27 | +** an N-byte hexadecimal hash of that string into zOut. | |
| 28 | +*/ | |
| 29 | +static void hash(const char *zIn, int N, char *zOut){ | |
| 30 | + unsigned char i, j, t; | |
| 31 | + int m, n; | |
| 32 | + unsigned char s[256]; | |
| 33 | + for(m=0; m<256; m++){ s[m] = m; } | |
| 34 | + for(j=0, m=n=0; m<256; m++, n++){ | |
| 35 | + j += s[m] + zIn[n]; | |
| 36 | + if( zIn[n]==0 ){ n = -1; } | |
| 37 | + t = s[j]; | |
| 38 | + s[j] = s[m]; | |
| 39 | + s[m] = t; | |
| 40 | + } | |
| 41 | + i = j = 0; | |
| 42 | + for(n=0; n<N-2; n+=2){ | |
| 43 | + i++; | |
| 44 | + t = s[i]; | |
| 45 | + j += t; | |
| 46 | + s[i] = s[j]; | |
| 47 | + s[j] = t; | |
| 48 | + t += s[i]; | |
| 49 | + zOut[n] = "0123456789abcdef"[(t>>4)&0xf]; | |
| 50 | + zOut[n+1] = "0123456789abcdef"[t&0xf]; | |
| 51 | + } | |
| 52 | + zOut[n] = 0; | |
| 53 | +} | |
| 22 | 54 | |
| 23 | 55 | int main(int argc, char *argv[]){ |
| 24 | 56 | FILE *m,*u,*v; |
| 25 | 57 | char *z; |
| 26 | 58 | #if defined(__DMC__) /* e.g. 0x857 */ |
| 27 | 59 | int i = 0; |
| 28 | 60 | #endif |
| 29 | 61 | int j = 0, x = 0, d = 0; |
| 62 | + size_t n; | |
| 30 | 63 | int vn[3]; |
| 31 | 64 | char b[1000]; |
| 32 | 65 | char vx[1000]; |
| 33 | 66 | if( argc!=4 ){ |
| 34 | 67 | fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]); |
| @@ -44,16 +77,37 @@ | ||
| 44 | 77 | fclose(u); |
| 45 | 78 | for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} |
| 46 | 79 | *z = 0; |
| 47 | 80 | printf("#define MANIFEST_UUID \"%s\"\n",b); |
| 48 | 81 | printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); |
| 82 | + n = strlen(b); | |
| 83 | + if( n + 50 < sizeof(b) ){ | |
| 84 | + sprintf(b+n, "%d", (int)time(0)); | |
| 85 | + hash(b,33,vx); | |
| 86 | + printf("#define FOSSIL_BUILD_HASH \"%s\"\n", vx); | |
| 87 | + } | |
| 49 | 88 | m = open_for_reading(argv[2]); |
| 50 | 89 | while(b == fgets(b, sizeof(b)-1,m)){ |
| 51 | - if(0 == strncmp("D ",b,2)){ | |
| 52 | - printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); | |
| 53 | - printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); | |
| 90 | + if(0 == strncmp("D ",b,2)){ | |
| 91 | + int k, n; | |
| 92 | + char zDateNum[30]; | |
| 93 | + printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); | |
| 94 | + printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); | |
| 95 | + n = 0; | |
| 96 | + for(k=0; k<10; k++){ | |
| 97 | + if( isdigit(b[k+2]) ) zDateNum[n++] = b[k+2]; | |
| 98 | + } | |
| 99 | + zDateNum[n] = 0; | |
| 100 | + printf("#define MANIFEST_NUMERIC_DATE %s\n", zDateNum); | |
| 101 | + n = 0; | |
| 102 | + for(k=0; k<8; k++){ | |
| 103 | + if( isdigit(b[k+13]) ) zDateNum[n++] = b[k+13]; | |
| 54 | 104 | } |
| 105 | + zDateNum[n] = 0; | |
| 106 | + for(k=0; zDateNum[k]=='0'; k++){} | |
| 107 | + printf("#define MANIFEST_NUMERIC_TIME %s\n", zDateNum+k); | |
| 108 | + } | |
| 55 | 109 | } |
| 56 | 110 | fclose(m); |
| 57 | 111 | v = open_for_reading(argv[3]); |
| 58 | 112 | if( fgets(b, sizeof(b)-1,v)==0 ){ |
| 59 | 113 | fprintf(stderr, "malformed VERSION file: %s\n", argv[3]); |
| 60 | 114 |
| --- src/mkversion.c | |
| +++ src/mkversion.c | |
| @@ -8,27 +8,60 @@ | |
| 8 | ** Note that the manifest.uuid and manifest files are generated by Fossil. |
| 9 | */ |
| 10 | #include <stdio.h> |
| 11 | #include <string.h> |
| 12 | #include <stdlib.h> |
| 13 | |
| 14 | static FILE *open_for_reading(const char *zFilename){ |
| 15 | FILE *f = fopen(zFilename, "r"); |
| 16 | if( f==0 ){ |
| 17 | fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); |
| 18 | exit(1); |
| 19 | } |
| 20 | return f; |
| 21 | } |
| 22 | |
| 23 | int main(int argc, char *argv[]){ |
| 24 | FILE *m,*u,*v; |
| 25 | char *z; |
| 26 | #if defined(__DMC__) /* e.g. 0x857 */ |
| 27 | int i = 0; |
| 28 | #endif |
| 29 | int j = 0, x = 0, d = 0; |
| 30 | int vn[3]; |
| 31 | char b[1000]; |
| 32 | char vx[1000]; |
| 33 | if( argc!=4 ){ |
| 34 | fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]); |
| @@ -44,16 +77,37 @@ | |
| 44 | fclose(u); |
| 45 | for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} |
| 46 | *z = 0; |
| 47 | printf("#define MANIFEST_UUID \"%s\"\n",b); |
| 48 | printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); |
| 49 | m = open_for_reading(argv[2]); |
| 50 | while(b == fgets(b, sizeof(b)-1,m)){ |
| 51 | if(0 == strncmp("D ",b,2)){ |
| 52 | printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); |
| 53 | printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); |
| 54 | } |
| 55 | } |
| 56 | fclose(m); |
| 57 | v = open_for_reading(argv[3]); |
| 58 | if( fgets(b, sizeof(b)-1,v)==0 ){ |
| 59 | fprintf(stderr, "malformed VERSION file: %s\n", argv[3]); |
| 60 |
| --- src/mkversion.c | |
| +++ src/mkversion.c | |
| @@ -8,27 +8,60 @@ | |
| 8 | ** Note that the manifest.uuid and manifest files are generated by Fossil. |
| 9 | */ |
| 10 | #include <stdio.h> |
| 11 | #include <string.h> |
| 12 | #include <stdlib.h> |
| 13 | #include <ctype.h> |
| 14 | #include <time.h> |
| 15 | |
| 16 | static FILE *open_for_reading(const char *zFilename){ |
| 17 | FILE *f = fopen(zFilename, "r"); |
| 18 | if( f==0 ){ |
| 19 | fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); |
| 20 | exit(1); |
| 21 | } |
| 22 | return f; |
| 23 | } |
| 24 | |
| 25 | /* |
| 26 | ** Given an arbitrary-length input string key zIn, generate |
| 27 | ** an N-byte hexadecimal hash of that string into zOut. |
| 28 | */ |
| 29 | static void hash(const char *zIn, int N, char *zOut){ |
| 30 | unsigned char i, j, t; |
| 31 | int m, n; |
| 32 | unsigned char s[256]; |
| 33 | for(m=0; m<256; m++){ s[m] = m; } |
| 34 | for(j=0, m=n=0; m<256; m++, n++){ |
| 35 | j += s[m] + zIn[n]; |
| 36 | if( zIn[n]==0 ){ n = -1; } |
| 37 | t = s[j]; |
| 38 | s[j] = s[m]; |
| 39 | s[m] = t; |
| 40 | } |
| 41 | i = j = 0; |
| 42 | for(n=0; n<N-2; n+=2){ |
| 43 | i++; |
| 44 | t = s[i]; |
| 45 | j += t; |
| 46 | s[i] = s[j]; |
| 47 | s[j] = t; |
| 48 | t += s[i]; |
| 49 | zOut[n] = "0123456789abcdef"[(t>>4)&0xf]; |
| 50 | zOut[n+1] = "0123456789abcdef"[t&0xf]; |
| 51 | } |
| 52 | zOut[n] = 0; |
| 53 | } |
| 54 | |
| 55 | int main(int argc, char *argv[]){ |
| 56 | FILE *m,*u,*v; |
| 57 | char *z; |
| 58 | #if defined(__DMC__) /* e.g. 0x857 */ |
| 59 | int i = 0; |
| 60 | #endif |
| 61 | int j = 0, x = 0, d = 0; |
| 62 | size_t n; |
| 63 | int vn[3]; |
| 64 | char b[1000]; |
| 65 | char vx[1000]; |
| 66 | if( argc!=4 ){ |
| 67 | fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]); |
| @@ -44,16 +77,37 @@ | |
| 77 | fclose(u); |
| 78 | for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} |
| 79 | *z = 0; |
| 80 | printf("#define MANIFEST_UUID \"%s\"\n",b); |
| 81 | printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); |
| 82 | n = strlen(b); |
| 83 | if( n + 50 < sizeof(b) ){ |
| 84 | sprintf(b+n, "%d", (int)time(0)); |
| 85 | hash(b,33,vx); |
| 86 | printf("#define FOSSIL_BUILD_HASH \"%s\"\n", vx); |
| 87 | } |
| 88 | m = open_for_reading(argv[2]); |
| 89 | while(b == fgets(b, sizeof(b)-1,m)){ |
| 90 | if(0 == strncmp("D ",b,2)){ |
| 91 | int k, n; |
| 92 | char zDateNum[30]; |
| 93 | printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); |
| 94 | printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); |
| 95 | n = 0; |
| 96 | for(k=0; k<10; k++){ |
| 97 | if( isdigit(b[k+2]) ) zDateNum[n++] = b[k+2]; |
| 98 | } |
| 99 | zDateNum[n] = 0; |
| 100 | printf("#define MANIFEST_NUMERIC_DATE %s\n", zDateNum); |
| 101 | n = 0; |
| 102 | for(k=0; k<8; k++){ |
| 103 | if( isdigit(b[k+13]) ) zDateNum[n++] = b[k+13]; |
| 104 | } |
| 105 | zDateNum[n] = 0; |
| 106 | for(k=0; zDateNum[k]=='0'; k++){} |
| 107 | printf("#define MANIFEST_NUMERIC_TIME %s\n", zDateNum+k); |
| 108 | } |
| 109 | } |
| 110 | fclose(m); |
| 111 | v = open_for_reading(argv[3]); |
| 112 | if( fgets(b, sizeof(b)-1,v)==0 ){ |
| 113 | fprintf(stderr, "malformed VERSION file: %s\n", argv[3]); |
| 114 |
+234
-39
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -614,11 +614,11 @@ | ||
| 614 | 614 | while( db_step(&q)==SQLITE_ROW ){ |
| 615 | 615 | const char *zUuid = db_column_text(&q, 0); |
| 616 | 616 | int rid = db_column_int(&q, 1); |
| 617 | 617 | @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> |
| 618 | 618 | @ %s(zUuid)</a> - |
| 619 | - object_description(rid, 0, 0); | |
| 619 | + object_description(rid, 0, 0, 0); | |
| 620 | 620 | @ </p></li> |
| 621 | 621 | } |
| 622 | 622 | db_finalize(&q); |
| 623 | 623 | db_prepare(&q, |
| 624 | 624 | " SELECT tkt_rid, tkt_uuid, title" |
| @@ -636,11 +636,11 @@ | ||
| 636 | 636 | @ <ul></ul> |
| 637 | 637 | @ Ticket |
| 638 | 638 | hyperlink_to_uuid(zUuid); |
| 639 | 639 | @ - %h(zTitle). |
| 640 | 640 | @ <ul><li> |
| 641 | - object_description(rid, 0, 0); | |
| 641 | + object_description(rid, 0, 0, 0); | |
| 642 | 642 | @ </li></ul> |
| 643 | 643 | @ </p></li> |
| 644 | 644 | } |
| 645 | 645 | db_finalize(&q); |
| 646 | 646 | db_prepare(&q, |
| @@ -652,11 +652,11 @@ | ||
| 652 | 652 | int rid = db_column_int(&q, 0); |
| 653 | 653 | const char* zUuid = db_column_text(&q, 1); |
| 654 | 654 | @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> |
| 655 | 655 | @ %s(zUuid)</a> - |
| 656 | 656 | @ <ul><li> |
| 657 | - object_description(rid, 0, 0); | |
| 657 | + object_description(rid, 0, 0, 0); | |
| 658 | 658 | @ </li></ul> |
| 659 | 659 | @ </p></li> |
| 660 | 660 | } |
| 661 | 661 | @ </ol> |
| 662 | 662 | db_finalize(&q); |
| @@ -962,14 +962,81 @@ | ||
| 962 | 962 | @ rid INTEGER PRIMARY KEY, -- RID of the object |
| 963 | 963 | @ uuid TEXT, -- hash of the object |
| 964 | 964 | @ ctime DATETIME, -- Time of creation |
| 965 | 965 | @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts |
| 966 | 966 | @ type TEXT, -- file, checkin, wiki, ticket, etc. |
| 967 | +@ rcvid INT, -- When the artifact was received | |
| 967 | 968 | @ summary TEXT, -- Summary comment for the object |
| 968 | -@ detail TEXT -- File name, check-in comment, etc | |
| 969 | +@ ref TEXT -- hash of an object to link against | |
| 969 | 970 | @ ); |
| 971 | +@ CREATE INDEX desctype ON description(summary) WHERE summary='unknown'; | |
| 970 | 972 | ; |
| 973 | + | |
| 974 | +/* | |
| 975 | +** Attempt to describe all phantom artifacts. The artifacts are | |
| 976 | +** already loaded into the description table and have summary='unknown'. | |
| 977 | +** This routine attempts to generate a better summary, and possibly | |
| 978 | +** fill in the ref field. | |
| 979 | +*/ | |
| 980 | +static void describe_unknown_artifacts(){ | |
| 981 | + /* Try to figure out the origin of unknown artifacts */ | |
| 982 | + db_multi_exec( | |
| 983 | + "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" | |
| 984 | + " SELECT description.rid, description.uuid, isPrivate, type,\n" | |
| 985 | + " CASE WHEN plink.isprim THEN '' ELSE 'merge ' END ||\n" | |
| 986 | + " 'parent of check-in', blob.uuid\n" | |
| 987 | + " FROM description, plink, blob\n" | |
| 988 | + " WHERE description.summary='unknown'\n" | |
| 989 | + " AND plink.pid=description.rid\n" | |
| 990 | + " AND blob.rid=plink.cid;" | |
| 991 | + ); | |
| 992 | + db_multi_exec( | |
| 993 | + "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" | |
| 994 | + " SELECT description.rid, description.uuid, isPrivate, type,\n" | |
| 995 | + " 'child of check-in', blob.uuid\n" | |
| 996 | + " FROM description, plink, blob\n" | |
| 997 | + " WHERE description.summary='unknown'\n" | |
| 998 | + " AND plink.cid=description.rid\n" | |
| 999 | + " AND blob.rid=plink.pid;" | |
| 1000 | + ); | |
| 1001 | + db_multi_exec( | |
| 1002 | + "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" | |
| 1003 | + " SELECT description.rid, description.uuid, isPrivate, type,\n" | |
| 1004 | + " 'check-in referenced by \"'||tag.tagname ||'\" tag',\n" | |
| 1005 | + " blob.uuid\n" | |
| 1006 | + " FROM description, tagxref, tag, blob\n" | |
| 1007 | + " WHERE description.summary='unknown'\n" | |
| 1008 | + " AND tagxref.origid=description.rid\n" | |
| 1009 | + " AND tag.tagid=tagxref.tagid\n" | |
| 1010 | + " AND blob.rid=tagxref.srcid;" | |
| 1011 | + ); | |
| 1012 | + db_multi_exec( | |
| 1013 | + "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" | |
| 1014 | + " SELECT description.rid, description.uuid, isPrivate, type,\n" | |
| 1015 | + " 'file \"'||filename.name||'\"',\n" | |
| 1016 | + " blob.uuid\n" | |
| 1017 | + " FROM description, mlink, filename, blob\n" | |
| 1018 | + " WHERE description.summary='unknown'\n" | |
| 1019 | + " AND mlink.fid=description.rid\n" | |
| 1020 | + " AND blob.rid=mlink.mid\n" | |
| 1021 | + " AND filename.fnid=mlink.fnid;" | |
| 1022 | + ); | |
| 1023 | + if( !db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){ | |
| 1024 | + return; | |
| 1025 | + } | |
| 1026 | + add_content_sql_commands(g.db); | |
| 1027 | + db_multi_exec( | |
| 1028 | + "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" | |
| 1029 | + " SELECT description.rid, description.uuid, isPrivate, type,\n" | |
| 1030 | + " 'referenced by cluster', blob.uuid\n" | |
| 1031 | + " FROM description, tagxref, blob\n" | |
| 1032 | + " WHERE description.summary='unknown'\n" | |
| 1033 | + " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" | |
| 1034 | + " AND blob.rid=tagxref.rid\n" | |
| 1035 | + " AND content(blob.uuid) GLOB ('*M '||blob.uuid||'*');" | |
| 1036 | + ); | |
| 1037 | +} | |
| 971 | 1038 | |
| 972 | 1039 | /* |
| 973 | 1040 | ** Create the description table if it does not already exists. |
| 974 | 1041 | ** Populate fields of this table with descriptions for all artifacts |
| 975 | 1042 | ** whose RID matches the SQL expression in zWhere. |
| @@ -977,23 +1044,24 @@ | ||
| 977 | 1044 | void describe_artifacts(const char *zWhere){ |
| 978 | 1045 | db_multi_exec("%s", zDescTab/*safe-for-%s*/); |
| 979 | 1046 | |
| 980 | 1047 | /* Describe check-ins */ |
| 981 | 1048 | db_multi_exec( |
| 982 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 983 | - "SELECT blob.rid, blob.uuid, event.mtime, 'checkin',\n" | |
| 1049 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1050 | + "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime, 'checkin',\n" | |
| 984 | 1051 | " 'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n" |
| 985 | 1052 | " FROM event, blob\n" |
| 986 | 1053 | " WHERE (event.objid %s) AND event.type='ci'\n" |
| 987 | 1054 | " AND event.objid=blob.rid;", |
| 988 | 1055 | zWhere /*safe-for-%s*/ |
| 989 | 1056 | ); |
| 990 | 1057 | |
| 991 | 1058 | /* Describe files */ |
| 992 | 1059 | db_multi_exec( |
| 993 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 994 | - "SELECT blob.rid, blob.uuid, event.mtime, 'file', 'file '||filename.name\n" | |
| 1060 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1061 | + "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime," | |
| 1062 | + " 'file', 'file '||filename.name\n" | |
| 995 | 1063 | " FROM mlink, blob, event, filename\n" |
| 996 | 1064 | " WHERE (mlink.fid %s)\n" |
| 997 | 1065 | " AND mlink.mid=event.objid\n" |
| 998 | 1066 | " AND filename.fnid=mlink.fnid\n" |
| 999 | 1067 | " AND mlink.fid=blob.rid;", |
| @@ -1000,23 +1068,24 @@ | ||
| 1000 | 1068 | zWhere /*safe-for-%s*/ |
| 1001 | 1069 | ); |
| 1002 | 1070 | |
| 1003 | 1071 | /* Describe tags */ |
| 1004 | 1072 | db_multi_exec( |
| 1005 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1006 | - "SELECT blob.rid, blob.uuid, tagxref.mtime, 'tag',\n" | |
| 1073 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1074 | + "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'tag',\n" | |
| 1007 | 1075 | " 'tag '||substr((SELECT uuid FROM blob WHERE rid=tagxref.rid),1,16)\n" |
| 1008 | 1076 | " FROM tagxref, blob\n" |
| 1009 | 1077 | " WHERE (tagxref.srcid %s) AND tagxref.srcid!=tagxref.rid\n" |
| 1010 | 1078 | " AND tagxref.srcid=blob.rid;", |
| 1011 | 1079 | zWhere /*safe-for-%s*/ |
| 1012 | 1080 | ); |
| 1013 | 1081 | |
| 1014 | 1082 | /* Cluster artifacts */ |
| 1015 | 1083 | db_multi_exec( |
| 1016 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1017 | - "SELECT blob.rid, blob.uuid, rcvfrom.mtime, 'cluster', 'cluster'\n" | |
| 1084 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1085 | + "SELECT blob.rid, blob.uuid, blob.rcvid, rcvfrom.mtime," | |
| 1086 | + " 'cluster', 'cluster'\n" | |
| 1018 | 1087 | " FROM tagxref, blob, rcvfrom\n" |
| 1019 | 1088 | " WHERE (tagxref.rid %s)\n" |
| 1020 | 1089 | " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" |
| 1021 | 1090 | " AND blob.rid=tagxref.rid" |
| 1022 | 1091 | " AND rcvfrom.rcvid=blob.rcvid;", |
| @@ -1023,12 +1092,12 @@ | ||
| 1023 | 1092 | zWhere /*safe-for-%s*/ |
| 1024 | 1093 | ); |
| 1025 | 1094 | |
| 1026 | 1095 | /* Ticket change artifacts */ |
| 1027 | 1096 | db_multi_exec( |
| 1028 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1029 | - "SELECT blob.rid, blob.uuid, tagxref.mtime, 'ticket',\n" | |
| 1097 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1098 | + "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'ticket',\n" | |
| 1030 | 1099 | " 'ticket '||substr(tag.tagname,5,21)\n" |
| 1031 | 1100 | " FROM tagxref, tag, blob\n" |
| 1032 | 1101 | " WHERE (tagxref.rid %s)\n" |
| 1033 | 1102 | " AND tag.tagid=tagxref.tagid\n" |
| 1034 | 1103 | " AND tag.tagname GLOB 'tkt-*'" |
| @@ -1036,12 +1105,12 @@ | ||
| 1036 | 1105 | zWhere /*safe-for-%s*/ |
| 1037 | 1106 | ); |
| 1038 | 1107 | |
| 1039 | 1108 | /* Wiki edit artifacts */ |
| 1040 | 1109 | db_multi_exec( |
| 1041 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1042 | - "SELECT blob.rid, blob.uuid, tagxref.mtime, 'wiki',\n" | |
| 1110 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1111 | + "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'wiki',\n" | |
| 1043 | 1112 | " printf('wiki \"%%s\"',substr(tag.tagname,6))\n" |
| 1044 | 1113 | " FROM tagxref, tag, blob\n" |
| 1045 | 1114 | " WHERE (tagxref.rid %s)\n" |
| 1046 | 1115 | " AND tag.tagid=tagxref.tagid\n" |
| 1047 | 1116 | " AND tag.tagname GLOB 'wiki-*'" |
| @@ -1049,12 +1118,12 @@ | ||
| 1049 | 1118 | zWhere /*safe-for-%s*/ |
| 1050 | 1119 | ); |
| 1051 | 1120 | |
| 1052 | 1121 | /* Event edit artifacts */ |
| 1053 | 1122 | db_multi_exec( |
| 1054 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1055 | - "SELECT blob.rid, blob.uuid, tagxref.mtime, 'event',\n" | |
| 1123 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1124 | + "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'event',\n" | |
| 1056 | 1125 | " 'event '||substr(tag.tagname,7)\n" |
| 1057 | 1126 | " FROM tagxref, tag, blob\n" |
| 1058 | 1127 | " WHERE (tagxref.rid %s)\n" |
| 1059 | 1128 | " AND tag.tagid=tagxref.tagid\n" |
| 1060 | 1129 | " AND tag.tagname GLOB 'event-*'" |
| @@ -1062,21 +1131,22 @@ | ||
| 1062 | 1131 | zWhere /*safe-for-%s*/ |
| 1063 | 1132 | ); |
| 1064 | 1133 | |
| 1065 | 1134 | /* Attachments */ |
| 1066 | 1135 | db_multi_exec( |
| 1067 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1068 | - "SELECT blob.rid, blob.uuid, attachment.mtime, 'attach-control',\n" | |
| 1136 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1137 | + "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime," | |
| 1138 | + " 'attach-control',\n" | |
| 1069 | 1139 | " 'attachment-control for '||attachment.filename\n" |
| 1070 | 1140 | " FROM attachment, blob\n" |
| 1071 | 1141 | " WHERE (attachment.attachid %s)\n" |
| 1072 | 1142 | " AND blob.rid=attachment.attachid", |
| 1073 | 1143 | zWhere /*safe-for-%s*/ |
| 1074 | 1144 | ); |
| 1075 | 1145 | db_multi_exec( |
| 1076 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1077 | - "SELECT blob.rid, blob.uuid, attachment.mtime, 'attachment',\n" | |
| 1146 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1147 | + "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime, 'attachment',\n" | |
| 1078 | 1148 | " 'attachment '||attachment.filename\n" |
| 1079 | 1149 | " FROM attachment, blob\n" |
| 1080 | 1150 | " WHERE (blob.rid %s)\n" |
| 1081 | 1151 | " AND blob.rid NOT IN (SELECT rid FROM description)\n" |
| 1082 | 1152 | " AND blob.uuid=attachment.src", |
| @@ -1084,12 +1154,13 @@ | ||
| 1084 | 1154 | ); |
| 1085 | 1155 | |
| 1086 | 1156 | /* Forum posts */ |
| 1087 | 1157 | if( db_table_exists("repository","forumpost") ){ |
| 1088 | 1158 | db_multi_exec( |
| 1089 | - "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" | |
| 1090 | - "SELECT postblob.rid, postblob.uuid, forumpost.fmtime, 'forumpost',\n" | |
| 1159 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" | |
| 1160 | + "SELECT postblob.rid, postblob.uuid, postblob.rcvid," | |
| 1161 | + " forumpost.fmtime, 'forumpost',\n" | |
| 1091 | 1162 | " CASE WHEN fpid=froot THEN 'forum-post '\n" |
| 1092 | 1163 | " ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n" |
| 1093 | 1164 | " FROM forumpost, blob AS postblob, blob AS rootblob\n" |
| 1094 | 1165 | " WHERE (forumpost.fpid %s)\n" |
| 1095 | 1166 | " AND postblob.rid=forumpost.fpid" |
| @@ -1096,24 +1167,31 @@ | ||
| 1096 | 1167 | " AND rootblob.rid=forumpost.froot", |
| 1097 | 1168 | zWhere /*safe-for-%s*/ |
| 1098 | 1169 | ); |
| 1099 | 1170 | } |
| 1100 | 1171 | |
| 1101 | - /* Everything else */ | |
| 1172 | + /* Mark all other artifacts as "unknown" for now */ | |
| 1102 | 1173 | db_multi_exec( |
| 1103 | - "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n" | |
| 1104 | - "SELECT blob.rid, blob.uuid," | |
| 1105 | - " CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n" | |
| 1174 | + "INSERT OR IGNORE INTO description(rid,uuid,rcvid,type,summary)\n" | |
| 1175 | + "SELECT blob.rid, blob.uuid,blob.rcvid,\n" | |
| 1176 | + " CASE WHEN EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)\n" | |
| 1177 | + " THEN 'phantom' ELSE '' END,\n" | |
| 1106 | 1178 | " 'unknown'\n" |
| 1107 | - " FROM blob WHERE (blob.rid %s);", | |
| 1179 | + " FROM blob\n" | |
| 1180 | + " WHERE (blob.rid %s)\n" | |
| 1181 | + " AND (blob.rid NOT IN (SELECT rid FROM description));", | |
| 1108 | 1182 | zWhere /*safe-for-%s*/ |
| 1109 | 1183 | ); |
| 1110 | 1184 | |
| 1111 | 1185 | /* Mark private elements */ |
| 1112 | 1186 | db_multi_exec( |
| 1113 | 1187 | "UPDATE description SET isPrivate=1 WHERE rid IN private" |
| 1114 | 1188 | ); |
| 1189 | + | |
| 1190 | + if( db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){ | |
| 1191 | + describe_unknown_artifacts(); | |
| 1192 | + } | |
| 1115 | 1193 | } |
| 1116 | 1194 | |
| 1117 | 1195 | /* |
| 1118 | 1196 | ** Print the content of the description table on stdout. |
| 1119 | 1197 | ** |
| @@ -1135,11 +1213,11 @@ | ||
| 1135 | 1213 | if( zLabel ){ |
| 1136 | 1214 | fossil_print("%s\n", zLabel); |
| 1137 | 1215 | zLabel = 0; |
| 1138 | 1216 | } |
| 1139 | 1217 | fossil_print(" %.16s %s", db_column_text(&q,0), db_column_text(&q,1)); |
| 1140 | - if( db_column_int(&q,2) ) fossil_print(" (unpublished)"); | |
| 1218 | + if( db_column_int(&q,2) ) fossil_print(" (private)"); | |
| 1141 | 1219 | fossil_print("\n"); |
| 1142 | 1220 | cnt++; |
| 1143 | 1221 | } |
| 1144 | 1222 | db_finalize(&q); |
| 1145 | 1223 | if( zWhere!=0 ) db_multi_exec("DELETE FROM description;"); |
| @@ -1172,19 +1250,21 @@ | ||
| 1172 | 1250 | ** |
| 1173 | 1251 | ** Return a page showing all artifacts in the repository. Query parameters: |
| 1174 | 1252 | ** |
| 1175 | 1253 | ** n=N Show N artifacts |
| 1176 | 1254 | ** s=S Start with artifact number S |
| 1177 | -** unpub Show only unpublished artifacts | |
| 1255 | +** priv Show only unpublished or private artifacts | |
| 1256 | +** phan Show only phantom artifacts | |
| 1178 | 1257 | ** hclr Color code hash types (SHA1 vs SHA3) |
| 1179 | 1258 | */ |
| 1180 | 1259 | void bloblist_page(void){ |
| 1181 | 1260 | Stmt q; |
| 1182 | 1261 | int s = atoi(PD("s","0")); |
| 1183 | 1262 | int n = atoi(PD("n","5000")); |
| 1184 | 1263 | int mx = db_int(0, "SELECT max(rid) FROM blob"); |
| 1185 | - int unpubOnly = PB("unpub"); | |
| 1264 | + int privOnly = PB("priv"); | |
| 1265 | + int phantomOnly = PB("phan"); | |
| 1186 | 1266 | int hashClr = PB("hclr"); |
| 1187 | 1267 | char *zRange; |
| 1188 | 1268 | char *zSha1Bg; |
| 1189 | 1269 | char *zSha3Bg; |
| 1190 | 1270 | |
| @@ -1192,15 +1272,25 @@ | ||
| 1192 | 1272 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1193 | 1273 | style_header("List Of Artifacts"); |
| 1194 | 1274 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1195 | 1275 | if( g.perm.Admin ){ |
| 1196 | 1276 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1277 | + } | |
| 1278 | + if( !phantomOnly ){ | |
| 1279 | + style_submenu_element("Phantoms", "bloblist?phan"); | |
| 1280 | + } | |
| 1281 | + if( g.perm.Private || g.perm.Admin ){ | |
| 1282 | + if( !privOnly ){ | |
| 1283 | + style_submenu_element("Private", "bloblist?priv"); | |
| 1284 | + } | |
| 1285 | + }else{ | |
| 1286 | + privOnly = 0; | |
| 1197 | 1287 | } |
| 1198 | 1288 | if( g.perm.Write ){ |
| 1199 | 1289 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1200 | 1290 | } |
| 1201 | - if( !unpubOnly && mx>n && P("s")==0 ){ | |
| 1291 | + if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){ | |
| 1202 | 1292 | int i; |
| 1203 | 1293 | @ <p>Select a range of artifacts to view:</p> |
| 1204 | 1294 | @ <ul> |
| 1205 | 1295 | for(i=1; i<=mx; i+=n){ |
| 1206 | 1296 | @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) |
| @@ -1208,51 +1298,156 @@ | ||
| 1208 | 1298 | } |
| 1209 | 1299 | @ </ul> |
| 1210 | 1300 | style_footer(); |
| 1211 | 1301 | return; |
| 1212 | 1302 | } |
| 1213 | - if( !unpubOnly && mx>n ){ | |
| 1303 | + if( phantomOnly || privOnly || mx>n ){ | |
| 1214 | 1304 | style_submenu_element("Index", "bloblist"); |
| 1215 | 1305 | } |
| 1216 | - if( unpubOnly ){ | |
| 1306 | + if( privOnly ){ | |
| 1217 | 1307 | zRange = mprintf("IN private"); |
| 1308 | + }else if( phantomOnly ){ | |
| 1309 | + zRange = mprintf("IN phantom"); | |
| 1218 | 1310 | }else{ |
| 1219 | 1311 | zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); |
| 1220 | 1312 | } |
| 1221 | 1313 | describe_artifacts(zRange); |
| 1222 | 1314 | fossil_free(zRange); |
| 1223 | 1315 | db_prepare(&q, |
| 1224 | - "SELECT rid, uuid, summary, isPrivate FROM description ORDER BY rid" | |
| 1316 | + "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref" | |
| 1317 | + " FROM description ORDER BY rid" | |
| 1225 | 1318 | ); |
| 1226 | 1319 | if( skin_detail_boolean("white-foreground") ){ |
| 1227 | 1320 | zSha1Bg = "#714417"; |
| 1228 | 1321 | zSha3Bg = "#177117"; |
| 1229 | 1322 | }else{ |
| 1230 | 1323 | zSha1Bg = "#ebffb0"; |
| 1231 | 1324 | zSha3Bg = "#b0ffb0"; |
| 1232 | 1325 | } |
| 1233 | - @ <table cellpadding="0" cellspacing="0"> | |
| 1326 | + @ <table cellpadding="2" cellspacing="0" border="1"> | |
| 1327 | + if( g.perm.Admin ){ | |
| 1328 | + @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks | |
| 1329 | + }else{ | |
| 1330 | + @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks | |
| 1331 | + } | |
| 1234 | 1332 | while( db_step(&q)==SQLITE_ROW ){ |
| 1235 | 1333 | int rid = db_column_int(&q,0); |
| 1236 | 1334 | const char *zUuid = db_column_text(&q, 1); |
| 1237 | 1335 | const char *zDesc = db_column_text(&q, 2); |
| 1238 | 1336 | int isPriv = db_column_int(&q,3); |
| 1337 | + int isPhantom = db_column_int(&q,4); | |
| 1338 | + const char *zRef = db_column_text(&q,6); | |
| 1339 | + if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ | |
| 1340 | + /* Don't show private artifacts to users without Private (x) permission */ | |
| 1341 | + continue; | |
| 1342 | + } | |
| 1239 | 1343 | if( hashClr ){ |
| 1240 | 1344 | const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg; |
| 1241 | 1345 | @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td> |
| 1242 | 1346 | }else{ |
| 1243 | 1347 | @ <tr><td align="right">%d(rid)</td> |
| 1244 | 1348 | } |
| 1245 | 1349 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1350 | + if( g.perm.Admin ){ | |
| 1351 | + int rcvid = db_column_int(&q,5); | |
| 1352 | + if( rcvid<=0 ){ | |
| 1353 | + @ <td> | |
| 1354 | + }else{ | |
| 1355 | + @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a> | |
| 1356 | + } | |
| 1357 | + } | |
| 1246 | 1358 | @ <td align="left">%h(zDesc)</td> |
| 1247 | - if( isPriv ){ | |
| 1248 | - @ <td>(unpublished)</td> | |
| 1359 | + if( zRef && zRef[0] ){ | |
| 1360 | + @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> | |
| 1361 | + }else{ | |
| 1362 | + @ <td> | |
| 1363 | + } | |
| 1364 | + if( isPriv || isPhantom ){ | |
| 1365 | + if( isPriv==0 ){ | |
| 1366 | + @ <td>phantom</td> | |
| 1367 | + }else if( isPhantom==0 ){ | |
| 1368 | + @ <td>private</td> | |
| 1369 | + }else{ | |
| 1370 | + @ <td>private,phantom</td> | |
| 1371 | + } | |
| 1372 | + }else{ | |
| 1373 | + @ <td> | |
| 1374 | + } | |
| 1375 | + @ </tr> | |
| 1376 | + } | |
| 1377 | + @ </table> | |
| 1378 | + db_finalize(&q); | |
| 1379 | + style_footer(); | |
| 1380 | +} | |
| 1381 | + | |
| 1382 | +/* | |
| 1383 | +** Output HTML that shows a table of all public phantoms. | |
| 1384 | +*/ | |
| 1385 | +void table_of_public_phantoms(void){ | |
| 1386 | + Stmt q; | |
| 1387 | + char *zRange; | |
| 1388 | + zRange = mprintf("IN (SELECT rid FROM phantom EXCEPT" | |
| 1389 | + " SELECT rid FROM private)"); | |
| 1390 | + describe_artifacts(zRange); | |
| 1391 | + fossil_free(zRange); | |
| 1392 | + db_prepare(&q, | |
| 1393 | + "SELECT rid, uuid, summary, ref" | |
| 1394 | + " FROM description ORDER BY rid" | |
| 1395 | + ); | |
| 1396 | + @ <table cellpadding="2" cellspacing="0" border="1"> | |
| 1397 | + @ <tr><th>RID<th>Description<th>Source | |
| 1398 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1399 | + int rid = db_column_int(&q,0); | |
| 1400 | + const char *zUuid = db_column_text(&q, 1); | |
| 1401 | + const char *zDesc = db_column_text(&q, 2); | |
| 1402 | + const char *zRef = db_column_text(&q,3); | |
| 1403 | + @ <tr><td valign="top">%d(rid)</td> | |
| 1404 | + @ <td valign="top" align="left">%h(zUuid)<br>%h(zDesc)</td> | |
| 1405 | + if( zRef && zRef[0] ){ | |
| 1406 | + @ <td valign="top">%z(href("%R/info/%!S",zRef))%!S(zRef)</a> | |
| 1407 | + }else{ | |
| 1408 | + @ <td> | |
| 1249 | 1409 | } |
| 1250 | 1410 | @ </tr> |
| 1251 | 1411 | } |
| 1252 | 1412 | @ </table> |
| 1253 | 1413 | db_finalize(&q); |
| 1414 | +} | |
| 1415 | + | |
| 1416 | +/* | |
| 1417 | +** WEBPAGE: phantoms | |
| 1418 | +** | |
| 1419 | +** Show a list of all "phantom" artifacts that are not marked as "private". | |
| 1420 | +** | |
| 1421 | +** A "phantom" artifact is an artifact whose hash named appears in some | |
| 1422 | +** artifact but whose content is unknown. For example, if a manifest | |
| 1423 | +** references a particular SHA3 hash of a file, but that SHA3 hash is | |
| 1424 | +** not on the shunning list and is not in the database, then the file | |
| 1425 | +** is a phantom. We know it exists, but we do not know its content. | |
| 1426 | +** | |
| 1427 | +** Whenever a sync occurs, both each party looks at its phantom list | |
| 1428 | +** and for every phantom that is not also marked private, it asks the | |
| 1429 | +** other party to send it the content. This mechanism helps keep all | |
| 1430 | +** repositories synced up. | |
| 1431 | +** | |
| 1432 | +** This page is similar to the /bloblist page in that it lists artifacts. | |
| 1433 | +** But this page is a special case in that it only shows phantoms that | |
| 1434 | +** are not private. In other words, this page shows all phantoms that | |
| 1435 | +** generate extra network traffic on every sync request. | |
| 1436 | +*/ | |
| 1437 | +void phantom_list_page(void){ | |
| 1438 | + login_check_credentials(); | |
| 1439 | + if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 1440 | + style_header("Public Phantom Artifacts"); | |
| 1441 | + if( g.perm.Admin ){ | |
| 1442 | + style_submenu_element("Artifact Log", "rcvfromlist"); | |
| 1443 | + style_submenu_element("Artifact List", "bloblist"); | |
| 1444 | + } | |
| 1445 | + if( g.perm.Write ){ | |
| 1446 | + style_submenu_element("Artifact Stats", "artifact_stats"); | |
| 1447 | + } | |
| 1448 | + table_of_public_phantoms(); | |
| 1254 | 1449 | style_footer(); |
| 1255 | 1450 | } |
| 1256 | 1451 | |
| 1257 | 1452 | /* |
| 1258 | 1453 | ** WEBPAGE: bigbloblist |
| 1259 | 1454 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -614,11 +614,11 @@ | |
| 614 | while( db_step(&q)==SQLITE_ROW ){ |
| 615 | const char *zUuid = db_column_text(&q, 0); |
| 616 | int rid = db_column_int(&q, 1); |
| 617 | @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> |
| 618 | @ %s(zUuid)</a> - |
| 619 | object_description(rid, 0, 0); |
| 620 | @ </p></li> |
| 621 | } |
| 622 | db_finalize(&q); |
| 623 | db_prepare(&q, |
| 624 | " SELECT tkt_rid, tkt_uuid, title" |
| @@ -636,11 +636,11 @@ | |
| 636 | @ <ul></ul> |
| 637 | @ Ticket |
| 638 | hyperlink_to_uuid(zUuid); |
| 639 | @ - %h(zTitle). |
| 640 | @ <ul><li> |
| 641 | object_description(rid, 0, 0); |
| 642 | @ </li></ul> |
| 643 | @ </p></li> |
| 644 | } |
| 645 | db_finalize(&q); |
| 646 | db_prepare(&q, |
| @@ -652,11 +652,11 @@ | |
| 652 | int rid = db_column_int(&q, 0); |
| 653 | const char* zUuid = db_column_text(&q, 1); |
| 654 | @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> |
| 655 | @ %s(zUuid)</a> - |
| 656 | @ <ul><li> |
| 657 | object_description(rid, 0, 0); |
| 658 | @ </li></ul> |
| 659 | @ </p></li> |
| 660 | } |
| 661 | @ </ol> |
| 662 | db_finalize(&q); |
| @@ -962,14 +962,81 @@ | |
| 962 | @ rid INTEGER PRIMARY KEY, -- RID of the object |
| 963 | @ uuid TEXT, -- hash of the object |
| 964 | @ ctime DATETIME, -- Time of creation |
| 965 | @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts |
| 966 | @ type TEXT, -- file, checkin, wiki, ticket, etc. |
| 967 | @ summary TEXT, -- Summary comment for the object |
| 968 | @ detail TEXT -- File name, check-in comment, etc |
| 969 | @ ); |
| 970 | ; |
| 971 | |
| 972 | /* |
| 973 | ** Create the description table if it does not already exists. |
| 974 | ** Populate fields of this table with descriptions for all artifacts |
| 975 | ** whose RID matches the SQL expression in zWhere. |
| @@ -977,23 +1044,24 @@ | |
| 977 | void describe_artifacts(const char *zWhere){ |
| 978 | db_multi_exec("%s", zDescTab/*safe-for-%s*/); |
| 979 | |
| 980 | /* Describe check-ins */ |
| 981 | db_multi_exec( |
| 982 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 983 | "SELECT blob.rid, blob.uuid, event.mtime, 'checkin',\n" |
| 984 | " 'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n" |
| 985 | " FROM event, blob\n" |
| 986 | " WHERE (event.objid %s) AND event.type='ci'\n" |
| 987 | " AND event.objid=blob.rid;", |
| 988 | zWhere /*safe-for-%s*/ |
| 989 | ); |
| 990 | |
| 991 | /* Describe files */ |
| 992 | db_multi_exec( |
| 993 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 994 | "SELECT blob.rid, blob.uuid, event.mtime, 'file', 'file '||filename.name\n" |
| 995 | " FROM mlink, blob, event, filename\n" |
| 996 | " WHERE (mlink.fid %s)\n" |
| 997 | " AND mlink.mid=event.objid\n" |
| 998 | " AND filename.fnid=mlink.fnid\n" |
| 999 | " AND mlink.fid=blob.rid;", |
| @@ -1000,23 +1068,24 @@ | |
| 1000 | zWhere /*safe-for-%s*/ |
| 1001 | ); |
| 1002 | |
| 1003 | /* Describe tags */ |
| 1004 | db_multi_exec( |
| 1005 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1006 | "SELECT blob.rid, blob.uuid, tagxref.mtime, 'tag',\n" |
| 1007 | " 'tag '||substr((SELECT uuid FROM blob WHERE rid=tagxref.rid),1,16)\n" |
| 1008 | " FROM tagxref, blob\n" |
| 1009 | " WHERE (tagxref.srcid %s) AND tagxref.srcid!=tagxref.rid\n" |
| 1010 | " AND tagxref.srcid=blob.rid;", |
| 1011 | zWhere /*safe-for-%s*/ |
| 1012 | ); |
| 1013 | |
| 1014 | /* Cluster artifacts */ |
| 1015 | db_multi_exec( |
| 1016 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1017 | "SELECT blob.rid, blob.uuid, rcvfrom.mtime, 'cluster', 'cluster'\n" |
| 1018 | " FROM tagxref, blob, rcvfrom\n" |
| 1019 | " WHERE (tagxref.rid %s)\n" |
| 1020 | " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" |
| 1021 | " AND blob.rid=tagxref.rid" |
| 1022 | " AND rcvfrom.rcvid=blob.rcvid;", |
| @@ -1023,12 +1092,12 @@ | |
| 1023 | zWhere /*safe-for-%s*/ |
| 1024 | ); |
| 1025 | |
| 1026 | /* Ticket change artifacts */ |
| 1027 | db_multi_exec( |
| 1028 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1029 | "SELECT blob.rid, blob.uuid, tagxref.mtime, 'ticket',\n" |
| 1030 | " 'ticket '||substr(tag.tagname,5,21)\n" |
| 1031 | " FROM tagxref, tag, blob\n" |
| 1032 | " WHERE (tagxref.rid %s)\n" |
| 1033 | " AND tag.tagid=tagxref.tagid\n" |
| 1034 | " AND tag.tagname GLOB 'tkt-*'" |
| @@ -1036,12 +1105,12 @@ | |
| 1036 | zWhere /*safe-for-%s*/ |
| 1037 | ); |
| 1038 | |
| 1039 | /* Wiki edit artifacts */ |
| 1040 | db_multi_exec( |
| 1041 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1042 | "SELECT blob.rid, blob.uuid, tagxref.mtime, 'wiki',\n" |
| 1043 | " printf('wiki \"%%s\"',substr(tag.tagname,6))\n" |
| 1044 | " FROM tagxref, tag, blob\n" |
| 1045 | " WHERE (tagxref.rid %s)\n" |
| 1046 | " AND tag.tagid=tagxref.tagid\n" |
| 1047 | " AND tag.tagname GLOB 'wiki-*'" |
| @@ -1049,12 +1118,12 @@ | |
| 1049 | zWhere /*safe-for-%s*/ |
| 1050 | ); |
| 1051 | |
| 1052 | /* Event edit artifacts */ |
| 1053 | db_multi_exec( |
| 1054 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1055 | "SELECT blob.rid, blob.uuid, tagxref.mtime, 'event',\n" |
| 1056 | " 'event '||substr(tag.tagname,7)\n" |
| 1057 | " FROM tagxref, tag, blob\n" |
| 1058 | " WHERE (tagxref.rid %s)\n" |
| 1059 | " AND tag.tagid=tagxref.tagid\n" |
| 1060 | " AND tag.tagname GLOB 'event-*'" |
| @@ -1062,21 +1131,22 @@ | |
| 1062 | zWhere /*safe-for-%s*/ |
| 1063 | ); |
| 1064 | |
| 1065 | /* Attachments */ |
| 1066 | db_multi_exec( |
| 1067 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1068 | "SELECT blob.rid, blob.uuid, attachment.mtime, 'attach-control',\n" |
| 1069 | " 'attachment-control for '||attachment.filename\n" |
| 1070 | " FROM attachment, blob\n" |
| 1071 | " WHERE (attachment.attachid %s)\n" |
| 1072 | " AND blob.rid=attachment.attachid", |
| 1073 | zWhere /*safe-for-%s*/ |
| 1074 | ); |
| 1075 | db_multi_exec( |
| 1076 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1077 | "SELECT blob.rid, blob.uuid, attachment.mtime, 'attachment',\n" |
| 1078 | " 'attachment '||attachment.filename\n" |
| 1079 | " FROM attachment, blob\n" |
| 1080 | " WHERE (blob.rid %s)\n" |
| 1081 | " AND blob.rid NOT IN (SELECT rid FROM description)\n" |
| 1082 | " AND blob.uuid=attachment.src", |
| @@ -1084,12 +1154,13 @@ | |
| 1084 | ); |
| 1085 | |
| 1086 | /* Forum posts */ |
| 1087 | if( db_table_exists("repository","forumpost") ){ |
| 1088 | db_multi_exec( |
| 1089 | "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" |
| 1090 | "SELECT postblob.rid, postblob.uuid, forumpost.fmtime, 'forumpost',\n" |
| 1091 | " CASE WHEN fpid=froot THEN 'forum-post '\n" |
| 1092 | " ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n" |
| 1093 | " FROM forumpost, blob AS postblob, blob AS rootblob\n" |
| 1094 | " WHERE (forumpost.fpid %s)\n" |
| 1095 | " AND postblob.rid=forumpost.fpid" |
| @@ -1096,24 +1167,31 @@ | |
| 1096 | " AND rootblob.rid=forumpost.froot", |
| 1097 | zWhere /*safe-for-%s*/ |
| 1098 | ); |
| 1099 | } |
| 1100 | |
| 1101 | /* Everything else */ |
| 1102 | db_multi_exec( |
| 1103 | "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n" |
| 1104 | "SELECT blob.rid, blob.uuid," |
| 1105 | " CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n" |
| 1106 | " 'unknown'\n" |
| 1107 | " FROM blob WHERE (blob.rid %s);", |
| 1108 | zWhere /*safe-for-%s*/ |
| 1109 | ); |
| 1110 | |
| 1111 | /* Mark private elements */ |
| 1112 | db_multi_exec( |
| 1113 | "UPDATE description SET isPrivate=1 WHERE rid IN private" |
| 1114 | ); |
| 1115 | } |
| 1116 | |
| 1117 | /* |
| 1118 | ** Print the content of the description table on stdout. |
| 1119 | ** |
| @@ -1135,11 +1213,11 @@ | |
| 1135 | if( zLabel ){ |
| 1136 | fossil_print("%s\n", zLabel); |
| 1137 | zLabel = 0; |
| 1138 | } |
| 1139 | fossil_print(" %.16s %s", db_column_text(&q,0), db_column_text(&q,1)); |
| 1140 | if( db_column_int(&q,2) ) fossil_print(" (unpublished)"); |
| 1141 | fossil_print("\n"); |
| 1142 | cnt++; |
| 1143 | } |
| 1144 | db_finalize(&q); |
| 1145 | if( zWhere!=0 ) db_multi_exec("DELETE FROM description;"); |
| @@ -1172,19 +1250,21 @@ | |
| 1172 | ** |
| 1173 | ** Return a page showing all artifacts in the repository. Query parameters: |
| 1174 | ** |
| 1175 | ** n=N Show N artifacts |
| 1176 | ** s=S Start with artifact number S |
| 1177 | ** unpub Show only unpublished artifacts |
| 1178 | ** hclr Color code hash types (SHA1 vs SHA3) |
| 1179 | */ |
| 1180 | void bloblist_page(void){ |
| 1181 | Stmt q; |
| 1182 | int s = atoi(PD("s","0")); |
| 1183 | int n = atoi(PD("n","5000")); |
| 1184 | int mx = db_int(0, "SELECT max(rid) FROM blob"); |
| 1185 | int unpubOnly = PB("unpub"); |
| 1186 | int hashClr = PB("hclr"); |
| 1187 | char *zRange; |
| 1188 | char *zSha1Bg; |
| 1189 | char *zSha3Bg; |
| 1190 | |
| @@ -1192,15 +1272,25 @@ | |
| 1192 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1193 | style_header("List Of Artifacts"); |
| 1194 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1195 | if( g.perm.Admin ){ |
| 1196 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1197 | } |
| 1198 | if( g.perm.Write ){ |
| 1199 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1200 | } |
| 1201 | if( !unpubOnly && mx>n && P("s")==0 ){ |
| 1202 | int i; |
| 1203 | @ <p>Select a range of artifacts to view:</p> |
| 1204 | @ <ul> |
| 1205 | for(i=1; i<=mx; i+=n){ |
| 1206 | @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) |
| @@ -1208,51 +1298,156 @@ | |
| 1208 | } |
| 1209 | @ </ul> |
| 1210 | style_footer(); |
| 1211 | return; |
| 1212 | } |
| 1213 | if( !unpubOnly && mx>n ){ |
| 1214 | style_submenu_element("Index", "bloblist"); |
| 1215 | } |
| 1216 | if( unpubOnly ){ |
| 1217 | zRange = mprintf("IN private"); |
| 1218 | }else{ |
| 1219 | zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); |
| 1220 | } |
| 1221 | describe_artifacts(zRange); |
| 1222 | fossil_free(zRange); |
| 1223 | db_prepare(&q, |
| 1224 | "SELECT rid, uuid, summary, isPrivate FROM description ORDER BY rid" |
| 1225 | ); |
| 1226 | if( skin_detail_boolean("white-foreground") ){ |
| 1227 | zSha1Bg = "#714417"; |
| 1228 | zSha3Bg = "#177117"; |
| 1229 | }else{ |
| 1230 | zSha1Bg = "#ebffb0"; |
| 1231 | zSha3Bg = "#b0ffb0"; |
| 1232 | } |
| 1233 | @ <table cellpadding="0" cellspacing="0"> |
| 1234 | while( db_step(&q)==SQLITE_ROW ){ |
| 1235 | int rid = db_column_int(&q,0); |
| 1236 | const char *zUuid = db_column_text(&q, 1); |
| 1237 | const char *zDesc = db_column_text(&q, 2); |
| 1238 | int isPriv = db_column_int(&q,3); |
| 1239 | if( hashClr ){ |
| 1240 | const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg; |
| 1241 | @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td> |
| 1242 | }else{ |
| 1243 | @ <tr><td align="right">%d(rid)</td> |
| 1244 | } |
| 1245 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1246 | @ <td align="left">%h(zDesc)</td> |
| 1247 | if( isPriv ){ |
| 1248 | @ <td>(unpublished)</td> |
| 1249 | } |
| 1250 | @ </tr> |
| 1251 | } |
| 1252 | @ </table> |
| 1253 | db_finalize(&q); |
| 1254 | style_footer(); |
| 1255 | } |
| 1256 | |
| 1257 | /* |
| 1258 | ** WEBPAGE: bigbloblist |
| 1259 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -614,11 +614,11 @@ | |
| 614 | while( db_step(&q)==SQLITE_ROW ){ |
| 615 | const char *zUuid = db_column_text(&q, 0); |
| 616 | int rid = db_column_int(&q, 1); |
| 617 | @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> |
| 618 | @ %s(zUuid)</a> - |
| 619 | object_description(rid, 0, 0, 0); |
| 620 | @ </p></li> |
| 621 | } |
| 622 | db_finalize(&q); |
| 623 | db_prepare(&q, |
| 624 | " SELECT tkt_rid, tkt_uuid, title" |
| @@ -636,11 +636,11 @@ | |
| 636 | @ <ul></ul> |
| 637 | @ Ticket |
| 638 | hyperlink_to_uuid(zUuid); |
| 639 | @ - %h(zTitle). |
| 640 | @ <ul><li> |
| 641 | object_description(rid, 0, 0, 0); |
| 642 | @ </li></ul> |
| 643 | @ </p></li> |
| 644 | } |
| 645 | db_finalize(&q); |
| 646 | db_prepare(&q, |
| @@ -652,11 +652,11 @@ | |
| 652 | int rid = db_column_int(&q, 0); |
| 653 | const char* zUuid = db_column_text(&q, 1); |
| 654 | @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> |
| 655 | @ %s(zUuid)</a> - |
| 656 | @ <ul><li> |
| 657 | object_description(rid, 0, 0, 0); |
| 658 | @ </li></ul> |
| 659 | @ </p></li> |
| 660 | } |
| 661 | @ </ol> |
| 662 | db_finalize(&q); |
| @@ -962,14 +962,81 @@ | |
| 962 | @ rid INTEGER PRIMARY KEY, -- RID of the object |
| 963 | @ uuid TEXT, -- hash of the object |
| 964 | @ ctime DATETIME, -- Time of creation |
| 965 | @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts |
| 966 | @ type TEXT, -- file, checkin, wiki, ticket, etc. |
| 967 | @ rcvid INT, -- When the artifact was received |
| 968 | @ summary TEXT, -- Summary comment for the object |
| 969 | @ ref TEXT -- hash of an object to link against |
| 970 | @ ); |
| 971 | @ CREATE INDEX desctype ON description(summary) WHERE summary='unknown'; |
| 972 | ; |
| 973 | |
| 974 | /* |
| 975 | ** Attempt to describe all phantom artifacts. The artifacts are |
| 976 | ** already loaded into the description table and have summary='unknown'. |
| 977 | ** This routine attempts to generate a better summary, and possibly |
| 978 | ** fill in the ref field. |
| 979 | */ |
| 980 | static void describe_unknown_artifacts(){ |
| 981 | /* Try to figure out the origin of unknown artifacts */ |
| 982 | db_multi_exec( |
| 983 | "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" |
| 984 | " SELECT description.rid, description.uuid, isPrivate, type,\n" |
| 985 | " CASE WHEN plink.isprim THEN '' ELSE 'merge ' END ||\n" |
| 986 | " 'parent of check-in', blob.uuid\n" |
| 987 | " FROM description, plink, blob\n" |
| 988 | " WHERE description.summary='unknown'\n" |
| 989 | " AND plink.pid=description.rid\n" |
| 990 | " AND blob.rid=plink.cid;" |
| 991 | ); |
| 992 | db_multi_exec( |
| 993 | "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" |
| 994 | " SELECT description.rid, description.uuid, isPrivate, type,\n" |
| 995 | " 'child of check-in', blob.uuid\n" |
| 996 | " FROM description, plink, blob\n" |
| 997 | " WHERE description.summary='unknown'\n" |
| 998 | " AND plink.cid=description.rid\n" |
| 999 | " AND blob.rid=plink.pid;" |
| 1000 | ); |
| 1001 | db_multi_exec( |
| 1002 | "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" |
| 1003 | " SELECT description.rid, description.uuid, isPrivate, type,\n" |
| 1004 | " 'check-in referenced by \"'||tag.tagname ||'\" tag',\n" |
| 1005 | " blob.uuid\n" |
| 1006 | " FROM description, tagxref, tag, blob\n" |
| 1007 | " WHERE description.summary='unknown'\n" |
| 1008 | " AND tagxref.origid=description.rid\n" |
| 1009 | " AND tag.tagid=tagxref.tagid\n" |
| 1010 | " AND blob.rid=tagxref.srcid;" |
| 1011 | ); |
| 1012 | db_multi_exec( |
| 1013 | "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" |
| 1014 | " SELECT description.rid, description.uuid, isPrivate, type,\n" |
| 1015 | " 'file \"'||filename.name||'\"',\n" |
| 1016 | " blob.uuid\n" |
| 1017 | " FROM description, mlink, filename, blob\n" |
| 1018 | " WHERE description.summary='unknown'\n" |
| 1019 | " AND mlink.fid=description.rid\n" |
| 1020 | " AND blob.rid=mlink.mid\n" |
| 1021 | " AND filename.fnid=mlink.fnid;" |
| 1022 | ); |
| 1023 | if( !db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){ |
| 1024 | return; |
| 1025 | } |
| 1026 | add_content_sql_commands(g.db); |
| 1027 | db_multi_exec( |
| 1028 | "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" |
| 1029 | " SELECT description.rid, description.uuid, isPrivate, type,\n" |
| 1030 | " 'referenced by cluster', blob.uuid\n" |
| 1031 | " FROM description, tagxref, blob\n" |
| 1032 | " WHERE description.summary='unknown'\n" |
| 1033 | " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" |
| 1034 | " AND blob.rid=tagxref.rid\n" |
| 1035 | " AND content(blob.uuid) GLOB ('*M '||blob.uuid||'*');" |
| 1036 | ); |
| 1037 | } |
| 1038 | |
| 1039 | /* |
| 1040 | ** Create the description table if it does not already exists. |
| 1041 | ** Populate fields of this table with descriptions for all artifacts |
| 1042 | ** whose RID matches the SQL expression in zWhere. |
| @@ -977,23 +1044,24 @@ | |
| 1044 | void describe_artifacts(const char *zWhere){ |
| 1045 | db_multi_exec("%s", zDescTab/*safe-for-%s*/); |
| 1046 | |
| 1047 | /* Describe check-ins */ |
| 1048 | db_multi_exec( |
| 1049 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1050 | "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime, 'checkin',\n" |
| 1051 | " 'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n" |
| 1052 | " FROM event, blob\n" |
| 1053 | " WHERE (event.objid %s) AND event.type='ci'\n" |
| 1054 | " AND event.objid=blob.rid;", |
| 1055 | zWhere /*safe-for-%s*/ |
| 1056 | ); |
| 1057 | |
| 1058 | /* Describe files */ |
| 1059 | db_multi_exec( |
| 1060 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1061 | "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime," |
| 1062 | " 'file', 'file '||filename.name\n" |
| 1063 | " FROM mlink, blob, event, filename\n" |
| 1064 | " WHERE (mlink.fid %s)\n" |
| 1065 | " AND mlink.mid=event.objid\n" |
| 1066 | " AND filename.fnid=mlink.fnid\n" |
| 1067 | " AND mlink.fid=blob.rid;", |
| @@ -1000,23 +1068,24 @@ | |
| 1068 | zWhere /*safe-for-%s*/ |
| 1069 | ); |
| 1070 | |
| 1071 | /* Describe tags */ |
| 1072 | db_multi_exec( |
| 1073 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1074 | "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'tag',\n" |
| 1075 | " 'tag '||substr((SELECT uuid FROM blob WHERE rid=tagxref.rid),1,16)\n" |
| 1076 | " FROM tagxref, blob\n" |
| 1077 | " WHERE (tagxref.srcid %s) AND tagxref.srcid!=tagxref.rid\n" |
| 1078 | " AND tagxref.srcid=blob.rid;", |
| 1079 | zWhere /*safe-for-%s*/ |
| 1080 | ); |
| 1081 | |
| 1082 | /* Cluster artifacts */ |
| 1083 | db_multi_exec( |
| 1084 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1085 | "SELECT blob.rid, blob.uuid, blob.rcvid, rcvfrom.mtime," |
| 1086 | " 'cluster', 'cluster'\n" |
| 1087 | " FROM tagxref, blob, rcvfrom\n" |
| 1088 | " WHERE (tagxref.rid %s)\n" |
| 1089 | " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" |
| 1090 | " AND blob.rid=tagxref.rid" |
| 1091 | " AND rcvfrom.rcvid=blob.rcvid;", |
| @@ -1023,12 +1092,12 @@ | |
| 1092 | zWhere /*safe-for-%s*/ |
| 1093 | ); |
| 1094 | |
| 1095 | /* Ticket change artifacts */ |
| 1096 | db_multi_exec( |
| 1097 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1098 | "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'ticket',\n" |
| 1099 | " 'ticket '||substr(tag.tagname,5,21)\n" |
| 1100 | " FROM tagxref, tag, blob\n" |
| 1101 | " WHERE (tagxref.rid %s)\n" |
| 1102 | " AND tag.tagid=tagxref.tagid\n" |
| 1103 | " AND tag.tagname GLOB 'tkt-*'" |
| @@ -1036,12 +1105,12 @@ | |
| 1105 | zWhere /*safe-for-%s*/ |
| 1106 | ); |
| 1107 | |
| 1108 | /* Wiki edit artifacts */ |
| 1109 | db_multi_exec( |
| 1110 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1111 | "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'wiki',\n" |
| 1112 | " printf('wiki \"%%s\"',substr(tag.tagname,6))\n" |
| 1113 | " FROM tagxref, tag, blob\n" |
| 1114 | " WHERE (tagxref.rid %s)\n" |
| 1115 | " AND tag.tagid=tagxref.tagid\n" |
| 1116 | " AND tag.tagname GLOB 'wiki-*'" |
| @@ -1049,12 +1118,12 @@ | |
| 1118 | zWhere /*safe-for-%s*/ |
| 1119 | ); |
| 1120 | |
| 1121 | /* Event edit artifacts */ |
| 1122 | db_multi_exec( |
| 1123 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1124 | "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'event',\n" |
| 1125 | " 'event '||substr(tag.tagname,7)\n" |
| 1126 | " FROM tagxref, tag, blob\n" |
| 1127 | " WHERE (tagxref.rid %s)\n" |
| 1128 | " AND tag.tagid=tagxref.tagid\n" |
| 1129 | " AND tag.tagname GLOB 'event-*'" |
| @@ -1062,21 +1131,22 @@ | |
| 1131 | zWhere /*safe-for-%s*/ |
| 1132 | ); |
| 1133 | |
| 1134 | /* Attachments */ |
| 1135 | db_multi_exec( |
| 1136 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1137 | "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime," |
| 1138 | " 'attach-control',\n" |
| 1139 | " 'attachment-control for '||attachment.filename\n" |
| 1140 | " FROM attachment, blob\n" |
| 1141 | " WHERE (attachment.attachid %s)\n" |
| 1142 | " AND blob.rid=attachment.attachid", |
| 1143 | zWhere /*safe-for-%s*/ |
| 1144 | ); |
| 1145 | db_multi_exec( |
| 1146 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1147 | "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime, 'attachment',\n" |
| 1148 | " 'attachment '||attachment.filename\n" |
| 1149 | " FROM attachment, blob\n" |
| 1150 | " WHERE (blob.rid %s)\n" |
| 1151 | " AND blob.rid NOT IN (SELECT rid FROM description)\n" |
| 1152 | " AND blob.uuid=attachment.src", |
| @@ -1084,12 +1154,13 @@ | |
| 1154 | ); |
| 1155 | |
| 1156 | /* Forum posts */ |
| 1157 | if( db_table_exists("repository","forumpost") ){ |
| 1158 | db_multi_exec( |
| 1159 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" |
| 1160 | "SELECT postblob.rid, postblob.uuid, postblob.rcvid," |
| 1161 | " forumpost.fmtime, 'forumpost',\n" |
| 1162 | " CASE WHEN fpid=froot THEN 'forum-post '\n" |
| 1163 | " ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n" |
| 1164 | " FROM forumpost, blob AS postblob, blob AS rootblob\n" |
| 1165 | " WHERE (forumpost.fpid %s)\n" |
| 1166 | " AND postblob.rid=forumpost.fpid" |
| @@ -1096,24 +1167,31 @@ | |
| 1167 | " AND rootblob.rid=forumpost.froot", |
| 1168 | zWhere /*safe-for-%s*/ |
| 1169 | ); |
| 1170 | } |
| 1171 | |
| 1172 | /* Mark all other artifacts as "unknown" for now */ |
| 1173 | db_multi_exec( |
| 1174 | "INSERT OR IGNORE INTO description(rid,uuid,rcvid,type,summary)\n" |
| 1175 | "SELECT blob.rid, blob.uuid,blob.rcvid,\n" |
| 1176 | " CASE WHEN EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)\n" |
| 1177 | " THEN 'phantom' ELSE '' END,\n" |
| 1178 | " 'unknown'\n" |
| 1179 | " FROM blob\n" |
| 1180 | " WHERE (blob.rid %s)\n" |
| 1181 | " AND (blob.rid NOT IN (SELECT rid FROM description));", |
| 1182 | zWhere /*safe-for-%s*/ |
| 1183 | ); |
| 1184 | |
| 1185 | /* Mark private elements */ |
| 1186 | db_multi_exec( |
| 1187 | "UPDATE description SET isPrivate=1 WHERE rid IN private" |
| 1188 | ); |
| 1189 | |
| 1190 | if( db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){ |
| 1191 | describe_unknown_artifacts(); |
| 1192 | } |
| 1193 | } |
| 1194 | |
| 1195 | /* |
| 1196 | ** Print the content of the description table on stdout. |
| 1197 | ** |
| @@ -1135,11 +1213,11 @@ | |
| 1213 | if( zLabel ){ |
| 1214 | fossil_print("%s\n", zLabel); |
| 1215 | zLabel = 0; |
| 1216 | } |
| 1217 | fossil_print(" %.16s %s", db_column_text(&q,0), db_column_text(&q,1)); |
| 1218 | if( db_column_int(&q,2) ) fossil_print(" (private)"); |
| 1219 | fossil_print("\n"); |
| 1220 | cnt++; |
| 1221 | } |
| 1222 | db_finalize(&q); |
| 1223 | if( zWhere!=0 ) db_multi_exec("DELETE FROM description;"); |
| @@ -1172,19 +1250,21 @@ | |
| 1250 | ** |
| 1251 | ** Return a page showing all artifacts in the repository. Query parameters: |
| 1252 | ** |
| 1253 | ** n=N Show N artifacts |
| 1254 | ** s=S Start with artifact number S |
| 1255 | ** priv Show only unpublished or private artifacts |
| 1256 | ** phan Show only phantom artifacts |
| 1257 | ** hclr Color code hash types (SHA1 vs SHA3) |
| 1258 | */ |
| 1259 | void bloblist_page(void){ |
| 1260 | Stmt q; |
| 1261 | int s = atoi(PD("s","0")); |
| 1262 | int n = atoi(PD("n","5000")); |
| 1263 | int mx = db_int(0, "SELECT max(rid) FROM blob"); |
| 1264 | int privOnly = PB("priv"); |
| 1265 | int phantomOnly = PB("phan"); |
| 1266 | int hashClr = PB("hclr"); |
| 1267 | char *zRange; |
| 1268 | char *zSha1Bg; |
| 1269 | char *zSha3Bg; |
| 1270 | |
| @@ -1192,15 +1272,25 @@ | |
| 1272 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1273 | style_header("List Of Artifacts"); |
| 1274 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1275 | if( g.perm.Admin ){ |
| 1276 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1277 | } |
| 1278 | if( !phantomOnly ){ |
| 1279 | style_submenu_element("Phantoms", "bloblist?phan"); |
| 1280 | } |
| 1281 | if( g.perm.Private || g.perm.Admin ){ |
| 1282 | if( !privOnly ){ |
| 1283 | style_submenu_element("Private", "bloblist?priv"); |
| 1284 | } |
| 1285 | }else{ |
| 1286 | privOnly = 0; |
| 1287 | } |
| 1288 | if( g.perm.Write ){ |
| 1289 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1290 | } |
| 1291 | if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){ |
| 1292 | int i; |
| 1293 | @ <p>Select a range of artifacts to view:</p> |
| 1294 | @ <ul> |
| 1295 | for(i=1; i<=mx; i+=n){ |
| 1296 | @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) |
| @@ -1208,51 +1298,156 @@ | |
| 1298 | } |
| 1299 | @ </ul> |
| 1300 | style_footer(); |
| 1301 | return; |
| 1302 | } |
| 1303 | if( phantomOnly || privOnly || mx>n ){ |
| 1304 | style_submenu_element("Index", "bloblist"); |
| 1305 | } |
| 1306 | if( privOnly ){ |
| 1307 | zRange = mprintf("IN private"); |
| 1308 | }else if( phantomOnly ){ |
| 1309 | zRange = mprintf("IN phantom"); |
| 1310 | }else{ |
| 1311 | zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); |
| 1312 | } |
| 1313 | describe_artifacts(zRange); |
| 1314 | fossil_free(zRange); |
| 1315 | db_prepare(&q, |
| 1316 | "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref" |
| 1317 | " FROM description ORDER BY rid" |
| 1318 | ); |
| 1319 | if( skin_detail_boolean("white-foreground") ){ |
| 1320 | zSha1Bg = "#714417"; |
| 1321 | zSha3Bg = "#177117"; |
| 1322 | }else{ |
| 1323 | zSha1Bg = "#ebffb0"; |
| 1324 | zSha3Bg = "#b0ffb0"; |
| 1325 | } |
| 1326 | @ <table cellpadding="2" cellspacing="0" border="1"> |
| 1327 | if( g.perm.Admin ){ |
| 1328 | @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks |
| 1329 | }else{ |
| 1330 | @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks |
| 1331 | } |
| 1332 | while( db_step(&q)==SQLITE_ROW ){ |
| 1333 | int rid = db_column_int(&q,0); |
| 1334 | const char *zUuid = db_column_text(&q, 1); |
| 1335 | const char *zDesc = db_column_text(&q, 2); |
| 1336 | int isPriv = db_column_int(&q,3); |
| 1337 | int isPhantom = db_column_int(&q,4); |
| 1338 | const char *zRef = db_column_text(&q,6); |
| 1339 | if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ |
| 1340 | /* Don't show private artifacts to users without Private (x) permission */ |
| 1341 | continue; |
| 1342 | } |
| 1343 | if( hashClr ){ |
| 1344 | const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg; |
| 1345 | @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td> |
| 1346 | }else{ |
| 1347 | @ <tr><td align="right">%d(rid)</td> |
| 1348 | } |
| 1349 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1350 | if( g.perm.Admin ){ |
| 1351 | int rcvid = db_column_int(&q,5); |
| 1352 | if( rcvid<=0 ){ |
| 1353 | @ <td> |
| 1354 | }else{ |
| 1355 | @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a> |
| 1356 | } |
| 1357 | } |
| 1358 | @ <td align="left">%h(zDesc)</td> |
| 1359 | if( zRef && zRef[0] ){ |
| 1360 | @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> |
| 1361 | }else{ |
| 1362 | @ <td> |
| 1363 | } |
| 1364 | if( isPriv || isPhantom ){ |
| 1365 | if( isPriv==0 ){ |
| 1366 | @ <td>phantom</td> |
| 1367 | }else if( isPhantom==0 ){ |
| 1368 | @ <td>private</td> |
| 1369 | }else{ |
| 1370 | @ <td>private,phantom</td> |
| 1371 | } |
| 1372 | }else{ |
| 1373 | @ <td> |
| 1374 | } |
| 1375 | @ </tr> |
| 1376 | } |
| 1377 | @ </table> |
| 1378 | db_finalize(&q); |
| 1379 | style_footer(); |
| 1380 | } |
| 1381 | |
| 1382 | /* |
| 1383 | ** Output HTML that shows a table of all public phantoms. |
| 1384 | */ |
| 1385 | void table_of_public_phantoms(void){ |
| 1386 | Stmt q; |
| 1387 | char *zRange; |
| 1388 | zRange = mprintf("IN (SELECT rid FROM phantom EXCEPT" |
| 1389 | " SELECT rid FROM private)"); |
| 1390 | describe_artifacts(zRange); |
| 1391 | fossil_free(zRange); |
| 1392 | db_prepare(&q, |
| 1393 | "SELECT rid, uuid, summary, ref" |
| 1394 | " FROM description ORDER BY rid" |
| 1395 | ); |
| 1396 | @ <table cellpadding="2" cellspacing="0" border="1"> |
| 1397 | @ <tr><th>RID<th>Description<th>Source |
| 1398 | while( db_step(&q)==SQLITE_ROW ){ |
| 1399 | int rid = db_column_int(&q,0); |
| 1400 | const char *zUuid = db_column_text(&q, 1); |
| 1401 | const char *zDesc = db_column_text(&q, 2); |
| 1402 | const char *zRef = db_column_text(&q,3); |
| 1403 | @ <tr><td valign="top">%d(rid)</td> |
| 1404 | @ <td valign="top" align="left">%h(zUuid)<br>%h(zDesc)</td> |
| 1405 | if( zRef && zRef[0] ){ |
| 1406 | @ <td valign="top">%z(href("%R/info/%!S",zRef))%!S(zRef)</a> |
| 1407 | }else{ |
| 1408 | @ <td> |
| 1409 | } |
| 1410 | @ </tr> |
| 1411 | } |
| 1412 | @ </table> |
| 1413 | db_finalize(&q); |
| 1414 | } |
| 1415 | |
| 1416 | /* |
| 1417 | ** WEBPAGE: phantoms |
| 1418 | ** |
| 1419 | ** Show a list of all "phantom" artifacts that are not marked as "private". |
| 1420 | ** |
| 1421 | ** A "phantom" artifact is an artifact whose hash named appears in some |
| 1422 | ** artifact but whose content is unknown. For example, if a manifest |
| 1423 | ** references a particular SHA3 hash of a file, but that SHA3 hash is |
| 1424 | ** not on the shunning list and is not in the database, then the file |
| 1425 | ** is a phantom. We know it exists, but we do not know its content. |
| 1426 | ** |
| 1427 | ** Whenever a sync occurs, both each party looks at its phantom list |
| 1428 | ** and for every phantom that is not also marked private, it asks the |
| 1429 | ** other party to send it the content. This mechanism helps keep all |
| 1430 | ** repositories synced up. |
| 1431 | ** |
| 1432 | ** This page is similar to the /bloblist page in that it lists artifacts. |
| 1433 | ** But this page is a special case in that it only shows phantoms that |
| 1434 | ** are not private. In other words, this page shows all phantoms that |
| 1435 | ** generate extra network traffic on every sync request. |
| 1436 | */ |
| 1437 | void phantom_list_page(void){ |
| 1438 | login_check_credentials(); |
| 1439 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1440 | style_header("Public Phantom Artifacts"); |
| 1441 | if( g.perm.Admin ){ |
| 1442 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1443 | style_submenu_element("Artifact List", "bloblist"); |
| 1444 | } |
| 1445 | if( g.perm.Write ){ |
| 1446 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1447 | } |
| 1448 | table_of_public_phantoms(); |
| 1449 | style_footer(); |
| 1450 | } |
| 1451 | |
| 1452 | /* |
| 1453 | ** WEBPAGE: bigbloblist |
| 1454 |
+84
-14
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -1140,30 +1140,79 @@ | ||
| 1140 | 1140 | free(zFNameFormat); |
| 1141 | 1141 | zFNameFormat = 0; |
| 1142 | 1142 | cchFNamePrefix = 0; |
| 1143 | 1143 | } |
| 1144 | 1144 | #endif |
| 1145 | + | |
| 1146 | +/* | |
| 1147 | +** Helper functions used by the `deconstruct' and `reconstruct' commands to | |
| 1148 | +** save and restore the contents of the PRIVATE table. | |
| 1149 | +*/ | |
| 1150 | +void private_export(char *zFileName) | |
| 1151 | +{ | |
| 1152 | + Stmt q; | |
| 1153 | + Blob fctx = empty_blob; | |
| 1154 | + blob_append(&fctx, "# The UUIDs of private artifacts\n", -1); | |
| 1155 | + db_prepare(&q, | |
| 1156 | + "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );"); | |
| 1157 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1158 | + const char *zUuid = db_column_text(&q, 0); | |
| 1159 | + blob_append(&fctx, zUuid, -1); | |
| 1160 | + blob_append(&fctx, "\n", -1); | |
| 1161 | + } | |
| 1162 | + db_finalize(&q); | |
| 1163 | + blob_write_to_file(&fctx, zFileName); | |
| 1164 | + blob_reset(&fctx); | |
| 1165 | +} | |
| 1166 | +void private_import(char *zFileName) | |
| 1167 | +{ | |
| 1168 | + Blob fctx; | |
| 1169 | + if( blob_read_from_file(&fctx, zFileName, ExtFILE)!=-1 ){ | |
| 1170 | + Blob line, value; | |
| 1171 | + while( blob_line(&fctx, &line)>0 ){ | |
| 1172 | + char *zUuid; | |
| 1173 | + int nUuid; | |
| 1174 | + if( blob_token(&line, &value)==0 ) continue; /* Empty line */ | |
| 1175 | + if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */ | |
| 1176 | + blob_trim(&value); | |
| 1177 | + zUuid = blob_buffer(&value); | |
| 1178 | + nUuid = blob_size(&value); | |
| 1179 | + zUuid[nUuid] = 0; | |
| 1180 | + if( hname_validate(zUuid, nUuid)!=HNAME_ERROR ){ | |
| 1181 | + canonical16(zUuid, nUuid); | |
| 1182 | + db_multi_exec( | |
| 1183 | + "INSERT OR IGNORE INTO private" | |
| 1184 | + " SELECT rid FROM blob WHERE uuid = %Q;", | |
| 1185 | + zUuid); | |
| 1186 | + } | |
| 1187 | + } | |
| 1188 | + blob_reset(&fctx); | |
| 1189 | + } | |
| 1190 | +} | |
| 1145 | 1191 | |
| 1146 | 1192 | /* |
| 1147 | 1193 | ** COMMAND: reconstruct* |
| 1148 | 1194 | ** |
| 1149 | 1195 | ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY |
| 1150 | 1196 | ** |
| 1151 | -** This command studies the artifacts (files) in DIRECTORY and | |
| 1152 | -** reconstructs the fossil record from them. It places the new | |
| 1153 | -** fossil repository in FILENAME. Subdirectories are read, files | |
| 1154 | -** with leading '.' in the filename are ignored. | |
| 1197 | +** This command studies the artifacts (files) in DIRECTORY and reconstructs the | |
| 1198 | +** Fossil record from them. It places the new Fossil repository in FILENAME. | |
| 1199 | +** Subdirectories are read, files with leading '.' in the filename are ignored. | |
| 1155 | 1200 | ** |
| 1156 | 1201 | ** Options: |
| 1157 | -** -K|--keep-rid1 Read the filename of the artifact with | |
| 1158 | -** RID=1 from the file .rid in DIRECTORY. | |
| 1202 | +** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the | |
| 1203 | +** file .rid in DIRECTORY. | |
| 1204 | +** -P|--keep-private Mark the artifacts listed in the file .private in | |
| 1205 | +** DIRECTORY as private in the new Fossil repository. | |
| 1159 | 1206 | ** |
| 1160 | 1207 | ** See also: deconstruct, rebuild |
| 1161 | 1208 | */ |
| 1162 | 1209 | void reconstruct_cmd(void) { |
| 1163 | 1210 | char *zPassword; |
| 1211 | + int fKeepPrivate; | |
| 1164 | 1212 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| 1213 | + fKeepPrivate = find_option("keep-private","P",0)!=0; | |
| 1165 | 1214 | if( g.argc!=4 ){ |
| 1166 | 1215 | usage("FILENAME DIRECTORY"); |
| 1167 | 1216 | } |
| 1168 | 1217 | if( file_isdir(g.argv[3], ExtFILE)!=1 ){ |
| 1169 | 1218 | fossil_print("\"%s\" is not a directory\n\n", g.argv[3]); |
| @@ -1182,11 +1231,19 @@ | ||
| 1182 | 1231 | fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]); |
| 1183 | 1232 | recon_read_dir(g.argv[3]); |
| 1184 | 1233 | fossil_print("\nBuilding the Fossil repository...\n"); |
| 1185 | 1234 | |
| 1186 | 1235 | rebuild_db(0, 1, 1); |
| 1236 | + | |
| 1237 | + /* Backwards compatibility: Mark check-ins with "+private" tags as private. */ | |
| 1187 | 1238 | reconstruct_private_table(); |
| 1239 | + /* Newer method: Import the list of private artifacts to the PRIVATE table. */ | |
| 1240 | + if( fKeepPrivate ){ | |
| 1241 | + char *zFnDotPrivate = mprintf("%s/.private", g.argv[3]); | |
| 1242 | + private_import(zFnDotPrivate); | |
| 1243 | + free(zFnDotPrivate); | |
| 1244 | + } | |
| 1188 | 1245 | |
| 1189 | 1246 | /* Skip the verify_before_commit() step on a reconstruct. Most artifacts |
| 1190 | 1247 | ** will have been changed and verification therefore takes a really, really |
| 1191 | 1248 | ** long time. |
| 1192 | 1249 | */ |
| @@ -1202,32 +1259,35 @@ | ||
| 1202 | 1259 | /* |
| 1203 | 1260 | ** COMMAND: deconstruct* |
| 1204 | 1261 | ** |
| 1205 | 1262 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 1206 | 1263 | ** |
| 1207 | -** | |
| 1208 | -** This command exports all artifacts of a given repository and | |
| 1209 | -** writes all artifacts to the file system. The DESTINATION directory | |
| 1210 | -** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where | |
| 1211 | -** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters. | |
| 1212 | -** If -L|--prefixlength is given, the length (default 2) of the directory | |
| 1213 | -** prefix can be set to 0,1,..,9 characters. | |
| 1264 | +** This command exports all artifacts of a given repository and writes all | |
| 1265 | +** artifacts to the file system. The DESTINATION directory will be populated | |
| 1266 | +** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the | |
| 1267 | +** 40+ character artifact ID, AA the first 2 characters. | |
| 1268 | +** If -L|--prefixlength is given, the length (default 2) of the directory prefix | |
| 1269 | +** can be set to 0,1,..,9 characters. | |
| 1214 | 1270 | ** |
| 1215 | 1271 | ** Options: |
| 1216 | 1272 | ** -R|--repository REPOSITORY Deconstruct given REPOSITORY. |
| 1217 | 1273 | ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to |
| 1218 | 1274 | ** the file .rid1 in the DESTINATION directory. |
| 1219 | 1275 | ** -L|--prefixlength N Set the length of the names of the DESTINATION |
| 1220 | 1276 | ** subdirectories to N. |
| 1221 | 1277 | ** --private Include private artifacts. |
| 1278 | +** -P|--keep-private Save the list of private artifacts to the file | |
| 1279 | +** .private in the DESTINATION directory (implies | |
| 1280 | +** the --private option). | |
| 1222 | 1281 | ** |
| 1223 | -** See also: rebuild, reconstruct | |
| 1282 | +** See also: reconstruct, rebuild | |
| 1224 | 1283 | */ |
| 1225 | 1284 | void deconstruct_cmd(void){ |
| 1226 | 1285 | const char *zPrefixOpt; |
| 1227 | 1286 | Stmt s; |
| 1228 | 1287 | int privateFlag; |
| 1288 | + int fKeepPrivate; | |
| 1229 | 1289 | |
| 1230 | 1290 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| 1231 | 1291 | /* get and check prefix length argument and build format string */ |
| 1232 | 1292 | zPrefixOpt=find_option("prefixlength","L",1); |
| 1233 | 1293 | if( !zPrefixOpt ){ |
| @@ -1240,10 +1300,12 @@ | ||
| 1240 | 1300 | } |
| 1241 | 1301 | } |
| 1242 | 1302 | /* open repository and open query for all artifacts */ |
| 1243 | 1303 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1244 | 1304 | privateFlag = find_option("private",0,0)!=0; |
| 1305 | + fKeepPrivate = find_option("keep-private","P",0)!=0; | |
| 1306 | + if( fKeepPrivate ) privateFlag = 1; | |
| 1245 | 1307 | verify_all_options(); |
| 1246 | 1308 | /* check number of arguments */ |
| 1247 | 1309 | if( g.argc!=3 ){ |
| 1248 | 1310 | usage ("?OPTIONS? DESTINATION"); |
| 1249 | 1311 | } |
| @@ -1307,13 +1369,21 @@ | ||
| 1307 | 1369 | rebuild_step(rid, size, &content); |
| 1308 | 1370 | } |
| 1309 | 1371 | } |
| 1310 | 1372 | } |
| 1311 | 1373 | db_finalize(&s); |
| 1374 | + | |
| 1375 | + /* Export the list of private artifacts. */ | |
| 1376 | + if( fKeepPrivate ){ | |
| 1377 | + char *zFnDotPrivate = mprintf("%s/.private", zDestDir); | |
| 1378 | + private_export(zFnDotPrivate); | |
| 1379 | + free(zFnDotPrivate); | |
| 1380 | + } | |
| 1381 | + | |
| 1312 | 1382 | if(!g.fQuiet && ttyOutput ){ |
| 1313 | 1383 | fossil_print("\n"); |
| 1314 | 1384 | } |
| 1315 | 1385 | |
| 1316 | 1386 | /* free filename format string */ |
| 1317 | 1387 | free(zFNameFormat); |
| 1318 | 1388 | zFNameFormat = 0; |
| 1319 | 1389 | } |
| 1320 | 1390 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -1140,30 +1140,79 @@ | |
| 1140 | free(zFNameFormat); |
| 1141 | zFNameFormat = 0; |
| 1142 | cchFNamePrefix = 0; |
| 1143 | } |
| 1144 | #endif |
| 1145 | |
| 1146 | /* |
| 1147 | ** COMMAND: reconstruct* |
| 1148 | ** |
| 1149 | ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY |
| 1150 | ** |
| 1151 | ** This command studies the artifacts (files) in DIRECTORY and |
| 1152 | ** reconstructs the fossil record from them. It places the new |
| 1153 | ** fossil repository in FILENAME. Subdirectories are read, files |
| 1154 | ** with leading '.' in the filename are ignored. |
| 1155 | ** |
| 1156 | ** Options: |
| 1157 | ** -K|--keep-rid1 Read the filename of the artifact with |
| 1158 | ** RID=1 from the file .rid in DIRECTORY. |
| 1159 | ** |
| 1160 | ** See also: deconstruct, rebuild |
| 1161 | */ |
| 1162 | void reconstruct_cmd(void) { |
| 1163 | char *zPassword; |
| 1164 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| 1165 | if( g.argc!=4 ){ |
| 1166 | usage("FILENAME DIRECTORY"); |
| 1167 | } |
| 1168 | if( file_isdir(g.argv[3], ExtFILE)!=1 ){ |
| 1169 | fossil_print("\"%s\" is not a directory\n\n", g.argv[3]); |
| @@ -1182,11 +1231,19 @@ | |
| 1182 | fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]); |
| 1183 | recon_read_dir(g.argv[3]); |
| 1184 | fossil_print("\nBuilding the Fossil repository...\n"); |
| 1185 | |
| 1186 | rebuild_db(0, 1, 1); |
| 1187 | reconstruct_private_table(); |
| 1188 | |
| 1189 | /* Skip the verify_before_commit() step on a reconstruct. Most artifacts |
| 1190 | ** will have been changed and verification therefore takes a really, really |
| 1191 | ** long time. |
| 1192 | */ |
| @@ -1202,32 +1259,35 @@ | |
| 1202 | /* |
| 1203 | ** COMMAND: deconstruct* |
| 1204 | ** |
| 1205 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 1206 | ** |
| 1207 | ** |
| 1208 | ** This command exports all artifacts of a given repository and |
| 1209 | ** writes all artifacts to the file system. The DESTINATION directory |
| 1210 | ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where |
| 1211 | ** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters. |
| 1212 | ** If -L|--prefixlength is given, the length (default 2) of the directory |
| 1213 | ** prefix can be set to 0,1,..,9 characters. |
| 1214 | ** |
| 1215 | ** Options: |
| 1216 | ** -R|--repository REPOSITORY Deconstruct given REPOSITORY. |
| 1217 | ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to |
| 1218 | ** the file .rid1 in the DESTINATION directory. |
| 1219 | ** -L|--prefixlength N Set the length of the names of the DESTINATION |
| 1220 | ** subdirectories to N. |
| 1221 | ** --private Include private artifacts. |
| 1222 | ** |
| 1223 | ** See also: rebuild, reconstruct |
| 1224 | */ |
| 1225 | void deconstruct_cmd(void){ |
| 1226 | const char *zPrefixOpt; |
| 1227 | Stmt s; |
| 1228 | int privateFlag; |
| 1229 | |
| 1230 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| 1231 | /* get and check prefix length argument and build format string */ |
| 1232 | zPrefixOpt=find_option("prefixlength","L",1); |
| 1233 | if( !zPrefixOpt ){ |
| @@ -1240,10 +1300,12 @@ | |
| 1240 | } |
| 1241 | } |
| 1242 | /* open repository and open query for all artifacts */ |
| 1243 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1244 | privateFlag = find_option("private",0,0)!=0; |
| 1245 | verify_all_options(); |
| 1246 | /* check number of arguments */ |
| 1247 | if( g.argc!=3 ){ |
| 1248 | usage ("?OPTIONS? DESTINATION"); |
| 1249 | } |
| @@ -1307,13 +1369,21 @@ | |
| 1307 | rebuild_step(rid, size, &content); |
| 1308 | } |
| 1309 | } |
| 1310 | } |
| 1311 | db_finalize(&s); |
| 1312 | if(!g.fQuiet && ttyOutput ){ |
| 1313 | fossil_print("\n"); |
| 1314 | } |
| 1315 | |
| 1316 | /* free filename format string */ |
| 1317 | free(zFNameFormat); |
| 1318 | zFNameFormat = 0; |
| 1319 | } |
| 1320 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -1140,30 +1140,79 @@ | |
| 1140 | free(zFNameFormat); |
| 1141 | zFNameFormat = 0; |
| 1142 | cchFNamePrefix = 0; |
| 1143 | } |
| 1144 | #endif |
| 1145 | |
| 1146 | /* |
| 1147 | ** Helper functions used by the `deconstruct' and `reconstruct' commands to |
| 1148 | ** save and restore the contents of the PRIVATE table. |
| 1149 | */ |
| 1150 | void private_export(char *zFileName) |
| 1151 | { |
| 1152 | Stmt q; |
| 1153 | Blob fctx = empty_blob; |
| 1154 | blob_append(&fctx, "# The UUIDs of private artifacts\n", -1); |
| 1155 | db_prepare(&q, |
| 1156 | "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );"); |
| 1157 | while( db_step(&q)==SQLITE_ROW ){ |
| 1158 | const char *zUuid = db_column_text(&q, 0); |
| 1159 | blob_append(&fctx, zUuid, -1); |
| 1160 | blob_append(&fctx, "\n", -1); |
| 1161 | } |
| 1162 | db_finalize(&q); |
| 1163 | blob_write_to_file(&fctx, zFileName); |
| 1164 | blob_reset(&fctx); |
| 1165 | } |
| 1166 | void private_import(char *zFileName) |
| 1167 | { |
| 1168 | Blob fctx; |
| 1169 | if( blob_read_from_file(&fctx, zFileName, ExtFILE)!=-1 ){ |
| 1170 | Blob line, value; |
| 1171 | while( blob_line(&fctx, &line)>0 ){ |
| 1172 | char *zUuid; |
| 1173 | int nUuid; |
| 1174 | if( blob_token(&line, &value)==0 ) continue; /* Empty line */ |
| 1175 | if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */ |
| 1176 | blob_trim(&value); |
| 1177 | zUuid = blob_buffer(&value); |
| 1178 | nUuid = blob_size(&value); |
| 1179 | zUuid[nUuid] = 0; |
| 1180 | if( hname_validate(zUuid, nUuid)!=HNAME_ERROR ){ |
| 1181 | canonical16(zUuid, nUuid); |
| 1182 | db_multi_exec( |
| 1183 | "INSERT OR IGNORE INTO private" |
| 1184 | " SELECT rid FROM blob WHERE uuid = %Q;", |
| 1185 | zUuid); |
| 1186 | } |
| 1187 | } |
| 1188 | blob_reset(&fctx); |
| 1189 | } |
| 1190 | } |
| 1191 | |
| 1192 | /* |
| 1193 | ** COMMAND: reconstruct* |
| 1194 | ** |
| 1195 | ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY |
| 1196 | ** |
| 1197 | ** This command studies the artifacts (files) in DIRECTORY and reconstructs the |
| 1198 | ** Fossil record from them. It places the new Fossil repository in FILENAME. |
| 1199 | ** Subdirectories are read, files with leading '.' in the filename are ignored. |
| 1200 | ** |
| 1201 | ** Options: |
| 1202 | ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the |
| 1203 | ** file .rid in DIRECTORY. |
| 1204 | ** -P|--keep-private Mark the artifacts listed in the file .private in |
| 1205 | ** DIRECTORY as private in the new Fossil repository. |
| 1206 | ** |
| 1207 | ** See also: deconstruct, rebuild |
| 1208 | */ |
| 1209 | void reconstruct_cmd(void) { |
| 1210 | char *zPassword; |
| 1211 | int fKeepPrivate; |
| 1212 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| 1213 | fKeepPrivate = find_option("keep-private","P",0)!=0; |
| 1214 | if( g.argc!=4 ){ |
| 1215 | usage("FILENAME DIRECTORY"); |
| 1216 | } |
| 1217 | if( file_isdir(g.argv[3], ExtFILE)!=1 ){ |
| 1218 | fossil_print("\"%s\" is not a directory\n\n", g.argv[3]); |
| @@ -1182,11 +1231,19 @@ | |
| 1231 | fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]); |
| 1232 | recon_read_dir(g.argv[3]); |
| 1233 | fossil_print("\nBuilding the Fossil repository...\n"); |
| 1234 | |
| 1235 | rebuild_db(0, 1, 1); |
| 1236 | |
| 1237 | /* Backwards compatibility: Mark check-ins with "+private" tags as private. */ |
| 1238 | reconstruct_private_table(); |
| 1239 | /* Newer method: Import the list of private artifacts to the PRIVATE table. */ |
| 1240 | if( fKeepPrivate ){ |
| 1241 | char *zFnDotPrivate = mprintf("%s/.private", g.argv[3]); |
| 1242 | private_import(zFnDotPrivate); |
| 1243 | free(zFnDotPrivate); |
| 1244 | } |
| 1245 | |
| 1246 | /* Skip the verify_before_commit() step on a reconstruct. Most artifacts |
| 1247 | ** will have been changed and verification therefore takes a really, really |
| 1248 | ** long time. |
| 1249 | */ |
| @@ -1202,32 +1259,35 @@ | |
| 1259 | /* |
| 1260 | ** COMMAND: deconstruct* |
| 1261 | ** |
| 1262 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 1263 | ** |
| 1264 | ** This command exports all artifacts of a given repository and writes all |
| 1265 | ** artifacts to the file system. The DESTINATION directory will be populated |
| 1266 | ** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the |
| 1267 | ** 40+ character artifact ID, AA the first 2 characters. |
| 1268 | ** If -L|--prefixlength is given, the length (default 2) of the directory prefix |
| 1269 | ** can be set to 0,1,..,9 characters. |
| 1270 | ** |
| 1271 | ** Options: |
| 1272 | ** -R|--repository REPOSITORY Deconstruct given REPOSITORY. |
| 1273 | ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to |
| 1274 | ** the file .rid1 in the DESTINATION directory. |
| 1275 | ** -L|--prefixlength N Set the length of the names of the DESTINATION |
| 1276 | ** subdirectories to N. |
| 1277 | ** --private Include private artifacts. |
| 1278 | ** -P|--keep-private Save the list of private artifacts to the file |
| 1279 | ** .private in the DESTINATION directory (implies |
| 1280 | ** the --private option). |
| 1281 | ** |
| 1282 | ** See also: reconstruct, rebuild |
| 1283 | */ |
| 1284 | void deconstruct_cmd(void){ |
| 1285 | const char *zPrefixOpt; |
| 1286 | Stmt s; |
| 1287 | int privateFlag; |
| 1288 | int fKeepPrivate; |
| 1289 | |
| 1290 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| 1291 | /* get and check prefix length argument and build format string */ |
| 1292 | zPrefixOpt=find_option("prefixlength","L",1); |
| 1293 | if( !zPrefixOpt ){ |
| @@ -1240,10 +1300,12 @@ | |
| 1300 | } |
| 1301 | } |
| 1302 | /* open repository and open query for all artifacts */ |
| 1303 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1304 | privateFlag = find_option("private",0,0)!=0; |
| 1305 | fKeepPrivate = find_option("keep-private","P",0)!=0; |
| 1306 | if( fKeepPrivate ) privateFlag = 1; |
| 1307 | verify_all_options(); |
| 1308 | /* check number of arguments */ |
| 1309 | if( g.argc!=3 ){ |
| 1310 | usage ("?OPTIONS? DESTINATION"); |
| 1311 | } |
| @@ -1307,13 +1369,21 @@ | |
| 1369 | rebuild_step(rid, size, &content); |
| 1370 | } |
| 1371 | } |
| 1372 | } |
| 1373 | db_finalize(&s); |
| 1374 | |
| 1375 | /* Export the list of private artifacts. */ |
| 1376 | if( fKeepPrivate ){ |
| 1377 | char *zFnDotPrivate = mprintf("%s/.private", zDestDir); |
| 1378 | private_export(zFnDotPrivate); |
| 1379 | free(zFnDotPrivate); |
| 1380 | } |
| 1381 | |
| 1382 | if(!g.fQuiet && ttyOutput ){ |
| 1383 | fossil_print("\n"); |
| 1384 | } |
| 1385 | |
| 1386 | /* free filename format string */ |
| 1387 | free(zFNameFormat); |
| 1388 | zFNameFormat = 0; |
| 1389 | } |
| 1390 |
+19
-1
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -158,10 +158,16 @@ | ||
| 158 | 158 | @ ); |
| 159 | 159 | @ |
| 160 | 160 | @ -- Artifacts that should not be pushed are stored in the "private" |
| 161 | 161 | @ -- table. Private artifacts are omitted from the "unclustered" and |
| 162 | 162 | @ -- "unsent" tables. |
| 163 | +@ -- | |
| 164 | +@ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact | |
| 165 | +@ -- for which we do not know the content) might also be marked as private. | |
| 166 | +@ -- This comes about when an artifact is named in a manifest or tag but | |
| 167 | +@ -- the content of that artifact is held privately by some other peer | |
| 168 | +@ -- repository. | |
| 163 | 169 | @ -- |
| 164 | 170 | @ CREATE TABLE private(rid INTEGER PRIMARY KEY); |
| 165 | 171 | @ |
| 166 | 172 | @ -- An entry in this table describes a database query that generates a |
| 167 | 173 | @ -- table of tickets. |
| @@ -401,11 +407,11 @@ | ||
| 401 | 407 | @ -- the following table for that hyperlink. This table is used to |
| 402 | 408 | @ -- facilitate the display of "back links". |
| 403 | 409 | @ -- |
| 404 | 410 | @ CREATE TABLE backlink( |
| 405 | 411 | @ target TEXT, -- Where the hyperlink points to |
| 406 | -@ srctype INT, -- 0: check-in 1: ticket 2: wiki | |
| 412 | +@ srctype INT, -- 0=comment 1=ticket 2=wiki. See BKLNK_* below. | |
| 407 | 413 | @ srcid INT, -- EVENT.OBJID for the source document |
| 408 | 414 | @ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day. |
| 409 | 415 | @ UNIQUE(target, srctype, srcid) |
| 410 | 416 | @ ); |
| 411 | 417 | @ CREATE INDEX backlink_src ON backlink(srcid, srctype); |
| @@ -470,10 +476,22 @@ | ||
| 470 | 476 | @ PRIMARY KEY(parentid, childid) |
| 471 | 477 | @ ) WITHOUT ROWID; |
| 472 | 478 | @ CREATE INDEX cherrypick_cid ON cherrypick(childid); |
| 473 | 479 | ; |
| 474 | 480 | |
| 481 | +/* | |
| 482 | +** Allowed values for backlink.srctype | |
| 483 | +*/ | |
| 484 | +#if INTERFACE | |
| 485 | +# define BKLNK_COMMENT 0 /* Check-in comment */ | |
| 486 | +# define BKLNK_TICKET 1 /* Ticket body or title */ | |
| 487 | +# define BKLNK_WIKI 2 /* Wiki */ | |
| 488 | +# define BKLNK_EVENT 3 /* Technote */ | |
| 489 | +# define BKLNK_FORUM 4 /* Forum post */ | |
| 490 | +# define ValidBklnk(X) (X>=0 && X<=4) /* True if backlink.srctype is valid */ | |
| 491 | +#endif | |
| 492 | + | |
| 475 | 493 | /* |
| 476 | 494 | ** Predefined tagid values |
| 477 | 495 | */ |
| 478 | 496 | #if INTERFACE |
| 479 | 497 | # define TAG_BGCOLOR 1 /* Set the background color for display */ |
| 480 | 498 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -158,10 +158,16 @@ | |
| 158 | @ ); |
| 159 | @ |
| 160 | @ -- Artifacts that should not be pushed are stored in the "private" |
| 161 | @ -- table. Private artifacts are omitted from the "unclustered" and |
| 162 | @ -- "unsent" tables. |
| 163 | @ -- |
| 164 | @ CREATE TABLE private(rid INTEGER PRIMARY KEY); |
| 165 | @ |
| 166 | @ -- An entry in this table describes a database query that generates a |
| 167 | @ -- table of tickets. |
| @@ -401,11 +407,11 @@ | |
| 401 | @ -- the following table for that hyperlink. This table is used to |
| 402 | @ -- facilitate the display of "back links". |
| 403 | @ -- |
| 404 | @ CREATE TABLE backlink( |
| 405 | @ target TEXT, -- Where the hyperlink points to |
| 406 | @ srctype INT, -- 0: check-in 1: ticket 2: wiki |
| 407 | @ srcid INT, -- EVENT.OBJID for the source document |
| 408 | @ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day. |
| 409 | @ UNIQUE(target, srctype, srcid) |
| 410 | @ ); |
| 411 | @ CREATE INDEX backlink_src ON backlink(srcid, srctype); |
| @@ -470,10 +476,22 @@ | |
| 470 | @ PRIMARY KEY(parentid, childid) |
| 471 | @ ) WITHOUT ROWID; |
| 472 | @ CREATE INDEX cherrypick_cid ON cherrypick(childid); |
| 473 | ; |
| 474 | |
| 475 | /* |
| 476 | ** Predefined tagid values |
| 477 | */ |
| 478 | #if INTERFACE |
| 479 | # define TAG_BGCOLOR 1 /* Set the background color for display */ |
| 480 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -158,10 +158,16 @@ | |
| 158 | @ ); |
| 159 | @ |
| 160 | @ -- Artifacts that should not be pushed are stored in the "private" |
| 161 | @ -- table. Private artifacts are omitted from the "unclustered" and |
| 162 | @ -- "unsent" tables. |
| 163 | @ -- |
| 164 | @ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact |
| 165 | @ -- for which we do not know the content) might also be marked as private. |
| 166 | @ -- This comes about when an artifact is named in a manifest or tag but |
| 167 | @ -- the content of that artifact is held privately by some other peer |
| 168 | @ -- repository. |
| 169 | @ -- |
| 170 | @ CREATE TABLE private(rid INTEGER PRIMARY KEY); |
| 171 | @ |
| 172 | @ -- An entry in this table describes a database query that generates a |
| 173 | @ -- table of tickets. |
| @@ -401,11 +407,11 @@ | |
| 407 | @ -- the following table for that hyperlink. This table is used to |
| 408 | @ -- facilitate the display of "back links". |
| 409 | @ -- |
| 410 | @ CREATE TABLE backlink( |
| 411 | @ target TEXT, -- Where the hyperlink points to |
| 412 | @ srctype INT, -- 0=comment 1=ticket 2=wiki. See BKLNK_* below. |
| 413 | @ srcid INT, -- EVENT.OBJID for the source document |
| 414 | @ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day. |
| 415 | @ UNIQUE(target, srctype, srcid) |
| 416 | @ ); |
| 417 | @ CREATE INDEX backlink_src ON backlink(srcid, srctype); |
| @@ -470,10 +476,22 @@ | |
| 476 | @ PRIMARY KEY(parentid, childid) |
| 477 | @ ) WITHOUT ROWID; |
| 478 | @ CREATE INDEX cherrypick_cid ON cherrypick(childid); |
| 479 | ; |
| 480 | |
| 481 | /* |
| 482 | ** Allowed values for backlink.srctype |
| 483 | */ |
| 484 | #if INTERFACE |
| 485 | # define BKLNK_COMMENT 0 /* Check-in comment */ |
| 486 | # define BKLNK_TICKET 1 /* Ticket body or title */ |
| 487 | # define BKLNK_WIKI 2 /* Wiki */ |
| 488 | # define BKLNK_EVENT 3 /* Technote */ |
| 489 | # define BKLNK_FORUM 4 /* Forum post */ |
| 490 | # define ValidBklnk(X) (X>=0 && X<=4) /* True if backlink.srctype is valid */ |
| 491 | #endif |
| 492 | |
| 493 | /* |
| 494 | ** Predefined tagid values |
| 495 | */ |
| 496 | #if INTERFACE |
| 497 | # define TAG_BGCOLOR 1 /* Set the background color for display */ |
| 498 |
+16
-1
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -122,11 +122,11 @@ | ||
| 122 | 122 | zAnonCap = db_text("", "SELECT fullcap(NULL)"); |
| 123 | 123 | zDevCap = db_text("", "SELECT fullcap('v')"); |
| 124 | 124 | zReadCap = db_text("", "SELECT fullcap('u')"); |
| 125 | 125 | zPubPages = db_get("public-pages",0); |
| 126 | 126 | hasSelfReg = db_get_boolean("self-register",0); |
| 127 | - pCap = capability_add(0, db_get("default-perms",0)); | |
| 127 | + pCap = capability_add(0, db_get("default-perms","u")); | |
| 128 | 128 | capability_expand(pCap); |
| 129 | 129 | zSelfCap = capability_string(pCap); |
| 130 | 130 | capability_free(pCap); |
| 131 | 131 | if( hasAnyCap(zAnonCap,"as") ){ |
| 132 | 132 | @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
| @@ -553,10 +553,25 @@ | ||
| 553 | 553 | stats_for_email(); |
| 554 | 554 | @ </table> |
| 555 | 555 | }else{ |
| 556 | 556 | @ <li><p> Email alerts are disabled |
| 557 | 557 | } |
| 558 | + | |
| 559 | + n = db_int(0,"SELECT count(*) FROM (" | |
| 560 | + "SELECT rid FROM phantom EXCEPT SELECT rid FROM private)"); | |
| 561 | + if( n>0 ){ | |
| 562 | + @ <li><p>\ | |
| 563 | + @ There exists public phantom artifacts in this repository, shown below. | |
| 564 | + @ Phantom artifacts are artifacts whose hash name is referenced by some | |
| 565 | + @ other artifact but whose content is unknown. Some phantoms are marked | |
| 566 | + @ private and those are ignored. But public phantoms cause unnecessary | |
| 567 | + @ sync traffic and might represent malicious attempts to corrupt the | |
| 568 | + @ repository structure. | |
| 569 | + @ </p> | |
| 570 | + table_of_public_phantoms(); | |
| 571 | + @ </li> | |
| 572 | + } | |
| 558 | 573 | |
| 559 | 574 | @ </ol> |
| 560 | 575 | style_footer(); |
| 561 | 576 | } |
| 562 | 577 | |
| 563 | 578 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -122,11 +122,11 @@ | |
| 122 | zAnonCap = db_text("", "SELECT fullcap(NULL)"); |
| 123 | zDevCap = db_text("", "SELECT fullcap('v')"); |
| 124 | zReadCap = db_text("", "SELECT fullcap('u')"); |
| 125 | zPubPages = db_get("public-pages",0); |
| 126 | hasSelfReg = db_get_boolean("self-register",0); |
| 127 | pCap = capability_add(0, db_get("default-perms",0)); |
| 128 | capability_expand(pCap); |
| 129 | zSelfCap = capability_string(pCap); |
| 130 | capability_free(pCap); |
| 131 | if( hasAnyCap(zAnonCap,"as") ){ |
| 132 | @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
| @@ -553,10 +553,25 @@ | |
| 553 | stats_for_email(); |
| 554 | @ </table> |
| 555 | }else{ |
| 556 | @ <li><p> Email alerts are disabled |
| 557 | } |
| 558 | |
| 559 | @ </ol> |
| 560 | style_footer(); |
| 561 | } |
| 562 | |
| 563 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -122,11 +122,11 @@ | |
| 122 | zAnonCap = db_text("", "SELECT fullcap(NULL)"); |
| 123 | zDevCap = db_text("", "SELECT fullcap('v')"); |
| 124 | zReadCap = db_text("", "SELECT fullcap('u')"); |
| 125 | zPubPages = db_get("public-pages",0); |
| 126 | hasSelfReg = db_get_boolean("self-register",0); |
| 127 | pCap = capability_add(0, db_get("default-perms","u")); |
| 128 | capability_expand(pCap); |
| 129 | zSelfCap = capability_string(pCap); |
| 130 | capability_free(pCap); |
| 131 | if( hasAnyCap(zAnonCap,"as") ){ |
| 132 | @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
| @@ -553,10 +553,25 @@ | |
| 553 | stats_for_email(); |
| 554 | @ </table> |
| 555 | }else{ |
| 556 | @ <li><p> Email alerts are disabled |
| 557 | } |
| 558 | |
| 559 | n = db_int(0,"SELECT count(*) FROM (" |
| 560 | "SELECT rid FROM phantom EXCEPT SELECT rid FROM private)"); |
| 561 | if( n>0 ){ |
| 562 | @ <li><p>\ |
| 563 | @ There exists public phantom artifacts in this repository, shown below. |
| 564 | @ Phantom artifacts are artifacts whose hash name is referenced by some |
| 565 | @ other artifact but whose content is unknown. Some phantoms are marked |
| 566 | @ private and those are ignored. But public phantoms cause unnecessary |
| 567 | @ sync traffic and might represent malicious attempts to corrupt the |
| 568 | @ repository structure. |
| 569 | @ </p> |
| 570 | table_of_public_phantoms(); |
| 571 | @ </li> |
| 572 | } |
| 573 | |
| 574 | @ </ol> |
| 575 | style_footer(); |
| 576 | } |
| 577 | |
| 578 |
+45
-13
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -500,17 +500,44 @@ | ||
| 500 | 500 | @ (Property: "public-pages") |
| 501 | 501 | @ </p> |
| 502 | 502 | |
| 503 | 503 | @ <hr /> |
| 504 | 504 | onoff_attribute("Allow users to register themselves", |
| 505 | - "self-register", "selfregister", 0, 0); | |
| 506 | - @ <p>Allow users to register themselves through the HTTP UI. | |
| 507 | - @ The registration form always requires filling in a CAPTCHA | |
| 508 | - @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone | |
| 509 | - @ can register under any user name. This option is useful for public projects | |
| 510 | - @ where you do not want everyone in any ticket discussion to be named | |
| 511 | - @ "Anonymous". (Property: "self-register")</p> | |
| 505 | + "self-register", "selfreg", 0, 0); | |
| 506 | + @ <p>Allow users to register themselves on the /register webpage. | |
| 507 | + @ A self-registration creates a new entry in the USER table and | |
| 508 | + @ perhaps also in the SUBSCRIBER table if email notification is | |
| 509 | + @ enabled. | |
| 510 | + @ (Property: "self-register")</p> | |
| 511 | + | |
| 512 | + @ <hr /> | |
| 513 | + onoff_attribute("Email verification required for self-registration", | |
| 514 | + "selfreg-verify", "sfverify", 0, 0); | |
| 515 | + @ <p>If enabled, self-registration creates a new entry in the USER table | |
| 516 | + @ with only capabilities "7". The default user capabilities are not | |
| 517 | + @ added until the email address associated with the self-registration | |
| 518 | + @ has been verified. This setting only makes sense if | |
| 519 | + @ email notifications are enabled. | |
| 520 | + @ (Property: "selfreg-verify")</p> | |
| 521 | + | |
| 522 | + @ <hr /> | |
| 523 | + onoff_attribute("Allow anonymous subscriptions", | |
| 524 | + "anon-subscribe", "anonsub", 1, 0); | |
| 525 | + @ <p>If disabled, email notification subscriptions are only allowed | |
| 526 | + @ for users with a login. If Nobody or Anonymous visit the /subscribe | |
| 527 | + @ page, they are redirected to /register or /login. | |
| 528 | + @ (Property: "anon-subscribe")</p> | |
| 529 | + | |
| 530 | + @ <hr /> | |
| 531 | + entry_attribute("Authorized subscription email addresses", 35, | |
| 532 | + "auth-sub-email", "asemail", "", 0); | |
| 533 | + @ <p>This is a comma-separated list of GLOB patterns that specify | |
| 534 | + @ email addresses that are authorized to subscriptions. If blank | |
| 535 | + @ (the usual case), then any email address can be used to self-register. | |
| 536 | + @ This setting is used to limit subscriptions to members of a particular | |
| 537 | + @ organization or group based on their email address. | |
| 538 | + @ (Property: "auth-sub-email")</p> | |
| 512 | 539 | |
| 513 | 540 | @ <hr /> |
| 514 | 541 | entry_attribute("Default privileges", 10, "default-perms", |
| 515 | 542 | "defaultperms", "u", 0); |
| 516 | 543 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| @@ -849,25 +876,30 @@ | ||
| 849 | 876 | } |
| 850 | 877 | } |
| 851 | 878 | } |
| 852 | 879 | @ <br /><input type="submit" name="submit" value="Apply Changes" /> |
| 853 | 880 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 881 | + @ <table> | |
| 854 | 882 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 855 | 883 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 856 | 884 | int hasVersionableValue = pSet->versionable && |
| 857 | 885 | (db_get_versioned(pSet->name, NULL)!=0); |
| 886 | + @ <tr><td> | |
| 887 | + @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> | |
| 888 | + if( pSet->versionable ){ | |
| 889 | + @ (v) | |
| 890 | + } else { | |
| 891 | + @ | |
| 892 | + } | |
| 893 | + @</td><td> | |
| 858 | 894 | entry_attribute("", /*pSet->width*/ 25, pSet->name, |
| 859 | 895 | pSet->var!=0 ? pSet->var : pSet->name, |
| 860 | 896 | (char*)pSet->def, hasVersionableValue); |
| 861 | - @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> | |
| 862 | - if( pSet->versionable ){ | |
| 863 | - @ (v)<br /> | |
| 864 | - } else { | |
| 865 | - @ <br /> | |
| 866 | - } | |
| 897 | + @</td></tr> | |
| 867 | 898 | } |
| 868 | 899 | } |
| 900 | + @</table> | |
| 869 | 901 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 870 | 902 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 871 | 903 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 872 | 904 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 873 | 905 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 874 | 906 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -500,17 +500,44 @@ | |
| 500 | @ (Property: "public-pages") |
| 501 | @ </p> |
| 502 | |
| 503 | @ <hr /> |
| 504 | onoff_attribute("Allow users to register themselves", |
| 505 | "self-register", "selfregister", 0, 0); |
| 506 | @ <p>Allow users to register themselves through the HTTP UI. |
| 507 | @ The registration form always requires filling in a CAPTCHA |
| 508 | @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone |
| 509 | @ can register under any user name. This option is useful for public projects |
| 510 | @ where you do not want everyone in any ticket discussion to be named |
| 511 | @ "Anonymous". (Property: "self-register")</p> |
| 512 | |
| 513 | @ <hr /> |
| 514 | entry_attribute("Default privileges", 10, "default-perms", |
| 515 | "defaultperms", "u", 0); |
| 516 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| @@ -849,25 +876,30 @@ | |
| 849 | } |
| 850 | } |
| 851 | } |
| 852 | @ <br /><input type="submit" name="submit" value="Apply Changes" /> |
| 853 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 854 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 855 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 856 | int hasVersionableValue = pSet->versionable && |
| 857 | (db_get_versioned(pSet->name, NULL)!=0); |
| 858 | entry_attribute("", /*pSet->width*/ 25, pSet->name, |
| 859 | pSet->var!=0 ? pSet->var : pSet->name, |
| 860 | (char*)pSet->def, hasVersionableValue); |
| 861 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 862 | if( pSet->versionable ){ |
| 863 | @ (v)<br /> |
| 864 | } else { |
| 865 | @ <br /> |
| 866 | } |
| 867 | } |
| 868 | } |
| 869 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 870 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 871 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 872 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 873 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 874 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -500,17 +500,44 @@ | |
| 500 | @ (Property: "public-pages") |
| 501 | @ </p> |
| 502 | |
| 503 | @ <hr /> |
| 504 | onoff_attribute("Allow users to register themselves", |
| 505 | "self-register", "selfreg", 0, 0); |
| 506 | @ <p>Allow users to register themselves on the /register webpage. |
| 507 | @ A self-registration creates a new entry in the USER table and |
| 508 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 509 | @ enabled. |
| 510 | @ (Property: "self-register")</p> |
| 511 | |
| 512 | @ <hr /> |
| 513 | onoff_attribute("Email verification required for self-registration", |
| 514 | "selfreg-verify", "sfverify", 0, 0); |
| 515 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 516 | @ with only capabilities "7". The default user capabilities are not |
| 517 | @ added until the email address associated with the self-registration |
| 518 | @ has been verified. This setting only makes sense if |
| 519 | @ email notifications are enabled. |
| 520 | @ (Property: "selfreg-verify")</p> |
| 521 | |
| 522 | @ <hr /> |
| 523 | onoff_attribute("Allow anonymous subscriptions", |
| 524 | "anon-subscribe", "anonsub", 1, 0); |
| 525 | @ <p>If disabled, email notification subscriptions are only allowed |
| 526 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 527 | @ page, they are redirected to /register or /login. |
| 528 | @ (Property: "anon-subscribe")</p> |
| 529 | |
| 530 | @ <hr /> |
| 531 | entry_attribute("Authorized subscription email addresses", 35, |
| 532 | "auth-sub-email", "asemail", "", 0); |
| 533 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 534 | @ email addresses that are authorized to subscriptions. If blank |
| 535 | @ (the usual case), then any email address can be used to self-register. |
| 536 | @ This setting is used to limit subscriptions to members of a particular |
| 537 | @ organization or group based on their email address. |
| 538 | @ (Property: "auth-sub-email")</p> |
| 539 | |
| 540 | @ <hr /> |
| 541 | entry_attribute("Default privileges", 10, "default-perms", |
| 542 | "defaultperms", "u", 0); |
| 543 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| @@ -849,25 +876,30 @@ | |
| 876 | } |
| 877 | } |
| 878 | } |
| 879 | @ <br /><input type="submit" name="submit" value="Apply Changes" /> |
| 880 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 881 | @ <table> |
| 882 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 883 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 884 | int hasVersionableValue = pSet->versionable && |
| 885 | (db_get_versioned(pSet->name, NULL)!=0); |
| 886 | @ <tr><td> |
| 887 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 888 | if( pSet->versionable ){ |
| 889 | @ (v) |
| 890 | } else { |
| 891 | @ |
| 892 | } |
| 893 | @</td><td> |
| 894 | entry_attribute("", /*pSet->width*/ 25, pSet->name, |
| 895 | pSet->var!=0 ? pSet->var : pSet->name, |
| 896 | (char*)pSet->def, hasVersionableValue); |
| 897 | @</td></tr> |
| 898 | } |
| 899 | } |
| 900 | @</table> |
| 901 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 902 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 903 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 904 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 905 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 906 |
+5
-6
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -549,18 +549,17 @@ | ||
| 549 | 549 | if( login_is_special(zLogin) ){ |
| 550 | 550 | @ <td><b>%h(zLogin)</b></td> |
| 551 | 551 | }else{ |
| 552 | 552 | @ <td><input type="text" name="login" value="%h(zLogin)" />\ |
| 553 | 553 | if( alert_tables_exist() ){ |
| 554 | - char *zSCode; /* Subscriber Code */ | |
| 555 | - zSCode = db_text(0, "SELECT hex(subscriberCode) FROM subscriber" | |
| 556 | - " WHERE suname=%Q", zLogin); | |
| 557 | - if( zSCode && zSCode[0] ){ | |
| 558 | - @ <a href="%R/alerts/%s(zSCode)">\ | |
| 554 | + int sid; | |
| 555 | + sid = db_int(0, "SELECT subscriberId FROM subscriber" | |
| 556 | + " WHERE suname=%Q", zLogin); | |
| 557 | + if( sid>0 ){ | |
| 558 | + @ <a href="%R/alerts?sid=%d(sid)">\ | |
| 559 | 559 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | 560 | } |
| 561 | - fossil_free(zSCode); | |
| 562 | 561 | } |
| 563 | 562 | @ </td></tr> |
| 564 | 563 | @ <tr> |
| 565 | 564 | @ <td class="usetupEditLabel">Contact Info:</td> |
| 566 | 565 | @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td> |
| 567 | 566 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -549,18 +549,17 @@ | |
| 549 | if( login_is_special(zLogin) ){ |
| 550 | @ <td><b>%h(zLogin)</b></td> |
| 551 | }else{ |
| 552 | @ <td><input type="text" name="login" value="%h(zLogin)" />\ |
| 553 | if( alert_tables_exist() ){ |
| 554 | char *zSCode; /* Subscriber Code */ |
| 555 | zSCode = db_text(0, "SELECT hex(subscriberCode) FROM subscriber" |
| 556 | " WHERE suname=%Q", zLogin); |
| 557 | if( zSCode && zSCode[0] ){ |
| 558 | @ <a href="%R/alerts/%s(zSCode)">\ |
| 559 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | } |
| 561 | fossil_free(zSCode); |
| 562 | } |
| 563 | @ </td></tr> |
| 564 | @ <tr> |
| 565 | @ <td class="usetupEditLabel">Contact Info:</td> |
| 566 | @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td> |
| 567 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -549,18 +549,17 @@ | |
| 549 | if( login_is_special(zLogin) ){ |
| 550 | @ <td><b>%h(zLogin)</b></td> |
| 551 | }else{ |
| 552 | @ <td><input type="text" name="login" value="%h(zLogin)" />\ |
| 553 | if( alert_tables_exist() ){ |
| 554 | int sid; |
| 555 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 556 | " WHERE suname=%Q", zLogin); |
| 557 | if( sid>0 ){ |
| 558 | @ <a href="%R/alerts?sid=%d(sid)">\ |
| 559 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | } |
| 561 | } |
| 562 | @ </td></tr> |
| 563 | @ <tr> |
| 564 | @ <td class="usetupEditLabel">Contact Info:</td> |
| 565 | @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td> |
| 566 |
+280
-102
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -33,10 +33,18 @@ | ||
| 33 | 33 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 34 | 34 | /* This needs to come before any includes for MSVC compiler */ |
| 35 | 35 | #define _CRT_SECURE_NO_WARNINGS |
| 36 | 36 | #endif |
| 37 | 37 | |
| 38 | +/* | |
| 39 | +** Determine if we are dealing with WinRT, which provides only a subset of | |
| 40 | +** the full Win32 API. | |
| 41 | +*/ | |
| 42 | +#if !defined(SQLITE_OS_WINRT) | |
| 43 | +# define SQLITE_OS_WINRT 0 | |
| 44 | +#endif | |
| 45 | + | |
| 38 | 46 | /* |
| 39 | 47 | ** Warning pragmas copied from msvc.h in the core. |
| 40 | 48 | */ |
| 41 | 49 | #if defined(_MSC_VER) |
| 42 | 50 | #pragma warning(disable : 4054) |
| @@ -145,26 +153,30 @@ | ||
| 145 | 153 | # define SHELL_USE_LOCAL_GETLINE 1 |
| 146 | 154 | #endif |
| 147 | 155 | |
| 148 | 156 | |
| 149 | 157 | #if defined(_WIN32) || defined(WIN32) |
| 150 | -# include <io.h> | |
| 151 | -# include <fcntl.h> | |
| 152 | -# define isatty(h) _isatty(h) | |
| 153 | -# ifndef access | |
| 154 | -# define access(f,m) _access((f),(m)) | |
| 155 | -# endif | |
| 156 | -# ifndef unlink | |
| 157 | -# define unlink _unlink | |
| 158 | -# endif | |
| 159 | -# ifndef strdup | |
| 160 | -# define strdup _strdup | |
| 161 | -# endif | |
| 162 | -# undef popen | |
| 163 | -# define popen _popen | |
| 164 | -# undef pclose | |
| 165 | -# define pclose _pclose | |
| 158 | +# if SQLITE_OS_WINRT | |
| 159 | +# define SQLITE_OMIT_POPEN 1 | |
| 160 | +# else | |
| 161 | +# include <io.h> | |
| 162 | +# include <fcntl.h> | |
| 163 | +# define isatty(h) _isatty(h) | |
| 164 | +# ifndef access | |
| 165 | +# define access(f,m) _access((f),(m)) | |
| 166 | +# endif | |
| 167 | +# ifndef unlink | |
| 168 | +# define unlink _unlink | |
| 169 | +# endif | |
| 170 | +# ifndef strdup | |
| 171 | +# define strdup _strdup | |
| 172 | +# endif | |
| 173 | +# undef popen | |
| 174 | +# define popen _popen | |
| 175 | +# undef pclose | |
| 176 | +# define pclose _pclose | |
| 177 | +# endif | |
| 166 | 178 | #else |
| 167 | 179 | /* Make sure isatty() has a prototype. */ |
| 168 | 180 | extern int isatty(int); |
| 169 | 181 | |
| 170 | 182 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) |
| @@ -189,10 +201,13 @@ | ||
| 189 | 201 | #define IsSpace(X) isspace((unsigned char)X) |
| 190 | 202 | #define IsDigit(X) isdigit((unsigned char)X) |
| 191 | 203 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 192 | 204 | |
| 193 | 205 | #if defined(_WIN32) || defined(WIN32) |
| 206 | +#if SQLITE_OS_WINRT | |
| 207 | +#include <intrin.h> | |
| 208 | +#endif | |
| 194 | 209 | #include <windows.h> |
| 195 | 210 | |
| 196 | 211 | /* string conversion routines only needed on Win32 */ |
| 197 | 212 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 198 | 213 | extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); |
| @@ -204,11 +219,11 @@ | ||
| 204 | 219 | ** are automatically translated into \r\n. However, this behavior needs |
| 205 | 220 | ** to be disabled in some cases (ex: when generating CSV output and when |
| 206 | 221 | ** rendering quoted strings that contain \n characters). The following |
| 207 | 222 | ** routines take care of that. |
| 208 | 223 | */ |
| 209 | -#if defined(_WIN32) || defined(WIN32) | |
| 224 | +#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT | |
| 210 | 225 | static void setBinaryMode(FILE *file, int isOutput){ |
| 211 | 226 | if( isOutput ) fflush(file); |
| 212 | 227 | _setmode(_fileno(file), _O_BINARY); |
| 213 | 228 | } |
| 214 | 229 | static void setTextMode(FILE *file, int isOutput){ |
| @@ -308,10 +323,11 @@ | ||
| 308 | 323 | */ |
| 309 | 324 | static int hasTimer(void){ |
| 310 | 325 | if( getProcessTimesAddr ){ |
| 311 | 326 | return 1; |
| 312 | 327 | } else { |
| 328 | +#if !SQLITE_OS_WINRT | |
| 313 | 329 | /* GetProcessTimes() isn't supported in WIN95 and some other Windows |
| 314 | 330 | ** versions. See if the version we are running on has it, and if it |
| 315 | 331 | ** does, save off a pointer to it and the current process handle. |
| 316 | 332 | */ |
| 317 | 333 | hProcess = GetCurrentProcess(); |
| @@ -324,10 +340,11 @@ | ||
| 324 | 340 | return 1; |
| 325 | 341 | } |
| 326 | 342 | FreeLibrary(hinstLib); |
| 327 | 343 | } |
| 328 | 344 | } |
| 345 | +#endif | |
| 329 | 346 | } |
| 330 | 347 | return 0; |
| 331 | 348 | } |
| 332 | 349 | |
| 333 | 350 | /* |
| @@ -2476,10 +2493,11 @@ | ||
| 2476 | 2493 | } |
| 2477 | 2494 | } |
| 2478 | 2495 | |
| 2479 | 2496 | if( mtime>=0 ){ |
| 2480 | 2497 | #if defined(_WIN32) |
| 2498 | +#if !SQLITE_OS_WINRT | |
| 2481 | 2499 | /* Windows */ |
| 2482 | 2500 | FILETIME lastAccess; |
| 2483 | 2501 | FILETIME lastWrite; |
| 2484 | 2502 | SYSTEMTIME currentTime; |
| 2485 | 2503 | LONGLONG intervals; |
| @@ -2506,10 +2524,11 @@ | ||
| 2506 | 2524 | CloseHandle(hFile); |
| 2507 | 2525 | return !bResult; |
| 2508 | 2526 | }else{ |
| 2509 | 2527 | return 1; |
| 2510 | 2528 | } |
| 2529 | +#endif | |
| 2511 | 2530 | #elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */ |
| 2512 | 2531 | /* Recent unix */ |
| 2513 | 2532 | struct timespec times[2]; |
| 2514 | 2533 | times[0].tv_nsec = times[1].tv_nsec = 0; |
| 2515 | 2534 | times[0].tv_sec = time(0); |
| @@ -4263,10 +4282,105 @@ | ||
| 4263 | 4282 | memtraceOut = 0; |
| 4264 | 4283 | return rc; |
| 4265 | 4284 | } |
| 4266 | 4285 | |
| 4267 | 4286 | /************************* End ../ext/misc/memtrace.c ********************/ |
| 4287 | +/************************* Begin ../ext/misc/uint.c ******************/ | |
| 4288 | +/* | |
| 4289 | +** 2020-04-14 | |
| 4290 | +** | |
| 4291 | +** The author disclaims copyright to this source code. In place of | |
| 4292 | +** a legal notice, here is a blessing: | |
| 4293 | +** | |
| 4294 | +** May you do good and not evil. | |
| 4295 | +** May you find forgiveness for yourself and forgive others. | |
| 4296 | +** May you share freely, never taking more than you give. | |
| 4297 | +** | |
| 4298 | +****************************************************************************** | |
| 4299 | +** | |
| 4300 | +** This SQLite extension implements the UINT collating sequence. | |
| 4301 | +** | |
| 4302 | +** UINT works like BINARY for text, except that embedded strings | |
| 4303 | +** of digits compare in numeric order. | |
| 4304 | +** | |
| 4305 | +** * Leading zeros are handled properly, in the sense that | |
| 4306 | +** they do not mess of the maginitude comparison of embedded | |
| 4307 | +** strings of digits. "x00123y" is equal to "x123y". | |
| 4308 | +** | |
| 4309 | +** * Only unsigned integers are recognized. Plus and minus | |
| 4310 | +** signs are ignored. Decimal points and exponential notation | |
| 4311 | +** are ignored. | |
| 4312 | +** | |
| 4313 | +** * Embedded integers can be of arbitrary length. Comparison | |
| 4314 | +** is *not* limited integers that can be expressed as a | |
| 4315 | +** 64-bit machine integer. | |
| 4316 | +*/ | |
| 4317 | +/* #include "sqlite3ext.h" */ | |
| 4318 | +SQLITE_EXTENSION_INIT1 | |
| 4319 | +#include <assert.h> | |
| 4320 | +#include <string.h> | |
| 4321 | +#include <ctype.h> | |
| 4322 | + | |
| 4323 | +/* | |
| 4324 | +** Compare text in lexicographic order, except strings of digits | |
| 4325 | +** compare in numeric order. | |
| 4326 | +*/ | |
| 4327 | +static int uintCollFunc( | |
| 4328 | + void *notUsed, | |
| 4329 | + int nKey1, const void *pKey1, | |
| 4330 | + int nKey2, const void *pKey2 | |
| 4331 | +){ | |
| 4332 | + const unsigned char *zA = (const unsigned char*)pKey1; | |
| 4333 | + const unsigned char *zB = (const unsigned char*)pKey2; | |
| 4334 | + int i=0, j=0, x; | |
| 4335 | + (void)notUsed; | |
| 4336 | + while( i<nKey1 && j<nKey2 ){ | |
| 4337 | + x = zA[i] - zB[j]; | |
| 4338 | + if( isdigit(zA[i]) ){ | |
| 4339 | + int k; | |
| 4340 | + if( !isdigit(zB[j]) ) return x; | |
| 4341 | + while( i<nKey1 && zA[i]=='0' ){ i++; } | |
| 4342 | + while( j<nKey2 && zB[j]=='0' ){ j++; } | |
| 4343 | + k = 0; | |
| 4344 | + while( i+k<nKey1 && isdigit(zA[i+k]) | |
| 4345 | + && j+k<nKey2 && isdigit(zB[j+k]) ){ | |
| 4346 | + k++; | |
| 4347 | + } | |
| 4348 | + if( i+k<nKey1 && isdigit(zA[i+k]) ){ | |
| 4349 | + return +1; | |
| 4350 | + }else if( j+k<nKey2 && isdigit(zB[j+k]) ){ | |
| 4351 | + return -1; | |
| 4352 | + }else{ | |
| 4353 | + x = memcmp(zA+i, zB+j, k); | |
| 4354 | + if( x ) return x; | |
| 4355 | + i += k; | |
| 4356 | + j += k; | |
| 4357 | + } | |
| 4358 | + }else if( x ){ | |
| 4359 | + return x; | |
| 4360 | + }else{ | |
| 4361 | + i++; | |
| 4362 | + j++; | |
| 4363 | + } | |
| 4364 | + } | |
| 4365 | + return (nKey1 - i) - (nKey2 - j); | |
| 4366 | +} | |
| 4367 | + | |
| 4368 | +#ifdef _WIN32 | |
| 4369 | + | |
| 4370 | +#endif | |
| 4371 | +int sqlite3_uint_init( | |
| 4372 | + sqlite3 *db, | |
| 4373 | + char **pzErrMsg, | |
| 4374 | + const sqlite3_api_routines *pApi | |
| 4375 | +){ | |
| 4376 | + SQLITE_EXTENSION_INIT2(pApi); | |
| 4377 | + (void)pzErrMsg; /* Unused parameter */ | |
| 4378 | + return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc); | |
| 4379 | +} | |
| 4380 | + | |
| 4381 | +/************************* End ../ext/misc/uint.c ********************/ | |
| 4268 | 4382 | #ifdef SQLITE_HAVE_ZLIB |
| 4269 | 4383 | /************************* Begin ../ext/misc/zipfile.c ******************/ |
| 4270 | 4384 | /* |
| 4271 | 4385 | ** 2017-12-26 |
| 4272 | 4386 | ** |
| @@ -9652,10 +9766,11 @@ | ||
| 9652 | 9766 | int nCheck; /* Number of ".check" commands run */ |
| 9653 | 9767 | unsigned nProgress; /* Number of progress callbacks encountered */ |
| 9654 | 9768 | unsigned mxProgress; /* Maximum progress callbacks before failing */ |
| 9655 | 9769 | unsigned flgProgress; /* Flags for the progress callback */ |
| 9656 | 9770 | unsigned shellFlgs; /* Various flags */ |
| 9771 | + unsigned priorShFlgs; /* Saved copy of flags */ | |
| 9657 | 9772 | sqlite3_int64 szMax; /* --maxsize argument to .open */ |
| 9658 | 9773 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 9659 | 9774 | char *zTempFile; /* Temporary file that might need deleting */ |
| 9660 | 9775 | char zTestcase[30]; /* Name of current test case */ |
| 9661 | 9776 | char colSeparator[20]; /* Column separator character for several modes */ |
| @@ -9952,15 +10067,17 @@ | ||
| 9952 | 10067 | /* |
| 9953 | 10068 | ** Save or restore the current output mode |
| 9954 | 10069 | */ |
| 9955 | 10070 | static void outputModePush(ShellState *p){ |
| 9956 | 10071 | p->modePrior = p->mode; |
| 10072 | + p->priorShFlgs = p->shellFlgs; | |
| 9957 | 10073 | memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator)); |
| 9958 | 10074 | memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator)); |
| 9959 | 10075 | } |
| 9960 | 10076 | static void outputModePop(ShellState *p){ |
| 9961 | 10077 | p->mode = p->modePrior; |
| 10078 | + p->shellFlgs = p->priorShFlgs; | |
| 9962 | 10079 | memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 9963 | 10080 | memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 9964 | 10081 | } |
| 9965 | 10082 | |
| 9966 | 10083 | /* |
| @@ -10921,12 +11038,11 @@ | ||
| 10921 | 11038 | ** "--" comment occurs at the end of the statement, the comment |
| 10922 | 11039 | ** won't consume the semicolon terminator. |
| 10923 | 11040 | */ |
| 10924 | 11041 | static int run_table_dump_query( |
| 10925 | 11042 | ShellState *p, /* Query context */ |
| 10926 | - const char *zSelect, /* SELECT statement to extract content */ | |
| 10927 | - const char *zFirstRow /* Print before first row, if not NULL */ | |
| 11043 | + const char *zSelect /* SELECT statement to extract content */ | |
| 10928 | 11044 | ){ |
| 10929 | 11045 | sqlite3_stmt *pSelect; |
| 10930 | 11046 | int rc; |
| 10931 | 11047 | int nResult; |
| 10932 | 11048 | int i; |
| @@ -10939,14 +11055,10 @@ | ||
| 10939 | 11055 | return rc; |
| 10940 | 11056 | } |
| 10941 | 11057 | rc = sqlite3_step(pSelect); |
| 10942 | 11058 | nResult = sqlite3_column_count(pSelect); |
| 10943 | 11059 | while( rc==SQLITE_ROW ){ |
| 10944 | - if( zFirstRow ){ | |
| 10945 | - utf8_printf(p->out, "%s", zFirstRow); | |
| 10946 | - zFirstRow = 0; | |
| 10947 | - } | |
| 10948 | 11060 | z = (const char*)sqlite3_column_text(pSelect, 0); |
| 10949 | 11061 | utf8_printf(p->out, "%s", z); |
| 10950 | 11062 | for(i=1; i<nResult; i++){ |
| 10951 | 11063 | utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); |
| 10952 | 11064 | } |
| @@ -11399,13 +11511,13 @@ | ||
| 11399 | 11511 | ** Parameter bindings are taken from a TEMP table of the form: |
| 11400 | 11512 | ** |
| 11401 | 11513 | ** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value) |
| 11402 | 11514 | ** WITHOUT ROWID; |
| 11403 | 11515 | ** |
| 11404 | -** No bindings occur if this table does not exist. The special character '$' | |
| 11405 | -** is included in the table name to help prevent collisions with actual tables. | |
| 11406 | -** The table must be in the TEMP schema. | |
| 11516 | +** No bindings occur if this table does not exist. The name of the table | |
| 11517 | +** begins with "sqlite_" so that it will not collide with ordinary application | |
| 11518 | +** tables. The table must be in the TEMP schema. | |
| 11407 | 11519 | */ |
| 11408 | 11520 | static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ |
| 11409 | 11521 | int nVar; |
| 11410 | 11522 | int i; |
| 11411 | 11523 | int rc; |
| @@ -11705,10 +11817,11 @@ | ||
| 11705 | 11817 | if( rc==SQLITE_OK ){ |
| 11706 | 11818 | while( sqlite3_step(pExplain)==SQLITE_ROW ){ |
| 11707 | 11819 | const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); |
| 11708 | 11820 | int iEqpId = sqlite3_column_int(pExplain, 0); |
| 11709 | 11821 | int iParentId = sqlite3_column_int(pExplain, 1); |
| 11822 | + if( zEQPLine==0 ) zEQPLine = ""; | |
| 11710 | 11823 | if( zEQPLine[0]=='-' ) eqp_render(pArg); |
| 11711 | 11824 | eqp_append(pArg, iEqpId, iParentId, zEQPLine); |
| 11712 | 11825 | } |
| 11713 | 11826 | eqp_render(pArg); |
| 11714 | 11827 | } |
| @@ -12115,29 +12228,32 @@ | ||
| 12115 | 12228 | ".check GLOB Fail if output since .testcase does not match", |
| 12116 | 12229 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 12117 | 12230 | ".databases List names and files of attached databases", |
| 12118 | 12231 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 12119 | 12232 | ".dbinfo ?DB? Show status information about the database", |
| 12120 | - ".dump ?TABLE? ... Render all database content as SQL", | |
| 12233 | + ".dump ?TABLE? Render database content as SQL", | |
| 12121 | 12234 | " Options:", |
| 12122 | 12235 | " --preserve-rowids Include ROWID values in the output", |
| 12123 | 12236 | " --newlines Allow unescaped newline characters in output", |
| 12124 | 12237 | " TABLE is a LIKE pattern for the tables to dump", |
| 12238 | + " Additional LIKE patterns can be given in subsequent arguments", | |
| 12125 | 12239 | ".echo on|off Turn command echo on or off", |
| 12126 | 12240 | ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", |
| 12127 | 12241 | " Other Modes:", |
| 12128 | 12242 | #ifdef SQLITE_DEBUG |
| 12129 | 12243 | " test Show raw EXPLAIN QUERY PLAN output", |
| 12130 | 12244 | " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"", |
| 12131 | 12245 | #endif |
| 12132 | 12246 | " trigger Like \"full\" but also show trigger bytecode", |
| 12133 | 12247 | ".excel Display the output of next command in spreadsheet", |
| 12248 | + " --bom Put a UTF8 byte-order mark on intermediate file", | |
| 12134 | 12249 | ".exit ?CODE? Exit this program with return-code CODE", |
| 12135 | 12250 | ".expert EXPERIMENTAL. Suggest indexes for queries", |
| 12136 | 12251 | ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", |
| 12137 | 12252 | ".filectrl CMD ... Run various sqlite3_file_control() operations", |
| 12138 | - " Run \".filectrl\" with no arguments for details", | |
| 12253 | + " --schema SCHEMA Use SCHEMA instead of \"main\"", | |
| 12254 | + " --help Show CMD details", | |
| 12139 | 12255 | ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", |
| 12140 | 12256 | ".headers on|off Turn display of headers on or off", |
| 12141 | 12257 | ".help ?-all? ?PATTERN? Show help text for PATTERN", |
| 12142 | 12258 | ".import FILE TABLE Import data from FILE into TABLE", |
| 12143 | 12259 | " Options:", |
| @@ -12180,15 +12296,15 @@ | ||
| 12180 | 12296 | " list Values delimited by \"|\"", |
| 12181 | 12297 | " quote Escape answers as for SQL", |
| 12182 | 12298 | " tabs Tab-separated values", |
| 12183 | 12299 | " tcl TCL list elements", |
| 12184 | 12300 | ".nullvalue STRING Use STRING in place of NULL values", |
| 12185 | - ".once (-e|-x|FILE) Output for the next SQL command only to FILE", | |
| 12301 | + ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", | |
| 12186 | 12302 | " If FILE begins with '|' then open as a pipe", |
| 12187 | - " Other options:", | |
| 12188 | - " -e Invoke system text editor", | |
| 12189 | - " -x Open in a spreadsheet", | |
| 12303 | + " --bom Put a UTF8 byte-order mark at the beginning", | |
| 12304 | + " -e Send output to the system text editor", | |
| 12305 | + " -x Send output as CSV to a spreadsheet (same as \".excel\")", | |
| 12190 | 12306 | #ifdef SQLITE_DEBUG |
| 12191 | 12307 | ".oom [--repeat M] [N] Simulate an OOM error on the N-th allocation", |
| 12192 | 12308 | #endif |
| 12193 | 12309 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", |
| 12194 | 12310 | " Options:", |
| @@ -12201,11 +12317,15 @@ | ||
| 12201 | 12317 | " --new Initialize FILE to an empty database", |
| 12202 | 12318 | " --nofollow Do not follow symbolic links", |
| 12203 | 12319 | " --readonly Open FILE readonly", |
| 12204 | 12320 | " --zip FILE is a ZIP archive", |
| 12205 | 12321 | ".output ?FILE? Send output to FILE or stdout if FILE is omitted", |
| 12206 | - " If FILE begins with '|' then open it as a pipe.", | |
| 12322 | + " If FILE begins with '|' then open it as a pipe.", | |
| 12323 | + " Options:", | |
| 12324 | + " --bom Prefix output with a UTF8 byte-order mark", | |
| 12325 | + " -e Send output to the system text editor", | |
| 12326 | + " -x Send output as CSV to a spreadsheet", | |
| 12207 | 12327 | ".parameter CMD ... Manage SQL parameter bindings", |
| 12208 | 12328 | " clear Erase all bindings", |
| 12209 | 12329 | " init Initialize the TEMP table that holds bindings", |
| 12210 | 12330 | " list List the current parameter bindings", |
| 12211 | 12331 | " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", |
| @@ -12321,10 +12441,11 @@ | ||
| 12321 | 12441 | char *zPat; |
| 12322 | 12442 | if( zPattern==0 |
| 12323 | 12443 | || zPattern[0]=='0' |
| 12324 | 12444 | || strcmp(zPattern,"-a")==0 |
| 12325 | 12445 | || strcmp(zPattern,"-all")==0 |
| 12446 | + || strcmp(zPattern,"--all")==0 | |
| 12326 | 12447 | ){ |
| 12327 | 12448 | /* Show all commands, but only one line per command */ |
| 12328 | 12449 | if( zPattern==0 ) zPattern = ""; |
| 12329 | 12450 | for(i=0; i<ArraySize(azHelp); i++){ |
| 12330 | 12451 | if( azHelp[i][0]=='.' || zPattern[0] ){ |
| @@ -12802,10 +12923,11 @@ | ||
| 12802 | 12923 | sqlite3_enable_load_extension(p->db, 1); |
| 12803 | 12924 | #endif |
| 12804 | 12925 | sqlite3_fileio_init(p->db, 0, 0); |
| 12805 | 12926 | sqlite3_shathree_init(p->db, 0, 0); |
| 12806 | 12927 | sqlite3_completion_init(p->db, 0, 0); |
| 12928 | + sqlite3_uint_init(p->db, 0, 0); | |
| 12807 | 12929 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 12808 | 12930 | sqlite3_dbdata_init(p->db, 0, 0); |
| 12809 | 12931 | #endif |
| 12810 | 12932 | #ifdef SQLITE_HAVE_ZLIB |
| 12811 | 12933 | sqlite3_zipfile_init(p->db, 0, 0); |
| @@ -13521,15 +13643,19 @@ | ||
| 13521 | 13643 | #endif |
| 13522 | 13644 | char *zCmd; |
| 13523 | 13645 | zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 13524 | 13646 | if( system(zCmd) ){ |
| 13525 | 13647 | utf8_printf(stderr, "Failed: [%s]\n", zCmd); |
| 13648 | + }else{ | |
| 13649 | + /* Give the start/open/xdg-open command some time to get | |
| 13650 | + ** going before we continue, and potential delete the | |
| 13651 | + ** p->zTempFile data file out from under it */ | |
| 13652 | + sqlite3_sleep(2000); | |
| 13526 | 13653 | } |
| 13527 | 13654 | sqlite3_free(zCmd); |
| 13528 | 13655 | outputModePop(p); |
| 13529 | 13656 | p->doXdgOpen = 0; |
| 13530 | - sqlite3_sleep(100); | |
| 13531 | 13657 | } |
| 13532 | 13658 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ |
| 13533 | 13659 | } |
| 13534 | 13660 | p->outfile[0] = 0; |
| 13535 | 13661 | p->out = stdout; |
| @@ -13601,16 +13727,11 @@ | ||
| 13601 | 13727 | if( p->db==0 ) return 1; |
| 13602 | 13728 | rc = sqlite3_prepare_v2(p->db, |
| 13603 | 13729 | "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
| 13604 | 13730 | -1, &pStmt, 0); |
| 13605 | 13731 | if( rc ){ |
| 13606 | - if( !sqlite3_compileoption_used("ENABLE_DBPAGE_VTAB") ){ | |
| 13607 | - utf8_printf(stderr, "the \".dbinfo\" command requires the " | |
| 13608 | - "-DSQLITE_ENABLE_DBPAGE_VTAB compile-time options\n"); | |
| 13609 | - }else{ | |
| 13610 | - utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); | |
| 13611 | - } | |
| 13732 | + utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); | |
| 13612 | 13733 | sqlite3_finalize(pStmt); |
| 13613 | 13734 | return 1; |
| 13614 | 13735 | } |
| 13615 | 13736 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 13616 | 13737 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| @@ -13815,13 +13936,25 @@ | ||
| 13815 | 13936 | p->zTempFile = 0; |
| 13816 | 13937 | if( p->db ){ |
| 13817 | 13938 | sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); |
| 13818 | 13939 | } |
| 13819 | 13940 | if( p->zTempFile==0 ){ |
| 13941 | + /* If p->db is an in-memory database then the TEMPFILENAME file-control | |
| 13942 | + ** will not work and we will need to fallback to guessing */ | |
| 13943 | + char *zTemp; | |
| 13820 | 13944 | sqlite3_uint64 r; |
| 13821 | 13945 | sqlite3_randomness(sizeof(r), &r); |
| 13822 | - p->zTempFile = sqlite3_mprintf("temp%llx.%s", r, zSuffix); | |
| 13946 | + zTemp = getenv("TEMP"); | |
| 13947 | + if( zTemp==0 ) zTemp = getenv("TMP"); | |
| 13948 | + if( zTemp==0 ){ | |
| 13949 | +#ifdef _WIN32 | |
| 13950 | + zTemp = "\\tmp"; | |
| 13951 | +#else | |
| 13952 | + zTemp = "/tmp"; | |
| 13953 | +#endif | |
| 13954 | + } | |
| 13955 | + p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix); | |
| 13823 | 13956 | }else{ |
| 13824 | 13957 | p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); |
| 13825 | 13958 | } |
| 13826 | 13959 | if( p->zTempFile==0 ){ |
| 13827 | 13960 | raw_printf(stderr, "out of memory\n"); |
| @@ -15841,11 +15974,12 @@ | ||
| 15841 | 15974 | rc = recoverDatabaseCmd(p, nArg, azArg); |
| 15842 | 15975 | }else |
| 15843 | 15976 | #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ |
| 15844 | 15977 | |
| 15845 | 15978 | if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ |
| 15846 | - const char *zLike = 0; | |
| 15979 | + char *zLike = 0; | |
| 15980 | + char *zSql; | |
| 15847 | 15981 | int i; |
| 15848 | 15982 | int savedShowHeader = p->showHeader; |
| 15849 | 15983 | int savedShellFlags = p->shellFlgs; |
| 15850 | 15984 | ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo); |
| 15851 | 15985 | for(i=1; i<nArg; i++){ |
| @@ -15869,16 +16003,14 @@ | ||
| 15869 | 16003 | raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); |
| 15870 | 16004 | rc = 1; |
| 15871 | 16005 | goto meta_command_exit; |
| 15872 | 16006 | } |
| 15873 | 16007 | }else if( zLike ){ |
| 15874 | - raw_printf(stderr, "Usage: .dump ?--preserve-rowids? " | |
| 15875 | - "?--newlines? ?LIKE-PATTERN?\n"); | |
| 15876 | - rc = 1; | |
| 15877 | - goto meta_command_exit; | |
| 16008 | + zLike = sqlite3_mprintf("%z OR name LIKE %Q ESCAPE '\\'", | |
| 16009 | + zLike, azArg[i]); | |
| 15878 | 16010 | }else{ |
| 15879 | - zLike = azArg[i]; | |
| 16011 | + zLike = sqlite3_mprintf("name LIKE %Q ESCAPE '\\'", azArg[i]); | |
| 15880 | 16012 | } |
| 15881 | 16013 | } |
| 15882 | 16014 | |
| 15883 | 16015 | open_db(p, 0); |
| 15884 | 16016 | |
| @@ -15892,39 +16024,29 @@ | ||
| 15892 | 16024 | /* Set writable_schema=ON since doing so forces SQLite to initialize |
| 15893 | 16025 | ** as much of the schema as it can even if the sqlite_master table is |
| 15894 | 16026 | ** corrupt. */ |
| 15895 | 16027 | sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); |
| 15896 | 16028 | p->nErr = 0; |
| 15897 | - if( zLike==0 ){ | |
| 15898 | - run_schema_dump_query(p, | |
| 15899 | - "SELECT name, type, sql FROM sqlite_master " | |
| 15900 | - "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" | |
| 15901 | - ); | |
| 15902 | - run_schema_dump_query(p, | |
| 15903 | - "SELECT name, type, sql FROM sqlite_master " | |
| 15904 | - "WHERE name=='sqlite_sequence'" | |
| 15905 | - ); | |
| 15906 | - run_table_dump_query(p, | |
| 15907 | - "SELECT sql FROM sqlite_master " | |
| 15908 | - "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 | |
| 15909 | - ); | |
| 15910 | - }else{ | |
| 15911 | - char *zSql; | |
| 15912 | - zSql = sqlite3_mprintf( | |
| 15913 | - "SELECT name, type, sql FROM sqlite_master " | |
| 15914 | - "WHERE tbl_name LIKE %Q AND type=='table'" | |
| 15915 | - " AND sql NOT NULL", zLike); | |
| 15916 | - run_schema_dump_query(p,zSql); | |
| 15917 | - sqlite3_free(zSql); | |
| 15918 | - zSql = sqlite3_mprintf( | |
| 15919 | - "SELECT sql FROM sqlite_master " | |
| 15920 | - "WHERE sql NOT NULL" | |
| 15921 | - " AND type IN ('index','trigger','view')" | |
| 15922 | - " AND tbl_name LIKE %Q", zLike); | |
| 15923 | - run_table_dump_query(p, zSql, 0); | |
| 15924 | - sqlite3_free(zSql); | |
| 15925 | - } | |
| 16029 | + if( zLike==0 ) zLike = sqlite3_mprintf("true"); | |
| 16030 | + zSql = sqlite3_mprintf( | |
| 16031 | + "SELECT name, type, sql FROM sqlite_master " | |
| 16032 | + "WHERE (%s) AND type=='table'" | |
| 16033 | + " AND sql NOT NULL" | |
| 16034 | + " ORDER BY tbl_name='sqlite_sequence', rowid", | |
| 16035 | + zLike | |
| 16036 | + ); | |
| 16037 | + run_schema_dump_query(p,zSql); | |
| 16038 | + sqlite3_free(zSql); | |
| 16039 | + zSql = sqlite3_mprintf( | |
| 16040 | + "SELECT sql FROM sqlite_master " | |
| 16041 | + "WHERE (%s) AND sql NOT NULL" | |
| 16042 | + " AND type IN ('index','trigger','view')", | |
| 16043 | + zLike | |
| 16044 | + ); | |
| 16045 | + run_table_dump_query(p, zSql); | |
| 16046 | + sqlite3_free(zSql); | |
| 16047 | + sqlite3_free(zLike); | |
| 15926 | 16048 | if( p->writableSchema ){ |
| 15927 | 16049 | raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); |
| 15928 | 16050 | p->writableSchema = 0; |
| 15929 | 16051 | } |
| 15930 | 16052 | sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); |
| @@ -16023,20 +16145,32 @@ | ||
| 16023 | 16145 | { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" }, |
| 16024 | 16146 | /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/ |
| 16025 | 16147 | { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" }, |
| 16026 | 16148 | { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" }, |
| 16027 | 16149 | { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" }, |
| 16150 | + { "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" }, | |
| 16028 | 16151 | }; |
| 16029 | 16152 | int filectrl = -1; |
| 16030 | 16153 | int iCtrl = -1; |
| 16031 | 16154 | sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */ |
| 16032 | 16155 | int isOk = 0; /* 0: usage 1: %lld 2: no-result */ |
| 16033 | 16156 | int n2, i; |
| 16034 | 16157 | const char *zCmd = 0; |
| 16158 | + const char *zSchema = 0; | |
| 16035 | 16159 | |
| 16036 | 16160 | open_db(p, 0); |
| 16037 | 16161 | zCmd = nArg>=2 ? azArg[1] : "help"; |
| 16162 | + | |
| 16163 | + if( zCmd[0]=='-' | |
| 16164 | + && (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0) | |
| 16165 | + && nArg>=4 | |
| 16166 | + ){ | |
| 16167 | + zSchema = azArg[2]; | |
| 16168 | + for(i=3; i<nArg; i++) azArg[i-2] = azArg[i]; | |
| 16169 | + nArg -= 2; | |
| 16170 | + zCmd = azArg[1]; | |
| 16171 | + } | |
| 16038 | 16172 | |
| 16039 | 16173 | /* The argument can optionally begin with "-" or "--" */ |
| 16040 | 16174 | if( zCmd[0]=='-' && zCmd[1] ){ |
| 16041 | 16175 | zCmd++; |
| 16042 | 16176 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| @@ -16075,51 +16209,63 @@ | ||
| 16075 | 16209 | }else{ |
| 16076 | 16210 | switch(filectrl){ |
| 16077 | 16211 | case SQLITE_FCNTL_SIZE_LIMIT: { |
| 16078 | 16212 | if( nArg!=2 && nArg!=3 ) break; |
| 16079 | 16213 | iRes = nArg==3 ? integerValue(azArg[2]) : -1; |
| 16080 | - sqlite3_file_control(p->db, 0, SQLITE_FCNTL_SIZE_LIMIT, &iRes); | |
| 16214 | + sqlite3_file_control(p->db, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes); | |
| 16081 | 16215 | isOk = 1; |
| 16082 | 16216 | break; |
| 16083 | 16217 | } |
| 16084 | 16218 | case SQLITE_FCNTL_LOCK_TIMEOUT: |
| 16085 | 16219 | case SQLITE_FCNTL_CHUNK_SIZE: { |
| 16086 | 16220 | int x; |
| 16087 | 16221 | if( nArg!=3 ) break; |
| 16088 | 16222 | x = (int)integerValue(azArg[2]); |
| 16089 | - sqlite3_file_control(p->db, 0, filectrl, &x); | |
| 16223 | + sqlite3_file_control(p->db, zSchema, filectrl, &x); | |
| 16090 | 16224 | isOk = 2; |
| 16091 | 16225 | break; |
| 16092 | 16226 | } |
| 16093 | 16227 | case SQLITE_FCNTL_PERSIST_WAL: |
| 16094 | 16228 | case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { |
| 16095 | 16229 | int x; |
| 16096 | 16230 | if( nArg!=2 && nArg!=3 ) break; |
| 16097 | 16231 | x = nArg==3 ? booleanValue(azArg[2]) : -1; |
| 16098 | - sqlite3_file_control(p->db, 0, filectrl, &x); | |
| 16232 | + sqlite3_file_control(p->db, zSchema, filectrl, &x); | |
| 16099 | 16233 | iRes = x; |
| 16100 | 16234 | isOk = 1; |
| 16101 | 16235 | break; |
| 16102 | 16236 | } |
| 16103 | 16237 | case SQLITE_FCNTL_HAS_MOVED: { |
| 16104 | 16238 | int x; |
| 16105 | 16239 | if( nArg!=2 ) break; |
| 16106 | - sqlite3_file_control(p->db, 0, filectrl, &x); | |
| 16240 | + sqlite3_file_control(p->db, zSchema, filectrl, &x); | |
| 16107 | 16241 | iRes = x; |
| 16108 | 16242 | isOk = 1; |
| 16109 | 16243 | break; |
| 16110 | 16244 | } |
| 16111 | 16245 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 16112 | 16246 | char *z = 0; |
| 16113 | 16247 | if( nArg!=2 ) break; |
| 16114 | - sqlite3_file_control(p->db, 0, filectrl, &z); | |
| 16248 | + sqlite3_file_control(p->db, zSchema, filectrl, &z); | |
| 16115 | 16249 | if( z ){ |
| 16116 | 16250 | utf8_printf(p->out, "%s\n", z); |
| 16117 | 16251 | sqlite3_free(z); |
| 16118 | 16252 | } |
| 16119 | 16253 | isOk = 2; |
| 16120 | 16254 | break; |
| 16255 | + } | |
| 16256 | + case SQLITE_FCNTL_RESERVE_BYTES: { | |
| 16257 | + int x; | |
| 16258 | + if( nArg>=3 ){ | |
| 16259 | + x = atoi(azArg[2]); | |
| 16260 | + sqlite3_file_control(p->db, zSchema, filectrl, &x); | |
| 16261 | + } | |
| 16262 | + x = -1; | |
| 16263 | + sqlite3_file_control(p->db, zSchema, filectrl, &x); | |
| 16264 | + utf8_printf(p->out,"%d\n", x); | |
| 16265 | + isOk = 2; | |
| 16266 | + break; | |
| 16121 | 16267 | } |
| 16122 | 16268 | } |
| 16123 | 16269 | } |
| 16124 | 16270 | if( isOk==0 && iCtrl>=0 ){ |
| 16125 | 16271 | utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); |
| @@ -16850,46 +16996,70 @@ | ||
| 16850 | 16996 | |
| 16851 | 16997 | if( (c=='o' |
| 16852 | 16998 | && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) |
| 16853 | 16999 | || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) |
| 16854 | 17000 | ){ |
| 16855 | - const char *zFile = nArg>=2 ? azArg[1] : "stdout"; | |
| 17001 | + const char *zFile = 0; | |
| 16856 | 17002 | int bTxtMode = 0; |
| 16857 | - if( azArg[0][0]=='e' ){ | |
| 16858 | - /* Transform the ".excel" command into ".once -x" */ | |
| 16859 | - nArg = 2; | |
| 16860 | - azArg[0] = "once"; | |
| 16861 | - zFile = azArg[1] = "-x"; | |
| 16862 | - n = 4; | |
| 16863 | - } | |
| 16864 | - if( nArg>2 ){ | |
| 16865 | - utf8_printf(stderr, "Usage: .%s [-e|-x|FILE]\n", azArg[0]); | |
| 16866 | - rc = 1; | |
| 16867 | - goto meta_command_exit; | |
| 16868 | - } | |
| 16869 | - if( n>1 && strncmp(azArg[0], "once", n)==0 ){ | |
| 16870 | - if( nArg<2 ){ | |
| 16871 | - raw_printf(stderr, "Usage: .once (-e|-x|FILE)\n"); | |
| 17003 | + int i; | |
| 17004 | + int eMode = 0; | |
| 17005 | + int bBOM = 0; | |
| 17006 | + int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ | |
| 17007 | + | |
| 17008 | + if( c=='e' ){ | |
| 17009 | + eMode = 'x'; | |
| 17010 | + bOnce = 2; | |
| 17011 | + }else if( strncmp(azArg[0],"once",n)==0 ){ | |
| 17012 | + bOnce = 1; | |
| 17013 | + } | |
| 17014 | + for(i=1; i<nArg; i++){ | |
| 17015 | + char *z = azArg[i]; | |
| 17016 | + if( z[0]=='-' ){ | |
| 17017 | + if( z[1]=='-' ) z++; | |
| 17018 | + if( strcmp(z,"-bom")==0 ){ | |
| 17019 | + bBOM = 1; | |
| 17020 | + }else if( c!='e' && strcmp(z,"-x")==0 ){ | |
| 17021 | + eMode = 'x'; /* spreadsheet */ | |
| 17022 | + }else if( c!='e' && strcmp(z,"-e")==0 ){ | |
| 17023 | + eMode = 'e'; /* text editor */ | |
| 17024 | + }else{ | |
| 17025 | + utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", | |
| 17026 | + azArg[i]); | |
| 17027 | + showHelp(p->out, azArg[0]); | |
| 17028 | + rc = 1; | |
| 17029 | + goto meta_command_exit; | |
| 17030 | + } | |
| 17031 | + }else if( zFile==0 ){ | |
| 17032 | + zFile = z; | |
| 17033 | + }else{ | |
| 17034 | + utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n", | |
| 17035 | + azArg[i]); | |
| 17036 | + showHelp(p->out, azArg[0]); | |
| 16872 | 17037 | rc = 1; |
| 16873 | 17038 | goto meta_command_exit; |
| 16874 | 17039 | } |
| 17040 | + } | |
| 17041 | + if( zFile==0 ) zFile = "stdout"; | |
| 17042 | + if( bOnce ){ | |
| 16875 | 17043 | p->outCount = 2; |
| 16876 | 17044 | }else{ |
| 16877 | 17045 | p->outCount = 0; |
| 16878 | 17046 | } |
| 16879 | 17047 | output_reset(p); |
| 16880 | - if( zFile[0]=='-' && zFile[1]=='-' ) zFile++; | |
| 16881 | 17048 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 16882 | - if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){ | |
| 17049 | + if( eMode=='e' || eMode=='x' ){ | |
| 16883 | 17050 | p->doXdgOpen = 1; |
| 16884 | 17051 | outputModePush(p); |
| 16885 | - if( zFile[1]=='x' ){ | |
| 17052 | + if( eMode=='x' ){ | |
| 17053 | + /* spreadsheet mode. Output as CSV. */ | |
| 16886 | 17054 | newTempFile(p, "csv"); |
| 17055 | + ShellClearFlag(p, SHFLG_Echo); | |
| 16887 | 17056 | p->mode = MODE_Csv; |
| 16888 | 17057 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 16889 | 17058 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 16890 | 17059 | }else{ |
| 17060 | + /* text editor mode */ | |
| 16891 | 17061 | newTempFile(p, "txt"); |
| 16892 | 17062 | bTxtMode = 1; |
| 16893 | 17063 | } |
| 16894 | 17064 | zFile = p->zTempFile; |
| 16895 | 17065 | } |
| @@ -16904,10 +17074,11 @@ | ||
| 16904 | 17074 | if( p->out==0 ){ |
| 16905 | 17075 | utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); |
| 16906 | 17076 | p->out = stdout; |
| 16907 | 17077 | rc = 1; |
| 16908 | 17078 | }else{ |
| 17079 | + if( bBOM ) fprintf(p->out,"\357\273\277"); | |
| 16909 | 17080 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 16910 | 17081 | } |
| 16911 | 17082 | #endif |
| 16912 | 17083 | }else{ |
| 16913 | 17084 | p->out = output_file_open(zFile, bTxtMode); |
| @@ -16916,10 +17087,11 @@ | ||
| 16916 | 17087 | utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
| 16917 | 17088 | } |
| 16918 | 17089 | p->out = stdout; |
| 16919 | 17090 | rc = 1; |
| 16920 | 17091 | } else { |
| 17092 | + if( bBOM ) fprintf(p->out,"\357\273\277"); | |
| 16921 | 17093 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 16922 | 17094 | } |
| 16923 | 17095 | } |
| 16924 | 17096 | }else |
| 16925 | 17097 | |
| @@ -17979,11 +18151,10 @@ | ||
| 17979 | 18151 | #endif |
| 17980 | 18152 | { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " }, |
| 17981 | 18153 | { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" }, |
| 17982 | 18154 | { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" }, |
| 17983 | 18155 | { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" }, |
| 17984 | - { "reserve", SQLITE_TESTCTRL_RESERVE, "BYTES-OF-RESERVE"}, | |
| 17985 | 18156 | }; |
| 17986 | 18157 | int testctrl = -1; |
| 17987 | 18158 | int iCtrl = -1; |
| 17988 | 18159 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 17989 | 18160 | int isOk = 0; |
| @@ -18032,11 +18203,10 @@ | ||
| 18032 | 18203 | }else{ |
| 18033 | 18204 | switch(testctrl){ |
| 18034 | 18205 | |
| 18035 | 18206 | /* sqlite3_test_control(int, db, int) */ |
| 18036 | 18207 | case SQLITE_TESTCTRL_OPTIMIZATIONS: |
| 18037 | - case SQLITE_TESTCTRL_RESERVE: | |
| 18038 | 18208 | if( nArg==3 ){ |
| 18039 | 18209 | int opt = (int)strtol(azArg[2], 0, 0); |
| 18040 | 18210 | rc2 = sqlite3_test_control(testctrl, p->db, opt); |
| 18041 | 18211 | isOk = 3; |
| 18042 | 18212 | } |
| @@ -18804,18 +18974,22 @@ | ||
| 18804 | 18974 | /* |
| 18805 | 18975 | ** Output text to the console in a font that attracts extra attention. |
| 18806 | 18976 | */ |
| 18807 | 18977 | #ifdef _WIN32 |
| 18808 | 18978 | static void printBold(const char *zText){ |
| 18979 | +#if !SQLITE_OS_WINRT | |
| 18809 | 18980 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); |
| 18810 | 18981 | CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; |
| 18811 | 18982 | GetConsoleScreenBufferInfo(out, &defaultScreenInfo); |
| 18812 | 18983 | SetConsoleTextAttribute(out, |
| 18813 | 18984 | FOREGROUND_RED|FOREGROUND_INTENSITY |
| 18814 | 18985 | ); |
| 18986 | +#endif | |
| 18815 | 18987 | printf("%s", zText); |
| 18988 | +#if !SQLITE_OS_WINRT | |
| 18816 | 18989 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 18990 | +#endif | |
| 18817 | 18991 | } |
| 18818 | 18992 | #else |
| 18819 | 18993 | static void printBold(const char *zText){ |
| 18820 | 18994 | printf("\033[1m%s\033[0m", zText); |
| 18821 | 18995 | } |
| @@ -18879,11 +19053,15 @@ | ||
| 18879 | 19053 | "attach debugger to process %d and press any key to continue.\n", |
| 18880 | 19054 | GETPID()); |
| 18881 | 19055 | fgetc(stdin); |
| 18882 | 19056 | }else{ |
| 18883 | 19057 | #if defined(_WIN32) || defined(WIN32) |
| 19058 | +#if SQLITE_OS_WINRT | |
| 19059 | + __debugbreak(); | |
| 19060 | +#else | |
| 18884 | 19061 | DebugBreak(); |
| 19062 | +#endif | |
| 18885 | 19063 | #elif defined(SIGTRAP) |
| 18886 | 19064 | raise(SIGTRAP); |
| 18887 | 19065 | #endif |
| 18888 | 19066 | } |
| 18889 | 19067 | } |
| 18890 | 19068 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -33,10 +33,18 @@ | |
| 33 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 34 | /* This needs to come before any includes for MSVC compiler */ |
| 35 | #define _CRT_SECURE_NO_WARNINGS |
| 36 | #endif |
| 37 | |
| 38 | /* |
| 39 | ** Warning pragmas copied from msvc.h in the core. |
| 40 | */ |
| 41 | #if defined(_MSC_VER) |
| 42 | #pragma warning(disable : 4054) |
| @@ -145,26 +153,30 @@ | |
| 145 | # define SHELL_USE_LOCAL_GETLINE 1 |
| 146 | #endif |
| 147 | |
| 148 | |
| 149 | #if defined(_WIN32) || defined(WIN32) |
| 150 | # include <io.h> |
| 151 | # include <fcntl.h> |
| 152 | # define isatty(h) _isatty(h) |
| 153 | # ifndef access |
| 154 | # define access(f,m) _access((f),(m)) |
| 155 | # endif |
| 156 | # ifndef unlink |
| 157 | # define unlink _unlink |
| 158 | # endif |
| 159 | # ifndef strdup |
| 160 | # define strdup _strdup |
| 161 | # endif |
| 162 | # undef popen |
| 163 | # define popen _popen |
| 164 | # undef pclose |
| 165 | # define pclose _pclose |
| 166 | #else |
| 167 | /* Make sure isatty() has a prototype. */ |
| 168 | extern int isatty(int); |
| 169 | |
| 170 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) |
| @@ -189,10 +201,13 @@ | |
| 189 | #define IsSpace(X) isspace((unsigned char)X) |
| 190 | #define IsDigit(X) isdigit((unsigned char)X) |
| 191 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 192 | |
| 193 | #if defined(_WIN32) || defined(WIN32) |
| 194 | #include <windows.h> |
| 195 | |
| 196 | /* string conversion routines only needed on Win32 */ |
| 197 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 198 | extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); |
| @@ -204,11 +219,11 @@ | |
| 204 | ** are automatically translated into \r\n. However, this behavior needs |
| 205 | ** to be disabled in some cases (ex: when generating CSV output and when |
| 206 | ** rendering quoted strings that contain \n characters). The following |
| 207 | ** routines take care of that. |
| 208 | */ |
| 209 | #if defined(_WIN32) || defined(WIN32) |
| 210 | static void setBinaryMode(FILE *file, int isOutput){ |
| 211 | if( isOutput ) fflush(file); |
| 212 | _setmode(_fileno(file), _O_BINARY); |
| 213 | } |
| 214 | static void setTextMode(FILE *file, int isOutput){ |
| @@ -308,10 +323,11 @@ | |
| 308 | */ |
| 309 | static int hasTimer(void){ |
| 310 | if( getProcessTimesAddr ){ |
| 311 | return 1; |
| 312 | } else { |
| 313 | /* GetProcessTimes() isn't supported in WIN95 and some other Windows |
| 314 | ** versions. See if the version we are running on has it, and if it |
| 315 | ** does, save off a pointer to it and the current process handle. |
| 316 | */ |
| 317 | hProcess = GetCurrentProcess(); |
| @@ -324,10 +340,11 @@ | |
| 324 | return 1; |
| 325 | } |
| 326 | FreeLibrary(hinstLib); |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | return 0; |
| 331 | } |
| 332 | |
| 333 | /* |
| @@ -2476,10 +2493,11 @@ | |
| 2476 | } |
| 2477 | } |
| 2478 | |
| 2479 | if( mtime>=0 ){ |
| 2480 | #if defined(_WIN32) |
| 2481 | /* Windows */ |
| 2482 | FILETIME lastAccess; |
| 2483 | FILETIME lastWrite; |
| 2484 | SYSTEMTIME currentTime; |
| 2485 | LONGLONG intervals; |
| @@ -2506,10 +2524,11 @@ | |
| 2506 | CloseHandle(hFile); |
| 2507 | return !bResult; |
| 2508 | }else{ |
| 2509 | return 1; |
| 2510 | } |
| 2511 | #elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */ |
| 2512 | /* Recent unix */ |
| 2513 | struct timespec times[2]; |
| 2514 | times[0].tv_nsec = times[1].tv_nsec = 0; |
| 2515 | times[0].tv_sec = time(0); |
| @@ -4263,10 +4282,105 @@ | |
| 4263 | memtraceOut = 0; |
| 4264 | return rc; |
| 4265 | } |
| 4266 | |
| 4267 | /************************* End ../ext/misc/memtrace.c ********************/ |
| 4268 | #ifdef SQLITE_HAVE_ZLIB |
| 4269 | /************************* Begin ../ext/misc/zipfile.c ******************/ |
| 4270 | /* |
| 4271 | ** 2017-12-26 |
| 4272 | ** |
| @@ -9652,10 +9766,11 @@ | |
| 9652 | int nCheck; /* Number of ".check" commands run */ |
| 9653 | unsigned nProgress; /* Number of progress callbacks encountered */ |
| 9654 | unsigned mxProgress; /* Maximum progress callbacks before failing */ |
| 9655 | unsigned flgProgress; /* Flags for the progress callback */ |
| 9656 | unsigned shellFlgs; /* Various flags */ |
| 9657 | sqlite3_int64 szMax; /* --maxsize argument to .open */ |
| 9658 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 9659 | char *zTempFile; /* Temporary file that might need deleting */ |
| 9660 | char zTestcase[30]; /* Name of current test case */ |
| 9661 | char colSeparator[20]; /* Column separator character for several modes */ |
| @@ -9952,15 +10067,17 @@ | |
| 9952 | /* |
| 9953 | ** Save or restore the current output mode |
| 9954 | */ |
| 9955 | static void outputModePush(ShellState *p){ |
| 9956 | p->modePrior = p->mode; |
| 9957 | memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator)); |
| 9958 | memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator)); |
| 9959 | } |
| 9960 | static void outputModePop(ShellState *p){ |
| 9961 | p->mode = p->modePrior; |
| 9962 | memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 9963 | memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 9964 | } |
| 9965 | |
| 9966 | /* |
| @@ -10921,12 +11038,11 @@ | |
| 10921 | ** "--" comment occurs at the end of the statement, the comment |
| 10922 | ** won't consume the semicolon terminator. |
| 10923 | */ |
| 10924 | static int run_table_dump_query( |
| 10925 | ShellState *p, /* Query context */ |
| 10926 | const char *zSelect, /* SELECT statement to extract content */ |
| 10927 | const char *zFirstRow /* Print before first row, if not NULL */ |
| 10928 | ){ |
| 10929 | sqlite3_stmt *pSelect; |
| 10930 | int rc; |
| 10931 | int nResult; |
| 10932 | int i; |
| @@ -10939,14 +11055,10 @@ | |
| 10939 | return rc; |
| 10940 | } |
| 10941 | rc = sqlite3_step(pSelect); |
| 10942 | nResult = sqlite3_column_count(pSelect); |
| 10943 | while( rc==SQLITE_ROW ){ |
| 10944 | if( zFirstRow ){ |
| 10945 | utf8_printf(p->out, "%s", zFirstRow); |
| 10946 | zFirstRow = 0; |
| 10947 | } |
| 10948 | z = (const char*)sqlite3_column_text(pSelect, 0); |
| 10949 | utf8_printf(p->out, "%s", z); |
| 10950 | for(i=1; i<nResult; i++){ |
| 10951 | utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); |
| 10952 | } |
| @@ -11399,13 +11511,13 @@ | |
| 11399 | ** Parameter bindings are taken from a TEMP table of the form: |
| 11400 | ** |
| 11401 | ** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value) |
| 11402 | ** WITHOUT ROWID; |
| 11403 | ** |
| 11404 | ** No bindings occur if this table does not exist. The special character '$' |
| 11405 | ** is included in the table name to help prevent collisions with actual tables. |
| 11406 | ** The table must be in the TEMP schema. |
| 11407 | */ |
| 11408 | static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ |
| 11409 | int nVar; |
| 11410 | int i; |
| 11411 | int rc; |
| @@ -11705,10 +11817,11 @@ | |
| 11705 | if( rc==SQLITE_OK ){ |
| 11706 | while( sqlite3_step(pExplain)==SQLITE_ROW ){ |
| 11707 | const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); |
| 11708 | int iEqpId = sqlite3_column_int(pExplain, 0); |
| 11709 | int iParentId = sqlite3_column_int(pExplain, 1); |
| 11710 | if( zEQPLine[0]=='-' ) eqp_render(pArg); |
| 11711 | eqp_append(pArg, iEqpId, iParentId, zEQPLine); |
| 11712 | } |
| 11713 | eqp_render(pArg); |
| 11714 | } |
| @@ -12115,29 +12228,32 @@ | |
| 12115 | ".check GLOB Fail if output since .testcase does not match", |
| 12116 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 12117 | ".databases List names and files of attached databases", |
| 12118 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 12119 | ".dbinfo ?DB? Show status information about the database", |
| 12120 | ".dump ?TABLE? ... Render all database content as SQL", |
| 12121 | " Options:", |
| 12122 | " --preserve-rowids Include ROWID values in the output", |
| 12123 | " --newlines Allow unescaped newline characters in output", |
| 12124 | " TABLE is a LIKE pattern for the tables to dump", |
| 12125 | ".echo on|off Turn command echo on or off", |
| 12126 | ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", |
| 12127 | " Other Modes:", |
| 12128 | #ifdef SQLITE_DEBUG |
| 12129 | " test Show raw EXPLAIN QUERY PLAN output", |
| 12130 | " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"", |
| 12131 | #endif |
| 12132 | " trigger Like \"full\" but also show trigger bytecode", |
| 12133 | ".excel Display the output of next command in spreadsheet", |
| 12134 | ".exit ?CODE? Exit this program with return-code CODE", |
| 12135 | ".expert EXPERIMENTAL. Suggest indexes for queries", |
| 12136 | ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", |
| 12137 | ".filectrl CMD ... Run various sqlite3_file_control() operations", |
| 12138 | " Run \".filectrl\" with no arguments for details", |
| 12139 | ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", |
| 12140 | ".headers on|off Turn display of headers on or off", |
| 12141 | ".help ?-all? ?PATTERN? Show help text for PATTERN", |
| 12142 | ".import FILE TABLE Import data from FILE into TABLE", |
| 12143 | " Options:", |
| @@ -12180,15 +12296,15 @@ | |
| 12180 | " list Values delimited by \"|\"", |
| 12181 | " quote Escape answers as for SQL", |
| 12182 | " tabs Tab-separated values", |
| 12183 | " tcl TCL list elements", |
| 12184 | ".nullvalue STRING Use STRING in place of NULL values", |
| 12185 | ".once (-e|-x|FILE) Output for the next SQL command only to FILE", |
| 12186 | " If FILE begins with '|' then open as a pipe", |
| 12187 | " Other options:", |
| 12188 | " -e Invoke system text editor", |
| 12189 | " -x Open in a spreadsheet", |
| 12190 | #ifdef SQLITE_DEBUG |
| 12191 | ".oom [--repeat M] [N] Simulate an OOM error on the N-th allocation", |
| 12192 | #endif |
| 12193 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", |
| 12194 | " Options:", |
| @@ -12201,11 +12317,15 @@ | |
| 12201 | " --new Initialize FILE to an empty database", |
| 12202 | " --nofollow Do not follow symbolic links", |
| 12203 | " --readonly Open FILE readonly", |
| 12204 | " --zip FILE is a ZIP archive", |
| 12205 | ".output ?FILE? Send output to FILE or stdout if FILE is omitted", |
| 12206 | " If FILE begins with '|' then open it as a pipe.", |
| 12207 | ".parameter CMD ... Manage SQL parameter bindings", |
| 12208 | " clear Erase all bindings", |
| 12209 | " init Initialize the TEMP table that holds bindings", |
| 12210 | " list List the current parameter bindings", |
| 12211 | " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", |
| @@ -12321,10 +12441,11 @@ | |
| 12321 | char *zPat; |
| 12322 | if( zPattern==0 |
| 12323 | || zPattern[0]=='0' |
| 12324 | || strcmp(zPattern,"-a")==0 |
| 12325 | || strcmp(zPattern,"-all")==0 |
| 12326 | ){ |
| 12327 | /* Show all commands, but only one line per command */ |
| 12328 | if( zPattern==0 ) zPattern = ""; |
| 12329 | for(i=0; i<ArraySize(azHelp); i++){ |
| 12330 | if( azHelp[i][0]=='.' || zPattern[0] ){ |
| @@ -12802,10 +12923,11 @@ | |
| 12802 | sqlite3_enable_load_extension(p->db, 1); |
| 12803 | #endif |
| 12804 | sqlite3_fileio_init(p->db, 0, 0); |
| 12805 | sqlite3_shathree_init(p->db, 0, 0); |
| 12806 | sqlite3_completion_init(p->db, 0, 0); |
| 12807 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 12808 | sqlite3_dbdata_init(p->db, 0, 0); |
| 12809 | #endif |
| 12810 | #ifdef SQLITE_HAVE_ZLIB |
| 12811 | sqlite3_zipfile_init(p->db, 0, 0); |
| @@ -13521,15 +13643,19 @@ | |
| 13521 | #endif |
| 13522 | char *zCmd; |
| 13523 | zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 13524 | if( system(zCmd) ){ |
| 13525 | utf8_printf(stderr, "Failed: [%s]\n", zCmd); |
| 13526 | } |
| 13527 | sqlite3_free(zCmd); |
| 13528 | outputModePop(p); |
| 13529 | p->doXdgOpen = 0; |
| 13530 | sqlite3_sleep(100); |
| 13531 | } |
| 13532 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ |
| 13533 | } |
| 13534 | p->outfile[0] = 0; |
| 13535 | p->out = stdout; |
| @@ -13601,16 +13727,11 @@ | |
| 13601 | if( p->db==0 ) return 1; |
| 13602 | rc = sqlite3_prepare_v2(p->db, |
| 13603 | "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
| 13604 | -1, &pStmt, 0); |
| 13605 | if( rc ){ |
| 13606 | if( !sqlite3_compileoption_used("ENABLE_DBPAGE_VTAB") ){ |
| 13607 | utf8_printf(stderr, "the \".dbinfo\" command requires the " |
| 13608 | "-DSQLITE_ENABLE_DBPAGE_VTAB compile-time options\n"); |
| 13609 | }else{ |
| 13610 | utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); |
| 13611 | } |
| 13612 | sqlite3_finalize(pStmt); |
| 13613 | return 1; |
| 13614 | } |
| 13615 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 13616 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| @@ -13815,13 +13936,25 @@ | |
| 13815 | p->zTempFile = 0; |
| 13816 | if( p->db ){ |
| 13817 | sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); |
| 13818 | } |
| 13819 | if( p->zTempFile==0 ){ |
| 13820 | sqlite3_uint64 r; |
| 13821 | sqlite3_randomness(sizeof(r), &r); |
| 13822 | p->zTempFile = sqlite3_mprintf("temp%llx.%s", r, zSuffix); |
| 13823 | }else{ |
| 13824 | p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); |
| 13825 | } |
| 13826 | if( p->zTempFile==0 ){ |
| 13827 | raw_printf(stderr, "out of memory\n"); |
| @@ -15841,11 +15974,12 @@ | |
| 15841 | rc = recoverDatabaseCmd(p, nArg, azArg); |
| 15842 | }else |
| 15843 | #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ |
| 15844 | |
| 15845 | if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ |
| 15846 | const char *zLike = 0; |
| 15847 | int i; |
| 15848 | int savedShowHeader = p->showHeader; |
| 15849 | int savedShellFlags = p->shellFlgs; |
| 15850 | ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo); |
| 15851 | for(i=1; i<nArg; i++){ |
| @@ -15869,16 +16003,14 @@ | |
| 15869 | raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); |
| 15870 | rc = 1; |
| 15871 | goto meta_command_exit; |
| 15872 | } |
| 15873 | }else if( zLike ){ |
| 15874 | raw_printf(stderr, "Usage: .dump ?--preserve-rowids? " |
| 15875 | "?--newlines? ?LIKE-PATTERN?\n"); |
| 15876 | rc = 1; |
| 15877 | goto meta_command_exit; |
| 15878 | }else{ |
| 15879 | zLike = azArg[i]; |
| 15880 | } |
| 15881 | } |
| 15882 | |
| 15883 | open_db(p, 0); |
| 15884 | |
| @@ -15892,39 +16024,29 @@ | |
| 15892 | /* Set writable_schema=ON since doing so forces SQLite to initialize |
| 15893 | ** as much of the schema as it can even if the sqlite_master table is |
| 15894 | ** corrupt. */ |
| 15895 | sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); |
| 15896 | p->nErr = 0; |
| 15897 | if( zLike==0 ){ |
| 15898 | run_schema_dump_query(p, |
| 15899 | "SELECT name, type, sql FROM sqlite_master " |
| 15900 | "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" |
| 15901 | ); |
| 15902 | run_schema_dump_query(p, |
| 15903 | "SELECT name, type, sql FROM sqlite_master " |
| 15904 | "WHERE name=='sqlite_sequence'" |
| 15905 | ); |
| 15906 | run_table_dump_query(p, |
| 15907 | "SELECT sql FROM sqlite_master " |
| 15908 | "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 |
| 15909 | ); |
| 15910 | }else{ |
| 15911 | char *zSql; |
| 15912 | zSql = sqlite3_mprintf( |
| 15913 | "SELECT name, type, sql FROM sqlite_master " |
| 15914 | "WHERE tbl_name LIKE %Q AND type=='table'" |
| 15915 | " AND sql NOT NULL", zLike); |
| 15916 | run_schema_dump_query(p,zSql); |
| 15917 | sqlite3_free(zSql); |
| 15918 | zSql = sqlite3_mprintf( |
| 15919 | "SELECT sql FROM sqlite_master " |
| 15920 | "WHERE sql NOT NULL" |
| 15921 | " AND type IN ('index','trigger','view')" |
| 15922 | " AND tbl_name LIKE %Q", zLike); |
| 15923 | run_table_dump_query(p, zSql, 0); |
| 15924 | sqlite3_free(zSql); |
| 15925 | } |
| 15926 | if( p->writableSchema ){ |
| 15927 | raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); |
| 15928 | p->writableSchema = 0; |
| 15929 | } |
| 15930 | sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); |
| @@ -16023,20 +16145,32 @@ | |
| 16023 | { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" }, |
| 16024 | /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/ |
| 16025 | { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" }, |
| 16026 | { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" }, |
| 16027 | { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" }, |
| 16028 | }; |
| 16029 | int filectrl = -1; |
| 16030 | int iCtrl = -1; |
| 16031 | sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */ |
| 16032 | int isOk = 0; /* 0: usage 1: %lld 2: no-result */ |
| 16033 | int n2, i; |
| 16034 | const char *zCmd = 0; |
| 16035 | |
| 16036 | open_db(p, 0); |
| 16037 | zCmd = nArg>=2 ? azArg[1] : "help"; |
| 16038 | |
| 16039 | /* The argument can optionally begin with "-" or "--" */ |
| 16040 | if( zCmd[0]=='-' && zCmd[1] ){ |
| 16041 | zCmd++; |
| 16042 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| @@ -16075,51 +16209,63 @@ | |
| 16075 | }else{ |
| 16076 | switch(filectrl){ |
| 16077 | case SQLITE_FCNTL_SIZE_LIMIT: { |
| 16078 | if( nArg!=2 && nArg!=3 ) break; |
| 16079 | iRes = nArg==3 ? integerValue(azArg[2]) : -1; |
| 16080 | sqlite3_file_control(p->db, 0, SQLITE_FCNTL_SIZE_LIMIT, &iRes); |
| 16081 | isOk = 1; |
| 16082 | break; |
| 16083 | } |
| 16084 | case SQLITE_FCNTL_LOCK_TIMEOUT: |
| 16085 | case SQLITE_FCNTL_CHUNK_SIZE: { |
| 16086 | int x; |
| 16087 | if( nArg!=3 ) break; |
| 16088 | x = (int)integerValue(azArg[2]); |
| 16089 | sqlite3_file_control(p->db, 0, filectrl, &x); |
| 16090 | isOk = 2; |
| 16091 | break; |
| 16092 | } |
| 16093 | case SQLITE_FCNTL_PERSIST_WAL: |
| 16094 | case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { |
| 16095 | int x; |
| 16096 | if( nArg!=2 && nArg!=3 ) break; |
| 16097 | x = nArg==3 ? booleanValue(azArg[2]) : -1; |
| 16098 | sqlite3_file_control(p->db, 0, filectrl, &x); |
| 16099 | iRes = x; |
| 16100 | isOk = 1; |
| 16101 | break; |
| 16102 | } |
| 16103 | case SQLITE_FCNTL_HAS_MOVED: { |
| 16104 | int x; |
| 16105 | if( nArg!=2 ) break; |
| 16106 | sqlite3_file_control(p->db, 0, filectrl, &x); |
| 16107 | iRes = x; |
| 16108 | isOk = 1; |
| 16109 | break; |
| 16110 | } |
| 16111 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 16112 | char *z = 0; |
| 16113 | if( nArg!=2 ) break; |
| 16114 | sqlite3_file_control(p->db, 0, filectrl, &z); |
| 16115 | if( z ){ |
| 16116 | utf8_printf(p->out, "%s\n", z); |
| 16117 | sqlite3_free(z); |
| 16118 | } |
| 16119 | isOk = 2; |
| 16120 | break; |
| 16121 | } |
| 16122 | } |
| 16123 | } |
| 16124 | if( isOk==0 && iCtrl>=0 ){ |
| 16125 | utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); |
| @@ -16850,46 +16996,70 @@ | |
| 16850 | |
| 16851 | if( (c=='o' |
| 16852 | && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) |
| 16853 | || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) |
| 16854 | ){ |
| 16855 | const char *zFile = nArg>=2 ? azArg[1] : "stdout"; |
| 16856 | int bTxtMode = 0; |
| 16857 | if( azArg[0][0]=='e' ){ |
| 16858 | /* Transform the ".excel" command into ".once -x" */ |
| 16859 | nArg = 2; |
| 16860 | azArg[0] = "once"; |
| 16861 | zFile = azArg[1] = "-x"; |
| 16862 | n = 4; |
| 16863 | } |
| 16864 | if( nArg>2 ){ |
| 16865 | utf8_printf(stderr, "Usage: .%s [-e|-x|FILE]\n", azArg[0]); |
| 16866 | rc = 1; |
| 16867 | goto meta_command_exit; |
| 16868 | } |
| 16869 | if( n>1 && strncmp(azArg[0], "once", n)==0 ){ |
| 16870 | if( nArg<2 ){ |
| 16871 | raw_printf(stderr, "Usage: .once (-e|-x|FILE)\n"); |
| 16872 | rc = 1; |
| 16873 | goto meta_command_exit; |
| 16874 | } |
| 16875 | p->outCount = 2; |
| 16876 | }else{ |
| 16877 | p->outCount = 0; |
| 16878 | } |
| 16879 | output_reset(p); |
| 16880 | if( zFile[0]=='-' && zFile[1]=='-' ) zFile++; |
| 16881 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 16882 | if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){ |
| 16883 | p->doXdgOpen = 1; |
| 16884 | outputModePush(p); |
| 16885 | if( zFile[1]=='x' ){ |
| 16886 | newTempFile(p, "csv"); |
| 16887 | p->mode = MODE_Csv; |
| 16888 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 16889 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 16890 | }else{ |
| 16891 | newTempFile(p, "txt"); |
| 16892 | bTxtMode = 1; |
| 16893 | } |
| 16894 | zFile = p->zTempFile; |
| 16895 | } |
| @@ -16904,10 +17074,11 @@ | |
| 16904 | if( p->out==0 ){ |
| 16905 | utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); |
| 16906 | p->out = stdout; |
| 16907 | rc = 1; |
| 16908 | }else{ |
| 16909 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 16910 | } |
| 16911 | #endif |
| 16912 | }else{ |
| 16913 | p->out = output_file_open(zFile, bTxtMode); |
| @@ -16916,10 +17087,11 @@ | |
| 16916 | utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
| 16917 | } |
| 16918 | p->out = stdout; |
| 16919 | rc = 1; |
| 16920 | } else { |
| 16921 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 16922 | } |
| 16923 | } |
| 16924 | }else |
| 16925 | |
| @@ -17979,11 +18151,10 @@ | |
| 17979 | #endif |
| 17980 | { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " }, |
| 17981 | { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" }, |
| 17982 | { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" }, |
| 17983 | { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" }, |
| 17984 | { "reserve", SQLITE_TESTCTRL_RESERVE, "BYTES-OF-RESERVE"}, |
| 17985 | }; |
| 17986 | int testctrl = -1; |
| 17987 | int iCtrl = -1; |
| 17988 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 17989 | int isOk = 0; |
| @@ -18032,11 +18203,10 @@ | |
| 18032 | }else{ |
| 18033 | switch(testctrl){ |
| 18034 | |
| 18035 | /* sqlite3_test_control(int, db, int) */ |
| 18036 | case SQLITE_TESTCTRL_OPTIMIZATIONS: |
| 18037 | case SQLITE_TESTCTRL_RESERVE: |
| 18038 | if( nArg==3 ){ |
| 18039 | int opt = (int)strtol(azArg[2], 0, 0); |
| 18040 | rc2 = sqlite3_test_control(testctrl, p->db, opt); |
| 18041 | isOk = 3; |
| 18042 | } |
| @@ -18804,18 +18974,22 @@ | |
| 18804 | /* |
| 18805 | ** Output text to the console in a font that attracts extra attention. |
| 18806 | */ |
| 18807 | #ifdef _WIN32 |
| 18808 | static void printBold(const char *zText){ |
| 18809 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); |
| 18810 | CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; |
| 18811 | GetConsoleScreenBufferInfo(out, &defaultScreenInfo); |
| 18812 | SetConsoleTextAttribute(out, |
| 18813 | FOREGROUND_RED|FOREGROUND_INTENSITY |
| 18814 | ); |
| 18815 | printf("%s", zText); |
| 18816 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 18817 | } |
| 18818 | #else |
| 18819 | static void printBold(const char *zText){ |
| 18820 | printf("\033[1m%s\033[0m", zText); |
| 18821 | } |
| @@ -18879,11 +19053,15 @@ | |
| 18879 | "attach debugger to process %d and press any key to continue.\n", |
| 18880 | GETPID()); |
| 18881 | fgetc(stdin); |
| 18882 | }else{ |
| 18883 | #if defined(_WIN32) || defined(WIN32) |
| 18884 | DebugBreak(); |
| 18885 | #elif defined(SIGTRAP) |
| 18886 | raise(SIGTRAP); |
| 18887 | #endif |
| 18888 | } |
| 18889 | } |
| 18890 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -33,10 +33,18 @@ | |
| 33 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 34 | /* This needs to come before any includes for MSVC compiler */ |
| 35 | #define _CRT_SECURE_NO_WARNINGS |
| 36 | #endif |
| 37 | |
| 38 | /* |
| 39 | ** Determine if we are dealing with WinRT, which provides only a subset of |
| 40 | ** the full Win32 API. |
| 41 | */ |
| 42 | #if !defined(SQLITE_OS_WINRT) |
| 43 | # define SQLITE_OS_WINRT 0 |
| 44 | #endif |
| 45 | |
| 46 | /* |
| 47 | ** Warning pragmas copied from msvc.h in the core. |
| 48 | */ |
| 49 | #if defined(_MSC_VER) |
| 50 | #pragma warning(disable : 4054) |
| @@ -145,26 +153,30 @@ | |
| 153 | # define SHELL_USE_LOCAL_GETLINE 1 |
| 154 | #endif |
| 155 | |
| 156 | |
| 157 | #if defined(_WIN32) || defined(WIN32) |
| 158 | # if SQLITE_OS_WINRT |
| 159 | # define SQLITE_OMIT_POPEN 1 |
| 160 | # else |
| 161 | # include <io.h> |
| 162 | # include <fcntl.h> |
| 163 | # define isatty(h) _isatty(h) |
| 164 | # ifndef access |
| 165 | # define access(f,m) _access((f),(m)) |
| 166 | # endif |
| 167 | # ifndef unlink |
| 168 | # define unlink _unlink |
| 169 | # endif |
| 170 | # ifndef strdup |
| 171 | # define strdup _strdup |
| 172 | # endif |
| 173 | # undef popen |
| 174 | # define popen _popen |
| 175 | # undef pclose |
| 176 | # define pclose _pclose |
| 177 | # endif |
| 178 | #else |
| 179 | /* Make sure isatty() has a prototype. */ |
| 180 | extern int isatty(int); |
| 181 | |
| 182 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) |
| @@ -189,10 +201,13 @@ | |
| 201 | #define IsSpace(X) isspace((unsigned char)X) |
| 202 | #define IsDigit(X) isdigit((unsigned char)X) |
| 203 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 204 | |
| 205 | #if defined(_WIN32) || defined(WIN32) |
| 206 | #if SQLITE_OS_WINRT |
| 207 | #include <intrin.h> |
| 208 | #endif |
| 209 | #include <windows.h> |
| 210 | |
| 211 | /* string conversion routines only needed on Win32 */ |
| 212 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 213 | extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); |
| @@ -204,11 +219,11 @@ | |
| 219 | ** are automatically translated into \r\n. However, this behavior needs |
| 220 | ** to be disabled in some cases (ex: when generating CSV output and when |
| 221 | ** rendering quoted strings that contain \n characters). The following |
| 222 | ** routines take care of that. |
| 223 | */ |
| 224 | #if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT |
| 225 | static void setBinaryMode(FILE *file, int isOutput){ |
| 226 | if( isOutput ) fflush(file); |
| 227 | _setmode(_fileno(file), _O_BINARY); |
| 228 | } |
| 229 | static void setTextMode(FILE *file, int isOutput){ |
| @@ -308,10 +323,11 @@ | |
| 323 | */ |
| 324 | static int hasTimer(void){ |
| 325 | if( getProcessTimesAddr ){ |
| 326 | return 1; |
| 327 | } else { |
| 328 | #if !SQLITE_OS_WINRT |
| 329 | /* GetProcessTimes() isn't supported in WIN95 and some other Windows |
| 330 | ** versions. See if the version we are running on has it, and if it |
| 331 | ** does, save off a pointer to it and the current process handle. |
| 332 | */ |
| 333 | hProcess = GetCurrentProcess(); |
| @@ -324,10 +340,11 @@ | |
| 340 | return 1; |
| 341 | } |
| 342 | FreeLibrary(hinstLib); |
| 343 | } |
| 344 | } |
| 345 | #endif |
| 346 | } |
| 347 | return 0; |
| 348 | } |
| 349 | |
| 350 | /* |
| @@ -2476,10 +2493,11 @@ | |
| 2493 | } |
| 2494 | } |
| 2495 | |
| 2496 | if( mtime>=0 ){ |
| 2497 | #if defined(_WIN32) |
| 2498 | #if !SQLITE_OS_WINRT |
| 2499 | /* Windows */ |
| 2500 | FILETIME lastAccess; |
| 2501 | FILETIME lastWrite; |
| 2502 | SYSTEMTIME currentTime; |
| 2503 | LONGLONG intervals; |
| @@ -2506,10 +2524,11 @@ | |
| 2524 | CloseHandle(hFile); |
| 2525 | return !bResult; |
| 2526 | }else{ |
| 2527 | return 1; |
| 2528 | } |
| 2529 | #endif |
| 2530 | #elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */ |
| 2531 | /* Recent unix */ |
| 2532 | struct timespec times[2]; |
| 2533 | times[0].tv_nsec = times[1].tv_nsec = 0; |
| 2534 | times[0].tv_sec = time(0); |
| @@ -4263,10 +4282,105 @@ | |
| 4282 | memtraceOut = 0; |
| 4283 | return rc; |
| 4284 | } |
| 4285 | |
| 4286 | /************************* End ../ext/misc/memtrace.c ********************/ |
| 4287 | /************************* Begin ../ext/misc/uint.c ******************/ |
| 4288 | /* |
| 4289 | ** 2020-04-14 |
| 4290 | ** |
| 4291 | ** The author disclaims copyright to this source code. In place of |
| 4292 | ** a legal notice, here is a blessing: |
| 4293 | ** |
| 4294 | ** May you do good and not evil. |
| 4295 | ** May you find forgiveness for yourself and forgive others. |
| 4296 | ** May you share freely, never taking more than you give. |
| 4297 | ** |
| 4298 | ****************************************************************************** |
| 4299 | ** |
| 4300 | ** This SQLite extension implements the UINT collating sequence. |
| 4301 | ** |
| 4302 | ** UINT works like BINARY for text, except that embedded strings |
| 4303 | ** of digits compare in numeric order. |
| 4304 | ** |
| 4305 | ** * Leading zeros are handled properly, in the sense that |
| 4306 | ** they do not mess of the maginitude comparison of embedded |
| 4307 | ** strings of digits. "x00123y" is equal to "x123y". |
| 4308 | ** |
| 4309 | ** * Only unsigned integers are recognized. Plus and minus |
| 4310 | ** signs are ignored. Decimal points and exponential notation |
| 4311 | ** are ignored. |
| 4312 | ** |
| 4313 | ** * Embedded integers can be of arbitrary length. Comparison |
| 4314 | ** is *not* limited integers that can be expressed as a |
| 4315 | ** 64-bit machine integer. |
| 4316 | */ |
| 4317 | /* #include "sqlite3ext.h" */ |
| 4318 | SQLITE_EXTENSION_INIT1 |
| 4319 | #include <assert.h> |
| 4320 | #include <string.h> |
| 4321 | #include <ctype.h> |
| 4322 | |
| 4323 | /* |
| 4324 | ** Compare text in lexicographic order, except strings of digits |
| 4325 | ** compare in numeric order. |
| 4326 | */ |
| 4327 | static int uintCollFunc( |
| 4328 | void *notUsed, |
| 4329 | int nKey1, const void *pKey1, |
| 4330 | int nKey2, const void *pKey2 |
| 4331 | ){ |
| 4332 | const unsigned char *zA = (const unsigned char*)pKey1; |
| 4333 | const unsigned char *zB = (const unsigned char*)pKey2; |
| 4334 | int i=0, j=0, x; |
| 4335 | (void)notUsed; |
| 4336 | while( i<nKey1 && j<nKey2 ){ |
| 4337 | x = zA[i] - zB[j]; |
| 4338 | if( isdigit(zA[i]) ){ |
| 4339 | int k; |
| 4340 | if( !isdigit(zB[j]) ) return x; |
| 4341 | while( i<nKey1 && zA[i]=='0' ){ i++; } |
| 4342 | while( j<nKey2 && zB[j]=='0' ){ j++; } |
| 4343 | k = 0; |
| 4344 | while( i+k<nKey1 && isdigit(zA[i+k]) |
| 4345 | && j+k<nKey2 && isdigit(zB[j+k]) ){ |
| 4346 | k++; |
| 4347 | } |
| 4348 | if( i+k<nKey1 && isdigit(zA[i+k]) ){ |
| 4349 | return +1; |
| 4350 | }else if( j+k<nKey2 && isdigit(zB[j+k]) ){ |
| 4351 | return -1; |
| 4352 | }else{ |
| 4353 | x = memcmp(zA+i, zB+j, k); |
| 4354 | if( x ) return x; |
| 4355 | i += k; |
| 4356 | j += k; |
| 4357 | } |
| 4358 | }else if( x ){ |
| 4359 | return x; |
| 4360 | }else{ |
| 4361 | i++; |
| 4362 | j++; |
| 4363 | } |
| 4364 | } |
| 4365 | return (nKey1 - i) - (nKey2 - j); |
| 4366 | } |
| 4367 | |
| 4368 | #ifdef _WIN32 |
| 4369 | |
| 4370 | #endif |
| 4371 | int sqlite3_uint_init( |
| 4372 | sqlite3 *db, |
| 4373 | char **pzErrMsg, |
| 4374 | const sqlite3_api_routines *pApi |
| 4375 | ){ |
| 4376 | SQLITE_EXTENSION_INIT2(pApi); |
| 4377 | (void)pzErrMsg; /* Unused parameter */ |
| 4378 | return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc); |
| 4379 | } |
| 4380 | |
| 4381 | /************************* End ../ext/misc/uint.c ********************/ |
| 4382 | #ifdef SQLITE_HAVE_ZLIB |
| 4383 | /************************* Begin ../ext/misc/zipfile.c ******************/ |
| 4384 | /* |
| 4385 | ** 2017-12-26 |
| 4386 | ** |
| @@ -9652,10 +9766,11 @@ | |
| 9766 | int nCheck; /* Number of ".check" commands run */ |
| 9767 | unsigned nProgress; /* Number of progress callbacks encountered */ |
| 9768 | unsigned mxProgress; /* Maximum progress callbacks before failing */ |
| 9769 | unsigned flgProgress; /* Flags for the progress callback */ |
| 9770 | unsigned shellFlgs; /* Various flags */ |
| 9771 | unsigned priorShFlgs; /* Saved copy of flags */ |
| 9772 | sqlite3_int64 szMax; /* --maxsize argument to .open */ |
| 9773 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 9774 | char *zTempFile; /* Temporary file that might need deleting */ |
| 9775 | char zTestcase[30]; /* Name of current test case */ |
| 9776 | char colSeparator[20]; /* Column separator character for several modes */ |
| @@ -9952,15 +10067,17 @@ | |
| 10067 | /* |
| 10068 | ** Save or restore the current output mode |
| 10069 | */ |
| 10070 | static void outputModePush(ShellState *p){ |
| 10071 | p->modePrior = p->mode; |
| 10072 | p->priorShFlgs = p->shellFlgs; |
| 10073 | memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator)); |
| 10074 | memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator)); |
| 10075 | } |
| 10076 | static void outputModePop(ShellState *p){ |
| 10077 | p->mode = p->modePrior; |
| 10078 | p->shellFlgs = p->priorShFlgs; |
| 10079 | memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 10080 | memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 10081 | } |
| 10082 | |
| 10083 | /* |
| @@ -10921,12 +11038,11 @@ | |
| 11038 | ** "--" comment occurs at the end of the statement, the comment |
| 11039 | ** won't consume the semicolon terminator. |
| 11040 | */ |
| 11041 | static int run_table_dump_query( |
| 11042 | ShellState *p, /* Query context */ |
| 11043 | const char *zSelect /* SELECT statement to extract content */ |
| 11044 | ){ |
| 11045 | sqlite3_stmt *pSelect; |
| 11046 | int rc; |
| 11047 | int nResult; |
| 11048 | int i; |
| @@ -10939,14 +11055,10 @@ | |
| 11055 | return rc; |
| 11056 | } |
| 11057 | rc = sqlite3_step(pSelect); |
| 11058 | nResult = sqlite3_column_count(pSelect); |
| 11059 | while( rc==SQLITE_ROW ){ |
| 11060 | z = (const char*)sqlite3_column_text(pSelect, 0); |
| 11061 | utf8_printf(p->out, "%s", z); |
| 11062 | for(i=1; i<nResult; i++){ |
| 11063 | utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); |
| 11064 | } |
| @@ -11399,13 +11511,13 @@ | |
| 11511 | ** Parameter bindings are taken from a TEMP table of the form: |
| 11512 | ** |
| 11513 | ** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value) |
| 11514 | ** WITHOUT ROWID; |
| 11515 | ** |
| 11516 | ** No bindings occur if this table does not exist. The name of the table |
| 11517 | ** begins with "sqlite_" so that it will not collide with ordinary application |
| 11518 | ** tables. The table must be in the TEMP schema. |
| 11519 | */ |
| 11520 | static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ |
| 11521 | int nVar; |
| 11522 | int i; |
| 11523 | int rc; |
| @@ -11705,10 +11817,11 @@ | |
| 11817 | if( rc==SQLITE_OK ){ |
| 11818 | while( sqlite3_step(pExplain)==SQLITE_ROW ){ |
| 11819 | const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); |
| 11820 | int iEqpId = sqlite3_column_int(pExplain, 0); |
| 11821 | int iParentId = sqlite3_column_int(pExplain, 1); |
| 11822 | if( zEQPLine==0 ) zEQPLine = ""; |
| 11823 | if( zEQPLine[0]=='-' ) eqp_render(pArg); |
| 11824 | eqp_append(pArg, iEqpId, iParentId, zEQPLine); |
| 11825 | } |
| 11826 | eqp_render(pArg); |
| 11827 | } |
| @@ -12115,29 +12228,32 @@ | |
| 12228 | ".check GLOB Fail if output since .testcase does not match", |
| 12229 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 12230 | ".databases List names and files of attached databases", |
| 12231 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 12232 | ".dbinfo ?DB? Show status information about the database", |
| 12233 | ".dump ?TABLE? Render database content as SQL", |
| 12234 | " Options:", |
| 12235 | " --preserve-rowids Include ROWID values in the output", |
| 12236 | " --newlines Allow unescaped newline characters in output", |
| 12237 | " TABLE is a LIKE pattern for the tables to dump", |
| 12238 | " Additional LIKE patterns can be given in subsequent arguments", |
| 12239 | ".echo on|off Turn command echo on or off", |
| 12240 | ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", |
| 12241 | " Other Modes:", |
| 12242 | #ifdef SQLITE_DEBUG |
| 12243 | " test Show raw EXPLAIN QUERY PLAN output", |
| 12244 | " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"", |
| 12245 | #endif |
| 12246 | " trigger Like \"full\" but also show trigger bytecode", |
| 12247 | ".excel Display the output of next command in spreadsheet", |
| 12248 | " --bom Put a UTF8 byte-order mark on intermediate file", |
| 12249 | ".exit ?CODE? Exit this program with return-code CODE", |
| 12250 | ".expert EXPERIMENTAL. Suggest indexes for queries", |
| 12251 | ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", |
| 12252 | ".filectrl CMD ... Run various sqlite3_file_control() operations", |
| 12253 | " --schema SCHEMA Use SCHEMA instead of \"main\"", |
| 12254 | " --help Show CMD details", |
| 12255 | ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", |
| 12256 | ".headers on|off Turn display of headers on or off", |
| 12257 | ".help ?-all? ?PATTERN? Show help text for PATTERN", |
| 12258 | ".import FILE TABLE Import data from FILE into TABLE", |
| 12259 | " Options:", |
| @@ -12180,15 +12296,15 @@ | |
| 12296 | " list Values delimited by \"|\"", |
| 12297 | " quote Escape answers as for SQL", |
| 12298 | " tabs Tab-separated values", |
| 12299 | " tcl TCL list elements", |
| 12300 | ".nullvalue STRING Use STRING in place of NULL values", |
| 12301 | ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", |
| 12302 | " If FILE begins with '|' then open as a pipe", |
| 12303 | " --bom Put a UTF8 byte-order mark at the beginning", |
| 12304 | " -e Send output to the system text editor", |
| 12305 | " -x Send output as CSV to a spreadsheet (same as \".excel\")", |
| 12306 | #ifdef SQLITE_DEBUG |
| 12307 | ".oom [--repeat M] [N] Simulate an OOM error on the N-th allocation", |
| 12308 | #endif |
| 12309 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", |
| 12310 | " Options:", |
| @@ -12201,11 +12317,15 @@ | |
| 12317 | " --new Initialize FILE to an empty database", |
| 12318 | " --nofollow Do not follow symbolic links", |
| 12319 | " --readonly Open FILE readonly", |
| 12320 | " --zip FILE is a ZIP archive", |
| 12321 | ".output ?FILE? Send output to FILE or stdout if FILE is omitted", |
| 12322 | " If FILE begins with '|' then open it as a pipe.", |
| 12323 | " Options:", |
| 12324 | " --bom Prefix output with a UTF8 byte-order mark", |
| 12325 | " -e Send output to the system text editor", |
| 12326 | " -x Send output as CSV to a spreadsheet", |
| 12327 | ".parameter CMD ... Manage SQL parameter bindings", |
| 12328 | " clear Erase all bindings", |
| 12329 | " init Initialize the TEMP table that holds bindings", |
| 12330 | " list List the current parameter bindings", |
| 12331 | " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", |
| @@ -12321,10 +12441,11 @@ | |
| 12441 | char *zPat; |
| 12442 | if( zPattern==0 |
| 12443 | || zPattern[0]=='0' |
| 12444 | || strcmp(zPattern,"-a")==0 |
| 12445 | || strcmp(zPattern,"-all")==0 |
| 12446 | || strcmp(zPattern,"--all")==0 |
| 12447 | ){ |
| 12448 | /* Show all commands, but only one line per command */ |
| 12449 | if( zPattern==0 ) zPattern = ""; |
| 12450 | for(i=0; i<ArraySize(azHelp); i++){ |
| 12451 | if( azHelp[i][0]=='.' || zPattern[0] ){ |
| @@ -12802,10 +12923,11 @@ | |
| 12923 | sqlite3_enable_load_extension(p->db, 1); |
| 12924 | #endif |
| 12925 | sqlite3_fileio_init(p->db, 0, 0); |
| 12926 | sqlite3_shathree_init(p->db, 0, 0); |
| 12927 | sqlite3_completion_init(p->db, 0, 0); |
| 12928 | sqlite3_uint_init(p->db, 0, 0); |
| 12929 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 12930 | sqlite3_dbdata_init(p->db, 0, 0); |
| 12931 | #endif |
| 12932 | #ifdef SQLITE_HAVE_ZLIB |
| 12933 | sqlite3_zipfile_init(p->db, 0, 0); |
| @@ -13521,15 +13643,19 @@ | |
| 13643 | #endif |
| 13644 | char *zCmd; |
| 13645 | zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 13646 | if( system(zCmd) ){ |
| 13647 | utf8_printf(stderr, "Failed: [%s]\n", zCmd); |
| 13648 | }else{ |
| 13649 | /* Give the start/open/xdg-open command some time to get |
| 13650 | ** going before we continue, and potential delete the |
| 13651 | ** p->zTempFile data file out from under it */ |
| 13652 | sqlite3_sleep(2000); |
| 13653 | } |
| 13654 | sqlite3_free(zCmd); |
| 13655 | outputModePop(p); |
| 13656 | p->doXdgOpen = 0; |
| 13657 | } |
| 13658 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ |
| 13659 | } |
| 13660 | p->outfile[0] = 0; |
| 13661 | p->out = stdout; |
| @@ -13601,16 +13727,11 @@ | |
| 13727 | if( p->db==0 ) return 1; |
| 13728 | rc = sqlite3_prepare_v2(p->db, |
| 13729 | "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
| 13730 | -1, &pStmt, 0); |
| 13731 | if( rc ){ |
| 13732 | utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); |
| 13733 | sqlite3_finalize(pStmt); |
| 13734 | return 1; |
| 13735 | } |
| 13736 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 13737 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| @@ -13815,13 +13936,25 @@ | |
| 13936 | p->zTempFile = 0; |
| 13937 | if( p->db ){ |
| 13938 | sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); |
| 13939 | } |
| 13940 | if( p->zTempFile==0 ){ |
| 13941 | /* If p->db is an in-memory database then the TEMPFILENAME file-control |
| 13942 | ** will not work and we will need to fallback to guessing */ |
| 13943 | char *zTemp; |
| 13944 | sqlite3_uint64 r; |
| 13945 | sqlite3_randomness(sizeof(r), &r); |
| 13946 | zTemp = getenv("TEMP"); |
| 13947 | if( zTemp==0 ) zTemp = getenv("TMP"); |
| 13948 | if( zTemp==0 ){ |
| 13949 | #ifdef _WIN32 |
| 13950 | zTemp = "\\tmp"; |
| 13951 | #else |
| 13952 | zTemp = "/tmp"; |
| 13953 | #endif |
| 13954 | } |
| 13955 | p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix); |
| 13956 | }else{ |
| 13957 | p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); |
| 13958 | } |
| 13959 | if( p->zTempFile==0 ){ |
| 13960 | raw_printf(stderr, "out of memory\n"); |
| @@ -15841,11 +15974,12 @@ | |
| 15974 | rc = recoverDatabaseCmd(p, nArg, azArg); |
| 15975 | }else |
| 15976 | #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ |
| 15977 | |
| 15978 | if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ |
| 15979 | char *zLike = 0; |
| 15980 | char *zSql; |
| 15981 | int i; |
| 15982 | int savedShowHeader = p->showHeader; |
| 15983 | int savedShellFlags = p->shellFlgs; |
| 15984 | ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo); |
| 15985 | for(i=1; i<nArg; i++){ |
| @@ -15869,16 +16003,14 @@ | |
| 16003 | raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); |
| 16004 | rc = 1; |
| 16005 | goto meta_command_exit; |
| 16006 | } |
| 16007 | }else if( zLike ){ |
| 16008 | zLike = sqlite3_mprintf("%z OR name LIKE %Q ESCAPE '\\'", |
| 16009 | zLike, azArg[i]); |
| 16010 | }else{ |
| 16011 | zLike = sqlite3_mprintf("name LIKE %Q ESCAPE '\\'", azArg[i]); |
| 16012 | } |
| 16013 | } |
| 16014 | |
| 16015 | open_db(p, 0); |
| 16016 | |
| @@ -15892,39 +16024,29 @@ | |
| 16024 | /* Set writable_schema=ON since doing so forces SQLite to initialize |
| 16025 | ** as much of the schema as it can even if the sqlite_master table is |
| 16026 | ** corrupt. */ |
| 16027 | sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); |
| 16028 | p->nErr = 0; |
| 16029 | if( zLike==0 ) zLike = sqlite3_mprintf("true"); |
| 16030 | zSql = sqlite3_mprintf( |
| 16031 | "SELECT name, type, sql FROM sqlite_master " |
| 16032 | "WHERE (%s) AND type=='table'" |
| 16033 | " AND sql NOT NULL" |
| 16034 | " ORDER BY tbl_name='sqlite_sequence', rowid", |
| 16035 | zLike |
| 16036 | ); |
| 16037 | run_schema_dump_query(p,zSql); |
| 16038 | sqlite3_free(zSql); |
| 16039 | zSql = sqlite3_mprintf( |
| 16040 | "SELECT sql FROM sqlite_master " |
| 16041 | "WHERE (%s) AND sql NOT NULL" |
| 16042 | " AND type IN ('index','trigger','view')", |
| 16043 | zLike |
| 16044 | ); |
| 16045 | run_table_dump_query(p, zSql); |
| 16046 | sqlite3_free(zSql); |
| 16047 | sqlite3_free(zLike); |
| 16048 | if( p->writableSchema ){ |
| 16049 | raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); |
| 16050 | p->writableSchema = 0; |
| 16051 | } |
| 16052 | sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); |
| @@ -16023,20 +16145,32 @@ | |
| 16145 | { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" }, |
| 16146 | /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/ |
| 16147 | { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" }, |
| 16148 | { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" }, |
| 16149 | { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" }, |
| 16150 | { "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" }, |
| 16151 | }; |
| 16152 | int filectrl = -1; |
| 16153 | int iCtrl = -1; |
| 16154 | sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */ |
| 16155 | int isOk = 0; /* 0: usage 1: %lld 2: no-result */ |
| 16156 | int n2, i; |
| 16157 | const char *zCmd = 0; |
| 16158 | const char *zSchema = 0; |
| 16159 | |
| 16160 | open_db(p, 0); |
| 16161 | zCmd = nArg>=2 ? azArg[1] : "help"; |
| 16162 | |
| 16163 | if( zCmd[0]=='-' |
| 16164 | && (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0) |
| 16165 | && nArg>=4 |
| 16166 | ){ |
| 16167 | zSchema = azArg[2]; |
| 16168 | for(i=3; i<nArg; i++) azArg[i-2] = azArg[i]; |
| 16169 | nArg -= 2; |
| 16170 | zCmd = azArg[1]; |
| 16171 | } |
| 16172 | |
| 16173 | /* The argument can optionally begin with "-" or "--" */ |
| 16174 | if( zCmd[0]=='-' && zCmd[1] ){ |
| 16175 | zCmd++; |
| 16176 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| @@ -16075,51 +16209,63 @@ | |
| 16209 | }else{ |
| 16210 | switch(filectrl){ |
| 16211 | case SQLITE_FCNTL_SIZE_LIMIT: { |
| 16212 | if( nArg!=2 && nArg!=3 ) break; |
| 16213 | iRes = nArg==3 ? integerValue(azArg[2]) : -1; |
| 16214 | sqlite3_file_control(p->db, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes); |
| 16215 | isOk = 1; |
| 16216 | break; |
| 16217 | } |
| 16218 | case SQLITE_FCNTL_LOCK_TIMEOUT: |
| 16219 | case SQLITE_FCNTL_CHUNK_SIZE: { |
| 16220 | int x; |
| 16221 | if( nArg!=3 ) break; |
| 16222 | x = (int)integerValue(azArg[2]); |
| 16223 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 16224 | isOk = 2; |
| 16225 | break; |
| 16226 | } |
| 16227 | case SQLITE_FCNTL_PERSIST_WAL: |
| 16228 | case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { |
| 16229 | int x; |
| 16230 | if( nArg!=2 && nArg!=3 ) break; |
| 16231 | x = nArg==3 ? booleanValue(azArg[2]) : -1; |
| 16232 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 16233 | iRes = x; |
| 16234 | isOk = 1; |
| 16235 | break; |
| 16236 | } |
| 16237 | case SQLITE_FCNTL_HAS_MOVED: { |
| 16238 | int x; |
| 16239 | if( nArg!=2 ) break; |
| 16240 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 16241 | iRes = x; |
| 16242 | isOk = 1; |
| 16243 | break; |
| 16244 | } |
| 16245 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 16246 | char *z = 0; |
| 16247 | if( nArg!=2 ) break; |
| 16248 | sqlite3_file_control(p->db, zSchema, filectrl, &z); |
| 16249 | if( z ){ |
| 16250 | utf8_printf(p->out, "%s\n", z); |
| 16251 | sqlite3_free(z); |
| 16252 | } |
| 16253 | isOk = 2; |
| 16254 | break; |
| 16255 | } |
| 16256 | case SQLITE_FCNTL_RESERVE_BYTES: { |
| 16257 | int x; |
| 16258 | if( nArg>=3 ){ |
| 16259 | x = atoi(azArg[2]); |
| 16260 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 16261 | } |
| 16262 | x = -1; |
| 16263 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 16264 | utf8_printf(p->out,"%d\n", x); |
| 16265 | isOk = 2; |
| 16266 | break; |
| 16267 | } |
| 16268 | } |
| 16269 | } |
| 16270 | if( isOk==0 && iCtrl>=0 ){ |
| 16271 | utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); |
| @@ -16850,46 +16996,70 @@ | |
| 16996 | |
| 16997 | if( (c=='o' |
| 16998 | && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) |
| 16999 | || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) |
| 17000 | ){ |
| 17001 | const char *zFile = 0; |
| 17002 | int bTxtMode = 0; |
| 17003 | int i; |
| 17004 | int eMode = 0; |
| 17005 | int bBOM = 0; |
| 17006 | int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ |
| 17007 | |
| 17008 | if( c=='e' ){ |
| 17009 | eMode = 'x'; |
| 17010 | bOnce = 2; |
| 17011 | }else if( strncmp(azArg[0],"once",n)==0 ){ |
| 17012 | bOnce = 1; |
| 17013 | } |
| 17014 | for(i=1; i<nArg; i++){ |
| 17015 | char *z = azArg[i]; |
| 17016 | if( z[0]=='-' ){ |
| 17017 | if( z[1]=='-' ) z++; |
| 17018 | if( strcmp(z,"-bom")==0 ){ |
| 17019 | bBOM = 1; |
| 17020 | }else if( c!='e' && strcmp(z,"-x")==0 ){ |
| 17021 | eMode = 'x'; /* spreadsheet */ |
| 17022 | }else if( c!='e' && strcmp(z,"-e")==0 ){ |
| 17023 | eMode = 'e'; /* text editor */ |
| 17024 | }else{ |
| 17025 | utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", |
| 17026 | azArg[i]); |
| 17027 | showHelp(p->out, azArg[0]); |
| 17028 | rc = 1; |
| 17029 | goto meta_command_exit; |
| 17030 | } |
| 17031 | }else if( zFile==0 ){ |
| 17032 | zFile = z; |
| 17033 | }else{ |
| 17034 | utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n", |
| 17035 | azArg[i]); |
| 17036 | showHelp(p->out, azArg[0]); |
| 17037 | rc = 1; |
| 17038 | goto meta_command_exit; |
| 17039 | } |
| 17040 | } |
| 17041 | if( zFile==0 ) zFile = "stdout"; |
| 17042 | if( bOnce ){ |
| 17043 | p->outCount = 2; |
| 17044 | }else{ |
| 17045 | p->outCount = 0; |
| 17046 | } |
| 17047 | output_reset(p); |
| 17048 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 17049 | if( eMode=='e' || eMode=='x' ){ |
| 17050 | p->doXdgOpen = 1; |
| 17051 | outputModePush(p); |
| 17052 | if( eMode=='x' ){ |
| 17053 | /* spreadsheet mode. Output as CSV. */ |
| 17054 | newTempFile(p, "csv"); |
| 17055 | ShellClearFlag(p, SHFLG_Echo); |
| 17056 | p->mode = MODE_Csv; |
| 17057 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 17058 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 17059 | }else{ |
| 17060 | /* text editor mode */ |
| 17061 | newTempFile(p, "txt"); |
| 17062 | bTxtMode = 1; |
| 17063 | } |
| 17064 | zFile = p->zTempFile; |
| 17065 | } |
| @@ -16904,10 +17074,11 @@ | |
| 17074 | if( p->out==0 ){ |
| 17075 | utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); |
| 17076 | p->out = stdout; |
| 17077 | rc = 1; |
| 17078 | }else{ |
| 17079 | if( bBOM ) fprintf(p->out,"\357\273\277"); |
| 17080 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 17081 | } |
| 17082 | #endif |
| 17083 | }else{ |
| 17084 | p->out = output_file_open(zFile, bTxtMode); |
| @@ -16916,10 +17087,11 @@ | |
| 17087 | utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
| 17088 | } |
| 17089 | p->out = stdout; |
| 17090 | rc = 1; |
| 17091 | } else { |
| 17092 | if( bBOM ) fprintf(p->out,"\357\273\277"); |
| 17093 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 17094 | } |
| 17095 | } |
| 17096 | }else |
| 17097 | |
| @@ -17979,11 +18151,10 @@ | |
| 18151 | #endif |
| 18152 | { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " }, |
| 18153 | { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" }, |
| 18154 | { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" }, |
| 18155 | { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" }, |
| 18156 | }; |
| 18157 | int testctrl = -1; |
| 18158 | int iCtrl = -1; |
| 18159 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 18160 | int isOk = 0; |
| @@ -18032,11 +18203,10 @@ | |
| 18203 | }else{ |
| 18204 | switch(testctrl){ |
| 18205 | |
| 18206 | /* sqlite3_test_control(int, db, int) */ |
| 18207 | case SQLITE_TESTCTRL_OPTIMIZATIONS: |
| 18208 | if( nArg==3 ){ |
| 18209 | int opt = (int)strtol(azArg[2], 0, 0); |
| 18210 | rc2 = sqlite3_test_control(testctrl, p->db, opt); |
| 18211 | isOk = 3; |
| 18212 | } |
| @@ -18804,18 +18974,22 @@ | |
| 18974 | /* |
| 18975 | ** Output text to the console in a font that attracts extra attention. |
| 18976 | */ |
| 18977 | #ifdef _WIN32 |
| 18978 | static void printBold(const char *zText){ |
| 18979 | #if !SQLITE_OS_WINRT |
| 18980 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); |
| 18981 | CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; |
| 18982 | GetConsoleScreenBufferInfo(out, &defaultScreenInfo); |
| 18983 | SetConsoleTextAttribute(out, |
| 18984 | FOREGROUND_RED|FOREGROUND_INTENSITY |
| 18985 | ); |
| 18986 | #endif |
| 18987 | printf("%s", zText); |
| 18988 | #if !SQLITE_OS_WINRT |
| 18989 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 18990 | #endif |
| 18991 | } |
| 18992 | #else |
| 18993 | static void printBold(const char *zText){ |
| 18994 | printf("\033[1m%s\033[0m", zText); |
| 18995 | } |
| @@ -18879,11 +19053,15 @@ | |
| 19053 | "attach debugger to process %d and press any key to continue.\n", |
| 19054 | GETPID()); |
| 19055 | fgetc(stdin); |
| 19056 | }else{ |
| 19057 | #if defined(_WIN32) || defined(WIN32) |
| 19058 | #if SQLITE_OS_WINRT |
| 19059 | __debugbreak(); |
| 19060 | #else |
| 19061 | DebugBreak(); |
| 19062 | #endif |
| 19063 | #elif defined(SIGTRAP) |
| 19064 | raise(SIGTRAP); |
| 19065 | #endif |
| 19066 | } |
| 19067 | } |
| 19068 |
+2
-2
| --- src/shun.c | ||
| +++ src/shun.c | ||
| @@ -187,11 +187,11 @@ | ||
| 187 | 187 | @ sight - set the "hidden" tag on such artifacts instead.</p> |
| 188 | 188 | @ |
| 189 | 189 | @ <blockquote> |
| 190 | 190 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 191 | 191 | login_insert_csrf_secret(); |
| 192 | - @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> | |
| 192 | + @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> | |
| 193 | 193 | if( zShun ){ |
| 194 | 194 | if( strlen(zShun) ){ |
| 195 | 195 | @ %h(zShun) |
| 196 | 196 | }else if( nRcvid ){ |
| 197 | 197 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| @@ -214,11 +214,11 @@ | ||
| 214 | 214 | @ operations.</p> |
| 215 | 215 | @ |
| 216 | 216 | @ <blockquote> |
| 217 | 217 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 218 | 218 | login_insert_csrf_secret(); |
| 219 | - @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> | |
| 219 | + @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> | |
| 220 | 220 | if( zAccept ){ |
| 221 | 221 | if( strlen(zAccept) ){ |
| 222 | 222 | @ %h(zAccept) |
| 223 | 223 | }else if( nRcvid ){ |
| 224 | 224 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| 225 | 225 |
| --- src/shun.c | |
| +++ src/shun.c | |
| @@ -187,11 +187,11 @@ | |
| 187 | @ sight - set the "hidden" tag on such artifacts instead.</p> |
| 188 | @ |
| 189 | @ <blockquote> |
| 190 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 191 | login_insert_csrf_secret(); |
| 192 | @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> |
| 193 | if( zShun ){ |
| 194 | if( strlen(zShun) ){ |
| 195 | @ %h(zShun) |
| 196 | }else if( nRcvid ){ |
| 197 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| @@ -214,11 +214,11 @@ | |
| 214 | @ operations.</p> |
| 215 | @ |
| 216 | @ <blockquote> |
| 217 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 218 | login_insert_csrf_secret(); |
| 219 | @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> |
| 220 | if( zAccept ){ |
| 221 | if( strlen(zAccept) ){ |
| 222 | @ %h(zAccept) |
| 223 | }else if( nRcvid ){ |
| 224 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| 225 |
| --- src/shun.c | |
| +++ src/shun.c | |
| @@ -187,11 +187,11 @@ | |
| 187 | @ sight - set the "hidden" tag on such artifacts instead.</p> |
| 188 | @ |
| 189 | @ <blockquote> |
| 190 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 191 | login_insert_csrf_secret(); |
| 192 | @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> |
| 193 | if( zShun ){ |
| 194 | if( strlen(zShun) ){ |
| 195 | @ %h(zShun) |
| 196 | }else if( nRcvid ){ |
| 197 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| @@ -214,11 +214,11 @@ | |
| 214 | @ operations.</p> |
| 215 | @ |
| 216 | @ <blockquote> |
| 217 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 218 | login_insert_csrf_secret(); |
| 219 | @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> |
| 220 | if( zAccept ){ |
| 221 | if( strlen(zAccept) ){ |
| 222 | @ %h(zAccept) |
| 223 | }else if( nRcvid ){ |
| 224 | db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
| 225 |
+55
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -688,10 +688,62 @@ | ||
| 688 | 688 | } |
| 689 | 689 | } |
| 690 | 690 | return zResult; |
| 691 | 691 | } |
| 692 | 692 | |
| 693 | +extern const struct strctCssDefaults { | |
| 694 | +/* From the generated default_css.h, which we cannot #include here | |
| 695 | +** without causing an ODR violation. | |
| 696 | +*/ | |
| 697 | + const char *elementClass; /* Name of element needed */ | |
| 698 | + const char *value; /* CSS text */ | |
| 699 | +} cssDefaultList[]; | |
| 700 | + | |
| 701 | +/* | |
| 702 | +** Emits the list of built-in default CSS selectors. Intended | |
| 703 | +** for use only from the /setup_skinedit page. | |
| 704 | +*/ | |
| 705 | +static void skin_emit_css_defaults(){ | |
| 706 | + struct strctCssDefaults const * pCss; | |
| 707 | + fossil_print("<h1>CSS Defaults</h1>"); | |
| 708 | + fossil_print("If a skin defines any of the following CSS selectors, " | |
| 709 | + "that definition replaces the default, as opposed to " | |
| 710 | + "cascading from it. "); | |
| 711 | + fossil_print("See <a href=\"https://fossil-scm.org/fossil/" | |
| 712 | + "doc/trunk/www/css-tricks.md\">this " | |
| 713 | + "document</a> for more details."); | |
| 714 | + /* To discuss: do we want to list only the default selectors or | |
| 715 | + ** also their default values? The latter increases the size of the | |
| 716 | + ** page considerably, but is arguably more useful. We could, of | |
| 717 | + ** course, offer a URL param to toggle the view, but that currently | |
| 718 | + ** seems like overkill. | |
| 719 | + ** | |
| 720 | + ** Be sure to adjust the default_css.txt #setup_skinedit_css entry | |
| 721 | + ** for whichever impl ends up being selected. | |
| 722 | + */ | |
| 723 | +#if 1 | |
| 724 | + /* List impl which elides style values */ | |
| 725 | + fossil_print("<div class=\"columns\" " | |
| 726 | + "id=\"setup_skinedit_css_defaults\"><ul>"); | |
| 727 | + for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){ | |
| 728 | + fossil_print("<li>%s</li>", pCss->elementClass); | |
| 729 | + } | |
| 730 | + fossil_print("</ul>"); | |
| 731 | +#else | |
| 732 | + /* Table impl which also includes style values. */ | |
| 733 | + fossil_print("<table id=\"setup_skinedit_css_defaults\"><tbody>"); | |
| 734 | + for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){ | |
| 735 | + fossil_print("<tr><td>%s</td>", pCss->elementClass); | |
| 736 | + /* A TD element apparently cannot be told to scroll its contents, | |
| 737 | + ** so we require a DIV inside the value TD to scroll the long | |
| 738 | + ** url(data:...) entries. */ | |
| 739 | + fossil_print("<td><div>%s</div></td>", pCss->value); | |
| 740 | + fossil_print("</td></tr>"); | |
| 741 | + } | |
| 742 | + fossil_print("</tbody></table>"); | |
| 743 | +#endif | |
| 744 | +} | |
| 693 | 745 | |
| 694 | 746 | /* |
| 695 | 747 | ** WEBPAGE: setup_skinedit |
| 696 | 748 | ** |
| 697 | 749 | ** Edit aspects of a skin determined by the w= query parameter. |
| @@ -814,10 +866,13 @@ | ||
| 814 | 866 | blob_reset(&from); |
| 815 | 867 | blob_reset(&to); |
| 816 | 868 | blob_reset(&out); |
| 817 | 869 | } |
| 818 | 870 | @ </div></form> |
| 871 | + if(ii==0/*CSS*/){ | |
| 872 | + skin_emit_css_defaults(); | |
| 873 | + } | |
| 819 | 874 | style_footer(); |
| 820 | 875 | db_end_transaction(0); |
| 821 | 876 | } |
| 822 | 877 | |
| 823 | 878 | /* |
| 824 | 879 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -688,10 +688,62 @@ | |
| 688 | } |
| 689 | } |
| 690 | return zResult; |
| 691 | } |
| 692 | |
| 693 | |
| 694 | /* |
| 695 | ** WEBPAGE: setup_skinedit |
| 696 | ** |
| 697 | ** Edit aspects of a skin determined by the w= query parameter. |
| @@ -814,10 +866,13 @@ | |
| 814 | blob_reset(&from); |
| 815 | blob_reset(&to); |
| 816 | blob_reset(&out); |
| 817 | } |
| 818 | @ </div></form> |
| 819 | style_footer(); |
| 820 | db_end_transaction(0); |
| 821 | } |
| 822 | |
| 823 | /* |
| 824 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -688,10 +688,62 @@ | |
| 688 | } |
| 689 | } |
| 690 | return zResult; |
| 691 | } |
| 692 | |
| 693 | extern const struct strctCssDefaults { |
| 694 | /* From the generated default_css.h, which we cannot #include here |
| 695 | ** without causing an ODR violation. |
| 696 | */ |
| 697 | const char *elementClass; /* Name of element needed */ |
| 698 | const char *value; /* CSS text */ |
| 699 | } cssDefaultList[]; |
| 700 | |
| 701 | /* |
| 702 | ** Emits the list of built-in default CSS selectors. Intended |
| 703 | ** for use only from the /setup_skinedit page. |
| 704 | */ |
| 705 | static void skin_emit_css_defaults(){ |
| 706 | struct strctCssDefaults const * pCss; |
| 707 | fossil_print("<h1>CSS Defaults</h1>"); |
| 708 | fossil_print("If a skin defines any of the following CSS selectors, " |
| 709 | "that definition replaces the default, as opposed to " |
| 710 | "cascading from it. "); |
| 711 | fossil_print("See <a href=\"https://fossil-scm.org/fossil/" |
| 712 | "doc/trunk/www/css-tricks.md\">this " |
| 713 | "document</a> for more details."); |
| 714 | /* To discuss: do we want to list only the default selectors or |
| 715 | ** also their default values? The latter increases the size of the |
| 716 | ** page considerably, but is arguably more useful. We could, of |
| 717 | ** course, offer a URL param to toggle the view, but that currently |
| 718 | ** seems like overkill. |
| 719 | ** |
| 720 | ** Be sure to adjust the default_css.txt #setup_skinedit_css entry |
| 721 | ** for whichever impl ends up being selected. |
| 722 | */ |
| 723 | #if 1 |
| 724 | /* List impl which elides style values */ |
| 725 | fossil_print("<div class=\"columns\" " |
| 726 | "id=\"setup_skinedit_css_defaults\"><ul>"); |
| 727 | for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){ |
| 728 | fossil_print("<li>%s</li>", pCss->elementClass); |
| 729 | } |
| 730 | fossil_print("</ul>"); |
| 731 | #else |
| 732 | /* Table impl which also includes style values. */ |
| 733 | fossil_print("<table id=\"setup_skinedit_css_defaults\"><tbody>"); |
| 734 | for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){ |
| 735 | fossil_print("<tr><td>%s</td>", pCss->elementClass); |
| 736 | /* A TD element apparently cannot be told to scroll its contents, |
| 737 | ** so we require a DIV inside the value TD to scroll the long |
| 738 | ** url(data:...) entries. */ |
| 739 | fossil_print("<td><div>%s</div></td>", pCss->value); |
| 740 | fossil_print("</td></tr>"); |
| 741 | } |
| 742 | fossil_print("</tbody></table>"); |
| 743 | #endif |
| 744 | } |
| 745 | |
| 746 | /* |
| 747 | ** WEBPAGE: setup_skinedit |
| 748 | ** |
| 749 | ** Edit aspects of a skin determined by the w= query parameter. |
| @@ -814,10 +866,13 @@ | |
| 866 | blob_reset(&from); |
| 867 | blob_reset(&to); |
| 868 | blob_reset(&out); |
| 869 | } |
| 870 | @ </div></form> |
| 871 | if(ii==0/*CSS*/){ |
| 872 | skin_emit_css_defaults(); |
| 873 | } |
| 874 | style_footer(); |
| 875 | db_end_transaction(0); |
| 876 | } |
| 877 | |
| 878 | /* |
| 879 |
+1673
-736
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -216,10 +216,13 @@ | ||
| 216 | 216 | "ENABLE_ATOMIC_WRITE", |
| 217 | 217 | #endif |
| 218 | 218 | #if SQLITE_ENABLE_BATCH_ATOMIC_WRITE |
| 219 | 219 | "ENABLE_BATCH_ATOMIC_WRITE", |
| 220 | 220 | #endif |
| 221 | +#if SQLITE_ENABLE_BYTECODE_VTAB | |
| 222 | + "ENABLE_BYTECODE_VTAB", | |
| 223 | +#endif | |
| 221 | 224 | #if SQLITE_ENABLE_CEROD |
| 222 | 225 | "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), |
| 223 | 226 | #endif |
| 224 | 227 | #if SQLITE_ENABLE_COLUMN_METADATA |
| 225 | 228 | "ENABLE_COLUMN_METADATA", |
| @@ -534,13 +537,10 @@ | ||
| 534 | 537 | "OMIT_BETWEEN_OPTIMIZATION", |
| 535 | 538 | #endif |
| 536 | 539 | #if SQLITE_OMIT_BLOB_LITERAL |
| 537 | 540 | "OMIT_BLOB_LITERAL", |
| 538 | 541 | #endif |
| 539 | -#if SQLITE_OMIT_BTREECOUNT | |
| 540 | - "OMIT_BTREECOUNT", | |
| 541 | -#endif | |
| 542 | 542 | #if SQLITE_OMIT_CAST |
| 543 | 543 | "OMIT_CAST", |
| 544 | 544 | #endif |
| 545 | 545 | #if SQLITE_OMIT_CHECK |
| 546 | 546 | "OMIT_CHECK", |
| @@ -1162,11 +1162,11 @@ | ||
| 1162 | 1162 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | 1163 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | 1164 | */ |
| 1165 | 1165 | #define SQLITE_VERSION "3.32.0" |
| 1166 | 1166 | #define SQLITE_VERSION_NUMBER 3032000 |
| 1167 | -#define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525" | |
| 1167 | +#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" | |
| 1168 | 1168 | |
| 1169 | 1169 | /* |
| 1170 | 1170 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | 1171 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | 1172 | ** |
| @@ -1336,30 +1336,26 @@ | ||
| 1336 | 1336 | ** for the [sqlite3] object. |
| 1337 | 1337 | ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if |
| 1338 | 1338 | ** the [sqlite3] object is successfully destroyed and all associated |
| 1339 | 1339 | ** resources are deallocated. |
| 1340 | 1340 | ** |
| 1341 | +** Ideally, applications should [sqlite3_finalize | finalize] all | |
| 1342 | +** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and | |
| 1343 | +** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated | |
| 1344 | +** with the [sqlite3] object prior to attempting to close the object. | |
| 1341 | 1345 | ** ^If the database connection is associated with unfinalized prepared |
| 1342 | -** statements or unfinished sqlite3_backup objects then sqlite3_close() | |
| 1343 | -** will leave the database connection open and return [SQLITE_BUSY]. | |
| 1344 | -** ^If sqlite3_close_v2() is called with unfinalized prepared statements | |
| 1345 | -** and/or unfinished sqlite3_backups, then the database connection becomes | |
| 1346 | -** an unusable "zombie" which will automatically be deallocated when the | |
| 1347 | -** last prepared statement is finalized or the last sqlite3_backup is | |
| 1348 | -** finished. The sqlite3_close_v2() interface is intended for use with | |
| 1349 | -** host languages that are garbage collected, and where the order in which | |
| 1350 | -** destructors are called is arbitrary. | |
| 1351 | -** | |
| 1352 | -** Applications should [sqlite3_finalize | finalize] all [prepared statements], | |
| 1353 | -** [sqlite3_blob_close | close] all [BLOB handles], and | |
| 1354 | -** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated | |
| 1355 | -** with the [sqlite3] object prior to attempting to close the object. ^If | |
| 1356 | -** sqlite3_close_v2() is called on a [database connection] that still has | |
| 1357 | -** outstanding [prepared statements], [BLOB handles], and/or | |
| 1358 | -** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation | |
| 1359 | -** of resources is deferred until all [prepared statements], [BLOB handles], | |
| 1360 | -** and [sqlite3_backup] objects are also destroyed. | |
| 1346 | +** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then | |
| 1347 | +** sqlite3_close() will leave the database connection open and return | |
| 1348 | +** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared | |
| 1349 | +** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups, | |
| 1350 | +** it returns [SQLITE_OK] regardless, but instead of deallocating the database | |
| 1351 | +** connection immediately, it marks the database connection as an unusable | |
| 1352 | +** "zombie" and makes arrangements to automatically deallocate the database | |
| 1353 | +** connection after all prepared statements are finalized, all BLOB handles | |
| 1354 | +** are closed, and all backups have finished. The sqlite3_close_v2() interface | |
| 1355 | +** is intended for use with host languages that are garbage collected, and | |
| 1356 | +** where the order in which destructors are called is arbitrary. | |
| 1361 | 1357 | ** |
| 1362 | 1358 | ** ^If an [sqlite3] object is destroyed while a transaction is open, |
| 1363 | 1359 | ** the transaction is automatically rolled back. |
| 1364 | 1360 | ** |
| 1365 | 1361 | ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] |
| @@ -1544,22 +1540,25 @@ | ||
| 1544 | 1540 | #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) |
| 1545 | 1541 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1546 | 1542 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1547 | 1543 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1548 | 1544 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1545 | +#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) | |
| 1549 | 1546 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1550 | 1547 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1551 | 1548 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1552 | 1549 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1550 | +#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) | |
| 1553 | 1551 | #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) |
| 1554 | 1552 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| 1555 | 1553 | #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) |
| 1556 | 1554 | #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) |
| 1557 | 1555 | #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ |
| 1558 | 1556 | #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8)) |
| 1559 | 1557 | #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) |
| 1560 | 1558 | #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) |
| 1559 | +#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8)) | |
| 1561 | 1560 | #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) |
| 1562 | 1561 | #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) |
| 1563 | 1562 | #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) |
| 1564 | 1563 | #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) |
| 1565 | 1564 | #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) |
| @@ -2124,14 +2123,16 @@ | ||
| 2124 | 2123 | ** so that all subsequent write operations are independent. |
| 2125 | 2124 | ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without |
| 2126 | 2125 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. |
| 2127 | 2126 | ** |
| 2128 | 2127 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] |
| 2129 | -** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain | |
| 2130 | -** a file lock using the xLock or xShmLock methods of the VFS to wait | |
| 2131 | -** for up to M milliseconds before failing, where M is the single | |
| 2132 | -** unsigned integer parameter. | |
| 2128 | +** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS | |
| 2129 | +** to block for up to M milliseconds before failing when attempting to | |
| 2130 | +** obtain a file lock using the xLock or xShmLock methods of the VFS. | |
| 2131 | +** The parameter is a pointer to a 32-bit signed integer that contains | |
| 2132 | +** the value that M is to be set to. Before returning, the 32-bit signed | |
| 2133 | +** integer is overwritten with the previous value of M. | |
| 2133 | 2134 | ** |
| 2134 | 2135 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 2135 | 2136 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 2136 | 2137 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 2137 | 2138 | ** The "data version" for the pager is written into the pointer. The |
| @@ -2148,10 +2149,15 @@ | ||
| 2148 | 2149 | ** a single attached database that occur due to other database connections, |
| 2149 | 2150 | ** but omits changes implemented by the database connection on which it is |
| 2150 | 2151 | ** called. This file control is the only mechanism to detect changes that |
| 2151 | 2152 | ** happen either internally or externally and that are associated with |
| 2152 | 2153 | ** a particular attached database. |
| 2154 | +** | |
| 2155 | +** <li>[[SQLITE_FCNTL_CKPT_START]] | |
| 2156 | +** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint | |
| 2157 | +** in wal mode before the client starts to copy pages from the wal | |
| 2158 | +** file to the database file. | |
| 2153 | 2159 | ** |
| 2154 | 2160 | ** <li>[[SQLITE_FCNTL_CKPT_DONE]] |
| 2155 | 2161 | ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint |
| 2156 | 2162 | ** in wal mode after the client has finished copying pages from the wal |
| 2157 | 2163 | ** file to the database file, but before the *-shm file is updated to |
| @@ -2192,10 +2198,12 @@ | ||
| 2192 | 2198 | #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 |
| 2193 | 2199 | #define SQLITE_FCNTL_LOCK_TIMEOUT 34 |
| 2194 | 2200 | #define SQLITE_FCNTL_DATA_VERSION 35 |
| 2195 | 2201 | #define SQLITE_FCNTL_SIZE_LIMIT 36 |
| 2196 | 2202 | #define SQLITE_FCNTL_CKPT_DONE 37 |
| 2203 | +#define SQLITE_FCNTL_RESERVE_BYTES 38 | |
| 2204 | +#define SQLITE_FCNTL_CKPT_START 39 | |
| 2197 | 2205 | |
| 2198 | 2206 | /* deprecated names */ |
| 2199 | 2207 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 2200 | 2208 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 2201 | 2209 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -4570,12 +4578,23 @@ | ||
| 4570 | 4578 | ** |
| 4571 | 4579 | ** These are utility routines, useful to [VFS|custom VFS implementations], |
| 4572 | 4580 | ** that check if a database file was a URI that contained a specific query |
| 4573 | 4581 | ** parameter, and if so obtains the value of that query parameter. |
| 4574 | 4582 | ** |
| 4575 | -** If F is the database filename pointer passed into the xOpen() method of | |
| 4576 | -** a VFS implementation or it is the return value of [sqlite3_db_filename()] | |
| 4583 | +** The first parameter to these interfaces (hereafter referred to | |
| 4584 | +** as F) must be one of: | |
| 4585 | +** <ul> | |
| 4586 | +** <li> A database filename pointer created by the SQLite core and | |
| 4587 | +** passed into the xOpen() method of a VFS implemention, or | |
| 4588 | +** <li> A filename obtained from [sqlite3_db_filename()], or | |
| 4589 | +** <li> A new filename constructed using [sqlite3_create_filename()]. | |
| 4590 | +** </ul> | |
| 4591 | +** If the F parameter is not one of the above, then the behavior is | |
| 4592 | +** undefined and probably undesirable. Older versions of SQLite were | |
| 4593 | +** more tolerant of invalid F parameters than newer versions. | |
| 4594 | +** | |
| 4595 | +** If F is a suitable filename (as described in the previous paragraph) | |
| 4577 | 4596 | ** and if P is the name of the query parameter, then |
| 4578 | 4597 | ** sqlite3_uri_parameter(F,P) returns the value of the P |
| 4579 | 4598 | ** parameter if it exists or a NULL pointer if P does not appear as a |
| 4580 | 4599 | ** query parameter on F. If P is a query parameter of F and it |
| 4581 | 4600 | ** has no explicit value, then sqlite3_uri_parameter(F,P) returns |
| @@ -4654,10 +4673,29 @@ | ||
| 4654 | 4673 | */ |
| 4655 | 4674 | SQLITE_API const char *sqlite3_filename_database(const char*); |
| 4656 | 4675 | SQLITE_API const char *sqlite3_filename_journal(const char*); |
| 4657 | 4676 | SQLITE_API const char *sqlite3_filename_wal(const char*); |
| 4658 | 4677 | |
| 4678 | +/* | |
| 4679 | +** CAPI3REF: Database File Corresponding To A Journal | |
| 4680 | +** | |
| 4681 | +** ^If X is the name of a rollback or WAL-mode journal file that is | |
| 4682 | +** passed into the xOpen method of [sqlite3_vfs], then | |
| 4683 | +** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file] | |
| 4684 | +** object that represents the main database file. | |
| 4685 | +** | |
| 4686 | +** This routine is intended for use in custom [VFS] implementations | |
| 4687 | +** only. It is not a general-purpose interface. | |
| 4688 | +** The argument sqlite3_file_object(X) must be a filename pointer that | |
| 4689 | +** has been passed into [sqlite3_vfs].xOpen method where the | |
| 4690 | +** flags parameter to xOpen contains one of the bits | |
| 4691 | +** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use | |
| 4692 | +** of this routine results in undefined and probably undesirable | |
| 4693 | +** behavior. | |
| 4694 | +*/ | |
| 4695 | +SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); | |
| 4696 | + | |
| 4659 | 4697 | /* |
| 4660 | 4698 | ** CAPI3REF: Create and Destroy VFS Filenames |
| 4661 | 4699 | ** |
| 4662 | 4700 | ** These interfces are provided for use by [VFS shim] implementations and |
| 4663 | 4701 | ** are not useful outside of that context. |
| @@ -4688,11 +4726,11 @@ | ||
| 4688 | 4726 | ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may |
| 4689 | 4727 | ** be NULL pointers, though they can be empty strings. |
| 4690 | 4728 | ** |
| 4691 | 4729 | ** The sqlite3_free_filename(Y) routine releases a memory allocation |
| 4692 | 4730 | ** previously obtained from sqlite3_create_filename(). Invoking |
| 4693 | -** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op. | |
| 4731 | +** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op. | |
| 4694 | 4732 | ** |
| 4695 | 4733 | ** If the Y parameter to sqlite3_free_filename(Y) is anything other |
| 4696 | 4734 | ** than a NULL pointer or a pointer previously acquired from |
| 4697 | 4735 | ** sqlite3_create_filename(), then bad things such as heap |
| 4698 | 4736 | ** corruption or segfaults may occur. The value Y should be |
| @@ -5295,10 +5333,28 @@ | ||
| 5295 | 5333 | ** |
| 5296 | 5334 | ** ^The third argument is the value to bind to the parameter. |
| 5297 | 5335 | ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| 5298 | 5336 | ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter |
| 5299 | 5337 | ** is ignored and the end result is the same as sqlite3_bind_null(). |
| 5338 | +** ^If the third parameter to sqlite3_bind_text() is not NULL, then | |
| 5339 | +** it should be a pointer to well-formed UTF8 text. | |
| 5340 | +** ^If the third parameter to sqlite3_bind_text16() is not NULL, then | |
| 5341 | +** it should be a pointer to well-formed UTF16 text. | |
| 5342 | +** ^If the third parameter to sqlite3_bind_text64() is not NULL, then | |
| 5343 | +** it should be a pointer to a well-formed unicode string that is | |
| 5344 | +** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 | |
| 5345 | +** otherwise. | |
| 5346 | +** | |
| 5347 | +** [[byte-order determination rules]] ^The byte-order of | |
| 5348 | +** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) | |
| 5349 | +** found in first character, which is removed, or in the absence of a BOM | |
| 5350 | +** the byte order is the native byte order of the host | |
| 5351 | +** machine for sqlite3_bind_text16() or the byte order specified in | |
| 5352 | +** the 6th parameter for sqlite3_bind_text64().)^ | |
| 5353 | +** ^If UTF16 input text contains invalid unicode | |
| 5354 | +** characters, then SQLite might change those invalid characters | |
| 5355 | +** into the unicode replacement character: U+FFFD. | |
| 5300 | 5356 | ** |
| 5301 | 5357 | ** ^(In those routines that have a fourth argument, its value is the |
| 5302 | 5358 | ** number of bytes in the parameter. To be clear: the value is the |
| 5303 | 5359 | ** number of <u>bytes</u> in the value, not the number of characters.)^ |
| 5304 | 5360 | ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| @@ -5308,11 +5364,11 @@ | ||
| 5308 | 5364 | ** the behavior is undefined. |
| 5309 | 5365 | ** If a non-negative fourth parameter is provided to sqlite3_bind_text() |
| 5310 | 5366 | ** or sqlite3_bind_text16() or sqlite3_bind_text64() then |
| 5311 | 5367 | ** that parameter must be the byte offset |
| 5312 | 5368 | ** where the NUL terminator would occur assuming the string were NUL |
| 5313 | -** terminated. If any NUL characters occur at byte offsets less than | |
| 5369 | +** terminated. If any NUL characters occurs at byte offsets less than | |
| 5314 | 5370 | ** the value of the fourth parameter then the resulting string value will |
| 5315 | 5371 | ** contain embedded NULs. The result of expressions involving strings |
| 5316 | 5372 | ** with embedded NULs is undefined. |
| 5317 | 5373 | ** |
| 5318 | 5374 | ** ^The fifth argument to the BLOB and string binding interfaces |
| @@ -6633,12 +6689,13 @@ | ||
| 6633 | 6689 | ** cause the implemented SQL function to throw an exception. |
| 6634 | 6690 | ** ^SQLite uses the string pointed to by the |
| 6635 | 6691 | ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() |
| 6636 | 6692 | ** as the text of an error message. ^SQLite interprets the error |
| 6637 | 6693 | ** message string from sqlite3_result_error() as UTF-8. ^SQLite |
| 6638 | -** interprets the string from sqlite3_result_error16() as UTF-16 in native | |
| 6639 | -** byte order. ^If the third parameter to sqlite3_result_error() | |
| 6694 | +** interprets the string from sqlite3_result_error16() as UTF-16 using | |
| 6695 | +** the same [byte-order determination rules] as [sqlite3_bind_text16()]. | |
| 6696 | +** ^If the third parameter to sqlite3_result_error() | |
| 6640 | 6697 | ** or sqlite3_result_error16() is negative then SQLite takes as the error |
| 6641 | 6698 | ** message all text up through the first zero character. |
| 6642 | 6699 | ** ^If the third parameter to sqlite3_result_error() or |
| 6643 | 6700 | ** sqlite3_result_error16() is non-negative then SQLite takes that many |
| 6644 | 6701 | ** bytes (not characters) from the 2nd parameter as the error message. |
| @@ -6701,10 +6758,29 @@ | ||
| 6701 | 6758 | ** when it has finished using that result. |
| 6702 | 6759 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces |
| 6703 | 6760 | ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT |
| 6704 | 6761 | ** then SQLite makes a copy of the result into space obtained |
| 6705 | 6762 | ** from [sqlite3_malloc()] before it returns. |
| 6763 | +** | |
| 6764 | +** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and | |
| 6765 | +** sqlite3_result_text16be() routines, and for sqlite3_result_text64() | |
| 6766 | +** when the encoding is not UTF8, if the input UTF16 begins with a | |
| 6767 | +** byte-order mark (BOM, U+FEFF) then the BOM is removed from the | |
| 6768 | +** string and the rest of the string is interpreted according to the | |
| 6769 | +** byte-order specified by the BOM. ^The byte-order specified by | |
| 6770 | +** the BOM at the beginning of the text overrides the byte-order | |
| 6771 | +** specified by the interface procedure. ^So, for example, if | |
| 6772 | +** sqlite3_result_text16le() is invoked with text that begins | |
| 6773 | +** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the | |
| 6774 | +** first two bytes of input are skipped and the remaining input | |
| 6775 | +** is interpreted as UTF16BE text. | |
| 6776 | +** | |
| 6777 | +** ^For UTF16 input text to the sqlite3_result_text16(), | |
| 6778 | +** sqlite3_result_text16be(), sqlite3_result_text16le(), and | |
| 6779 | +** sqlite3_result_text64() routines, if the text contains invalid | |
| 6780 | +** UTF16 characters, the invalid characters might be converted | |
| 6781 | +** into the unicode replacement character, U+FFFD. | |
| 6706 | 6782 | ** |
| 6707 | 6783 | ** ^The sqlite3_result_value() interface sets the result of |
| 6708 | 6784 | ** the application-defined function to be a copy of the |
| 6709 | 6785 | ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The |
| 6710 | 6786 | ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] |
| @@ -8649,11 +8725,11 @@ | ||
| 8649 | 8725 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8650 | 8726 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8651 | 8727 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8652 | 8728 | #define SQLITE_TESTCTRL_ASSERT 12 |
| 8653 | 8729 | #define SQLITE_TESTCTRL_ALWAYS 13 |
| 8654 | -#define SQLITE_TESTCTRL_RESERVE 14 | |
| 8730 | +#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ | |
| 8655 | 8731 | #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 |
| 8656 | 8732 | #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ |
| 8657 | 8733 | #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ |
| 8658 | 8734 | #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 |
| 8659 | 8735 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 |
| @@ -13369,10 +13445,25 @@ | ||
| 13369 | 13445 | #pragma warn -aus /* Assigned value is never used */ |
| 13370 | 13446 | #pragma warn -csu /* Comparing signed and unsigned */ |
| 13371 | 13447 | #pragma warn -spa /* Suspicious pointer arithmetic */ |
| 13372 | 13448 | #endif |
| 13373 | 13449 | |
| 13450 | +/* | |
| 13451 | +** WAL mode depends on atomic aligned 32-bit loads and stores in a few | |
| 13452 | +** places. The following macros try to make this explicit. | |
| 13453 | +*/ | |
| 13454 | +#ifndef __has_feature | |
| 13455 | +# define __has_feature(x) 0 /* compatibility with non-clang compilers */ | |
| 13456 | +#endif | |
| 13457 | +#if GCC_VERSION>=4007000 || __has_feature(c_atomic) | |
| 13458 | +# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) | |
| 13459 | +# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) | |
| 13460 | +#else | |
| 13461 | +# define AtomicLoad(PTR) (*(PTR)) | |
| 13462 | +# define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) | |
| 13463 | +#endif | |
| 13464 | + | |
| 13374 | 13465 | /* |
| 13375 | 13466 | ** Include standard header files as necessary |
| 13376 | 13467 | */ |
| 13377 | 13468 | #ifdef HAVE_STDINT_H |
| 13378 | 13469 | #include <stdint.h> |
| @@ -14446,11 +14537,10 @@ | ||
| 14446 | 14537 | typedef struct BusyHandler BusyHandler; |
| 14447 | 14538 | struct BusyHandler { |
| 14448 | 14539 | int (*xBusyHandler)(void *,int); /* The busy callback */ |
| 14449 | 14540 | void *pBusyArg; /* First arg to busy callback */ |
| 14450 | 14541 | int nBusy; /* Incremented with each busy call */ |
| 14451 | - u8 bExtraFileArg; /* Include sqlite3_file as callback arg */ | |
| 14452 | 14542 | }; |
| 14453 | 14543 | |
| 14454 | 14544 | /* |
| 14455 | 14545 | ** Name of the master database table. The master database table |
| 14456 | 14546 | ** is a special table that holds the names and attributes of all |
| @@ -14704,11 +14794,11 @@ | ||
| 14704 | 14794 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); |
| 14705 | 14795 | SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); |
| 14706 | 14796 | SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); |
| 14707 | 14797 | SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); |
| 14708 | 14798 | SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); |
| 14709 | -SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*); | |
| 14799 | +SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*); | |
| 14710 | 14800 | SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); |
| 14711 | 14801 | SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); |
| 14712 | 14802 | SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); |
| 14713 | 14803 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*); |
| 14714 | 14804 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); |
| @@ -14966,13 +15056,11 @@ | ||
| 14966 | 15056 | #ifndef NDEBUG |
| 14967 | 15057 | SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); |
| 14968 | 15058 | #endif |
| 14969 | 15059 | SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*); |
| 14970 | 15060 | |
| 14971 | -#ifndef SQLITE_OMIT_BTREECOUNT | |
| 14972 | 15061 | SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*); |
| 14973 | -#endif | |
| 14974 | 15062 | |
| 14975 | 15063 | #ifdef SQLITE_TEST |
| 14976 | 15064 | SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); |
| 14977 | 15065 | SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); |
| 14978 | 15066 | #endif |
| @@ -15543,10 +15631,13 @@ | ||
| 15543 | 15631 | |
| 15544 | 15632 | SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); |
| 15545 | 15633 | SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*); |
| 15546 | 15634 | |
| 15547 | 15635 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 15636 | +#ifdef SQLITE_ENABLE_BYTECODE_VTAB | |
| 15637 | +SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); | |
| 15638 | +#endif | |
| 15548 | 15639 | |
| 15549 | 15640 | /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on |
| 15550 | 15641 | ** each VDBE opcode. |
| 15551 | 15642 | ** |
| 15552 | 15643 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| @@ -15828,17 +15919,25 @@ | ||
| 15828 | 15919 | SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); |
| 15829 | 15920 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); |
| 15830 | 15921 | SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); |
| 15831 | 15922 | SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); |
| 15832 | 15923 | # ifdef SQLITE_ENABLE_SNAPSHOT |
| 15833 | -SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); | |
| 15834 | -SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); | |
| 15924 | +SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); | |
| 15925 | +SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); | |
| 15835 | 15926 | SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); |
| 15836 | 15927 | SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); |
| 15837 | 15928 | SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); |
| 15838 | 15929 | # endif |
| 15839 | 15930 | #endif |
| 15931 | + | |
| 15932 | +#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) | |
| 15933 | +SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int); | |
| 15934 | +SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*); | |
| 15935 | +#else | |
| 15936 | +# define sqlite3PagerWalWriteLock(y,z) SQLITE_OK | |
| 15937 | +# define sqlite3PagerWalDb(x,y) | |
| 15938 | +#endif | |
| 15840 | 15939 | |
| 15841 | 15940 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 15842 | 15941 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); |
| 15843 | 15942 | #endif |
| 15844 | 15943 | |
| @@ -15861,15 +15960,10 @@ | ||
| 15861 | 15960 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 15862 | 15961 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 15863 | 15962 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 15864 | 15963 | SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); |
| 15865 | 15964 | SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); |
| 15866 | -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 15867 | -SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager); | |
| 15868 | -#else | |
| 15869 | -# define sqlite3PagerResetLockTimeout(X) | |
| 15870 | -#endif | |
| 15871 | 15965 | |
| 15872 | 15966 | /* Functions used to truncate the database file. */ |
| 15873 | 15967 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 15874 | 15968 | |
| 15875 | 15969 | SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); |
| @@ -16799,10 +16893,11 @@ | ||
| 16799 | 16893 | Hash aFunc; /* Hash table of connection functions */ |
| 16800 | 16894 | Hash aCollSeq; /* All collating sequences */ |
| 16801 | 16895 | BusyHandler busyHandler; /* Busy callback */ |
| 16802 | 16896 | Db aDbStatic[2]; /* Static space for the 2 default backends */ |
| 16803 | 16897 | Savepoint *pSavepoint; /* List of active savepoints */ |
| 16898 | + int nAnalysisLimit; /* Number of index rows to ANALYZE */ | |
| 16804 | 16899 | int busyTimeout; /* Busy handler timeout, in msec */ |
| 16805 | 16900 | int nSavepoint; /* Number of non-transaction savepoints */ |
| 16806 | 16901 | int nStatement; /* Number of nested statement-transactions */ |
| 16807 | 16902 | i64 nDeferredCons; /* Net deferred constraints this transaction. */ |
| 16808 | 16903 | i64 nDeferredImmCons; /* Net deferred immediate constraints */ |
| @@ -17209,10 +17304,11 @@ | ||
| 17209 | 17304 | Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */ |
| 17210 | 17305 | char *zColl; /* Collating sequence. If NULL, use the default */ |
| 17211 | 17306 | u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ |
| 17212 | 17307 | char affinity; /* One of the SQLITE_AFF_... values */ |
| 17213 | 17308 | u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ |
| 17309 | + u8 hName; /* Column name hash for faster lookup */ | |
| 17214 | 17310 | u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ |
| 17215 | 17311 | }; |
| 17216 | 17312 | |
| 17217 | 17313 | /* Allowed values for Column.colFlags: |
| 17218 | 17314 | */ |
| @@ -19827,10 +19923,11 @@ | ||
| 19827 | 19923 | const char*, |
| 19828 | 19924 | const char*, |
| 19829 | 19925 | const char* |
| 19830 | 19926 | ); |
| 19831 | 19927 | SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*); |
| 19928 | +SQLITE_PRIVATE u8 sqlite3StrIHash(const char*); | |
| 19832 | 19929 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 19833 | 19930 | SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*); |
| 19834 | 19931 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 19835 | 19932 | SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); |
| 19836 | 19933 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| @@ -19842,11 +19939,11 @@ | ||
| 19842 | 19939 | SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*); |
| 19843 | 19940 | SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*); |
| 19844 | 19941 | SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); |
| 19845 | 19942 | SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*); |
| 19846 | 19943 | SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*); |
| 19847 | -SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*); | |
| 19944 | +SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*); | |
| 19848 | 19945 | SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*); |
| 19849 | 19946 | SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *); |
| 19850 | 19947 | SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB); |
| 19851 | 19948 | SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*); |
| 19852 | 19949 | SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*); |
| @@ -20578,11 +20675,12 @@ | ||
| 20578 | 20675 | /* |
| 20579 | 20676 | ** VDBE_DISPLAY_P4 is true or false depending on whether or not the |
| 20580 | 20677 | ** "explain" P4 display logic is enabled. |
| 20581 | 20678 | */ |
| 20582 | 20679 | #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ |
| 20583 | - || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) | |
| 20680 | + || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ | |
| 20681 | + || defined(SQLITE_ENABLE_BYTECODE_VTAB) | |
| 20584 | 20682 | # define VDBE_DISPLAY_P4 1 |
| 20585 | 20683 | #else |
| 20586 | 20684 | # define VDBE_DISPLAY_P4 0 |
| 20587 | 20685 | #endif |
| 20588 | 20686 | |
| @@ -21043,11 +21141,18 @@ | ||
| 21043 | 21141 | |
| 21044 | 21142 | int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); |
| 21045 | 21143 | SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*); |
| 21046 | 21144 | SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*); |
| 21047 | 21145 | SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*); |
| 21048 | -#ifndef SQLITE_OMIT_EXPLAIN | |
| 21146 | +#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) | |
| 21147 | +SQLITE_PRIVATE int sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**); | |
| 21148 | +SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3*,Op*); | |
| 21149 | +#endif | |
| 21150 | +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) | |
| 21151 | +SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*); | |
| 21152 | +#endif | |
| 21153 | +#if !defined(SQLITE_OMIT_EXPLAIN) | |
| 21049 | 21154 | SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*); |
| 21050 | 21155 | #endif |
| 21051 | 21156 | SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*); |
| 21052 | 21157 | SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int); |
| 21053 | 21158 | SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); |
| @@ -21085,11 +21190,11 @@ | ||
| 21085 | 21190 | SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); |
| 21086 | 21191 | SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); |
| 21087 | 21192 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 21088 | 21193 | SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); |
| 21089 | 21194 | #endif |
| 21090 | -#ifndef SQLITE_OMIT_EXPLAIN | |
| 21195 | +#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) | |
| 21091 | 21196 | SQLITE_PRIVATE const char *sqlite3OpcodeName(int); |
| 21092 | 21197 | #endif |
| 21093 | 21198 | SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); |
| 21094 | 21199 | SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n); |
| 21095 | 21200 | SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); |
| @@ -22165,16 +22270,16 @@ | ||
| 22165 | 22270 | u8 nName; /* Length of th name */ |
| 22166 | 22271 | char *zName; /* Name of the transformation */ |
| 22167 | 22272 | double rLimit; /* Maximum NNN value for this transform */ |
| 22168 | 22273 | double rXform; /* Constant used for this transform */ |
| 22169 | 22274 | } aXformType[] = { |
| 22170 | - { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) }, | |
| 22171 | - { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) }, | |
| 22172 | - { 0, 4, "hour", 128963628.0, 86400000.0/24.0 }, | |
| 22173 | - { 0, 3, "day", 5373485.0, 86400000.0 }, | |
| 22174 | - { 1, 5, "month", 176546.0, 30.0*86400000.0 }, | |
| 22175 | - { 2, 4, "year", 14713.0, 365.0*86400000.0 }, | |
| 22275 | + { 0, 6, "second", 464269060800.0, 1000.0 }, | |
| 22276 | + { 0, 6, "minute", 7737817680.0, 60000.0 }, | |
| 22277 | + { 0, 4, "hour", 128963628.0, 3600000.0 }, | |
| 22278 | + { 0, 3, "day", 5373485.0, 86400000.0 }, | |
| 22279 | + { 1, 5, "month", 176546.0, 2592000000.0 }, | |
| 22280 | + { 2, 4, "year", 14713.0, 31536000000.0 }, | |
| 22176 | 22281 | }; |
| 22177 | 22282 | |
| 22178 | 22283 | /* |
| 22179 | 22284 | ** Process a modifier to a date-time stamp. The modifiers are |
| 22180 | 22285 | ** as follows: |
| @@ -27258,11 +27363,11 @@ | ||
| 27258 | 27363 | if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){ |
| 27259 | 27364 | n = mem0.hardLimit; |
| 27260 | 27365 | } |
| 27261 | 27366 | mem0.alarmThreshold = n; |
| 27262 | 27367 | nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27263 | - mem0.nearlyFull = (n>0 && n<=nUsed); | |
| 27368 | + AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed); | |
| 27264 | 27369 | sqlite3_mutex_leave(mem0.mutex); |
| 27265 | 27370 | excess = sqlite3_memory_used() - n; |
| 27266 | 27371 | if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); |
| 27267 | 27372 | return priorLimit; |
| 27268 | 27373 | } |
| @@ -27326,11 +27431,11 @@ | ||
| 27326 | 27431 | ** Return true if the heap is currently under memory pressure - in other |
| 27327 | 27432 | ** words if the amount of heap used is close to the limit set by |
| 27328 | 27433 | ** sqlite3_soft_heap_limit(). |
| 27329 | 27434 | */ |
| 27330 | 27435 | SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){ |
| 27331 | - return mem0.nearlyFull; | |
| 27436 | + return AtomicLoad(&mem0.nearlyFull); | |
| 27332 | 27437 | } |
| 27333 | 27438 | |
| 27334 | 27439 | /* |
| 27335 | 27440 | ** Deinitialize the memory allocation subsystem. |
| 27336 | 27441 | */ |
| @@ -27390,21 +27495,21 @@ | ||
| 27390 | 27495 | |
| 27391 | 27496 | sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); |
| 27392 | 27497 | if( mem0.alarmThreshold>0 ){ |
| 27393 | 27498 | sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27394 | 27499 | if( nUsed >= mem0.alarmThreshold - nFull ){ |
| 27395 | - mem0.nearlyFull = 1; | |
| 27500 | + AtomicStore(&mem0.nearlyFull, 1); | |
| 27396 | 27501 | sqlite3MallocAlarm(nFull); |
| 27397 | 27502 | if( mem0.hardLimit ){ |
| 27398 | 27503 | nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27399 | 27504 | if( nUsed >= mem0.hardLimit - nFull ){ |
| 27400 | 27505 | *pp = 0; |
| 27401 | 27506 | return; |
| 27402 | 27507 | } |
| 27403 | 27508 | } |
| 27404 | 27509 | }else{ |
| 27405 | - mem0.nearlyFull = 0; | |
| 27510 | + AtomicStore(&mem0.nearlyFull, 0); | |
| 27406 | 27511 | } |
| 27407 | 27512 | } |
| 27408 | 27513 | p = sqlite3GlobalConfig.m.xMalloc(nFull); |
| 27409 | 27514 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 27410 | 27515 | if( p==0 && mem0.alarmThreshold>0 ){ |
| @@ -27629,14 +27734,16 @@ | ||
| 27629 | 27734 | if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= |
| 27630 | 27735 | mem0.alarmThreshold-nDiff ){ |
| 27631 | 27736 | sqlite3MallocAlarm(nDiff); |
| 27632 | 27737 | } |
| 27633 | 27738 | pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
| 27739 | +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT | |
| 27634 | 27740 | if( pNew==0 && mem0.alarmThreshold>0 ){ |
| 27635 | 27741 | sqlite3MallocAlarm((int)nBytes); |
| 27636 | 27742 | pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
| 27637 | 27743 | } |
| 27744 | +#endif | |
| 27638 | 27745 | if( pNew ){ |
| 27639 | 27746 | nNew = sqlite3MallocSize(pNew); |
| 27640 | 27747 | sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); |
| 27641 | 27748 | } |
| 27642 | 27749 | sqlite3_mutex_leave(mem0.mutex); |
| @@ -27907,11 +28014,11 @@ | ||
| 27907 | 28014 | */ |
| 27908 | 28015 | SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ |
| 27909 | 28016 | if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ |
| 27910 | 28017 | db->mallocFailed = 1; |
| 27911 | 28018 | if( db->nVdbeExec>0 ){ |
| 27912 | - db->u1.isInterrupted = 1; | |
| 28019 | + AtomicStore(&db->u1.isInterrupted, 1); | |
| 27913 | 28020 | } |
| 27914 | 28021 | DisableLookaside; |
| 27915 | 28022 | if( db->pParse ){ |
| 27916 | 28023 | db->pParse->rc = SQLITE_NOMEM_BKPT; |
| 27917 | 28024 | } |
| @@ -27926,11 +28033,11 @@ | ||
| 27926 | 28033 | ** VDBEs. |
| 27927 | 28034 | */ |
| 27928 | 28035 | SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){ |
| 27929 | 28036 | if( db->mallocFailed && db->nVdbeExec==0 ){ |
| 27930 | 28037 | db->mallocFailed = 0; |
| 27931 | - db->u1.isInterrupted = 0; | |
| 28038 | + AtomicStore(&db->u1.isInterrupted, 0); | |
| 27932 | 28039 | assert( db->lookaside.bDisable>0 ); |
| 27933 | 28040 | EnableLookaside; |
| 27934 | 28041 | } |
| 27935 | 28042 | } |
| 27936 | 28043 | |
| @@ -31300,10 +31407,23 @@ | ||
| 31300 | 31407 | a = (unsigned char *)zLeft; |
| 31301 | 31408 | b = (unsigned char *)zRight; |
| 31302 | 31409 | while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
| 31303 | 31410 | return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; |
| 31304 | 31411 | } |
| 31412 | + | |
| 31413 | +/* | |
| 31414 | +** Compute an 8-bit hash on a string that is insensitive to case differences | |
| 31415 | +*/ | |
| 31416 | +SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){ | |
| 31417 | + u8 h = 0; | |
| 31418 | + if( z==0 ) return 0; | |
| 31419 | + while( z[0] ){ | |
| 31420 | + h += UpperToLower[(unsigned char)z[0]]; | |
| 31421 | + z++; | |
| 31422 | + } | |
| 31423 | + return h; | |
| 31424 | +} | |
| 31305 | 31425 | |
| 31306 | 31426 | /* |
| 31307 | 31427 | ** Compute 10 to the E-th power. Examples: E==1 results in 10. |
| 31308 | 31428 | ** E==2 results in 100. E==50 results in 1.0e50. |
| 31309 | 31429 | ** |
| @@ -34857,20 +34977,21 @@ | ||
| 34857 | 34977 | static int osSetPosixAdvisoryLock( |
| 34858 | 34978 | int h, /* The file descriptor on which to take the lock */ |
| 34859 | 34979 | struct flock *pLock, /* The description of the lock */ |
| 34860 | 34980 | unixFile *pFile /* Structure holding timeout value */ |
| 34861 | 34981 | ){ |
| 34982 | + int tm = pFile->iBusyTimeout; | |
| 34862 | 34983 | int rc = osFcntl(h,F_SETLK,pLock); |
| 34863 | - while( rc<0 && pFile->iBusyTimeout>0 ){ | |
| 34984 | + while( rc<0 && tm>0 ){ | |
| 34864 | 34985 | /* On systems that support some kind of blocking file lock with a timeout, |
| 34865 | 34986 | ** make appropriate changes here to invoke that blocking file lock. On |
| 34866 | 34987 | ** generic posix, however, there is no such API. So we simply try the |
| 34867 | 34988 | ** lock once every millisecond until either the timeout expires, or until |
| 34868 | 34989 | ** the lock is obtained. */ |
| 34869 | 34990 | usleep(1000); |
| 34870 | 34991 | rc = osFcntl(h,F_SETLK,pLock); |
| 34871 | - pFile->iBusyTimeout--; | |
| 34992 | + tm--; | |
| 34872 | 34993 | } |
| 34873 | 34994 | return rc; |
| 34874 | 34995 | } |
| 34875 | 34996 | #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ |
| 34876 | 34997 | |
| @@ -36977,11 +37098,11 @@ | ||
| 36977 | 37098 | zDirname[ii] = '\0'; |
| 36978 | 37099 | }else{ |
| 36979 | 37100 | if( zDirname[0]!='/' ) zDirname[0] = '.'; |
| 36980 | 37101 | zDirname[1] = 0; |
| 36981 | 37102 | } |
| 36982 | - fd = robust_open(zDirname, O_RDONLY|O_BINARY|O_NOFOLLOW, 0); | |
| 37103 | + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); | |
| 36983 | 37104 | if( fd>=0 ){ |
| 36984 | 37105 | OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); |
| 36985 | 37106 | } |
| 36986 | 37107 | *pFd = fd; |
| 36987 | 37108 | if( fd>=0 ) return SQLITE_OK; |
| @@ -37287,11 +37408,13 @@ | ||
| 37287 | 37408 | *(int*)pArg = fileHasMoved(pFile); |
| 37288 | 37409 | return SQLITE_OK; |
| 37289 | 37410 | } |
| 37290 | 37411 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 37291 | 37412 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 37413 | + int iOld = pFile->iBusyTimeout; | |
| 37292 | 37414 | pFile->iBusyTimeout = *(int*)pArg; |
| 37415 | + *(int*)pArg = iOld; | |
| 37293 | 37416 | return SQLITE_OK; |
| 37294 | 37417 | } |
| 37295 | 37418 | #endif |
| 37296 | 37419 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 37297 | 37420 | case SQLITE_FCNTL_MMAP_SIZE: { |
| @@ -37606,17 +37729,24 @@ | ||
| 37606 | 37729 | |
| 37607 | 37730 | /* Locks are within range */ |
| 37608 | 37731 | assert( n>=1 && n<=SQLITE_SHM_NLOCK ); |
| 37609 | 37732 | |
| 37610 | 37733 | if( pShmNode->hShm>=0 ){ |
| 37734 | + int res; | |
| 37611 | 37735 | /* Initialize the locking parameters */ |
| 37612 | 37736 | f.l_type = lockType; |
| 37613 | 37737 | f.l_whence = SEEK_SET; |
| 37614 | 37738 | f.l_start = ofst; |
| 37615 | 37739 | f.l_len = n; |
| 37616 | - rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); | |
| 37617 | - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; | |
| 37740 | + res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); | |
| 37741 | + if( res==-1 ){ | |
| 37742 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 37743 | + rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); | |
| 37744 | +#else | |
| 37745 | + rc = SQLITE_BUSY; | |
| 37746 | +#endif | |
| 37747 | + } | |
| 37618 | 37748 | } |
| 37619 | 37749 | |
| 37620 | 37750 | /* Update the global lock state and do debug tracing */ |
| 37621 | 37751 | #ifdef SQLITE_DEBUG |
| 37622 | 37752 | { u16 mask; |
| @@ -38108,10 +38238,29 @@ | ||
| 38108 | 38238 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
| 38109 | 38239 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
| 38110 | 38240 | assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
| 38111 | 38241 | assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); |
| 38112 | 38242 | assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); |
| 38243 | + | |
| 38244 | + /* Check that, if this to be a blocking lock, no locks that occur later | |
| 38245 | + ** in the following list than the lock being obtained are already held: | |
| 38246 | + ** | |
| 38247 | + ** 1. Checkpointer lock (ofst==1). | |
| 38248 | + ** 2. Write lock (ofst==0). | |
| 38249 | + ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK). | |
| 38250 | + ** | |
| 38251 | + ** In other words, if this is a blocking lock, none of the locks that | |
| 38252 | + ** occur later in the above list than the lock being obtained may be | |
| 38253 | + ** held. */ | |
| 38254 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 38255 | + assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( | |
| 38256 | + (ofst!=2) /* not RECOVER */ | |
| 38257 | + && (ofst!=1 || (p->exclMask|p->sharedMask)==0) | |
| 38258 | + && (ofst!=0 || (p->exclMask|p->sharedMask)<3) | |
| 38259 | + && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst)) | |
| 38260 | + )); | |
| 38261 | +#endif | |
| 38113 | 38262 | |
| 38114 | 38263 | mask = (1<<(ofst+n)) - (1<<ofst); |
| 38115 | 38264 | assert( n>1 || mask==(1<<ofst) ); |
| 38116 | 38265 | sqlite3_mutex_enter(pShmNode->pShmMutex); |
| 38117 | 38266 | if( flags & SQLITE_SHM_UNLOCK ){ |
| @@ -51414,10 +51563,15 @@ | ||
| 51414 | 51563 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal); |
| 51415 | 51564 | #endif |
| 51416 | 51565 | |
| 51417 | 51566 | /* Return the sqlite3_file object for the WAL file */ |
| 51418 | 51567 | SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal); |
| 51568 | + | |
| 51569 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 51570 | +SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock); | |
| 51571 | +SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db); | |
| 51572 | +#endif | |
| 51419 | 51573 | |
| 51420 | 51574 | #endif /* ifndef SQLITE_OMIT_WAL */ |
| 51421 | 51575 | #endif /* SQLITE_WAL_H */ |
| 51422 | 51576 | |
| 51423 | 51577 | /************** End of wal.h *************************************************/ |
| @@ -53935,13 +54089,16 @@ | ||
| 53935 | 54089 | } |
| 53936 | 54090 | if( exists ){ |
| 53937 | 54091 | /* One of the journals pointed to by the master journal exists. |
| 53938 | 54092 | ** Open it and check if it points at the master journal. If |
| 53939 | 54093 | ** so, return without deleting the master journal file. |
| 54094 | + ** NB: zJournal is really a MAIN_JOURNAL. But call it a | |
| 54095 | + ** MASTER_JOURNAL here so that the VFS will not send the zJournal | |
| 54096 | + ** name into sqlite3_database_file_object(). | |
| 53940 | 54097 | */ |
| 53941 | 54098 | int c; |
| 53942 | - int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL); | |
| 54099 | + int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); | |
| 53943 | 54100 | rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0); |
| 53944 | 54101 | if( rc!=SQLITE_OK ){ |
| 53945 | 54102 | goto delmaster_out; |
| 53946 | 54103 | } |
| 53947 | 54104 | |
| @@ -56141,10 +56298,11 @@ | ||
| 56141 | 56298 | ** Pager object (sizeof(Pager) bytes) |
| 56142 | 56299 | ** PCache object (sqlite3PcacheSize() bytes) |
| 56143 | 56300 | ** Database file handle (pVfs->szOsFile bytes) |
| 56144 | 56301 | ** Sub-journal file handle (journalFileSize bytes) |
| 56145 | 56302 | ** Main journal file handle (journalFileSize bytes) |
| 56303 | + ** Ptr back to the Pager (sizeof(Pager*) bytes) | |
| 56146 | 56304 | ** \0\0\0\0 database prefix (4 bytes) |
| 56147 | 56305 | ** Database file name (nPathname+1 bytes) |
| 56148 | 56306 | ** URI query parameters (nUriByte bytes) |
| 56149 | 56307 | ** Journal filename (nPathname+8+1 bytes) |
| 56150 | 56308 | ** WAL filename (nPathname+4+1 bytes) |
| @@ -56180,10 +56338,11 @@ | ||
| 56180 | 56338 | pPtr = (u8 *)sqlite3MallocZero( |
| 56181 | 56339 | ROUND8(sizeof(*pPager)) + /* Pager structure */ |
| 56182 | 56340 | ROUND8(pcacheSize) + /* PCache object */ |
| 56183 | 56341 | ROUND8(pVfs->szOsFile) + /* The main db file */ |
| 56184 | 56342 | journalFileSize * 2 + /* The two journal files */ |
| 56343 | + sizeof(pPager) + /* Space to hold a pointer */ | |
| 56185 | 56344 | 4 + /* Database prefix */ |
| 56186 | 56345 | nPathname + 1 + /* database filename */ |
| 56187 | 56346 | nUriByte + /* query parameters */ |
| 56188 | 56347 | nPathname + 8 + 1 + /* Journal filename */ |
| 56189 | 56348 | #ifndef SQLITE_OMIT_WAL |
| @@ -56200,10 +56359,11 @@ | ||
| 56200 | 56359 | pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize); |
| 56201 | 56360 | pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile); |
| 56202 | 56361 | pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; |
| 56203 | 56362 | pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; |
| 56204 | 56363 | assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); |
| 56364 | + memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); | |
| 56205 | 56365 | |
| 56206 | 56366 | /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ |
| 56207 | 56367 | pPtr += 4; /* Skip zero prefix */ |
| 56208 | 56368 | pPager->zFilename = (char*)pPtr; |
| 56209 | 56369 | if( nPathname>0 ){ |
| @@ -56400,10 +56560,23 @@ | ||
| 56400 | 56560 | |
| 56401 | 56561 | *ppPager = pPager; |
| 56402 | 56562 | return SQLITE_OK; |
| 56403 | 56563 | } |
| 56404 | 56564 | |
| 56565 | +/* | |
| 56566 | +** Return the sqlite3_file for the main database given the name | |
| 56567 | +** of the corresonding WAL or Journal name as passed into | |
| 56568 | +** xOpen. | |
| 56569 | +*/ | |
| 56570 | +SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){ | |
| 56571 | + Pager *pPager; | |
| 56572 | + while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ | |
| 56573 | + zName--; | |
| 56574 | + } | |
| 56575 | + pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); | |
| 56576 | + return pPager->fd; | |
| 56577 | +} | |
| 56405 | 56578 | |
| 56406 | 56579 | |
| 56407 | 56580 | /* |
| 56408 | 56581 | ** This function is called after transitioning from PAGER_UNLOCK to |
| 56409 | 56582 | ** PAGER_SHARED state. It tests if there is a hot journal present in |
| @@ -57085,11 +57258,10 @@ | ||
| 57085 | 57258 | Pager *pPager; |
| 57086 | 57259 | assert( pPg!=0 ); |
| 57087 | 57260 | assert( pPg->pgno==1 ); |
| 57088 | 57261 | assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ |
| 57089 | 57262 | pPager = pPg->pPager; |
| 57090 | - sqlite3PagerResetLockTimeout(pPager); | |
| 57091 | 57263 | sqlite3PcacheRelease(pPg); |
| 57092 | 57264 | pagerUnlockIfUnused(pPager); |
| 57093 | 57265 | } |
| 57094 | 57266 | |
| 57095 | 57267 | /* |
| @@ -58378,20 +58550,10 @@ | ||
| 58378 | 58550 | */ |
| 58379 | 58551 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ |
| 58380 | 58552 | return pPager->fd; |
| 58381 | 58553 | } |
| 58382 | 58554 | |
| 58383 | -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 58384 | -/* | |
| 58385 | -** Reset the lock timeout for pager. | |
| 58386 | -*/ | |
| 58387 | -SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){ | |
| 58388 | - int x = 0; | |
| 58389 | - sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x); | |
| 58390 | -} | |
| 58391 | -#endif | |
| 58392 | - | |
| 58393 | 58555 | /* |
| 58394 | 58556 | ** Return the file handle for the journal file (if it exists). |
| 58395 | 58557 | ** This will be either the rollback journal or the WAL file. |
| 58396 | 58558 | */ |
| 58397 | 58559 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ |
| @@ -58801,11 +58963,10 @@ | ||
| 58801 | 58963 | (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), |
| 58802 | 58964 | pPager->pBusyHandlerArg, |
| 58803 | 58965 | pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, |
| 58804 | 58966 | pnLog, pnCkpt |
| 58805 | 58967 | ); |
| 58806 | - sqlite3PagerResetLockTimeout(pPager); | |
| 58807 | 58968 | } |
| 58808 | 58969 | return rc; |
| 58809 | 58970 | } |
| 58810 | 58971 | |
| 58811 | 58972 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){ |
| @@ -58966,11 +59127,35 @@ | ||
| 58966 | 59127 | } |
| 58967 | 59128 | } |
| 58968 | 59129 | return rc; |
| 58969 | 59130 | } |
| 58970 | 59131 | |
| 59132 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 59133 | +/* | |
| 59134 | +** If pager pPager is a wal-mode database not in exclusive locking mode, | |
| 59135 | +** invoke the sqlite3WalWriteLock() function on the associated Wal object | |
| 59136 | +** with the same db and bLock parameters as were passed to this function. | |
| 59137 | +** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise. | |
| 59138 | +*/ | |
| 59139 | +SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){ | |
| 59140 | + int rc = SQLITE_OK; | |
| 59141 | + if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){ | |
| 59142 | + rc = sqlite3WalWriteLock(pPager->pWal, bLock); | |
| 59143 | + } | |
| 59144 | + return rc; | |
| 59145 | +} | |
| 58971 | 59146 | |
| 59147 | +/* | |
| 59148 | +** Set the database handle used by the wal layer to determine if | |
| 59149 | +** blocking locks are required. | |
| 59150 | +*/ | |
| 59151 | +SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){ | |
| 59152 | + if( pagerUseWal(pPager) ){ | |
| 59153 | + sqlite3WalDb(pPager->pWal, db); | |
| 59154 | + } | |
| 59155 | +} | |
| 59156 | +#endif | |
| 58972 | 59157 | |
| 58973 | 59158 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 58974 | 59159 | /* |
| 58975 | 59160 | ** If this is a WAL database, obtain a snapshot handle for the snapshot |
| 58976 | 59161 | ** currently open. Otherwise, return an error. |
| @@ -58986,11 +59171,14 @@ | ||
| 58986 | 59171 | /* |
| 58987 | 59172 | ** If this is a WAL database, store a pointer to pSnapshot. Next time a |
| 58988 | 59173 | ** read transaction is opened, attempt to read from the snapshot it |
| 58989 | 59174 | ** identifies. If this is not a WAL database, return an error. |
| 58990 | 59175 | */ |
| 58991 | -SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){ | |
| 59176 | +SQLITE_PRIVATE int sqlite3PagerSnapshotOpen( | |
| 59177 | + Pager *pPager, | |
| 59178 | + sqlite3_snapshot *pSnapshot | |
| 59179 | +){ | |
| 58992 | 59180 | int rc = SQLITE_OK; |
| 58993 | 59181 | if( pPager->pWal ){ |
| 58994 | 59182 | sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); |
| 58995 | 59183 | }else{ |
| 58996 | 59184 | rc = SQLITE_ERROR; |
| @@ -59322,22 +59510,10 @@ | ||
| 59322 | 59510 | # define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X |
| 59323 | 59511 | #else |
| 59324 | 59512 | # define WALTRACE(X) |
| 59325 | 59513 | #endif |
| 59326 | 59514 | |
| 59327 | -/* | |
| 59328 | -** WAL mode depends on atomic aligned 32-bit loads and stores in a few | |
| 59329 | -** places. The following macros try to make this explicit. | |
| 59330 | -*/ | |
| 59331 | -#if GCC_VESRION>=5004000 | |
| 59332 | -# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) | |
| 59333 | -# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) | |
| 59334 | -#else | |
| 59335 | -# define AtomicLoad(PTR) (*(PTR)) | |
| 59336 | -# define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) | |
| 59337 | -#endif | |
| 59338 | - | |
| 59339 | 59515 | /* |
| 59340 | 59516 | ** The maximum (and only) versions of the wal and wal-index formats |
| 59341 | 59517 | ** that may be interpreted by this version of SQLite. |
| 59342 | 59518 | ** |
| 59343 | 59519 | ** If a client begins recovering a WAL file and finds that (a) the checksum |
| @@ -59542,10 +59718,13 @@ | ||
| 59542 | 59718 | #ifdef SQLITE_DEBUG |
| 59543 | 59719 | u8 lockError; /* True if a locking error has occurred */ |
| 59544 | 59720 | #endif |
| 59545 | 59721 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 59546 | 59722 | WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ |
| 59723 | +#endif | |
| 59724 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 59725 | + sqlite3 *db; | |
| 59547 | 59726 | #endif |
| 59548 | 59727 | }; |
| 59549 | 59728 | |
| 59550 | 59729 | /* |
| 59551 | 59730 | ** Candidate values for Wal.exclusiveMode. |
| @@ -59916,11 +60095,11 @@ | ||
| 59916 | 60095 | if( pWal->exclusiveMode ) return SQLITE_OK; |
| 59917 | 60096 | rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, |
| 59918 | 60097 | SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); |
| 59919 | 60098 | WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, |
| 59920 | 60099 | walLockName(lockIdx), rc ? "failed" : "ok")); |
| 59921 | - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) | |
| 60100 | + VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) | |
| 59922 | 60101 | return rc; |
| 59923 | 60102 | } |
| 59924 | 60103 | static void walUnlockShared(Wal *pWal, int lockIdx){ |
| 59925 | 60104 | if( pWal->exclusiveMode ) return; |
| 59926 | 60105 | (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, |
| @@ -59932,11 +60111,11 @@ | ||
| 59932 | 60111 | if( pWal->exclusiveMode ) return SQLITE_OK; |
| 59933 | 60112 | rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, |
| 59934 | 60113 | SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); |
| 59935 | 60114 | WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, |
| 59936 | 60115 | walLockName(lockIdx), n, rc ? "failed" : "ok")); |
| 59937 | - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) | |
| 60116 | + VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) | |
| 59938 | 60117 | return rc; |
| 59939 | 60118 | } |
| 59940 | 60119 | static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ |
| 59941 | 60120 | if( pWal->exclusiveMode ) return; |
| 59942 | 60121 | (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, |
| @@ -60751,10 +60930,93 @@ | ||
| 60751 | 60930 | p = 0; |
| 60752 | 60931 | } |
| 60753 | 60932 | *pp = p; |
| 60754 | 60933 | return rc; |
| 60755 | 60934 | } |
| 60935 | + | |
| 60936 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 60937 | +/* | |
| 60938 | +** Attempt to enable blocking locks. Blocking locks are enabled only if (a) | |
| 60939 | +** they are supported by the VFS, and (b) the database handle is configured | |
| 60940 | +** with a busy-timeout. Return 1 if blocking locks are successfully enabled, | |
| 60941 | +** or 0 otherwise. | |
| 60942 | +*/ | |
| 60943 | +static int walEnableBlocking(Wal *pWal){ | |
| 60944 | + int res = 0; | |
| 60945 | + if( pWal->db ){ | |
| 60946 | + int tmout = pWal->db->busyTimeout; | |
| 60947 | + if( tmout ){ | |
| 60948 | + int rc; | |
| 60949 | + rc = sqlite3OsFileControl( | |
| 60950 | + pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout | |
| 60951 | + ); | |
| 60952 | + res = (rc==SQLITE_OK); | |
| 60953 | + } | |
| 60954 | + } | |
| 60955 | + return res; | |
| 60956 | +} | |
| 60957 | + | |
| 60958 | +/* | |
| 60959 | +** Disable blocking locks. | |
| 60960 | +*/ | |
| 60961 | +static void walDisableBlocking(Wal *pWal){ | |
| 60962 | + int tmout = 0; | |
| 60963 | + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout); | |
| 60964 | +} | |
| 60965 | + | |
| 60966 | +/* | |
| 60967 | +** If parameter bLock is true, attempt to enable blocking locks, take | |
| 60968 | +** the WRITER lock, and then disable blocking locks. If blocking locks | |
| 60969 | +** cannot be enabled, no attempt to obtain the WRITER lock is made. Return | |
| 60970 | +** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not | |
| 60971 | +** an error if blocking locks can not be enabled. | |
| 60972 | +** | |
| 60973 | +** If the bLock parameter is false and the WRITER lock is held, release it. | |
| 60974 | +*/ | |
| 60975 | +SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock){ | |
| 60976 | + int rc = SQLITE_OK; | |
| 60977 | + assert( pWal->readLock<0 || bLock==0 ); | |
| 60978 | + if( bLock ){ | |
| 60979 | + assert( pWal->db ); | |
| 60980 | + if( walEnableBlocking(pWal) ){ | |
| 60981 | + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); | |
| 60982 | + if( rc==SQLITE_OK ){ | |
| 60983 | + pWal->writeLock = 1; | |
| 60984 | + } | |
| 60985 | + walDisableBlocking(pWal); | |
| 60986 | + } | |
| 60987 | + }else if( pWal->writeLock ){ | |
| 60988 | + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); | |
| 60989 | + pWal->writeLock = 0; | |
| 60990 | + } | |
| 60991 | + return rc; | |
| 60992 | +} | |
| 60993 | + | |
| 60994 | +/* | |
| 60995 | +** Set the database handle used to determine if blocking locks are required. | |
| 60996 | +*/ | |
| 60997 | +SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){ | |
| 60998 | + pWal->db = db; | |
| 60999 | +} | |
| 61000 | + | |
| 61001 | +/* | |
| 61002 | +** Take an exclusive WRITE lock. Blocking if so configured. | |
| 61003 | +*/ | |
| 61004 | +static int walLockWriter(Wal *pWal){ | |
| 61005 | + int rc; | |
| 61006 | + walEnableBlocking(pWal); | |
| 61007 | + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); | |
| 61008 | + walDisableBlocking(pWal); | |
| 61009 | + return rc; | |
| 61010 | +} | |
| 61011 | +#else | |
| 61012 | +# define walEnableBlocking(x) 0 | |
| 61013 | +# define walDisableBlocking(x) | |
| 61014 | +# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) | |
| 61015 | +# define sqlite3WalDb(pWal, db) | |
| 61016 | +#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ | |
| 61017 | + | |
| 60756 | 61018 | |
| 60757 | 61019 | /* |
| 60758 | 61020 | ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and |
| 60759 | 61021 | ** n. If the attempt fails and parameter xBusy is not NULL, then it is a |
| 60760 | 61022 | ** busy-handler function. Invoke it and retry the lock until either the |
| @@ -60769,10 +61031,16 @@ | ||
| 60769 | 61031 | ){ |
| 60770 | 61032 | int rc; |
| 60771 | 61033 | do { |
| 60772 | 61034 | rc = walLockExclusive(pWal, lockIdx, n); |
| 60773 | 61035 | }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); |
| 61036 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 61037 | + if( rc==SQLITE_BUSY_TIMEOUT ){ | |
| 61038 | + walDisableBlocking(pWal); | |
| 61039 | + rc = SQLITE_BUSY; | |
| 61040 | + } | |
| 61041 | +#endif | |
| 60774 | 61042 | return rc; |
| 60775 | 61043 | } |
| 60776 | 61044 | |
| 60777 | 61045 | /* |
| 60778 | 61046 | ** The cache of the wal-index header must be valid to call this function. |
| @@ -60939,10 +61207,11 @@ | ||
| 60939 | 61207 | ** about the eventual size of the db file to the VFS layer. |
| 60940 | 61208 | */ |
| 60941 | 61209 | if( rc==SQLITE_OK ){ |
| 60942 | 61210 | i64 nReq = ((i64)mxPage * szPage); |
| 60943 | 61211 | i64 nSize; /* Current size of database file */ |
| 61212 | + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); | |
| 60944 | 61213 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 60945 | 61214 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 60946 | 61215 | sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); |
| 60947 | 61216 | } |
| 60948 | 61217 | } |
| @@ -60950,11 +61219,11 @@ | ||
| 60950 | 61219 | |
| 60951 | 61220 | /* Iterate through the contents of the WAL, copying data to the db file */ |
| 60952 | 61221 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 60953 | 61222 | i64 iOffset; |
| 60954 | 61223 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| 60955 | - if( db->u1.isInterrupted ){ | |
| 61224 | + if( AtomicLoad(&db->u1.isInterrupted) ){ | |
| 60956 | 61225 | rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; |
| 60957 | 61226 | break; |
| 60958 | 61227 | } |
| 60959 | 61228 | if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ |
| 60960 | 61229 | continue; |
| @@ -60966,10 +61235,11 @@ | ||
| 60966 | 61235 | iOffset = (iDbpage-1)*(i64)szPage; |
| 60967 | 61236 | testcase( IS_BIG_INT(iOffset) ); |
| 60968 | 61237 | rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); |
| 60969 | 61238 | if( rc!=SQLITE_OK ) break; |
| 60970 | 61239 | } |
| 61240 | + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); | |
| 60971 | 61241 | |
| 60972 | 61242 | /* If work was actually accomplished... */ |
| 60973 | 61243 | if( rc==SQLITE_OK ){ |
| 60974 | 61244 | if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ |
| 60975 | 61245 | i64 szDb = pWal->hdr.nPage*(i64)szPage; |
| @@ -60977,14 +61247,10 @@ | ||
| 60977 | 61247 | rc = sqlite3OsTruncate(pWal->pDbFd, szDb); |
| 60978 | 61248 | if( rc==SQLITE_OK ){ |
| 60979 | 61249 | rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 60980 | 61250 | } |
| 60981 | 61251 | } |
| 60982 | - if( rc==SQLITE_OK ){ | |
| 60983 | - rc = sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); | |
| 60984 | - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; | |
| 60985 | - } | |
| 60986 | 61252 | if( rc==SQLITE_OK ){ |
| 60987 | 61253 | pInfo->nBackfill = mxSafeFrame; |
| 60988 | 61254 | } |
| 60989 | 61255 | } |
| 60990 | 61256 | |
| @@ -61250,32 +61516,36 @@ | ||
| 61250 | 61516 | badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); |
| 61251 | 61517 | |
| 61252 | 61518 | /* If the first attempt failed, it might have been due to a race |
| 61253 | 61519 | ** with a writer. So get a WRITE lock and try again. |
| 61254 | 61520 | */ |
| 61255 | - assert( badHdr==0 || pWal->writeLock==0 ); | |
| 61256 | 61521 | if( badHdr ){ |
| 61257 | 61522 | if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ |
| 61258 | 61523 | if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ |
| 61259 | 61524 | walUnlockShared(pWal, WAL_WRITE_LOCK); |
| 61260 | 61525 | rc = SQLITE_READONLY_RECOVERY; |
| 61261 | 61526 | } |
| 61262 | - }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ | |
| 61263 | - pWal->writeLock = 1; | |
| 61264 | - if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ | |
| 61265 | - badHdr = walIndexTryHdr(pWal, pChanged); | |
| 61266 | - if( badHdr ){ | |
| 61267 | - /* If the wal-index header is still malformed even while holding | |
| 61268 | - ** a WRITE lock, it can only mean that the header is corrupted and | |
| 61269 | - ** needs to be reconstructed. So run recovery to do exactly that. | |
| 61270 | - */ | |
| 61271 | - rc = walIndexRecover(pWal); | |
| 61272 | - *pChanged = 1; | |
| 61273 | - } | |
| 61274 | - } | |
| 61275 | - pWal->writeLock = 0; | |
| 61276 | - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); | |
| 61527 | + }else{ | |
| 61528 | + int bWriteLock = pWal->writeLock; | |
| 61529 | + if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ | |
| 61530 | + pWal->writeLock = 1; | |
| 61531 | + if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ | |
| 61532 | + badHdr = walIndexTryHdr(pWal, pChanged); | |
| 61533 | + if( badHdr ){ | |
| 61534 | + /* If the wal-index header is still malformed even while holding | |
| 61535 | + ** a WRITE lock, it can only mean that the header is corrupted and | |
| 61536 | + ** needs to be reconstructed. So run recovery to do exactly that. | |
| 61537 | + */ | |
| 61538 | + rc = walIndexRecover(pWal); | |
| 61539 | + *pChanged = 1; | |
| 61540 | + } | |
| 61541 | + } | |
| 61542 | + if( bWriteLock==0 ){ | |
| 61543 | + pWal->writeLock = 0; | |
| 61544 | + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); | |
| 61545 | + } | |
| 61546 | + } | |
| 61277 | 61547 | } |
| 61278 | 61548 | } |
| 61279 | 61549 | |
| 61280 | 61550 | /* If the header is read successfully, check the version number to make |
| 61281 | 61551 | ** sure the wal-index was not constructed with some future format that |
| @@ -61663,11 +61933,12 @@ | ||
| 61663 | 61933 | && (mxReadMark<mxFrame || mxI==0) |
| 61664 | 61934 | ){ |
| 61665 | 61935 | for(i=1; i<WAL_NREADER; i++){ |
| 61666 | 61936 | rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61667 | 61937 | if( rc==SQLITE_OK ){ |
| 61668 | - mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame); | |
| 61938 | + AtomicStore(pInfo->aReadMark+i,mxFrame); | |
| 61939 | + mxReadMark = mxFrame; | |
| 61669 | 61940 | mxI = i; |
| 61670 | 61941 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61671 | 61942 | break; |
| 61672 | 61943 | }else if( rc!=SQLITE_BUSY ){ |
| 61673 | 61944 | return rc; |
| @@ -61823,15 +62094,36 @@ | ||
| 61823 | 62094 | */ |
| 61824 | 62095 | SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ |
| 61825 | 62096 | int rc; /* Return code */ |
| 61826 | 62097 | int cnt = 0; /* Number of TryBeginRead attempts */ |
| 61827 | 62098 | |
| 62099 | + assert( pWal->ckptLock==0 ); | |
| 62100 | + | |
| 61828 | 62101 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 61829 | 62102 | int bChanged = 0; |
| 61830 | 62103 | WalIndexHdr *pSnapshot = pWal->pSnapshot; |
| 61831 | - if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ | |
| 61832 | - bChanged = 1; | |
| 62104 | + if( pSnapshot ){ | |
| 62105 | + if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ | |
| 62106 | + bChanged = 1; | |
| 62107 | + } | |
| 62108 | + | |
| 62109 | + /* It is possible that there is a checkpointer thread running | |
| 62110 | + ** concurrent with this code. If this is the case, it may be that the | |
| 62111 | + ** checkpointer has already determined that it will checkpoint | |
| 62112 | + ** snapshot X, where X is later in the wal file than pSnapshot, but | |
| 62113 | + ** has not yet set the pInfo->nBackfillAttempted variable to indicate | |
| 62114 | + ** its intent. To avoid the race condition this leads to, ensure that | |
| 62115 | + ** there is no checkpointer process by taking a shared CKPT lock | |
| 62116 | + ** before checking pInfo->nBackfillAttempted. */ | |
| 62117 | + (void)walEnableBlocking(pWal); | |
| 62118 | + rc = walLockShared(pWal, WAL_CKPT_LOCK); | |
| 62119 | + walDisableBlocking(pWal); | |
| 62120 | + | |
| 62121 | + if( rc!=SQLITE_OK ){ | |
| 62122 | + return rc; | |
| 62123 | + } | |
| 62124 | + pWal->ckptLock = 1; | |
| 61833 | 62125 | } |
| 61834 | 62126 | #endif |
| 61835 | 62127 | |
| 61836 | 62128 | do{ |
| 61837 | 62129 | rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); |
| @@ -61860,52 +62152,46 @@ | ||
| 61860 | 62152 | volatile WalCkptInfo *pInfo = walCkptInfo(pWal); |
| 61861 | 62153 | |
| 61862 | 62154 | assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 ); |
| 61863 | 62155 | assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); |
| 61864 | 62156 | |
| 61865 | - /* It is possible that there is a checkpointer thread running | |
| 61866 | - ** concurrent with this code. If this is the case, it may be that the | |
| 61867 | - ** checkpointer has already determined that it will checkpoint | |
| 61868 | - ** snapshot X, where X is later in the wal file than pSnapshot, but | |
| 61869 | - ** has not yet set the pInfo->nBackfillAttempted variable to indicate | |
| 61870 | - ** its intent. To avoid the race condition this leads to, ensure that | |
| 61871 | - ** there is no checkpointer process by taking a shared CKPT lock | |
| 61872 | - ** before checking pInfo->nBackfillAttempted. | |
| 61873 | - ** | |
| 61874 | - ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing | |
| 61875 | - ** this already? | |
| 61876 | - */ | |
| 61877 | - rc = walLockShared(pWal, WAL_CKPT_LOCK); | |
| 61878 | - | |
| 61879 | - if( rc==SQLITE_OK ){ | |
| 61880 | - /* Check that the wal file has not been wrapped. Assuming that it has | |
| 61881 | - ** not, also check that no checkpointer has attempted to checkpoint any | |
| 61882 | - ** frames beyond pSnapshot->mxFrame. If either of these conditions are | |
| 61883 | - ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr | |
| 61884 | - ** with *pSnapshot and set *pChanged as appropriate for opening the | |
| 61885 | - ** snapshot. */ | |
| 61886 | - if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) | |
| 61887 | - && pSnapshot->mxFrame>=pInfo->nBackfillAttempted | |
| 61888 | - ){ | |
| 61889 | - assert( pWal->readLock>0 ); | |
| 61890 | - memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); | |
| 61891 | - *pChanged = bChanged; | |
| 61892 | - }else{ | |
| 61893 | - rc = SQLITE_ERROR_SNAPSHOT; | |
| 61894 | - } | |
| 61895 | - | |
| 61896 | - /* Release the shared CKPT lock obtained above. */ | |
| 61897 | - walUnlockShared(pWal, WAL_CKPT_LOCK); | |
| 61898 | - pWal->minFrame = 1; | |
| 61899 | - } | |
| 61900 | - | |
| 62157 | + /* Check that the wal file has not been wrapped. Assuming that it has | |
| 62158 | + ** not, also check that no checkpointer has attempted to checkpoint any | |
| 62159 | + ** frames beyond pSnapshot->mxFrame. If either of these conditions are | |
| 62160 | + ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr | |
| 62161 | + ** with *pSnapshot and set *pChanged as appropriate for opening the | |
| 62162 | + ** snapshot. */ | |
| 62163 | + if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) | |
| 62164 | + && pSnapshot->mxFrame>=pInfo->nBackfillAttempted | |
| 62165 | + ){ | |
| 62166 | + assert( pWal->readLock>0 ); | |
| 62167 | + memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); | |
| 62168 | + *pChanged = bChanged; | |
| 62169 | + }else{ | |
| 62170 | + rc = SQLITE_ERROR_SNAPSHOT; | |
| 62171 | + } | |
| 62172 | + | |
| 62173 | + /* A client using a non-current snapshot may not ignore any frames | |
| 62174 | + ** from the start of the wal file. This is because, for a system | |
| 62175 | + ** where (minFrame < iSnapshot < maxFrame), a checkpointer may | |
| 62176 | + ** have omitted to checkpoint a frame earlier than minFrame in | |
| 62177 | + ** the file because there exists a frame after iSnapshot that | |
| 62178 | + ** is the same database page. */ | |
| 62179 | + pWal->minFrame = 1; | |
| 61901 | 62180 | |
| 61902 | 62181 | if( rc!=SQLITE_OK ){ |
| 61903 | 62182 | sqlite3WalEndReadTransaction(pWal); |
| 61904 | 62183 | } |
| 61905 | 62184 | } |
| 61906 | 62185 | } |
| 62186 | + | |
| 62187 | + /* Release the shared CKPT lock obtained above. */ | |
| 62188 | + if( pWal->ckptLock ){ | |
| 62189 | + assert( pSnapshot ); | |
| 62190 | + walUnlockShared(pWal, WAL_CKPT_LOCK); | |
| 62191 | + pWal->ckptLock = 0; | |
| 62192 | + } | |
| 61907 | 62193 | #endif |
| 61908 | 62194 | return rc; |
| 61909 | 62195 | } |
| 61910 | 62196 | |
| 61911 | 62197 | /* |
| @@ -62071,10 +62357,20 @@ | ||
| 62071 | 62357 | ** |
| 62072 | 62358 | ** There can only be a single writer active at a time. |
| 62073 | 62359 | */ |
| 62074 | 62360 | SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ |
| 62075 | 62361 | int rc; |
| 62362 | + | |
| 62363 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 62364 | + /* If the write-lock is already held, then it was obtained before the | |
| 62365 | + ** read-transaction was even opened, making this call a no-op. | |
| 62366 | + ** Return early. */ | |
| 62367 | + if( pWal->writeLock ){ | |
| 62368 | + assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); | |
| 62369 | + return SQLITE_OK; | |
| 62370 | + } | |
| 62371 | +#endif | |
| 62076 | 62372 | |
| 62077 | 62373 | /* Cannot start a write transaction without first holding a read |
| 62078 | 62374 | ** transaction. */ |
| 62079 | 62375 | assert( pWal->readLock>=0 ); |
| 62080 | 62376 | assert( pWal->writeLock==0 && pWal->iReCksum==0 ); |
| @@ -62647,50 +62943,57 @@ | ||
| 62647 | 62943 | ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ |
| 62648 | 62944 | assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); |
| 62649 | 62945 | |
| 62650 | 62946 | if( pWal->readOnly ) return SQLITE_READONLY; |
| 62651 | 62947 | WALTRACE(("WAL%p: checkpoint begins\n", pWal)); |
| 62948 | + | |
| 62949 | + /* Enable blocking locks, if possible. If blocking locks are successfully | |
| 62950 | + ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ | |
| 62951 | + sqlite3WalDb(pWal, db); | |
| 62952 | + (void)walEnableBlocking(pWal); | |
| 62652 | 62953 | |
| 62653 | 62954 | /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive |
| 62654 | - ** "checkpoint" lock on the database file. */ | |
| 62955 | + ** "checkpoint" lock on the database file. | |
| 62956 | + ** EVIDENCE-OF: R-10421-19736 If any other process is running a | |
| 62957 | + ** checkpoint operation at the same time, the lock cannot be obtained and | |
| 62958 | + ** SQLITE_BUSY is returned. | |
| 62959 | + ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, | |
| 62960 | + ** it will not be invoked in this case. | |
| 62961 | + */ | |
| 62655 | 62962 | rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); |
| 62656 | - if( rc ){ | |
| 62657 | - /* EVIDENCE-OF: R-10421-19736 If any other process is running a | |
| 62658 | - ** checkpoint operation at the same time, the lock cannot be obtained and | |
| 62659 | - ** SQLITE_BUSY is returned. | |
| 62660 | - ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, | |
| 62661 | - ** it will not be invoked in this case. | |
| 62662 | - */ | |
| 62663 | - testcase( rc==SQLITE_BUSY ); | |
| 62664 | - testcase( xBusy!=0 ); | |
| 62665 | - return rc; | |
| 62666 | - } | |
| 62667 | - pWal->ckptLock = 1; | |
| 62668 | - | |
| 62669 | - /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and | |
| 62670 | - ** TRUNCATE modes also obtain the exclusive "writer" lock on the database | |
| 62671 | - ** file. | |
| 62672 | - ** | |
| 62673 | - ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained | |
| 62674 | - ** immediately, and a busy-handler is configured, it is invoked and the | |
| 62675 | - ** writer lock retried until either the busy-handler returns 0 or the | |
| 62676 | - ** lock is successfully obtained. | |
| 62677 | - */ | |
| 62678 | - if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ | |
| 62679 | - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); | |
| 62680 | - if( rc==SQLITE_OK ){ | |
| 62681 | - pWal->writeLock = 1; | |
| 62682 | - }else if( rc==SQLITE_BUSY ){ | |
| 62683 | - eMode2 = SQLITE_CHECKPOINT_PASSIVE; | |
| 62684 | - xBusy2 = 0; | |
| 62685 | - rc = SQLITE_OK; | |
| 62686 | - } | |
| 62687 | - } | |
| 62963 | + testcase( rc==SQLITE_BUSY ); | |
| 62964 | + testcase( rc!=SQLITE_OK && xBusy2!=0 ); | |
| 62965 | + if( rc==SQLITE_OK ){ | |
| 62966 | + pWal->ckptLock = 1; | |
| 62967 | + | |
| 62968 | + /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and | |
| 62969 | + ** TRUNCATE modes also obtain the exclusive "writer" lock on the database | |
| 62970 | + ** file. | |
| 62971 | + ** | |
| 62972 | + ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained | |
| 62973 | + ** immediately, and a busy-handler is configured, it is invoked and the | |
| 62974 | + ** writer lock retried until either the busy-handler returns 0 or the | |
| 62975 | + ** lock is successfully obtained. | |
| 62976 | + */ | |
| 62977 | + if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ | |
| 62978 | + rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1); | |
| 62979 | + if( rc==SQLITE_OK ){ | |
| 62980 | + pWal->writeLock = 1; | |
| 62981 | + }else if( rc==SQLITE_BUSY ){ | |
| 62982 | + eMode2 = SQLITE_CHECKPOINT_PASSIVE; | |
| 62983 | + xBusy2 = 0; | |
| 62984 | + rc = SQLITE_OK; | |
| 62985 | + } | |
| 62986 | + } | |
| 62987 | + } | |
| 62988 | + | |
| 62688 | 62989 | |
| 62689 | 62990 | /* Read the wal-index header. */ |
| 62690 | 62991 | if( rc==SQLITE_OK ){ |
| 62992 | + walDisableBlocking(pWal); | |
| 62691 | 62993 | rc = walIndexReadHdr(pWal, &isChanged); |
| 62994 | + (void)walEnableBlocking(pWal); | |
| 62692 | 62995 | if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ |
| 62693 | 62996 | sqlite3OsUnfetch(pWal->pDbFd, 0, 0); |
| 62694 | 62997 | } |
| 62695 | 62998 | } |
| 62696 | 62999 | |
| @@ -62717,16 +63020,24 @@ | ||
| 62717 | 63020 | ** next time the pager opens a snapshot on this database it knows that |
| 62718 | 63021 | ** the cache needs to be reset. |
| 62719 | 63022 | */ |
| 62720 | 63023 | memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); |
| 62721 | 63024 | } |
| 63025 | + | |
| 63026 | + walDisableBlocking(pWal); | |
| 63027 | + sqlite3WalDb(pWal, 0); | |
| 62722 | 63028 | |
| 62723 | 63029 | /* Release the locks. */ |
| 62724 | 63030 | sqlite3WalEndWriteTransaction(pWal); |
| 62725 | - walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); | |
| 62726 | - pWal->ckptLock = 0; | |
| 63031 | + if( pWal->ckptLock ){ | |
| 63032 | + walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); | |
| 63033 | + pWal->ckptLock = 0; | |
| 63034 | + } | |
| 62727 | 63035 | WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); |
| 63036 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 63037 | + if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; | |
| 63038 | +#endif | |
| 62728 | 63039 | return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); |
| 62729 | 63040 | } |
| 62730 | 63041 | |
| 62731 | 63042 | /* Return the value to pass to a sqlite3_wal_hook callback, the |
| 62732 | 63043 | ** number of frames in the WAL at the point of the last commit since |
| @@ -62839,11 +63150,14 @@ | ||
| 62839 | 63150 | return rc; |
| 62840 | 63151 | } |
| 62841 | 63152 | |
| 62842 | 63153 | /* Try to open on pSnapshot when the next read-transaction starts |
| 62843 | 63154 | */ |
| 62844 | -SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){ | |
| 63155 | +SQLITE_PRIVATE void sqlite3WalSnapshotOpen( | |
| 63156 | + Wal *pWal, | |
| 63157 | + sqlite3_snapshot *pSnapshot | |
| 63158 | +){ | |
| 62845 | 63159 | pWal->pSnapshot = (WalIndexHdr*)pSnapshot; |
| 62846 | 63160 | } |
| 62847 | 63161 | |
| 62848 | 63162 | /* |
| 62849 | 63163 | ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if |
| @@ -63358,10 +63672,11 @@ | ||
| 63358 | 63672 | u8 incrVacuum; /* True if incr-vacuum is enabled */ |
| 63359 | 63673 | u8 bDoTruncate; /* True to truncate db on commit */ |
| 63360 | 63674 | #endif |
| 63361 | 63675 | u8 inTransaction; /* Transaction state */ |
| 63362 | 63676 | u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ |
| 63677 | + u8 nReserveWanted; /* Desired number of extra bytes per page */ | |
| 63363 | 63678 | u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ |
| 63364 | 63679 | u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ |
| 63365 | 63680 | u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ |
| 63366 | 63681 | u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ |
| 63367 | 63682 | u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ |
| @@ -66251,12 +66566,11 @@ | ||
| 66251 | 66566 | */ |
| 66252 | 66567 | static int btreeInvokeBusyHandler(void *pArg){ |
| 66253 | 66568 | BtShared *pBt = (BtShared*)pArg; |
| 66254 | 66569 | assert( pBt->db ); |
| 66255 | 66570 | assert( sqlite3_mutex_held(pBt->db->mutex) ); |
| 66256 | - return sqlite3InvokeBusyHandler(&pBt->db->busyHandler, | |
| 66257 | - sqlite3PagerFile(pBt->pPager)); | |
| 66571 | + return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); | |
| 66258 | 66572 | } |
| 66259 | 66573 | |
| 66260 | 66574 | /* |
| 66261 | 66575 | ** Open a database file. |
| 66262 | 66576 | ** |
| @@ -66803,20 +67117,21 @@ | ||
| 66803 | 67117 | ** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size |
| 66804 | 67118 | ** and autovacuum mode can no longer be changed. |
| 66805 | 67119 | */ |
| 66806 | 67120 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ |
| 66807 | 67121 | int rc = SQLITE_OK; |
| 67122 | + int x; | |
| 66808 | 67123 | BtShared *pBt = p->pBt; |
| 66809 | - assert( nReserve>=-1 && nReserve<=255 ); | |
| 67124 | + assert( nReserve>=0 && nReserve<=255 ); | |
| 66810 | 67125 | sqlite3BtreeEnter(p); |
| 67126 | + pBt->nReserveWanted = nReserve; | |
| 67127 | + x = pBt->pageSize - pBt->usableSize; | |
| 67128 | + if( nReserve<x ) nReserve = x; | |
| 66811 | 67129 | if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ |
| 66812 | 67130 | sqlite3BtreeLeave(p); |
| 66813 | 67131 | return SQLITE_READONLY; |
| 66814 | 67132 | } |
| 66815 | - if( nReserve<0 ){ | |
| 66816 | - nReserve = pBt->pageSize - pBt->usableSize; | |
| 66817 | - } | |
| 66818 | 67133 | assert( nReserve>=0 && nReserve<=255 ); |
| 66819 | 67134 | if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && |
| 66820 | 67135 | ((pageSize-1)&pageSize)==0 ){ |
| 66821 | 67136 | assert( (pageSize & 7)==0 ); |
| 66822 | 67137 | assert( !pBt->pCursor ); |
| @@ -66858,20 +67173,21 @@ | ||
| 66858 | 67173 | /* |
| 66859 | 67174 | ** Return the number of bytes of space at the end of every page that |
| 66860 | 67175 | ** are intentually left unused. This is the "reserved" space that is |
| 66861 | 67176 | ** sometimes used by extensions. |
| 66862 | 67177 | ** |
| 66863 | -** If SQLITE_HAS_MUTEX is defined then the number returned is the | |
| 66864 | -** greater of the current reserved space and the maximum requested | |
| 66865 | -** reserve space. | |
| 67178 | +** The value returned is the larger of the current reserve size and | |
| 67179 | +** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES. | |
| 67180 | +** The amount of reserve can only grow - never shrink. | |
| 66866 | 67181 | */ |
| 66867 | -SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){ | |
| 66868 | - int n; | |
| 67182 | +SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree *p){ | |
| 67183 | + int n1, n2; | |
| 66869 | 67184 | sqlite3BtreeEnter(p); |
| 66870 | - n = sqlite3BtreeGetReserveNoMutex(p); | |
| 67185 | + n1 = (int)p->pBt->nReserveWanted; | |
| 67186 | + n2 = sqlite3BtreeGetReserveNoMutex(p); | |
| 66871 | 67187 | sqlite3BtreeLeave(p); |
| 66872 | - return n; | |
| 67188 | + return n1>n2 ? n1 : n2; | |
| 66873 | 67189 | } |
| 66874 | 67190 | |
| 66875 | 67191 | |
| 66876 | 67192 | /* |
| 66877 | 67193 | ** Set the maximum page count for a database if mxPage is positive. |
| @@ -67317,10 +67633,11 @@ | ||
| 67317 | 67633 | ** when A already has a read lock, we encourage A to give up and let B |
| 67318 | 67634 | ** proceed. |
| 67319 | 67635 | */ |
| 67320 | 67636 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ |
| 67321 | 67637 | BtShared *pBt = p->pBt; |
| 67638 | + Pager *pPager = pBt->pPager; | |
| 67322 | 67639 | int rc = SQLITE_OK; |
| 67323 | 67640 | |
| 67324 | 67641 | sqlite3BtreeEnter(p); |
| 67325 | 67642 | btreeIntegrity(p); |
| 67326 | 67643 | |
| @@ -67332,11 +67649,11 @@ | ||
| 67332 | 67649 | goto trans_begun; |
| 67333 | 67650 | } |
| 67334 | 67651 | assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 ); |
| 67335 | 67652 | |
| 67336 | 67653 | if( (p->db->flags & SQLITE_ResetDatabase) |
| 67337 | - && sqlite3PagerIsreadonly(pBt->pPager)==0 | |
| 67654 | + && sqlite3PagerIsreadonly(pPager)==0 | |
| 67338 | 67655 | ){ |
| 67339 | 67656 | pBt->btsFlags &= ~BTS_READ_ONLY; |
| 67340 | 67657 | } |
| 67341 | 67658 | |
| 67342 | 67659 | /* Write transactions are not possible on a read-only database */ |
| @@ -67380,10 +67697,22 @@ | ||
| 67380 | 67697 | if( SQLITE_OK!=rc ) goto trans_begun; |
| 67381 | 67698 | |
| 67382 | 67699 | pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; |
| 67383 | 67700 | if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; |
| 67384 | 67701 | do { |
| 67702 | + sqlite3PagerWalDb(pPager, p->db); | |
| 67703 | + | |
| 67704 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 67705 | + /* If transitioning from no transaction directly to a write transaction, | |
| 67706 | + ** block for the WRITER lock first if possible. */ | |
| 67707 | + if( pBt->pPage1==0 && wrflag ){ | |
| 67708 | + assert( pBt->inTransaction==TRANS_NONE ); | |
| 67709 | + rc = sqlite3PagerWalWriteLock(pPager, 1); | |
| 67710 | + if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break; | |
| 67711 | + } | |
| 67712 | +#endif | |
| 67713 | + | |
| 67385 | 67714 | /* Call lockBtree() until either pBt->pPage1 is populated or |
| 67386 | 67715 | ** lockBtree() returns something other than SQLITE_OK. lockBtree() |
| 67387 | 67716 | ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after |
| 67388 | 67717 | ** reading page 1 it discovers that the page-size of the database |
| 67389 | 67718 | ** file is not pBt->pageSize. In this case lockBtree() will update |
| @@ -67393,11 +67722,11 @@ | ||
| 67393 | 67722 | |
| 67394 | 67723 | if( rc==SQLITE_OK && wrflag ){ |
| 67395 | 67724 | if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ |
| 67396 | 67725 | rc = SQLITE_READONLY; |
| 67397 | 67726 | }else{ |
| 67398 | - rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db)); | |
| 67727 | + rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db)); | |
| 67399 | 67728 | if( rc==SQLITE_OK ){ |
| 67400 | 67729 | rc = newDatabase(pBt); |
| 67401 | 67730 | }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){ |
| 67402 | 67731 | /* if there was no transaction opened when this function was |
| 67403 | 67732 | ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error |
| @@ -67406,15 +67735,19 @@ | ||
| 67406 | 67735 | } |
| 67407 | 67736 | } |
| 67408 | 67737 | } |
| 67409 | 67738 | |
| 67410 | 67739 | if( rc!=SQLITE_OK ){ |
| 67740 | + (void)sqlite3PagerWalWriteLock(pPager, 0); | |
| 67411 | 67741 | unlockBtreeIfUnused(pBt); |
| 67412 | 67742 | } |
| 67413 | 67743 | }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && |
| 67414 | 67744 | btreeInvokeBusyHandler(pBt) ); |
| 67415 | - sqlite3PagerResetLockTimeout(pBt->pPager); | |
| 67745 | + sqlite3PagerWalDb(pPager, 0); | |
| 67746 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 67747 | + if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; | |
| 67748 | +#endif | |
| 67416 | 67749 | |
| 67417 | 67750 | if( rc==SQLITE_OK ){ |
| 67418 | 67751 | if( p->inTrans==TRANS_NONE ){ |
| 67419 | 67752 | pBt->nTransaction++; |
| 67420 | 67753 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| @@ -67462,11 +67795,11 @@ | ||
| 67462 | 67795 | if( wrflag ){ |
| 67463 | 67796 | /* This call makes sure that the pager has the correct number of |
| 67464 | 67797 | ** open savepoints. If the second parameter is greater than 0 and |
| 67465 | 67798 | ** the sub-journal is not already open, then it will be opened here. |
| 67466 | 67799 | */ |
| 67467 | - rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); | |
| 67800 | + rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint); | |
| 67468 | 67801 | } |
| 67469 | 67802 | } |
| 67470 | 67803 | |
| 67471 | 67804 | btreeIntegrity(p); |
| 67472 | 67805 | sqlite3BtreeLeave(p); |
| @@ -71098,11 +71431,11 @@ | ||
| 71098 | 71431 | |
| 71099 | 71432 | /* Remove cells from the start and end of the page */ |
| 71100 | 71433 | assert( nCell>=0 ); |
| 71101 | 71434 | if( iOld<iNew ){ |
| 71102 | 71435 | int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray); |
| 71103 | - if( nShift>nCell ) return SQLITE_CORRUPT_BKPT; | |
| 71436 | + if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT; | |
| 71104 | 71437 | memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2); |
| 71105 | 71438 | nCell -= nShift; |
| 71106 | 71439 | } |
| 71107 | 71440 | if( iNewEnd < iOldEnd ){ |
| 71108 | 71441 | int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); |
| @@ -73455,11 +73788,10 @@ | ||
| 73455 | 73788 | } |
| 73456 | 73789 | sqlite3BtreeLeave(p); |
| 73457 | 73790 | return rc; |
| 73458 | 73791 | } |
| 73459 | 73792 | |
| 73460 | -#ifndef SQLITE_OMIT_BTREECOUNT | |
| 73461 | 73793 | /* |
| 73462 | 73794 | ** The first argument, pCur, is a cursor opened on some b-tree. Count the |
| 73463 | 73795 | ** number of entries in the b-tree and write the result to *pnEntry. |
| 73464 | 73796 | ** |
| 73465 | 73797 | ** SQLITE_OK is returned if the operation is successfully executed. |
| @@ -73477,11 +73809,11 @@ | ||
| 73477 | 73809 | } |
| 73478 | 73810 | |
| 73479 | 73811 | /* Unless an error occurs, the following loop runs one iteration for each |
| 73480 | 73812 | ** page in the B-Tree structure (not including overflow pages). |
| 73481 | 73813 | */ |
| 73482 | - while( rc==SQLITE_OK && !db->u1.isInterrupted ){ | |
| 73814 | + while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){ | |
| 73483 | 73815 | int iIdx; /* Index of child node in parent */ |
| 73484 | 73816 | MemPage *pPage; /* Current page of the b-tree */ |
| 73485 | 73817 | |
| 73486 | 73818 | /* If this is a leaf page or the tree is not an int-key tree, then |
| 73487 | 73819 | ** this page contains countable entries. Increment the entry counter |
| @@ -73528,11 +73860,10 @@ | ||
| 73528 | 73860 | } |
| 73529 | 73861 | |
| 73530 | 73862 | /* An error has occurred. Return an error code. */ |
| 73531 | 73863 | return rc; |
| 73532 | 73864 | } |
| 73533 | -#endif | |
| 73534 | 73865 | |
| 73535 | 73866 | /* |
| 73536 | 73867 | ** Return the pager associated with a BTree. This routine is used for |
| 73537 | 73868 | ** testing and debugging only. |
| 73538 | 73869 | */ |
| @@ -73603,11 +73934,11 @@ | ||
| 73603 | 73934 | } |
| 73604 | 73935 | if( getPageReferenced(pCheck, iPage) ){ |
| 73605 | 73936 | checkAppendMsg(pCheck, "2nd reference to page %d", iPage); |
| 73606 | 73937 | return 1; |
| 73607 | 73938 | } |
| 73608 | - if( pCheck->db->u1.isInterrupted ) return 1; | |
| 73939 | + if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1; | |
| 73609 | 73940 | setPageReferenced(pCheck, iPage); |
| 73610 | 73941 | return 0; |
| 73611 | 73942 | } |
| 73612 | 73943 | |
| 73613 | 73944 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| @@ -74579,11 +74910,11 @@ | ||
| 74579 | 74910 | ** Attempt to set the page size of the destination to match the page size |
| 74580 | 74911 | ** of the source. |
| 74581 | 74912 | */ |
| 74582 | 74913 | static int setDestPgsz(sqlite3_backup *p){ |
| 74583 | 74914 | int rc; |
| 74584 | - rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0); | |
| 74915 | + rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0); | |
| 74585 | 74916 | return rc; |
| 74586 | 74917 | } |
| 74587 | 74918 | |
| 74588 | 74919 | /* |
| 74589 | 74920 | ** Check that there is no open read-transaction on the b-tree passed as the |
| @@ -78604,24 +78935,23 @@ | ||
| 78604 | 78935 | ** "PX" -> "r[X]" |
| 78605 | 78936 | ** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 |
| 78606 | 78937 | ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 |
| 78607 | 78938 | ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x |
| 78608 | 78939 | */ |
| 78609 | -static int displayComment( | |
| 78940 | +SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( | |
| 78941 | + sqlite3 *db, /* Optional - Oom error reporting only */ | |
| 78610 | 78942 | const Op *pOp, /* The opcode to be commented */ |
| 78611 | - const char *zP4, /* Previously obtained value for P4 */ | |
| 78612 | - char *zTemp, /* Write result here */ | |
| 78613 | - int nTemp /* Space available in zTemp[] */ | |
| 78943 | + const char *zP4 /* Previously obtained value for P4 */ | |
| 78614 | 78944 | ){ |
| 78615 | 78945 | const char *zOpName; |
| 78616 | 78946 | const char *zSynopsis; |
| 78617 | 78947 | int nOpName; |
| 78618 | 78948 | int ii; |
| 78619 | 78949 | char zAlt[50]; |
| 78620 | 78950 | StrAccum x; |
| 78621 | - sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0); | |
| 78622 | 78951 | |
| 78952 | + sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); | |
| 78623 | 78953 | zOpName = sqlite3OpcodeName(pOp->opcode); |
| 78624 | 78954 | nOpName = sqlite3Strlen30(zOpName); |
| 78625 | 78955 | if( zOpName[nOpName+1] ){ |
| 78626 | 78956 | int seenCom = 0; |
| 78627 | 78957 | char c; |
| @@ -78684,14 +79014,16 @@ | ||
| 78684 | 79014 | sqlite3_str_appendf(&x, "; %s", pOp->zComment); |
| 78685 | 79015 | } |
| 78686 | 79016 | }else if( pOp->zComment ){ |
| 78687 | 79017 | sqlite3_str_appendall(&x, pOp->zComment); |
| 78688 | 79018 | } |
| 78689 | - sqlite3StrAccumFinish(&x); | |
| 78690 | - return x.nChar; | |
| 79019 | + if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){ | |
| 79020 | + sqlite3OomFault(db); | |
| 79021 | + } | |
| 79022 | + return sqlite3StrAccumFinish(&x); | |
| 78691 | 79023 | } |
| 78692 | -#endif /* SQLITE_DEBUG */ | |
| 79024 | +#endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */ | |
| 78693 | 79025 | |
| 78694 | 79026 | #if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) |
| 78695 | 79027 | /* |
| 78696 | 79028 | ** Translate the P4.pExpr value for an OP_CursorHint opcode into text |
| 78697 | 79029 | ** that can be displayed in the P4 column of EXPLAIN output. |
| @@ -78768,15 +79100,15 @@ | ||
| 78768 | 79100 | #if VDBE_DISPLAY_P4 |
| 78769 | 79101 | /* |
| 78770 | 79102 | ** Compute a string that describes the P4 parameter for an opcode. |
| 78771 | 79103 | ** Use zTemp for any required temporary buffer space. |
| 78772 | 79104 | */ |
| 78773 | -static char *displayP4(Op *pOp, char *zTemp, int nTemp){ | |
| 78774 | - char *zP4 = zTemp; | |
| 79105 | +SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ | |
| 79106 | + char *zP4 = 0; | |
| 78775 | 79107 | StrAccum x; |
| 78776 | - assert( nTemp>=20 ); | |
| 78777 | - sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0); | |
| 79108 | + | |
| 79109 | + sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); | |
| 78778 | 79110 | switch( pOp->p4type ){ |
| 78779 | 79111 | case P4_KEYINFO: { |
| 78780 | 79112 | int j; |
| 78781 | 79113 | KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; |
| 78782 | 79114 | assert( pKeyInfo->aSortFlags!=0 ); |
| @@ -78856,40 +79188,36 @@ | ||
| 78856 | 79188 | int i; |
| 78857 | 79189 | int *ai = pOp->p4.ai; |
| 78858 | 79190 | int n = ai[0]; /* The first element of an INTARRAY is always the |
| 78859 | 79191 | ** count of the number of elements to follow */ |
| 78860 | 79192 | for(i=1; i<=n; i++){ |
| 78861 | - sqlite3_str_appendf(&x, ",%d", ai[i]); | |
| 79193 | + sqlite3_str_appendf(&x, "%c%d", (i==1 ? '[' : ','), ai[i]); | |
| 78862 | 79194 | } |
| 78863 | - zTemp[0] = '['; | |
| 78864 | 79195 | sqlite3_str_append(&x, "]", 1); |
| 78865 | 79196 | break; |
| 78866 | 79197 | } |
| 78867 | 79198 | case P4_SUBPROGRAM: { |
| 78868 | - sqlite3_str_appendf(&x, "program"); | |
| 79199 | + zP4 = "program"; | |
| 78869 | 79200 | break; |
| 78870 | 79201 | } |
| 78871 | 79202 | case P4_DYNBLOB: |
| 78872 | 79203 | case P4_ADVANCE: { |
| 78873 | - zTemp[0] = 0; | |
| 78874 | 79204 | break; |
| 78875 | 79205 | } |
| 78876 | 79206 | case P4_TABLE: { |
| 78877 | - sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName); | |
| 79207 | + zP4 = pOp->p4.pTab->zName; | |
| 78878 | 79208 | break; |
| 78879 | 79209 | } |
| 78880 | 79210 | default: { |
| 78881 | 79211 | zP4 = pOp->p4.z; |
| 78882 | - if( zP4==0 ){ | |
| 78883 | - zP4 = zTemp; | |
| 78884 | - zTemp[0] = 0; | |
| 78885 | - } | |
| 78886 | 79212 | } |
| 78887 | 79213 | } |
| 78888 | - sqlite3StrAccumFinish(&x); | |
| 78889 | - assert( zP4!=0 ); | |
| 78890 | - return zP4; | |
| 79214 | + if( zP4 ) sqlite3_str_appendall(&x, zP4); | |
| 79215 | + if( (x.accError & SQLITE_NOMEM)!=0 ){ | |
| 79216 | + sqlite3OomFault(db); | |
| 79217 | + } | |
| 79218 | + return sqlite3StrAccumFinish(&x); | |
| 78891 | 79219 | } |
| 78892 | 79220 | #endif /* VDBE_DISPLAY_P4 */ |
| 78893 | 79221 | |
| 78894 | 79222 | /* |
| 78895 | 79223 | ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. |
| @@ -78975,28 +79303,32 @@ | ||
| 78975 | 79303 | /* |
| 78976 | 79304 | ** Print a single opcode. This routine is used for debugging only. |
| 78977 | 79305 | */ |
| 78978 | 79306 | SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ |
| 78979 | 79307 | char *zP4; |
| 78980 | - char zPtr[50]; | |
| 78981 | - char zCom[100]; | |
| 79308 | + char *zCom; | |
| 79309 | + sqlite3 dummyDb; | |
| 78982 | 79310 | static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; |
| 78983 | 79311 | if( pOut==0 ) pOut = stdout; |
| 78984 | - zP4 = displayP4(pOp, zPtr, sizeof(zPtr)); | |
| 79312 | + dummyDb.mallocFailed = 1; | |
| 79313 | + zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp); | |
| 78985 | 79314 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 78986 | - displayComment(pOp, zP4, zCom, sizeof(zCom)); | |
| 79315 | + zCom = sqlite3VdbeDisplayComment(0, pOp, zP4); | |
| 78987 | 79316 | #else |
| 78988 | - zCom[0] = 0; | |
| 79317 | + zCom = 0; | |
| 78989 | 79318 | #endif |
| 78990 | 79319 | /* NB: The sqlite3OpcodeName() function is implemented by code created |
| 78991 | 79320 | ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the |
| 78992 | 79321 | ** information from the vdbe.c source text */ |
| 78993 | 79322 | fprintf(pOut, zFormat1, pc, |
| 78994 | - sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, | |
| 78995 | - zCom | |
| 79323 | + sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, | |
| 79324 | + zP4 ? zP4 : "", pOp->p5, | |
| 79325 | + zCom ? zCom : "" | |
| 78996 | 79326 | ); |
| 78997 | 79327 | fflush(pOut); |
| 79328 | + sqlite3_free(zP4); | |
| 79329 | + sqlite3_free(zCom); | |
| 78998 | 79330 | } |
| 78999 | 79331 | #endif |
| 79000 | 79332 | |
| 79001 | 79333 | /* |
| 79002 | 79334 | ** Initialize an array of N Mem element. |
| @@ -79083,10 +79415,125 @@ | ||
| 79083 | 79415 | assert( sqlite3VdbeFrameIsValid(pFrame) ); |
| 79084 | 79416 | pFrame->pParent = pFrame->v->pDelFrame; |
| 79085 | 79417 | pFrame->v->pDelFrame = pFrame; |
| 79086 | 79418 | } |
| 79087 | 79419 | |
| 79420 | +#if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN) | |
| 79421 | +/* | |
| 79422 | +** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN | |
| 79423 | +** QUERY PLAN output. | |
| 79424 | +** | |
| 79425 | +** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no | |
| 79426 | +** more opcodes to be displayed. | |
| 79427 | +*/ | |
| 79428 | +SQLITE_PRIVATE int sqlite3VdbeNextOpcode( | |
| 79429 | + Vdbe *p, /* The statement being explained */ | |
| 79430 | + Mem *pSub, /* Storage for keeping track of subprogram nesting */ | |
| 79431 | + int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */ | |
| 79432 | + int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */ | |
| 79433 | + int *piAddr, /* OUT: Write index into (*paOp)[] here */ | |
| 79434 | + Op **paOp /* OUT: Write the opcode array here */ | |
| 79435 | +){ | |
| 79436 | + int nRow; /* Stop when row count reaches this */ | |
| 79437 | + int nSub = 0; /* Number of sub-vdbes seen so far */ | |
| 79438 | + SubProgram **apSub = 0; /* Array of sub-vdbes */ | |
| 79439 | + int i; /* Next instruction address */ | |
| 79440 | + int rc = SQLITE_OK; /* Result code */ | |
| 79441 | + Op *aOp = 0; /* Opcode array */ | |
| 79442 | + int iPc; /* Rowid. Copy of value in *piPc */ | |
| 79443 | + | |
| 79444 | + /* When the number of output rows reaches nRow, that means the | |
| 79445 | + ** listing has finished and sqlite3_step() should return SQLITE_DONE. | |
| 79446 | + ** nRow is the sum of the number of rows in the main program, plus | |
| 79447 | + ** the sum of the number of rows in all trigger subprograms encountered | |
| 79448 | + ** so far. The nRow value will increase as new trigger subprograms are | |
| 79449 | + ** encountered, but p->pc will eventually catch up to nRow. | |
| 79450 | + */ | |
| 79451 | + nRow = p->nOp; | |
| 79452 | + if( pSub!=0 ){ | |
| 79453 | + if( pSub->flags&MEM_Blob ){ | |
| 79454 | + /* pSub is initiallly NULL. It is initialized to a BLOB by | |
| 79455 | + ** the P4_SUBPROGRAM processing logic below */ | |
| 79456 | + nSub = pSub->n/sizeof(Vdbe*); | |
| 79457 | + apSub = (SubProgram **)pSub->z; | |
| 79458 | + } | |
| 79459 | + for(i=0; i<nSub; i++){ | |
| 79460 | + nRow += apSub[i]->nOp; | |
| 79461 | + } | |
| 79462 | + } | |
| 79463 | + iPc = *piPc; | |
| 79464 | + while(1){ /* Loop exits via break */ | |
| 79465 | + i = iPc++; | |
| 79466 | + if( i>=nRow ){ | |
| 79467 | + p->rc = SQLITE_OK; | |
| 79468 | + rc = SQLITE_DONE; | |
| 79469 | + break; | |
| 79470 | + } | |
| 79471 | + if( i<p->nOp ){ | |
| 79472 | + /* The rowid is small enough that we are still in the | |
| 79473 | + ** main program. */ | |
| 79474 | + aOp = p->aOp; | |
| 79475 | + }else{ | |
| 79476 | + /* We are currently listing subprograms. Figure out which one and | |
| 79477 | + ** pick up the appropriate opcode. */ | |
| 79478 | + int j; | |
| 79479 | + i -= p->nOp; | |
| 79480 | + assert( apSub!=0 ); | |
| 79481 | + assert( nSub>0 ); | |
| 79482 | + for(j=0; i>=apSub[j]->nOp; j++){ | |
| 79483 | + i -= apSub[j]->nOp; | |
| 79484 | + assert( i<apSub[j]->nOp || j+1<nSub ); | |
| 79485 | + } | |
| 79486 | + aOp = apSub[j]->aOp; | |
| 79487 | + } | |
| 79488 | + | |
| 79489 | + /* When an OP_Program opcode is encounter (the only opcode that has | |
| 79490 | + ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms | |
| 79491 | + ** kept in p->aMem[9].z to hold the new program - assuming this subprogram | |
| 79492 | + ** has not already been seen. | |
| 79493 | + */ | |
| 79494 | + if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){ | |
| 79495 | + int nByte = (nSub+1)*sizeof(SubProgram*); | |
| 79496 | + int j; | |
| 79497 | + for(j=0; j<nSub; j++){ | |
| 79498 | + if( apSub[j]==aOp[i].p4.pProgram ) break; | |
| 79499 | + } | |
| 79500 | + if( j==nSub ){ | |
| 79501 | + p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); | |
| 79502 | + if( p->rc!=SQLITE_OK ){ | |
| 79503 | + rc = SQLITE_ERROR; | |
| 79504 | + break; | |
| 79505 | + } | |
| 79506 | + apSub = (SubProgram **)pSub->z; | |
| 79507 | + apSub[nSub++] = aOp[i].p4.pProgram; | |
| 79508 | + MemSetTypeFlag(pSub, MEM_Blob); | |
| 79509 | + pSub->n = nSub*sizeof(SubProgram*); | |
| 79510 | + nRow += aOp[i].p4.pProgram->nOp; | |
| 79511 | + } | |
| 79512 | + } | |
| 79513 | + if( eMode==0 ) break; | |
| 79514 | +#ifdef SQLITE_ENABLE_BYTECODE_VTAB | |
| 79515 | + if( eMode==2 ){ | |
| 79516 | + Op *pOp = aOp + i; | |
| 79517 | + if( pOp->opcode==OP_OpenRead ) break; | |
| 79518 | + if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break; | |
| 79519 | + if( pOp->opcode==OP_ReopenIdx ) break; | |
| 79520 | + }else | |
| 79521 | +#endif | |
| 79522 | + { | |
| 79523 | + assert( eMode==1 ); | |
| 79524 | + if( aOp[i].opcode==OP_Explain ) break; | |
| 79525 | + if( aOp[i].opcode==OP_Init && iPc>1 ) break; | |
| 79526 | + } | |
| 79527 | + } | |
| 79528 | + *piPc = iPc; | |
| 79529 | + *piAddr = i; | |
| 79530 | + *paOp = aOp; | |
| 79531 | + return rc; | |
| 79532 | +} | |
| 79533 | +#endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */ | |
| 79534 | + | |
| 79088 | 79535 | |
| 79089 | 79536 | /* |
| 79090 | 79537 | ** Delete a VdbeFrame object and its contents. VdbeFrame objects are |
| 79091 | 79538 | ** allocated by the OP_Program opcode in sqlite3VdbeExec(). |
| 79092 | 79539 | */ |
| @@ -79123,20 +79570,18 @@ | ||
| 79123 | 79570 | ** the trigger subprograms are listed one by one. |
| 79124 | 79571 | */ |
| 79125 | 79572 | SQLITE_PRIVATE int sqlite3VdbeList( |
| 79126 | 79573 | Vdbe *p /* The VDBE */ |
| 79127 | 79574 | ){ |
| 79128 | - int nRow; /* Stop when row count reaches this */ | |
| 79129 | - int nSub = 0; /* Number of sub-vdbes seen so far */ | |
| 79130 | - SubProgram **apSub = 0; /* Array of sub-vdbes */ | |
| 79131 | 79575 | Mem *pSub = 0; /* Memory cell hold array of subprogs */ |
| 79132 | 79576 | sqlite3 *db = p->db; /* The database connection */ |
| 79133 | 79577 | int i; /* Loop counter */ |
| 79134 | 79578 | int rc = SQLITE_OK; /* Return code */ |
| 79135 | 79579 | Mem *pMem = &p->aMem[1]; /* First Mem of result set */ |
| 79136 | 79580 | int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0); |
| 79137 | - Op *pOp = 0; | |
| 79581 | + Op *aOp; /* Array of opcodes */ | |
| 79582 | + Op *pOp; /* Current opcode */ | |
| 79138 | 79583 | |
| 79139 | 79584 | assert( p->explain ); |
| 79140 | 79585 | assert( p->magic==VDBE_MAGIC_RUN ); |
| 79141 | 79586 | assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); |
| 79142 | 79587 | |
| @@ -79152,166 +79597,66 @@ | ||
| 79152 | 79597 | ** sqlite3_column_text16() failed. */ |
| 79153 | 79598 | sqlite3OomFault(db); |
| 79154 | 79599 | return SQLITE_ERROR; |
| 79155 | 79600 | } |
| 79156 | 79601 | |
| 79157 | - /* When the number of output rows reaches nRow, that means the | |
| 79158 | - ** listing has finished and sqlite3_step() should return SQLITE_DONE. | |
| 79159 | - ** nRow is the sum of the number of rows in the main program, plus | |
| 79160 | - ** the sum of the number of rows in all trigger subprograms encountered | |
| 79161 | - ** so far. The nRow value will increase as new trigger subprograms are | |
| 79162 | - ** encountered, but p->pc will eventually catch up to nRow. | |
| 79163 | - */ | |
| 79164 | - nRow = p->nOp; | |
| 79165 | 79602 | if( bListSubprogs ){ |
| 79166 | 79603 | /* The first 8 memory cells are used for the result set. So we will |
| 79167 | 79604 | ** commandeer the 9th cell to use as storage for an array of pointers |
| 79168 | 79605 | ** to trigger subprograms. The VDBE is guaranteed to have at least 9 |
| 79169 | 79606 | ** cells. */ |
| 79170 | 79607 | assert( p->nMem>9 ); |
| 79171 | 79608 | pSub = &p->aMem[9]; |
| 79172 | - if( pSub->flags&MEM_Blob ){ | |
| 79173 | - /* On the first call to sqlite3_step(), pSub will hold a NULL. It is | |
| 79174 | - ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */ | |
| 79175 | - nSub = pSub->n/sizeof(Vdbe*); | |
| 79176 | - apSub = (SubProgram **)pSub->z; | |
| 79177 | - } | |
| 79178 | - for(i=0; i<nSub; i++){ | |
| 79179 | - nRow += apSub[i]->nOp; | |
| 79180 | - } | |
| 79181 | - } | |
| 79182 | - | |
| 79183 | - while(1){ /* Loop exits via break */ | |
| 79184 | - i = p->pc++; | |
| 79185 | - if( i>=nRow ){ | |
| 79186 | - p->rc = SQLITE_OK; | |
| 79187 | - rc = SQLITE_DONE; | |
| 79188 | - break; | |
| 79189 | - } | |
| 79190 | - if( i<p->nOp ){ | |
| 79191 | - /* The output line number is small enough that we are still in the | |
| 79192 | - ** main program. */ | |
| 79193 | - pOp = &p->aOp[i]; | |
| 79194 | - }else{ | |
| 79195 | - /* We are currently listing subprograms. Figure out which one and | |
| 79196 | - ** pick up the appropriate opcode. */ | |
| 79197 | - int j; | |
| 79198 | - i -= p->nOp; | |
| 79199 | - assert( apSub!=0 ); | |
| 79200 | - assert( nSub>0 ); | |
| 79201 | - for(j=0; i>=apSub[j]->nOp; j++){ | |
| 79202 | - i -= apSub[j]->nOp; | |
| 79203 | - assert( i<apSub[j]->nOp || j+1<nSub ); | |
| 79204 | - } | |
| 79205 | - pOp = &apSub[j]->aOp[i]; | |
| 79206 | - } | |
| 79207 | - | |
| 79208 | - /* When an OP_Program opcode is encounter (the only opcode that has | |
| 79209 | - ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms | |
| 79210 | - ** kept in p->aMem[9].z to hold the new program - assuming this subprogram | |
| 79211 | - ** has not already been seen. | |
| 79212 | - */ | |
| 79213 | - if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){ | |
| 79214 | - int nByte = (nSub+1)*sizeof(SubProgram*); | |
| 79215 | - int j; | |
| 79216 | - for(j=0; j<nSub; j++){ | |
| 79217 | - if( apSub[j]==pOp->p4.pProgram ) break; | |
| 79218 | - } | |
| 79219 | - if( j==nSub ){ | |
| 79220 | - p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); | |
| 79221 | - if( p->rc!=SQLITE_OK ){ | |
| 79222 | - rc = SQLITE_ERROR; | |
| 79223 | - break; | |
| 79224 | - } | |
| 79225 | - apSub = (SubProgram **)pSub->z; | |
| 79226 | - apSub[nSub++] = pOp->p4.pProgram; | |
| 79227 | - pSub->flags |= MEM_Blob; | |
| 79228 | - pSub->n = nSub*sizeof(SubProgram*); | |
| 79229 | - nRow += pOp->p4.pProgram->nOp; | |
| 79230 | - } | |
| 79231 | - } | |
| 79232 | - if( p->explain<2 ) break; | |
| 79233 | - if( pOp->opcode==OP_Explain ) break; | |
| 79234 | - if( pOp->opcode==OP_Init && p->pc>1 ) break; | |
| 79235 | - } | |
| 79236 | - | |
| 79237 | - if( rc==SQLITE_OK ){ | |
| 79238 | - if( db->u1.isInterrupted ){ | |
| 79609 | + }else{ | |
| 79610 | + pSub = 0; | |
| 79611 | + } | |
| 79612 | + | |
| 79613 | + /* Figure out which opcode is next to display */ | |
| 79614 | + rc = sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp); | |
| 79615 | + | |
| 79616 | + if( rc==SQLITE_OK ){ | |
| 79617 | + pOp = aOp + i; | |
| 79618 | + if( AtomicLoad(&db->u1.isInterrupted) ){ | |
| 79239 | 79619 | p->rc = SQLITE_INTERRUPT; |
| 79240 | 79620 | rc = SQLITE_ERROR; |
| 79241 | 79621 | sqlite3VdbeError(p, sqlite3ErrStr(p->rc)); |
| 79242 | 79622 | }else{ |
| 79243 | - char *zP4; | |
| 79244 | - if( p->explain==1 ){ | |
| 79245 | - pMem->flags = MEM_Int; | |
| 79246 | - pMem->u.i = i; /* Program counter */ | |
| 79247 | - pMem++; | |
| 79248 | - | |
| 79249 | - pMem->flags = MEM_Static|MEM_Str|MEM_Term; | |
| 79250 | - pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ | |
| 79251 | - assert( pMem->z!=0 ); | |
| 79252 | - pMem->n = sqlite3Strlen30(pMem->z); | |
| 79253 | - pMem->enc = SQLITE_UTF8; | |
| 79254 | - pMem++; | |
| 79255 | - } | |
| 79256 | - | |
| 79257 | - pMem->flags = MEM_Int; | |
| 79258 | - pMem->u.i = pOp->p1; /* P1 */ | |
| 79259 | - pMem++; | |
| 79260 | - | |
| 79261 | - pMem->flags = MEM_Int; | |
| 79262 | - pMem->u.i = pOp->p2; /* P2 */ | |
| 79263 | - pMem++; | |
| 79264 | - | |
| 79265 | - pMem->flags = MEM_Int; | |
| 79266 | - pMem->u.i = pOp->p3; /* P3 */ | |
| 79267 | - pMem++; | |
| 79268 | - | |
| 79269 | - if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */ | |
| 79270 | - assert( p->db->mallocFailed ); | |
| 79271 | - return SQLITE_ERROR; | |
| 79272 | - } | |
| 79273 | - pMem->flags = MEM_Str|MEM_Term; | |
| 79274 | - zP4 = displayP4(pOp, pMem->z, pMem->szMalloc); | |
| 79275 | - if( zP4!=pMem->z ){ | |
| 79276 | - pMem->n = 0; | |
| 79277 | - sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0); | |
| 79623 | + char *zP4 = sqlite3VdbeDisplayP4(db, pOp); | |
| 79624 | + if( p->explain==2 ){ | |
| 79625 | + sqlite3VdbeMemSetInt64(pMem, pOp->p1); | |
| 79626 | + sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); | |
| 79627 | + sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); | |
| 79628 | + sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free); | |
| 79629 | + p->nResColumn = 4; | |
| 79278 | 79630 | }else{ |
| 79279 | - assert( pMem->z!=0 ); | |
| 79280 | - pMem->n = sqlite3Strlen30(pMem->z); | |
| 79281 | - pMem->enc = SQLITE_UTF8; | |
| 79282 | - } | |
| 79283 | - pMem++; | |
| 79284 | - | |
| 79285 | - if( p->explain==1 ){ | |
| 79286 | - if( sqlite3VdbeMemClearAndResize(pMem, 4) ){ | |
| 79287 | - assert( p->db->mallocFailed ); | |
| 79288 | - return SQLITE_ERROR; | |
| 79289 | - } | |
| 79290 | - pMem->flags = MEM_Str|MEM_Term; | |
| 79291 | - pMem->n = 2; | |
| 79292 | - sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ | |
| 79293 | - pMem->enc = SQLITE_UTF8; | |
| 79294 | - pMem++; | |
| 79295 | - | |
| 79631 | + sqlite3VdbeMemSetInt64(pMem+0, i); | |
| 79632 | + sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode), | |
| 79633 | + -1, SQLITE_UTF8, SQLITE_STATIC); | |
| 79634 | + sqlite3VdbeMemSetInt64(pMem+2, pOp->p1); | |
| 79635 | + sqlite3VdbeMemSetInt64(pMem+3, pOp->p2); | |
| 79636 | + sqlite3VdbeMemSetInt64(pMem+4, pOp->p3); | |
| 79637 | + /* pMem+5 for p4 is done last */ | |
| 79638 | + sqlite3VdbeMemSetInt64(pMem+6, pOp->p5); | |
| 79296 | 79639 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 79297 | - if( sqlite3VdbeMemClearAndResize(pMem, 500) ){ | |
| 79298 | - assert( p->db->mallocFailed ); | |
| 79299 | - return SQLITE_ERROR; | |
| 79300 | - } | |
| 79301 | - pMem->flags = MEM_Str|MEM_Term; | |
| 79302 | - pMem->n = displayComment(pOp, zP4, pMem->z, 500); | |
| 79303 | - pMem->enc = SQLITE_UTF8; | |
| 79304 | -#else | |
| 79305 | - pMem->flags = MEM_Null; /* Comment */ | |
| 79306 | -#endif | |
| 79307 | - } | |
| 79308 | - | |
| 79309 | - p->nResColumn = 8 - 4*(p->explain-1); | |
| 79310 | - p->pResultSet = &p->aMem[1]; | |
| 79311 | - p->rc = SQLITE_OK; | |
| 79312 | - rc = SQLITE_ROW; | |
| 79640 | + { | |
| 79641 | + char *zCom = sqlite3VdbeDisplayComment(db, pOp, zP4); | |
| 79642 | + sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlite3_free); | |
| 79643 | + } | |
| 79644 | +#else | |
| 79645 | + sqlite3VdbeMemSetNull(pMem+7); | |
| 79646 | +#endif | |
| 79647 | + sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free); | |
| 79648 | + p->nResColumn = 8; | |
| 79649 | + } | |
| 79650 | + p->pResultSet = pMem; | |
| 79651 | + if( db->mallocFailed ){ | |
| 79652 | + p->rc = SQLITE_NOMEM; | |
| 79653 | + rc = SQLITE_ERROR; | |
| 79654 | + }else{ | |
| 79655 | + p->rc = SQLITE_OK; | |
| 79656 | + rc = SQLITE_ROW; | |
| 79657 | + } | |
| 79313 | 79658 | } |
| 79314 | 79659 | } |
| 79315 | 79660 | return rc; |
| 79316 | 79661 | } |
| 79317 | 79662 | #endif /* SQLITE_OMIT_EXPLAIN */ |
| @@ -79877,12 +80222,13 @@ | ||
| 79877 | 80222 | int retryCount = 0; |
| 79878 | 80223 | int nMainFile; |
| 79879 | 80224 | |
| 79880 | 80225 | /* Select a master journal file name */ |
| 79881 | 80226 | nMainFile = sqlite3Strlen30(zMainFile); |
| 79882 | - zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz%c%c", zMainFile, 0, 0); | |
| 80227 | + zMaster = sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0); | |
| 79883 | 80228 | if( zMaster==0 ) return SQLITE_NOMEM_BKPT; |
| 80229 | + zMaster += 4; | |
| 79884 | 80230 | do { |
| 79885 | 80231 | u32 iRandom; |
| 79886 | 80232 | if( retryCount ){ |
| 79887 | 80233 | if( retryCount>100 ){ |
| 79888 | 80234 | sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster); |
| @@ -79908,11 +80254,11 @@ | ||
| 79908 | 80254 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| |
| 79909 | 80255 | SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0 |
| 79910 | 80256 | ); |
| 79911 | 80257 | } |
| 79912 | 80258 | if( rc!=SQLITE_OK ){ |
| 79913 | - sqlite3DbFree(db, zMaster); | |
| 80259 | + sqlite3DbFree(db, zMaster-4); | |
| 79914 | 80260 | return rc; |
| 79915 | 80261 | } |
| 79916 | 80262 | |
| 79917 | 80263 | /* Write the name of each database file in the transaction into the new |
| 79918 | 80264 | ** master journal file. If an error occurs at this point close |
| @@ -79931,11 +80277,11 @@ | ||
| 79931 | 80277 | rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); |
| 79932 | 80278 | offset += sqlite3Strlen30(zFile)+1; |
| 79933 | 80279 | if( rc!=SQLITE_OK ){ |
| 79934 | 80280 | sqlite3OsCloseFree(pMaster); |
| 79935 | 80281 | sqlite3OsDelete(pVfs, zMaster, 0); |
| 79936 | - sqlite3DbFree(db, zMaster); | |
| 80282 | + sqlite3DbFree(db, zMaster-4); | |
| 79937 | 80283 | return rc; |
| 79938 | 80284 | } |
| 79939 | 80285 | } |
| 79940 | 80286 | } |
| 79941 | 80287 | |
| @@ -79945,11 +80291,11 @@ | ||
| 79945 | 80291 | if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) |
| 79946 | 80292 | && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL)) |
| 79947 | 80293 | ){ |
| 79948 | 80294 | sqlite3OsCloseFree(pMaster); |
| 79949 | 80295 | sqlite3OsDelete(pVfs, zMaster, 0); |
| 79950 | - sqlite3DbFree(db, zMaster); | |
| 80296 | + sqlite3DbFree(db, zMaster-4); | |
| 79951 | 80297 | return rc; |
| 79952 | 80298 | } |
| 79953 | 80299 | |
| 79954 | 80300 | /* Sync all the db files involved in the transaction. The same call |
| 79955 | 80301 | ** sets the master journal pointer in each individual journal. If |
| @@ -79968,20 +80314,20 @@ | ||
| 79968 | 80314 | } |
| 79969 | 80315 | } |
| 79970 | 80316 | sqlite3OsCloseFree(pMaster); |
| 79971 | 80317 | assert( rc!=SQLITE_BUSY ); |
| 79972 | 80318 | if( rc!=SQLITE_OK ){ |
| 79973 | - sqlite3DbFree(db, zMaster); | |
| 80319 | + sqlite3DbFree(db, zMaster-4); | |
| 79974 | 80320 | return rc; |
| 79975 | 80321 | } |
| 79976 | 80322 | |
| 79977 | 80323 | /* Delete the master journal file. This commits the transaction. After |
| 79978 | 80324 | ** doing this the directory is synced again before any individual |
| 79979 | 80325 | ** transaction files are deleted. |
| 79980 | 80326 | */ |
| 79981 | 80327 | rc = sqlite3OsDelete(pVfs, zMaster, 1); |
| 79982 | - sqlite3DbFree(db, zMaster); | |
| 80328 | + sqlite3DbFree(db, zMaster-4); | |
| 79983 | 80329 | zMaster = 0; |
| 79984 | 80330 | if( rc ){ |
| 79985 | 80331 | return rc; |
| 79986 | 80332 | } |
| 79987 | 80333 | |
| @@ -83028,11 +83374,11 @@ | ||
| 83028 | 83374 | /* If there are no other statements currently running, then |
| 83029 | 83375 | ** reset the interrupt flag. This prevents a call to sqlite3_interrupt |
| 83030 | 83376 | ** from interrupting a statement that has not yet started. |
| 83031 | 83377 | */ |
| 83032 | 83378 | if( db->nVdbeActive==0 ){ |
| 83033 | - db->u1.isInterrupted = 0; | |
| 83379 | + AtomicStore(&db->u1.isInterrupted, 0); | |
| 83034 | 83380 | } |
| 83035 | 83381 | |
| 83036 | 83382 | assert( db->nVdbeWrite>0 || db->autoCommit==0 |
| 83037 | 83383 | || (db->nDeferredCons==0 && db->nDeferredImmCons==0) |
| 83038 | 83384 | ); |
| @@ -85413,11 +85759,11 @@ | ||
| 85413 | 85759 | assert( p->bIsReader || p->readOnly!=0 ); |
| 85414 | 85760 | p->iCurrentTime = 0; |
| 85415 | 85761 | assert( p->explain==0 ); |
| 85416 | 85762 | p->pResultSet = 0; |
| 85417 | 85763 | db->busyHandler.nBusy = 0; |
| 85418 | - if( db->u1.isInterrupted ) goto abort_due_to_interrupt; | |
| 85764 | + if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; | |
| 85419 | 85765 | sqlite3VdbeIOTraceSql(p); |
| 85420 | 85766 | #ifdef SQLITE_DEBUG |
| 85421 | 85767 | sqlite3BeginBenignMalloc(); |
| 85422 | 85768 | if( p->pc==0 |
| 85423 | 85769 | && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 |
| @@ -85597,11 +85943,11 @@ | ||
| 85597 | 85943 | ** But that is not due to sloppy coding habits. The code is written this |
| 85598 | 85944 | ** way for performance, to avoid having to run the interrupt and progress |
| 85599 | 85945 | ** checks on every opcode. This helps sqlite3_step() to run about 1.5% |
| 85600 | 85946 | ** faster according to "valgrind --tool=cachegrind" */ |
| 85601 | 85947 | check_for_interrupt: |
| 85602 | - if( db->u1.isInterrupted ) goto abort_due_to_interrupt; | |
| 85948 | + if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; | |
| 85603 | 85949 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 85604 | 85950 | /* Call the progress callback if it is configured and the required number |
| 85605 | 85951 | ** of VDBE ops have been executed (either since this invocation of |
| 85606 | 85952 | ** sqlite3VdbeExec() or since last time the progress callback was called). |
| 85607 | 85953 | ** If the progress callback returns non-zero, exit the virtual machine with |
| @@ -87893,32 +88239,38 @@ | ||
| 87893 | 88239 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 87894 | 88240 | REGISTER_TRACE(pOp->p3, pOut); |
| 87895 | 88241 | break; |
| 87896 | 88242 | } |
| 87897 | 88243 | |
| 87898 | -/* Opcode: Count P1 P2 * * * | |
| 88244 | +/* Opcode: Count P1 P2 p3 * * | |
| 87899 | 88245 | ** Synopsis: r[P2]=count() |
| 87900 | 88246 | ** |
| 87901 | 88247 | ** Store the number of entries (an integer value) in the table or index |
| 87902 | -** opened by cursor P1 in register P2 | |
| 88248 | +** opened by cursor P1 in register P2. | |
| 88249 | +** | |
| 88250 | +** If P3==0, then an exact count is obtained, which involves visiting | |
| 88251 | +** every btree page of the table. But if P3 is non-zero, an estimate | |
| 88252 | +** is returned based on the current cursor position. | |
| 87903 | 88253 | */ |
| 87904 | -#ifndef SQLITE_OMIT_BTREECOUNT | |
| 87905 | 88254 | case OP_Count: { /* out2 */ |
| 87906 | 88255 | i64 nEntry; |
| 87907 | 88256 | BtCursor *pCrsr; |
| 87908 | 88257 | |
| 87909 | 88258 | assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); |
| 87910 | 88259 | pCrsr = p->apCsr[pOp->p1]->uc.pCursor; |
| 87911 | 88260 | assert( pCrsr ); |
| 87912 | - nEntry = 0; /* Not needed. Only used to silence a warning. */ | |
| 87913 | - rc = sqlite3BtreeCount(db, pCrsr, &nEntry); | |
| 87914 | - if( rc ) goto abort_due_to_error; | |
| 88261 | + if( pOp->p3 ){ | |
| 88262 | + nEntry = sqlite3BtreeRowCountEst(pCrsr); | |
| 88263 | + }else{ | |
| 88264 | + nEntry = 0; /* Not needed. Only used to silence a warning. */ | |
| 88265 | + rc = sqlite3BtreeCount(db, pCrsr, &nEntry); | |
| 88266 | + if( rc ) goto abort_due_to_error; | |
| 88267 | + } | |
| 87915 | 88268 | pOut = out2Prerelease(p, pOp); |
| 87916 | 88269 | pOut->u.i = nEntry; |
| 87917 | 88270 | goto check_for_interrupt; |
| 87918 | 88271 | } |
| 87919 | -#endif | |
| 87920 | 88272 | |
| 87921 | 88273 | /* Opcode: Savepoint P1 * * P4 * |
| 87922 | 88274 | ** |
| 87923 | 88275 | ** Open, release or rollback the savepoint named by parameter P4, depending |
| 87924 | 88276 | ** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN). |
| @@ -90352,16 +90704,23 @@ | ||
| 90352 | 90704 | rc = sqlite3VdbeSorterWrite(pC, pIn2); |
| 90353 | 90705 | if( rc) goto abort_due_to_error; |
| 90354 | 90706 | break; |
| 90355 | 90707 | } |
| 90356 | 90708 | |
| 90357 | -/* Opcode: IdxDelete P1 P2 P3 * * | |
| 90709 | +/* Opcode: IdxDelete P1 P2 P3 * P5 | |
| 90358 | 90710 | ** Synopsis: key=r[P2@P3] |
| 90359 | 90711 | ** |
| 90360 | 90712 | ** The content of P3 registers starting at register P2 form |
| 90361 | 90713 | ** an unpacked index key. This opcode removes that entry from the |
| 90362 | 90714 | ** index opened by cursor P1. |
| 90715 | +** | |
| 90716 | +** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error | |
| 90717 | +** if no matching index entry is found. This happens when running | |
| 90718 | +** an UPDATE or DELETE statement and the index entry to be updated | |
| 90719 | +** or deleted is not found. For some uses of IdxDelete | |
| 90720 | +** (example: the EXCEPT operator) it does not matter that no matching | |
| 90721 | +** entry is found. For those cases, P5 is zero. | |
| 90363 | 90722 | */ |
| 90364 | 90723 | case OP_IdxDelete: { |
| 90365 | 90724 | VdbeCursor *pC; |
| 90366 | 90725 | BtCursor *pCrsr; |
| 90367 | 90726 | int res; |
| @@ -90374,20 +90733,22 @@ | ||
| 90374 | 90733 | assert( pC!=0 ); |
| 90375 | 90734 | assert( pC->eCurType==CURTYPE_BTREE ); |
| 90376 | 90735 | sqlite3VdbeIncrWriteCounter(p, pC); |
| 90377 | 90736 | pCrsr = pC->uc.pCursor; |
| 90378 | 90737 | assert( pCrsr!=0 ); |
| 90379 | - assert( pOp->p5==0 ); | |
| 90380 | 90738 | r.pKeyInfo = pC->pKeyInfo; |
| 90381 | 90739 | r.nField = (u16)pOp->p3; |
| 90382 | 90740 | r.default_rc = 0; |
| 90383 | 90741 | r.aMem = &aMem[pOp->p2]; |
| 90384 | 90742 | rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); |
| 90385 | 90743 | if( rc ) goto abort_due_to_error; |
| 90386 | 90744 | if( res==0 ){ |
| 90387 | 90745 | rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); |
| 90388 | 90746 | if( rc ) goto abort_due_to_error; |
| 90747 | + }else if( pOp->p5 ){ | |
| 90748 | + rc = SQLITE_CORRUPT_INDEX; | |
| 90749 | + goto abort_due_to_error; | |
| 90389 | 90750 | } |
| 90390 | 90751 | assert( pC->deferredMoveto==0 ); |
| 90391 | 90752 | pC->cacheStatus = CACHE_STALE; |
| 90392 | 90753 | pC->seekResult = 0; |
| 90393 | 90754 | break; |
| @@ -92705,11 +93066,11 @@ | ||
| 92705 | 93066 | |
| 92706 | 93067 | /* Jump to here if the sqlite3_interrupt() API sets the interrupt |
| 92707 | 93068 | ** flag. |
| 92708 | 93069 | */ |
| 92709 | 93070 | abort_due_to_interrupt: |
| 92710 | - assert( db->u1.isInterrupted ); | |
| 93071 | + assert( AtomicLoad(&db->u1.isInterrupted) ); | |
| 92711 | 93072 | rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; |
| 92712 | 93073 | p->rc = rc; |
| 92713 | 93074 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 92714 | 93075 | goto abort_due_to_error; |
| 92715 | 93076 | } |
| @@ -95988,10 +96349,435 @@ | ||
| 95988 | 96349 | *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2); |
| 95989 | 96350 | return SQLITE_OK; |
| 95990 | 96351 | } |
| 95991 | 96352 | |
| 95992 | 96353 | /************** End of vdbesort.c ********************************************/ |
| 96354 | +/************** Begin file vdbevtab.c ****************************************/ | |
| 96355 | +/* | |
| 96356 | +** 2020-03-23 | |
| 96357 | +** | |
| 96358 | +** The author disclaims copyright to this source code. In place of | |
| 96359 | +** a legal notice, here is a blessing: | |
| 96360 | +** | |
| 96361 | +** May you do good and not evil. | |
| 96362 | +** May you find forgiveness for yourself and forgive others. | |
| 96363 | +** May you share freely, never taking more than you give. | |
| 96364 | +** | |
| 96365 | +************************************************************************* | |
| 96366 | +** | |
| 96367 | +** This file implements virtual-tables for examining the bytecode content | |
| 96368 | +** of a prepared statement. | |
| 96369 | +*/ | |
| 96370 | +#ifdef SQLITE_ENABLE_BYTECODE_VTAB | |
| 96371 | +/* #include "sqliteInt.h" */ | |
| 96372 | +/* #include "vdbeInt.h" */ | |
| 96373 | + | |
| 96374 | +/* An instance of the bytecode() table-valued function. | |
| 96375 | +*/ | |
| 96376 | +typedef struct bytecodevtab bytecodevtab; | |
| 96377 | +struct bytecodevtab { | |
| 96378 | + sqlite3_vtab base; /* Base class - must be first */ | |
| 96379 | + sqlite3 *db; /* Database connection */ | |
| 96380 | + int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */ | |
| 96381 | +}; | |
| 96382 | + | |
| 96383 | +/* A cursor for scanning through the bytecode | |
| 96384 | +*/ | |
| 96385 | +typedef struct bytecodevtab_cursor bytecodevtab_cursor; | |
| 96386 | +struct bytecodevtab_cursor { | |
| 96387 | + sqlite3_vtab_cursor base; /* Base class - must be first */ | |
| 96388 | + sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */ | |
| 96389 | + int iRowid; /* The rowid of the output table */ | |
| 96390 | + int iAddr; /* Address */ | |
| 96391 | + int needFinalize; /* Cursors owns pStmt and must finalize it */ | |
| 96392 | + int showSubprograms; /* Provide a listing of subprograms */ | |
| 96393 | + Op *aOp; /* Operand array */ | |
| 96394 | + char *zP4; /* Rendered P4 value */ | |
| 96395 | + const char *zType; /* tables_used.type */ | |
| 96396 | + const char *zSchema; /* tables_used.schema */ | |
| 96397 | + const char *zName; /* tables_used.name */ | |
| 96398 | + Mem sub; /* Subprograms */ | |
| 96399 | +}; | |
| 96400 | + | |
| 96401 | +/* | |
| 96402 | +** Create a new bytecode() table-valued function. | |
| 96403 | +*/ | |
| 96404 | +static int bytecodevtabConnect( | |
| 96405 | + sqlite3 *db, | |
| 96406 | + void *pAux, | |
| 96407 | + int argc, const char *const*argv, | |
| 96408 | + sqlite3_vtab **ppVtab, | |
| 96409 | + char **pzErr | |
| 96410 | +){ | |
| 96411 | + bytecodevtab *pNew; | |
| 96412 | + int rc; | |
| 96413 | + int isTabUsed = pAux!=0; | |
| 96414 | + const char *azSchema[2] = { | |
| 96415 | + /* bytecode() schema */ | |
| 96416 | + "CREATE TABLE x(" | |
| 96417 | + "addr INT," | |
| 96418 | + "opcode TEXT," | |
| 96419 | + "p1 INT," | |
| 96420 | + "p2 INT," | |
| 96421 | + "p3 INT," | |
| 96422 | + "p4 TEXT," | |
| 96423 | + "p5 INT," | |
| 96424 | + "comment TEXT," | |
| 96425 | + "subprog TEXT," | |
| 96426 | + "stmt HIDDEN" | |
| 96427 | + ");", | |
| 96428 | + | |
| 96429 | + /* Tables_used() schema */ | |
| 96430 | + "CREATE TABLE x(" | |
| 96431 | + "type TEXT," | |
| 96432 | + "schema TEXT," | |
| 96433 | + "name TEXT," | |
| 96434 | + "wr INT," | |
| 96435 | + "subprog TEXT," | |
| 96436 | + "stmt HIDDEN" | |
| 96437 | + ");" | |
| 96438 | + }; | |
| 96439 | + | |
| 96440 | + rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]); | |
| 96441 | + if( rc==SQLITE_OK ){ | |
| 96442 | + pNew = sqlite3_malloc( sizeof(*pNew) ); | |
| 96443 | + *ppVtab = (sqlite3_vtab*)pNew; | |
| 96444 | + if( pNew==0 ) return SQLITE_NOMEM; | |
| 96445 | + memset(pNew, 0, sizeof(*pNew)); | |
| 96446 | + pNew->db = db; | |
| 96447 | + pNew->bTablesUsed = isTabUsed*2; | |
| 96448 | + } | |
| 96449 | + return rc; | |
| 96450 | +} | |
| 96451 | + | |
| 96452 | +/* | |
| 96453 | +** This method is the destructor for bytecodevtab objects. | |
| 96454 | +*/ | |
| 96455 | +static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){ | |
| 96456 | + bytecodevtab *p = (bytecodevtab*)pVtab; | |
| 96457 | + sqlite3_free(p); | |
| 96458 | + return SQLITE_OK; | |
| 96459 | +} | |
| 96460 | + | |
| 96461 | +/* | |
| 96462 | +** Constructor for a new bytecodevtab_cursor object. | |
| 96463 | +*/ | |
| 96464 | +static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ | |
| 96465 | + bytecodevtab *pVTab = (bytecodevtab*)p; | |
| 96466 | + bytecodevtab_cursor *pCur; | |
| 96467 | + pCur = sqlite3_malloc( sizeof(*pCur) ); | |
| 96468 | + if( pCur==0 ) return SQLITE_NOMEM; | |
| 96469 | + memset(pCur, 0, sizeof(*pCur)); | |
| 96470 | + sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1); | |
| 96471 | + *ppCursor = &pCur->base; | |
| 96472 | + return SQLITE_OK; | |
| 96473 | +} | |
| 96474 | + | |
| 96475 | +/* | |
| 96476 | +** Clear all internal content from a bytecodevtab cursor. | |
| 96477 | +*/ | |
| 96478 | +static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){ | |
| 96479 | + sqlite3_free(pCur->zP4); | |
| 96480 | + pCur->zP4 = 0; | |
| 96481 | + sqlite3VdbeMemRelease(&pCur->sub); | |
| 96482 | + sqlite3VdbeMemSetNull(&pCur->sub); | |
| 96483 | + if( pCur->needFinalize ){ | |
| 96484 | + sqlite3_finalize(pCur->pStmt); | |
| 96485 | + } | |
| 96486 | + pCur->pStmt = 0; | |
| 96487 | + pCur->needFinalize = 0; | |
| 96488 | + pCur->zType = 0; | |
| 96489 | + pCur->zSchema = 0; | |
| 96490 | + pCur->zName = 0; | |
| 96491 | +} | |
| 96492 | + | |
| 96493 | +/* | |
| 96494 | +** Destructor for a bytecodevtab_cursor. | |
| 96495 | +*/ | |
| 96496 | +static int bytecodevtabClose(sqlite3_vtab_cursor *cur){ | |
| 96497 | + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; | |
| 96498 | + bytecodevtabCursorClear(pCur); | |
| 96499 | + sqlite3_free(pCur); | |
| 96500 | + return SQLITE_OK; | |
| 96501 | +} | |
| 96502 | + | |
| 96503 | + | |
| 96504 | +/* | |
| 96505 | +** Advance a bytecodevtab_cursor to its next row of output. | |
| 96506 | +*/ | |
| 96507 | +static int bytecodevtabNext(sqlite3_vtab_cursor *cur){ | |
| 96508 | + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; | |
| 96509 | + bytecodevtab *pTab = (bytecodevtab*)cur->pVtab; | |
| 96510 | + int rc; | |
| 96511 | + if( pCur->zP4 ){ | |
| 96512 | + sqlite3_free(pCur->zP4); | |
| 96513 | + pCur->zP4 = 0; | |
| 96514 | + } | |
| 96515 | + if( pCur->zName ){ | |
| 96516 | + pCur->zName = 0; | |
| 96517 | + pCur->zType = 0; | |
| 96518 | + pCur->zSchema = 0; | |
| 96519 | + } | |
| 96520 | + rc = sqlite3VdbeNextOpcode( | |
| 96521 | + (Vdbe*)pCur->pStmt, | |
| 96522 | + pCur->showSubprograms ? &pCur->sub : 0, | |
| 96523 | + pTab->bTablesUsed, | |
| 96524 | + &pCur->iRowid, | |
| 96525 | + &pCur->iAddr, | |
| 96526 | + &pCur->aOp); | |
| 96527 | + if( rc!=SQLITE_OK ){ | |
| 96528 | + sqlite3VdbeMemSetNull(&pCur->sub); | |
| 96529 | + pCur->aOp = 0; | |
| 96530 | + } | |
| 96531 | + return SQLITE_OK; | |
| 96532 | +} | |
| 96533 | + | |
| 96534 | +/* | |
| 96535 | +** Return TRUE if the cursor has been moved off of the last | |
| 96536 | +** row of output. | |
| 96537 | +*/ | |
| 96538 | +static int bytecodevtabEof(sqlite3_vtab_cursor *cur){ | |
| 96539 | + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; | |
| 96540 | + return pCur->aOp==0; | |
| 96541 | +} | |
| 96542 | + | |
| 96543 | +/* | |
| 96544 | +** Return values of columns for the row at which the bytecodevtab_cursor | |
| 96545 | +** is currently pointing. | |
| 96546 | +*/ | |
| 96547 | +static int bytecodevtabColumn( | |
| 96548 | + sqlite3_vtab_cursor *cur, /* The cursor */ | |
| 96549 | + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ | |
| 96550 | + int i /* Which column to return */ | |
| 96551 | +){ | |
| 96552 | + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; | |
| 96553 | + bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab; | |
| 96554 | + Op *pOp = pCur->aOp + pCur->iAddr; | |
| 96555 | + if( pVTab->bTablesUsed ){ | |
| 96556 | + if( i==4 ){ | |
| 96557 | + i = 8; | |
| 96558 | + }else{ | |
| 96559 | + if( i<=2 && pCur->zType==0 ){ | |
| 96560 | + Schema *pSchema; | |
| 96561 | + HashElem *k; | |
| 96562 | + int iDb = pOp->p3; | |
| 96563 | + int iRoot = pOp->p2; | |
| 96564 | + sqlite3 *db = pVTab->db; | |
| 96565 | + pSchema = db->aDb[iDb].pSchema; | |
| 96566 | + pCur->zSchema = db->aDb[iDb].zDbSName; | |
| 96567 | + for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ | |
| 96568 | + Table *pTab = (Table*)sqliteHashData(k); | |
| 96569 | + if( !IsVirtual(pTab) && pTab->tnum==iRoot ){ | |
| 96570 | + pCur->zName = pTab->zName; | |
| 96571 | + pCur->zType = "table"; | |
| 96572 | + break; | |
| 96573 | + } | |
| 96574 | + } | |
| 96575 | + if( pCur->zName==0 ){ | |
| 96576 | + for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){ | |
| 96577 | + Index *pIdx = (Index*)sqliteHashData(k); | |
| 96578 | + if( pIdx->tnum==iRoot ){ | |
| 96579 | + pCur->zName = pIdx->zName; | |
| 96580 | + pCur->zType = "index"; | |
| 96581 | + } | |
| 96582 | + } | |
| 96583 | + } | |
| 96584 | + } | |
| 96585 | + i += 10; | |
| 96586 | + } | |
| 96587 | + } | |
| 96588 | + switch( i ){ | |
| 96589 | + case 0: /* addr */ | |
| 96590 | + sqlite3_result_int(ctx, pCur->iAddr); | |
| 96591 | + break; | |
| 96592 | + case 1: /* opcode */ | |
| 96593 | + sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode), | |
| 96594 | + -1, SQLITE_STATIC); | |
| 96595 | + break; | |
| 96596 | + case 2: /* p1 */ | |
| 96597 | + sqlite3_result_int(ctx, pOp->p1); | |
| 96598 | + break; | |
| 96599 | + case 3: /* p2 */ | |
| 96600 | + sqlite3_result_int(ctx, pOp->p2); | |
| 96601 | + break; | |
| 96602 | + case 4: /* p3 */ | |
| 96603 | + sqlite3_result_int(ctx, pOp->p3); | |
| 96604 | + break; | |
| 96605 | + case 5: /* p4 */ | |
| 96606 | + case 7: /* comment */ | |
| 96607 | + if( pCur->zP4==0 ){ | |
| 96608 | + pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp); | |
| 96609 | + } | |
| 96610 | + if( i==5 ){ | |
| 96611 | + sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC); | |
| 96612 | + }else{ | |
| 96613 | +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS | |
| 96614 | + char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4); | |
| 96615 | + sqlite3_result_text(ctx, zCom, -1, sqlite3_free); | |
| 96616 | +#endif | |
| 96617 | + } | |
| 96618 | + break; | |
| 96619 | + case 6: /* p5 */ | |
| 96620 | + sqlite3_result_int(ctx, pOp->p5); | |
| 96621 | + break; | |
| 96622 | + case 8: { /* subprog */ | |
| 96623 | + Op *aOp = pCur->aOp; | |
| 96624 | + assert( aOp[0].opcode==OP_Init ); | |
| 96625 | + assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 ); | |
| 96626 | + if( pCur->iRowid==pCur->iAddr+1 ){ | |
| 96627 | + break; /* Result is NULL for the main program */ | |
| 96628 | + }else if( aOp[0].p4.z!=0 ){ | |
| 96629 | + sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC); | |
| 96630 | + }else{ | |
| 96631 | + sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); | |
| 96632 | + } | |
| 96633 | + break; | |
| 96634 | + } | |
| 96635 | + case 10: /* tables_used.type */ | |
| 96636 | + sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); | |
| 96637 | + break; | |
| 96638 | + case 11: /* tables_used.schema */ | |
| 96639 | + sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); | |
| 96640 | + break; | |
| 96641 | + case 12: /* tables_used.name */ | |
| 96642 | + sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); | |
| 96643 | + break; | |
| 96644 | + case 13: /* tables_used.wr */ | |
| 96645 | + sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); | |
| 96646 | + break; | |
| 96647 | + } | |
| 96648 | + return SQLITE_OK; | |
| 96649 | +} | |
| 96650 | + | |
| 96651 | +/* | |
| 96652 | +** Return the rowid for the current row. In this implementation, the | |
| 96653 | +** rowid is the same as the output value. | |
| 96654 | +*/ | |
| 96655 | +static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ | |
| 96656 | + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; | |
| 96657 | + *pRowid = pCur->iRowid; | |
| 96658 | + return SQLITE_OK; | |
| 96659 | +} | |
| 96660 | + | |
| 96661 | +/* | |
| 96662 | +** Initialize a cursor. | |
| 96663 | +** | |
| 96664 | +** idxNum==0 means show all subprograms | |
| 96665 | +** idxNum==1 means show only the main bytecode and omit subprograms. | |
| 96666 | +*/ | |
| 96667 | +static int bytecodevtabFilter( | |
| 96668 | + sqlite3_vtab_cursor *pVtabCursor, | |
| 96669 | + int idxNum, const char *idxStr, | |
| 96670 | + int argc, sqlite3_value **argv | |
| 96671 | +){ | |
| 96672 | + bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; | |
| 96673 | + bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; | |
| 96674 | + int rc = SQLITE_OK; | |
| 96675 | + | |
| 96676 | + bytecodevtabCursorClear(pCur); | |
| 96677 | + pCur->iRowid = 0; | |
| 96678 | + pCur->iAddr = 0; | |
| 96679 | + pCur->showSubprograms = idxNum==0; | |
| 96680 | + assert( argc==1 ); | |
| 96681 | + if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){ | |
| 96682 | + const char *zSql = (const char*)sqlite3_value_text(argv[0]); | |
| 96683 | + if( zSql==0 ){ | |
| 96684 | + rc = SQLITE_NOMEM; | |
| 96685 | + }else{ | |
| 96686 | + rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0); | |
| 96687 | + pCur->needFinalize = 1; | |
| 96688 | + } | |
| 96689 | + }else{ | |
| 96690 | + pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer"); | |
| 96691 | + } | |
| 96692 | + if( pCur->pStmt==0 ){ | |
| 96693 | + pVTab->base.zErrMsg = sqlite3_mprintf( | |
| 96694 | + "argument to %s() is not a valid SQL statement", | |
| 96695 | + pVTab->bTablesUsed ? "tables_used" : "bytecode" | |
| 96696 | + ); | |
| 96697 | + rc = SQLITE_ERROR; | |
| 96698 | + }else{ | |
| 96699 | + bytecodevtabNext(pVtabCursor); | |
| 96700 | + } | |
| 96701 | + return rc; | |
| 96702 | +} | |
| 96703 | + | |
| 96704 | +/* | |
| 96705 | +** We must have a single stmt=? constraint that will be passed through | |
| 96706 | +** into the xFilter method. If there is no valid stmt=? constraint, | |
| 96707 | +** then return an SQLITE_CONSTRAINT error. | |
| 96708 | +*/ | |
| 96709 | +static int bytecodevtabBestIndex( | |
| 96710 | + sqlite3_vtab *tab, | |
| 96711 | + sqlite3_index_info *pIdxInfo | |
| 96712 | +){ | |
| 96713 | + int i; | |
| 96714 | + int rc = SQLITE_CONSTRAINT; | |
| 96715 | + struct sqlite3_index_constraint *p; | |
| 96716 | + bytecodevtab *pVTab = (bytecodevtab*)tab; | |
| 96717 | + int iBaseCol = pVTab->bTablesUsed ? 4 : 8; | |
| 96718 | + pIdxInfo->estimatedCost = (double)100; | |
| 96719 | + pIdxInfo->estimatedRows = 100; | |
| 96720 | + pIdxInfo->idxNum = 0; | |
| 96721 | + for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){ | |
| 96722 | + if( p->usable==0 ) continue; | |
| 96723 | + if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){ | |
| 96724 | + rc = SQLITE_OK; | |
| 96725 | + pIdxInfo->aConstraintUsage[i].omit = 1; | |
| 96726 | + pIdxInfo->aConstraintUsage[i].argvIndex = 1; | |
| 96727 | + } | |
| 96728 | + if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){ | |
| 96729 | + pIdxInfo->aConstraintUsage[i].omit = 1; | |
| 96730 | + pIdxInfo->idxNum = 1; | |
| 96731 | + } | |
| 96732 | + } | |
| 96733 | + return rc; | |
| 96734 | +} | |
| 96735 | + | |
| 96736 | +/* | |
| 96737 | +** This following structure defines all the methods for the | |
| 96738 | +** virtual table. | |
| 96739 | +*/ | |
| 96740 | +static sqlite3_module bytecodevtabModule = { | |
| 96741 | + /* iVersion */ 0, | |
| 96742 | + /* xCreate */ 0, | |
| 96743 | + /* xConnect */ bytecodevtabConnect, | |
| 96744 | + /* xBestIndex */ bytecodevtabBestIndex, | |
| 96745 | + /* xDisconnect */ bytecodevtabDisconnect, | |
| 96746 | + /* xDestroy */ 0, | |
| 96747 | + /* xOpen */ bytecodevtabOpen, | |
| 96748 | + /* xClose */ bytecodevtabClose, | |
| 96749 | + /* xFilter */ bytecodevtabFilter, | |
| 96750 | + /* xNext */ bytecodevtabNext, | |
| 96751 | + /* xEof */ bytecodevtabEof, | |
| 96752 | + /* xColumn */ bytecodevtabColumn, | |
| 96753 | + /* xRowid */ bytecodevtabRowid, | |
| 96754 | + /* xUpdate */ 0, | |
| 96755 | + /* xBegin */ 0, | |
| 96756 | + /* xSync */ 0, | |
| 96757 | + /* xCommit */ 0, | |
| 96758 | + /* xRollback */ 0, | |
| 96759 | + /* xFindMethod */ 0, | |
| 96760 | + /* xRename */ 0, | |
| 96761 | + /* xSavepoint */ 0, | |
| 96762 | + /* xRelease */ 0, | |
| 96763 | + /* xRollbackTo */ 0, | |
| 96764 | + /* xShadowName */ 0 | |
| 96765 | +}; | |
| 96766 | + | |
| 96767 | + | |
| 96768 | +SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ | |
| 96769 | + int rc; | |
| 96770 | + rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0); | |
| 96771 | + if( rc==SQLITE_OK ){ | |
| 96772 | + rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); | |
| 96773 | + } | |
| 96774 | + return rc; | |
| 96775 | +} | |
| 96776 | +#endif /* SQLITE_ENABLE_BYTECODE_VTAB */ | |
| 96777 | + | |
| 96778 | +/************** End of vdbevtab.c ********************************************/ | |
| 95993 | 96779 | /************** Begin file memjournal.c **************************************/ |
| 95994 | 96780 | /* |
| 95995 | 96781 | ** 2008 October 7 |
| 95996 | 96782 | ** |
| 95997 | 96783 | ** The author disclaims copyright to this source code. In place of |
| @@ -96775,11 +97561,11 @@ | ||
| 96775 | 97561 | const char *zTab, |
| 96776 | 97562 | const char *zDb |
| 96777 | 97563 | ){ |
| 96778 | 97564 | int n; |
| 96779 | 97565 | const char *zSpan; |
| 96780 | - if( NEVER(pItem->eEName!=ENAME_TAB) ) return 0; | |
| 97566 | + if( pItem->eEName!=ENAME_TAB ) return 0; | |
| 96781 | 97567 | zSpan = pItem->zEName; |
| 96782 | 97568 | for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} |
| 96783 | 97569 | if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ |
| 96784 | 97570 | return 0; |
| 96785 | 97571 | } |
| @@ -96929,10 +97715,11 @@ | ||
| 96929 | 97715 | ExprList *pEList; |
| 96930 | 97716 | SrcList *pSrcList = pNC->pSrcList; |
| 96931 | 97717 | |
| 96932 | 97718 | if( pSrcList ){ |
| 96933 | 97719 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 97720 | + u8 hCol; | |
| 96934 | 97721 | pTab = pItem->pTab; |
| 96935 | 97722 | assert( pTab!=0 && pTab->zName!=0 ); |
| 96936 | 97723 | assert( pTab->nCol>0 ); |
| 96937 | 97724 | if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ |
| 96938 | 97725 | int hit = 0; |
| @@ -96962,12 +97749,13 @@ | ||
| 96962 | 97749 | } |
| 96963 | 97750 | } |
| 96964 | 97751 | if( 0==(cntTab++) ){ |
| 96965 | 97752 | pMatch = pItem; |
| 96966 | 97753 | } |
| 97754 | + hCol = sqlite3StrIHash(zCol); | |
| 96967 | 97755 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 96968 | - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ | |
| 97756 | + if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ | |
| 96969 | 97757 | /* If there has been exactly one prior match and this match |
| 96970 | 97758 | ** is for the right-hand table of a NATURAL JOIN or is in a |
| 96971 | 97759 | ** USING clause, then skip this match. |
| 96972 | 97760 | */ |
| 96973 | 97761 | if( cnt==1 ){ |
| @@ -97024,14 +97812,15 @@ | ||
| 97024 | 97812 | } |
| 97025 | 97813 | #endif /* SQLITE_OMIT_UPSERT */ |
| 97026 | 97814 | |
| 97027 | 97815 | if( pTab ){ |
| 97028 | 97816 | int iCol; |
| 97817 | + u8 hCol = sqlite3StrIHash(zCol); | |
| 97029 | 97818 | pSchema = pTab->pSchema; |
| 97030 | 97819 | cntTab++; |
| 97031 | 97820 | for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){ |
| 97032 | - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ | |
| 97821 | + if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ | |
| 97033 | 97822 | if( iCol==pTab->iPKey ){ |
| 97034 | 97823 | iCol = -1; |
| 97035 | 97824 | } |
| 97036 | 97825 | break; |
| 97037 | 97826 | } |
| @@ -97828,11 +98617,11 @@ | ||
| 97828 | 98617 | nc.uNC.pEList = pEList; |
| 97829 | 98618 | nc.ncFlags = NC_AllowAgg|NC_UEList; |
| 97830 | 98619 | nc.nErr = 0; |
| 97831 | 98620 | db = pParse->db; |
| 97832 | 98621 | savedSuppErr = db->suppressErr; |
| 97833 | - db->suppressErr = 1; | |
| 98622 | + if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1; | |
| 97834 | 98623 | rc = sqlite3ResolveExprNames(&nc, pE); |
| 97835 | 98624 | db->suppressErr = savedSuppErr; |
| 97836 | 98625 | if( rc ) return 0; |
| 97837 | 98626 | |
| 97838 | 98627 | /* Try to match the ORDER BY expression against an expression |
| @@ -98463,15 +99252,45 @@ | ||
| 98463 | 99252 | SQLITE_PRIVATE int sqlite3ResolveExprListNames( |
| 98464 | 99253 | NameContext *pNC, /* Namespace to resolve expressions in. */ |
| 98465 | 99254 | ExprList *pList /* The expression list to be analyzed. */ |
| 98466 | 99255 | ){ |
| 98467 | 99256 | int i; |
| 98468 | - if( pList ){ | |
| 98469 | - for(i=0; i<pList->nExpr; i++){ | |
| 98470 | - if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort; | |
| 99257 | + int savedHasAgg = 0; | |
| 99258 | + Walker w; | |
| 99259 | + if( pList==0 ) return WRC_Continue; | |
| 99260 | + w.pParse = pNC->pParse; | |
| 99261 | + w.xExprCallback = resolveExprStep; | |
| 99262 | + w.xSelectCallback = resolveSelectStep; | |
| 99263 | + w.xSelectCallback2 = 0; | |
| 99264 | + w.u.pNC = pNC; | |
| 99265 | + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); | |
| 99266 | + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); | |
| 99267 | + for(i=0; i<pList->nExpr; i++){ | |
| 99268 | + Expr *pExpr = pList->a[i].pExpr; | |
| 99269 | + if( pExpr==0 ) continue; | |
| 99270 | +#if SQLITE_MAX_EXPR_DEPTH>0 | |
| 99271 | + w.pParse->nHeight += pExpr->nHeight; | |
| 99272 | + if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){ | |
| 99273 | + return WRC_Abort; | |
| 98471 | 99274 | } |
| 99275 | +#endif | |
| 99276 | + sqlite3WalkExpr(&w, pExpr); | |
| 99277 | +#if SQLITE_MAX_EXPR_DEPTH>0 | |
| 99278 | + w.pParse->nHeight -= pExpr->nHeight; | |
| 99279 | +#endif | |
| 99280 | + assert( EP_Agg==NC_HasAgg ); | |
| 99281 | + assert( EP_Win==NC_HasWin ); | |
| 99282 | + testcase( pNC->ncFlags & NC_HasAgg ); | |
| 99283 | + testcase( pNC->ncFlags & NC_HasWin ); | |
| 99284 | + if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){ | |
| 99285 | + ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); | |
| 99286 | + savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); | |
| 99287 | + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); | |
| 99288 | + } | |
| 99289 | + if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort; | |
| 98472 | 99290 | } |
| 99291 | + pNC->ncFlags |= savedHasAgg; | |
| 98473 | 99292 | return WRC_Continue; |
| 98474 | 99293 | } |
| 98475 | 99294 | |
| 98476 | 99295 | /* |
| 98477 | 99296 | ** Resolve all names in all expressions of a SELECT and in all |
| @@ -98601,11 +99420,11 @@ | ||
| 98601 | 99420 | ** SELECT * FROM t1 WHERE (select a from t1); |
| 98602 | 99421 | */ |
| 98603 | 99422 | SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ |
| 98604 | 99423 | int op; |
| 98605 | 99424 | while( ExprHasProperty(pExpr, EP_Skip) ){ |
| 98606 | - assert( pExpr->op==TK_COLLATE ); | |
| 99425 | + assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); | |
| 98607 | 99426 | pExpr = pExpr->pLeft; |
| 98608 | 99427 | assert( pExpr!=0 ); |
| 98609 | 99428 | } |
| 98610 | 99429 | op = pExpr->op; |
| 98611 | 99430 | if( op==TK_SELECT ){ |
| @@ -98668,11 +99487,11 @@ | ||
| 98668 | 99487 | /* |
| 98669 | 99488 | ** Skip over any TK_COLLATE operators. |
| 98670 | 99489 | */ |
| 98671 | 99490 | SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ |
| 98672 | 99491 | while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){ |
| 98673 | - assert( pExpr->op==TK_COLLATE ); | |
| 99492 | + assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); | |
| 98674 | 99493 | pExpr = pExpr->pLeft; |
| 98675 | 99494 | } |
| 98676 | 99495 | return pExpr; |
| 98677 | 99496 | } |
| 98678 | 99497 | |
| @@ -98687,11 +99506,11 @@ | ||
| 98687 | 99506 | assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); |
| 98688 | 99507 | assert( pExpr->x.pList->nExpr>0 ); |
| 98689 | 99508 | assert( pExpr->op==TK_FUNCTION ); |
| 98690 | 99509 | pExpr = pExpr->x.pList->a[0].pExpr; |
| 98691 | 99510 | }else{ |
| 98692 | - assert( pExpr->op==TK_COLLATE ); | |
| 99511 | + assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); | |
| 98693 | 99512 | pExpr = pExpr->pLeft; |
| 98694 | 99513 | } |
| 98695 | 99514 | } |
| 98696 | 99515 | return pExpr; |
| 98697 | 99516 | } |
| @@ -100343,20 +101162,26 @@ | ||
| 100343 | 101162 | ExprList *pList, /* List to which to add the span. */ |
| 100344 | 101163 | Token *pName, /* Name to be added */ |
| 100345 | 101164 | int dequote /* True to cause the name to be dequoted */ |
| 100346 | 101165 | ){ |
| 100347 | 101166 | assert( pList!=0 || pParse->db->mallocFailed!=0 ); |
| 101167 | + assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 ); | |
| 100348 | 101168 | if( pList ){ |
| 100349 | 101169 | struct ExprList_item *pItem; |
| 100350 | 101170 | assert( pList->nExpr>0 ); |
| 100351 | 101171 | pItem = &pList->a[pList->nExpr-1]; |
| 100352 | 101172 | assert( pItem->zEName==0 ); |
| 100353 | 101173 | assert( pItem->eEName==ENAME_NAME ); |
| 100354 | 101174 | pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); |
| 100355 | - if( dequote ) sqlite3Dequote(pItem->zEName); | |
| 100356 | - if( IN_RENAME_OBJECT ){ | |
| 100357 | - sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); | |
| 101175 | + if( dequote ){ | |
| 101176 | + /* If dequote==0, then pName->z does not point to part of a DDL | |
| 101177 | + ** statement handled by the parser. And so no token need be added | |
| 101178 | + ** to the token-map. */ | |
| 101179 | + sqlite3Dequote(pItem->zEName); | |
| 101180 | + if( IN_RENAME_OBJECT ){ | |
| 101181 | + sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); | |
| 101182 | + } | |
| 100358 | 101183 | } |
| 100359 | 101184 | } |
| 100360 | 101185 | } |
| 100361 | 101186 | |
| 100362 | 101187 | /* |
| @@ -101497,10 +102322,12 @@ | ||
| 101497 | 102322 | struct ExprList_item *pItem; |
| 101498 | 102323 | int r1, r2; |
| 101499 | 102324 | affinity = sqlite3ExprAffinity(pLeft); |
| 101500 | 102325 | if( affinity<=SQLITE_AFF_NONE ){ |
| 101501 | 102326 | affinity = SQLITE_AFF_BLOB; |
| 102327 | + }else if( affinity==SQLITE_AFF_REAL ){ | |
| 102328 | + affinity = SQLITE_AFF_NUMERIC; | |
| 101502 | 102329 | } |
| 101503 | 102330 | if( pKeyInfo ){ |
| 101504 | 102331 | assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); |
| 101505 | 102332 | pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); |
| 101506 | 102333 | } |
| @@ -101735,10 +102562,11 @@ | ||
| 101735 | 102562 | int destStep6 = 0; /* Start of code for Step 6 */ |
| 101736 | 102563 | int addrTruthOp; /* Address of opcode that determines the IN is true */ |
| 101737 | 102564 | int destNotNull; /* Jump here if a comparison is not true in step 6 */ |
| 101738 | 102565 | int addrTop; /* Top of the step-6 loop */ |
| 101739 | 102566 | int iTab = 0; /* Index to use */ |
| 102567 | + u8 okConstFactor = pParse->okConstFactor; | |
| 101740 | 102568 | |
| 101741 | 102569 | assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); |
| 101742 | 102570 | pLeft = pExpr->pLeft; |
| 101743 | 102571 | if( sqlite3ExprCheckIN(pParse, pExpr) ) return; |
| 101744 | 102572 | zAff = exprINAffinity(pParse, pExpr); |
| @@ -101779,12 +102607,18 @@ | ||
| 101779 | 102607 | ** |
| 101780 | 102608 | ** sqlite3FindInIndex() might have reordered the fields of the LHS vector |
| 101781 | 102609 | ** so that the fields are in the same order as an existing index. The |
| 101782 | 102610 | ** aiMap[] array contains a mapping from the original LHS field order to |
| 101783 | 102611 | ** the field order that matches the RHS index. |
| 101784 | - */ | |
| 102612 | + ** | |
| 102613 | + ** Avoid factoring the LHS of the IN(...) expression out of the loop, | |
| 102614 | + ** even if it is constant, as OP_Affinity may be used on the register | |
| 102615 | + ** by code generated below. */ | |
| 102616 | + assert( pParse->okConstFactor==okConstFactor ); | |
| 102617 | + pParse->okConstFactor = 0; | |
| 101785 | 102618 | rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy); |
| 102619 | + pParse->okConstFactor = okConstFactor; | |
| 101786 | 102620 | for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */ |
| 101787 | 102621 | if( i==nVector ){ |
| 101788 | 102622 | /* LHS fields are not reordered */ |
| 101789 | 102623 | rLhs = rLhsOrig; |
| 101790 | 102624 | }else{ |
| @@ -101806,25 +102640,17 @@ | ||
| 101806 | 102640 | CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); |
| 101807 | 102641 | int labelOk = sqlite3VdbeMakeLabel(pParse); |
| 101808 | 102642 | int r2, regToFree; |
| 101809 | 102643 | int regCkNull = 0; |
| 101810 | 102644 | int ii; |
| 101811 | - int bLhsReal; /* True if the LHS of the IN has REAL affinity */ | |
| 101812 | 102645 | assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); |
| 101813 | 102646 | if( destIfNull!=destIfFalse ){ |
| 101814 | 102647 | regCkNull = sqlite3GetTempReg(pParse); |
| 101815 | 102648 | sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); |
| 101816 | 102649 | } |
| 101817 | - bLhsReal = sqlite3ExprAffinity(pExpr->pLeft)==SQLITE_AFF_REAL; | |
| 101818 | 102650 | for(ii=0; ii<pList->nExpr; ii++){ |
| 101819 | - if( bLhsReal ){ | |
| 101820 | - r2 = regToFree = sqlite3GetTempReg(pParse); | |
| 101821 | - sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2); | |
| 101822 | - sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC); | |
| 101823 | - }else{ | |
| 101824 | - r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree); | |
| 101825 | - } | |
| 102651 | + r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree); | |
| 101826 | 102652 | if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){ |
| 101827 | 102653 | sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull); |
| 101828 | 102654 | } |
| 101829 | 102655 | sqlite3ReleaseTempReg(pParse, regToFree); |
| 101830 | 102656 | if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){ |
| @@ -102394,14 +103220,10 @@ | ||
| 102394 | 103220 | } |
| 102395 | 103221 | if( aff>SQLITE_AFF_BLOB ){ |
| 102396 | 103222 | static const char zAff[] = "B\000C\000D\000E"; |
| 102397 | 103223 | assert( SQLITE_AFF_BLOB=='A' ); |
| 102398 | 103224 | assert( SQLITE_AFF_TEXT=='B' ); |
| 102399 | - if( iReg!=target ){ | |
| 102400 | - sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target); | |
| 102401 | - iReg = target; | |
| 102402 | - } | |
| 102403 | 103225 | sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0, |
| 102404 | 103226 | &zAff[(aff-'B')*2], P4_STATIC); |
| 102405 | 103227 | } |
| 102406 | 103228 | return iReg; |
| 102407 | 103229 | } |
| @@ -103035,11 +103857,11 @@ | ||
| 103035 | 103857 | assert( pExpr->affExpr==OE_Rollback |
| 103036 | 103858 | || pExpr->affExpr==OE_Abort |
| 103037 | 103859 | || pExpr->affExpr==OE_Fail |
| 103038 | 103860 | || pExpr->affExpr==OE_Ignore |
| 103039 | 103861 | ); |
| 103040 | - if( !pParse->pTriggerTab ){ | |
| 103862 | + if( !pParse->pTriggerTab && !pParse->nested ){ | |
| 103041 | 103863 | sqlite3ErrorMsg(pParse, |
| 103042 | 103864 | "RAISE() may only be used within a trigger-program"); |
| 103043 | 103865 | return 0; |
| 103044 | 103866 | } |
| 103045 | 103867 | if( pExpr->affExpr==OE_Abort ){ |
| @@ -103049,12 +103871,13 @@ | ||
| 103049 | 103871 | if( pExpr->affExpr==OE_Ignore ){ |
| 103050 | 103872 | sqlite3VdbeAddOp4( |
| 103051 | 103873 | v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); |
| 103052 | 103874 | VdbeCoverage(v); |
| 103053 | 103875 | }else{ |
| 103054 | - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, | |
| 103055 | - pExpr->affExpr, pExpr->u.zToken, 0, 0); | |
| 103876 | + sqlite3HaltConstraint(pParse, | |
| 103877 | + pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR, | |
| 103878 | + pExpr->affExpr, pExpr->u.zToken, 0, 0); | |
| 103056 | 103879 | } |
| 103057 | 103880 | |
| 103058 | 103881 | break; |
| 103059 | 103882 | } |
| 103060 | 103883 | #endif |
| @@ -104805,10 +105628,26 @@ | ||
| 104805 | 105628 | exit_rename_table: |
| 104806 | 105629 | sqlite3SrcListDelete(db, pSrc); |
| 104807 | 105630 | sqlite3DbFree(db, zName); |
| 104808 | 105631 | db->mDbFlags = savedDbFlags; |
| 104809 | 105632 | } |
| 105633 | + | |
| 105634 | +/* | |
| 105635 | +** Write code that will raise an error if the table described by | |
| 105636 | +** zDb and zTab is not empty. | |
| 105637 | +*/ | |
| 105638 | +static void sqlite3ErrorIfNotEmpty( | |
| 105639 | + Parse *pParse, /* Parsing context */ | |
| 105640 | + const char *zDb, /* Schema holding the table */ | |
| 105641 | + const char *zTab, /* Table to check for empty */ | |
| 105642 | + const char *zErr /* Error message text */ | |
| 105643 | +){ | |
| 105644 | + sqlite3NestedParse(pParse, | |
| 105645 | + "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"", | |
| 105646 | + zErr, zDb, zTab | |
| 105647 | + ); | |
| 105648 | +} | |
| 104810 | 105649 | |
| 104811 | 105650 | /* |
| 104812 | 105651 | ** This function is called after an "ALTER TABLE ... ADD" statement |
| 104813 | 105652 | ** has been parsed. Argument pColDef contains the text of the new |
| 104814 | 105653 | ** column definition. |
| @@ -104858,11 +105697,12 @@ | ||
| 104858 | 105697 | if( pCol->colFlags & COLFLAG_PRIMKEY ){ |
| 104859 | 105698 | sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column"); |
| 104860 | 105699 | return; |
| 104861 | 105700 | } |
| 104862 | 105701 | if( pNew->pIndex ){ |
| 104863 | - sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column"); | |
| 105702 | + sqlite3ErrorMsg(pParse, | |
| 105703 | + "Cannot add a UNIQUE column"); | |
| 104864 | 105704 | return; |
| 104865 | 105705 | } |
| 104866 | 105706 | if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){ |
| 104867 | 105707 | /* If the default value for the new column was specified with a |
| 104868 | 105708 | ** literal NULL, then set pDflt to 0. This simplifies checking |
| @@ -104871,19 +105711,18 @@ | ||
| 104871 | 105711 | assert( pDflt==0 || pDflt->op==TK_SPAN ); |
| 104872 | 105712 | if( pDflt && pDflt->pLeft->op==TK_NULL ){ |
| 104873 | 105713 | pDflt = 0; |
| 104874 | 105714 | } |
| 104875 | 105715 | if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ |
| 104876 | - sqlite3ErrorMsg(pParse, | |
| 105716 | + sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, | |
| 104877 | 105717 | "Cannot add a REFERENCES column with non-NULL default value"); |
| 104878 | - return; | |
| 104879 | 105718 | } |
| 104880 | 105719 | if( pCol->notNull && !pDflt ){ |
| 104881 | - sqlite3ErrorMsg(pParse, | |
| 105720 | + sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, | |
| 104882 | 105721 | "Cannot add a NOT NULL column with default value NULL"); |
| 104883 | - return; | |
| 104884 | 105722 | } |
| 105723 | + | |
| 104885 | 105724 | |
| 104886 | 105725 | /* Ensure the default expression is something that sqlite3ValueFromExpr() |
| 104887 | 105726 | ** can handle (i.e. not CURRENT_TIME etc.) |
| 104888 | 105727 | */ |
| 104889 | 105728 | if( pDflt ){ |
| @@ -104894,18 +105733,17 @@ | ||
| 104894 | 105733 | if( rc!=SQLITE_OK ){ |
| 104895 | 105734 | assert( db->mallocFailed == 1 ); |
| 104896 | 105735 | return; |
| 104897 | 105736 | } |
| 104898 | 105737 | if( !pVal ){ |
| 104899 | - sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default"); | |
| 104900 | - return; | |
| 105738 | + sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, | |
| 105739 | + "Cannot add a column with non-constant default"); | |
| 104901 | 105740 | } |
| 104902 | 105741 | sqlite3ValueFree(pVal); |
| 104903 | 105742 | } |
| 104904 | 105743 | }else if( pCol->colFlags & COLFLAG_STORED ){ |
| 104905 | - sqlite3ErrorMsg(pParse, "cannot add a STORED column"); | |
| 104906 | - return; | |
| 105744 | + sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column"); | |
| 104907 | 105745 | } |
| 104908 | 105746 | |
| 104909 | 105747 | |
| 104910 | 105748 | /* Modify the CREATE TABLE statement. */ |
| 104911 | 105749 | zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); |
| @@ -105020,10 +105858,11 @@ | ||
| 105020 | 105858 | } |
| 105021 | 105859 | memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); |
| 105022 | 105860 | for(i=0; i<pNew->nCol; i++){ |
| 105023 | 105861 | Column *pCol = &pNew->aCol[i]; |
| 105024 | 105862 | pCol->zName = sqlite3DbStrDup(db, pCol->zName); |
| 105863 | + pCol->hName = sqlite3StrIHash(pCol->zName); | |
| 105025 | 105864 | pCol->zColl = 0; |
| 105026 | 105865 | pCol->pDflt = 0; |
| 105027 | 105866 | } |
| 105028 | 105867 | pNew->pSchema = db->aDb[iDb].pSchema; |
| 105029 | 105868 | pNew->addColOffset = pTab->addColOffset; |
| @@ -105248,11 +106087,11 @@ | ||
| 105248 | 106087 | */ |
| 105249 | 106088 | SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ |
| 105250 | 106089 | RenameToken *pNew; |
| 105251 | 106090 | assert( pPtr || pParse->db->mallocFailed ); |
| 105252 | 106091 | renameTokenCheckAll(pParse, pPtr); |
| 105253 | - if( pParse->eParseMode!=PARSE_MODE_UNMAP ){ | |
| 106092 | + if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){ | |
| 105254 | 106093 | pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken)); |
| 105255 | 106094 | if( pNew ){ |
| 105256 | 106095 | pNew->p = pPtr; |
| 105257 | 106096 | pNew->t = *pToken; |
| 105258 | 106097 | pNew->pNext = pParse->pRename; |
| @@ -105305,10 +106144,25 @@ | ||
| 105305 | 106144 | sqlite3WalkSelect(pWalker, p); |
| 105306 | 106145 | sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); |
| 105307 | 106146 | } |
| 105308 | 106147 | } |
| 105309 | 106148 | } |
| 106149 | + | |
| 106150 | +/* | |
| 106151 | +** Unmap all tokens in the IdList object passed as the second argument. | |
| 106152 | +*/ | |
| 106153 | +static void unmapColumnIdlistNames( | |
| 106154 | + Parse *pParse, | |
| 106155 | + IdList *pIdList | |
| 106156 | +){ | |
| 106157 | + if( pIdList ){ | |
| 106158 | + int ii; | |
| 106159 | + for(ii=0; ii<pIdList->nId; ii++){ | |
| 106160 | + sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName); | |
| 106161 | + } | |
| 106162 | + } | |
| 106163 | +} | |
| 105310 | 106164 | |
| 105311 | 106165 | /* |
| 105312 | 106166 | ** Walker callback used by sqlite3RenameExprUnmap(). |
| 105313 | 106167 | */ |
| 105314 | 106168 | static int renameUnmapSelectCb(Walker *pWalker, Select *p){ |
| @@ -105327,10 +106181,11 @@ | ||
| 105327 | 106181 | if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */ |
| 105328 | 106182 | SrcList *pSrc = p->pSrc; |
| 105329 | 106183 | for(i=0; i<pSrc->nSrc; i++){ |
| 105330 | 106184 | sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); |
| 105331 | 106185 | if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; |
| 106186 | + unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); | |
| 105332 | 106187 | } |
| 105333 | 106188 | } |
| 105334 | 106189 | |
| 105335 | 106190 | renameWalkWith(pWalker, p); |
| 105336 | 106191 | return WRC_Continue; |
| @@ -105534,10 +106389,11 @@ | ||
| 105534 | 106389 | renameTokenFind(pParse, pCtx, (void*)zName); |
| 105535 | 106390 | } |
| 105536 | 106391 | } |
| 105537 | 106392 | } |
| 105538 | 106393 | } |
| 106394 | + | |
| 105539 | 106395 | |
| 105540 | 106396 | /* |
| 105541 | 106397 | ** Parse the SQL statement zSql using Parse object (*p). The Parse object |
| 105542 | 106398 | ** is initialized by this function before it is used. |
| 105543 | 106399 | */ |
| @@ -106446,10 +107302,15 @@ | ||
| 106446 | 107302 | sqlite3 *db = pParse->db; |
| 106447 | 107303 | Db *pDb; |
| 106448 | 107304 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 106449 | 107305 | int aRoot[ArraySize(aTable)]; |
| 106450 | 107306 | u8 aCreateTbl[ArraySize(aTable)]; |
| 107307 | +#ifdef SQLITE_ENABLE_STAT4 | |
| 107308 | + const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; | |
| 107309 | +#else | |
| 107310 | + const int nToOpen = 1; | |
| 107311 | +#endif | |
| 106451 | 107312 | |
| 106452 | 107313 | if( v==0 ) return; |
| 106453 | 107314 | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| 106454 | 107315 | assert( sqlite3VdbeDb(v)==db ); |
| 106455 | 107316 | pDb = &db->aDb[iDb]; |
| @@ -106458,12 +107319,13 @@ | ||
| 106458 | 107319 | ** if they do already exist. |
| 106459 | 107320 | */ |
| 106460 | 107321 | for(i=0; i<ArraySize(aTable); i++){ |
| 106461 | 107322 | const char *zTab = aTable[i].zName; |
| 106462 | 107323 | Table *pStat; |
| 107324 | + aCreateTbl[i] = 0; | |
| 106463 | 107325 | if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){ |
| 106464 | - if( aTable[i].zCols ){ | |
| 107326 | + if( i<nToOpen ){ | |
| 106465 | 107327 | /* The sqlite_statN table does not exist. Create it. Note that a |
| 106466 | 107328 | ** side-effect of the CREATE TABLE statement is to leave the rootpage |
| 106467 | 107329 | ** of the new table in register pParse->regRoot. This is important |
| 106468 | 107330 | ** because the OpenWrite opcode below will be needing it. */ |
| 106469 | 107331 | sqlite3NestedParse(pParse, |
| @@ -106475,11 +107337,10 @@ | ||
| 106475 | 107337 | }else{ |
| 106476 | 107338 | /* The table already exists. If zWhere is not NULL, delete all entries |
| 106477 | 107339 | ** associated with the table zWhere. If zWhere is NULL, delete the |
| 106478 | 107340 | ** entire contents of the table. */ |
| 106479 | 107341 | aRoot[i] = pStat->tnum; |
| 106480 | - aCreateTbl[i] = 0; | |
| 106481 | 107342 | sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); |
| 106482 | 107343 | if( zWhere ){ |
| 106483 | 107344 | sqlite3NestedParse(pParse, |
| 106484 | 107345 | "DELETE FROM %Q.%s WHERE %s=%Q", |
| 106485 | 107346 | pDb->zDbSName, zTab, zWhereType, zWhere |
| @@ -106494,11 +107355,11 @@ | ||
| 106494 | 107355 | } |
| 106495 | 107356 | } |
| 106496 | 107357 | } |
| 106497 | 107358 | |
| 106498 | 107359 | /* Open the sqlite_stat[134] tables for writing. */ |
| 106499 | - for(i=0; aTable[i].zCols; i++){ | |
| 107360 | + for(i=0; i<nToOpen; i++){ | |
| 106500 | 107361 | assert( i<ArraySize(aTable) ); |
| 106501 | 107362 | sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3); |
| 106502 | 107363 | sqlite3VdbeChangeP5(v, aCreateTbl[i]); |
| 106503 | 107364 | VdbeComment((v, aTable[i].zName)); |
| 106504 | 107365 | } |
| @@ -106533,13 +107394,16 @@ | ||
| 106533 | 107394 | u32 iHash; /* Tiebreaker hash */ |
| 106534 | 107395 | #endif |
| 106535 | 107396 | }; |
| 106536 | 107397 | struct StatAccum { |
| 106537 | 107398 | sqlite3 *db; /* Database connection, for malloc() */ |
| 106538 | - tRowcnt nRow; /* Number of rows in the entire table */ | |
| 107399 | + tRowcnt nEst; /* Estimated number of rows */ | |
| 107400 | + tRowcnt nRow; /* Number of rows visited so far */ | |
| 107401 | + int nLimit; /* Analysis row-scan limit */ | |
| 106539 | 107402 | int nCol; /* Number of columns in index + pk/rowid */ |
| 106540 | 107403 | int nKeyCol; /* Number of index columns w/o the pk/rowid */ |
| 107404 | + u8 nSkipAhead; /* Number of times of skip-ahead */ | |
| 106541 | 107405 | StatSample current; /* Current row as a StatSample */ |
| 106542 | 107406 | #ifdef SQLITE_ENABLE_STAT4 |
| 106543 | 107407 | tRowcnt nPSample; /* How often to do a periodic sample */ |
| 106544 | 107408 | int mxSample; /* Maximum number of samples to accumulate */ |
| 106545 | 107409 | u32 iPrn; /* Pseudo-random number used for sampling */ |
| @@ -106615,31 +107479,32 @@ | ||
| 106615 | 107479 | ** Reclaim all memory of a StatAccum structure. |
| 106616 | 107480 | */ |
| 106617 | 107481 | static void statAccumDestructor(void *pOld){ |
| 106618 | 107482 | StatAccum *p = (StatAccum*)pOld; |
| 106619 | 107483 | #ifdef SQLITE_ENABLE_STAT4 |
| 106620 | - int i; | |
| 106621 | - for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i); | |
| 106622 | - for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i); | |
| 106623 | - sampleClear(p->db, &p->current); | |
| 107484 | + if( p->mxSample ){ | |
| 107485 | + int i; | |
| 107486 | + for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i); | |
| 107487 | + for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i); | |
| 107488 | + sampleClear(p->db, &p->current); | |
| 107489 | + } | |
| 106624 | 107490 | #endif |
| 106625 | 107491 | sqlite3DbFree(p->db, p); |
| 106626 | 107492 | } |
| 106627 | 107493 | |
| 106628 | 107494 | /* |
| 106629 | -** Implementation of the stat_init(N,K,C) SQL function. The three parameters | |
| 107495 | +** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters | |
| 106630 | 107496 | ** are: |
| 106631 | 107497 | ** N: The number of columns in the index including the rowid/pk (note 1) |
| 106632 | 107498 | ** K: The number of columns in the index excluding the rowid/pk. |
| 106633 | -** C: The number of rows in the index (note 2) | |
| 107499 | +** C: Estimated number of rows in the index | |
| 107500 | +** L: A limit on the number of rows to scan, or 0 for no-limit | |
| 106634 | 107501 | ** |
| 106635 | 107502 | ** Note 1: In the special case of the covering index that implements a |
| 106636 | 107503 | ** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the |
| 106637 | 107504 | ** total number of columns in the table. |
| 106638 | 107505 | ** |
| 106639 | -** Note 2: C is only used for STAT4. | |
| 106640 | -** | |
| 106641 | 107506 | ** For indexes on ordinary rowid tables, N==K+1. But for indexes on |
| 106642 | 107507 | ** WITHOUT ROWID tables, N=K+P where P is the number of columns in the |
| 106643 | 107508 | ** PRIMARY KEY of the table. The covering index that implements the |
| 106644 | 107509 | ** original WITHOUT ROWID table as N==K as a special case. |
| 106645 | 107510 | ** |
| @@ -106656,13 +107521,14 @@ | ||
| 106656 | 107521 | StatAccum *p; |
| 106657 | 107522 | int nCol; /* Number of columns in index being sampled */ |
| 106658 | 107523 | int nKeyCol; /* Number of key columns */ |
| 106659 | 107524 | int nColUp; /* nCol rounded up for alignment */ |
| 106660 | 107525 | int n; /* Bytes of space to allocate */ |
| 106661 | - sqlite3 *db; /* Database connection */ | |
| 107526 | + sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */ | |
| 106662 | 107527 | #ifdef SQLITE_ENABLE_STAT4 |
| 106663 | - int mxSample = SQLITE_STAT4_SAMPLES; | |
| 107528 | + /* Maximum number of samples. 0 if STAT4 data is not collected */ | |
| 107529 | + int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0; | |
| 106664 | 107530 | #endif |
| 106665 | 107531 | |
| 106666 | 107532 | /* Decode the three function arguments */ |
| 106667 | 107533 | UNUSED_PARAMETER(argc); |
| 106668 | 107534 | nCol = sqlite3_value_int(argv[0]); |
| @@ -106673,39 +107539,43 @@ | ||
| 106673 | 107539 | assert( nKeyCol>0 ); |
| 106674 | 107540 | |
| 106675 | 107541 | /* Allocate the space required for the StatAccum object */ |
| 106676 | 107542 | n = sizeof(*p) |
| 106677 | 107543 | + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ |
| 106678 | - + sizeof(tRowcnt)*nColUp /* StatAccum.anDLt */ | |
| 107544 | + + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ | |
| 106679 | 107545 | #ifdef SQLITE_ENABLE_STAT4 |
| 106680 | - + sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ | |
| 106681 | - + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ | |
| 106682 | - + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample) | |
| 107546 | + if( mxSample ){ | |
| 107547 | + n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ | |
| 107548 | + + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ | |
| 107549 | + + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); | |
| 107550 | + } | |
| 106683 | 107551 | #endif |
| 106684 | - ; | |
| 106685 | 107552 | db = sqlite3_context_db_handle(context); |
| 106686 | 107553 | p = sqlite3DbMallocZero(db, n); |
| 106687 | 107554 | if( p==0 ){ |
| 106688 | 107555 | sqlite3_result_error_nomem(context); |
| 106689 | 107556 | return; |
| 106690 | 107557 | } |
| 106691 | 107558 | |
| 106692 | 107559 | p->db = db; |
| 107560 | + p->nEst = sqlite3_value_int64(argv[2]); | |
| 106693 | 107561 | p->nRow = 0; |
| 107562 | + p->nLimit = sqlite3_value_int64(argv[3]); | |
| 106694 | 107563 | p->nCol = nCol; |
| 106695 | 107564 | p->nKeyCol = nKeyCol; |
| 107565 | + p->nSkipAhead = 0; | |
| 106696 | 107566 | p->current.anDLt = (tRowcnt*)&p[1]; |
| 106697 | 107567 | p->current.anEq = &p->current.anDLt[nColUp]; |
| 106698 | 107568 | |
| 106699 | 107569 | #ifdef SQLITE_ENABLE_STAT4 |
| 106700 | - { | |
| 107570 | + p->mxSample = p->nLimit==0 ? mxSample : 0; | |
| 107571 | + if( mxSample ){ | |
| 106701 | 107572 | u8 *pSpace; /* Allocated space not yet assigned */ |
| 106702 | 107573 | int i; /* Used to iterate through p->aSample[] */ |
| 106703 | 107574 | |
| 106704 | 107575 | p->iGet = -1; |
| 106705 | - p->mxSample = mxSample; | |
| 106706 | - p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1); | |
| 107576 | + p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1); | |
| 106707 | 107577 | p->current.anLt = &p->current.anEq[nColUp]; |
| 106708 | 107578 | p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); |
| 106709 | 107579 | |
| 106710 | 107580 | /* Set up the StatAccum.a[] and aBest[] arrays */ |
| 106711 | 107581 | p->a = (struct StatSample*)&p->current.anLt[nColUp]; |
| @@ -106729,11 +107599,11 @@ | ||
| 106729 | 107599 | ** (given by the 3rd parameter) is never used and can be any positive |
| 106730 | 107600 | ** value. */ |
| 106731 | 107601 | sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor); |
| 106732 | 107602 | } |
| 106733 | 107603 | static const FuncDef statInitFuncdef = { |
| 106734 | - 2+IsStat4, /* nArg */ | |
| 107604 | + 4, /* nArg */ | |
| 106735 | 107605 | SQLITE_UTF8, /* funcFlags */ |
| 106736 | 107606 | 0, /* pUserData */ |
| 106737 | 107607 | 0, /* pNext */ |
| 106738 | 107608 | statInit, /* xSFunc */ |
| 106739 | 107609 | 0, /* xFinalize */ |
| @@ -106933,14 +107803,17 @@ | ||
| 106933 | 107803 | ** P Pointer to the StatAccum object created by stat_init() |
| 106934 | 107804 | ** C Index of left-most column to differ from previous row |
| 106935 | 107805 | ** R Rowid for the current row. Might be a key record for |
| 106936 | 107806 | ** WITHOUT ROWID tables. |
| 106937 | 107807 | ** |
| 106938 | -** This SQL function always returns NULL. It's purpose it to accumulate | |
| 106939 | -** statistical data and/or samples in the StatAccum object about the | |
| 106940 | -** index being analyzed. The stat_get() SQL function will later be used to | |
| 106941 | -** extract relevant information for constructing the sqlite_statN tables. | |
| 107808 | +** The purpose of this routine is to collect statistical data and/or | |
| 107809 | +** samples from the index being analyzed into the StatAccum object. | |
| 107810 | +** The stat_get() SQL function will be used afterwards to | |
| 107811 | +** retrieve the information gathered. | |
| 107812 | +** | |
| 107813 | +** This SQL function usually returns NULL, but might return an integer | |
| 107814 | +** if it wants the byte-code to do special processing. | |
| 106942 | 107815 | ** |
| 106943 | 107816 | ** The R parameter is only used for STAT4 |
| 106944 | 107817 | */ |
| 106945 | 107818 | static void statPush( |
| 106946 | 107819 | sqlite3_context *context, |
| @@ -106962,11 +107835,11 @@ | ||
| 106962 | 107835 | /* This is the first call to this function. Do initialization. */ |
| 106963 | 107836 | for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1; |
| 106964 | 107837 | }else{ |
| 106965 | 107838 | /* Second and subsequent calls get processed here */ |
| 106966 | 107839 | #ifdef SQLITE_ENABLE_STAT4 |
| 106967 | - samplePushPrevious(p, iChng); | |
| 107840 | + if( p->mxSample ) samplePushPrevious(p, iChng); | |
| 106968 | 107841 | #endif |
| 106969 | 107842 | |
| 106970 | 107843 | /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply |
| 106971 | 107844 | ** to the current row of the index. */ |
| 106972 | 107845 | for(i=0; i<iChng; i++){ |
| @@ -106973,30 +107846,29 @@ | ||
| 106973 | 107846 | p->current.anEq[i]++; |
| 106974 | 107847 | } |
| 106975 | 107848 | for(i=iChng; i<p->nCol; i++){ |
| 106976 | 107849 | p->current.anDLt[i]++; |
| 106977 | 107850 | #ifdef SQLITE_ENABLE_STAT4 |
| 106978 | - p->current.anLt[i] += p->current.anEq[i]; | |
| 107851 | + if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; | |
| 106979 | 107852 | #endif |
| 106980 | 107853 | p->current.anEq[i] = 1; |
| 106981 | 107854 | } |
| 106982 | 107855 | } |
| 107856 | + | |
| 106983 | 107857 | p->nRow++; |
| 106984 | 107858 | #ifdef SQLITE_ENABLE_STAT4 |
| 106985 | - if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ | |
| 106986 | - sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); | |
| 106987 | - }else{ | |
| 106988 | - sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), | |
| 106989 | - sqlite3_value_blob(argv[2])); | |
| 106990 | - } | |
| 106991 | - p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; | |
| 106992 | -#endif | |
| 106993 | - | |
| 106994 | -#ifdef SQLITE_ENABLE_STAT4 | |
| 106995 | - { | |
| 106996 | - tRowcnt nLt = p->current.anLt[p->nCol-1]; | |
| 106997 | - | |
| 107859 | + if( p->mxSample ){ | |
| 107860 | + tRowcnt nLt; | |
| 107861 | + if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ | |
| 107862 | + sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); | |
| 107863 | + }else{ | |
| 107864 | + sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), | |
| 107865 | + sqlite3_value_blob(argv[2])); | |
| 107866 | + } | |
| 107867 | + p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; | |
| 107868 | + | |
| 107869 | + nLt = p->current.anLt[p->nCol-1]; | |
| 106998 | 107870 | /* Check if this is to be a periodic sample. If so, add it. */ |
| 106999 | 107871 | if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){ |
| 107000 | 107872 | p->current.isPSample = 1; |
| 107001 | 107873 | p->current.iCol = 0; |
| 107002 | 107874 | sampleInsert(p, &p->current, p->nCol-1); |
| @@ -107008,13 +107880,18 @@ | ||
| 107008 | 107880 | p->current.iCol = i; |
| 107009 | 107881 | if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){ |
| 107010 | 107882 | sampleCopy(p, &p->aBest[i], &p->current); |
| 107011 | 107883 | } |
| 107012 | 107884 | } |
| 107885 | + }else | |
| 107886 | +#endif | |
| 107887 | + if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){ | |
| 107888 | + p->nSkipAhead++; | |
| 107889 | + sqlite3_result_int(context, p->current.anDLt[0]>0); | |
| 107013 | 107890 | } |
| 107014 | -#endif | |
| 107015 | 107891 | } |
| 107892 | + | |
| 107016 | 107893 | static const FuncDef statPushFuncdef = { |
| 107017 | 107894 | 2+IsStat4, /* nArg */ |
| 107018 | 107895 | SQLITE_UTF8, /* funcFlags */ |
| 107019 | 107896 | 0, /* pUserData */ |
| 107020 | 107897 | 0, /* pNext */ |
| @@ -107062,10 +107939,11 @@ | ||
| 107062 | 107939 | assert( argc==2 ); |
| 107063 | 107940 | assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ |
| 107064 | 107941 | || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT |
| 107065 | 107942 | || eCall==STAT_GET_NDLT |
| 107066 | 107943 | ); |
| 107944 | + assert( eCall==STAT_GET_STAT1 || p->mxSample ); | |
| 107067 | 107945 | if( eCall==STAT_GET_STAT1 ) |
| 107068 | 107946 | #else |
| 107069 | 107947 | assert( argc==1 ); |
| 107070 | 107948 | #endif |
| 107071 | 107949 | { |
| @@ -107097,11 +107975,12 @@ | ||
| 107097 | 107975 | if( zRet==0 ){ |
| 107098 | 107976 | sqlite3_result_error_nomem(context); |
| 107099 | 107977 | return; |
| 107100 | 107978 | } |
| 107101 | 107979 | |
| 107102 | - sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow); | |
| 107980 | + sqlite3_snprintf(24, zRet, "%llu", | |
| 107981 | + p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); | |
| 107103 | 107982 | z = zRet + sqlite3Strlen30(zRet); |
| 107104 | 107983 | for(i=0; i<p->nKeyCol; i++){ |
| 107105 | 107984 | u64 nDistinct = p->current.anDLt[i] + 1; |
| 107106 | 107985 | u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; |
| 107107 | 107986 | sqlite3_snprintf(24, z, " %llu", iVal); |
| @@ -107173,20 +108052,20 @@ | ||
| 107173 | 108052 | 0, 0, /* xValue, xInverse */ |
| 107174 | 108053 | "stat_get", /* zName */ |
| 107175 | 108054 | {0} |
| 107176 | 108055 | }; |
| 107177 | 108056 | |
| 107178 | -static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){ | |
| 108057 | +static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){ | |
| 107179 | 108058 | #ifdef SQLITE_ENABLE_STAT4 |
| 107180 | - sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1); | |
| 108059 | + sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1); | |
| 107181 | 108060 | #elif SQLITE_DEBUG |
| 107182 | 108061 | assert( iParam==STAT_GET_STAT1 ); |
| 107183 | 108062 | #else |
| 107184 | 108063 | UNUSED_PARAMETER( iParam ); |
| 107185 | 108064 | #endif |
| 107186 | - assert( regOut!=regStat4 && regOut!=regStat4+1 ); | |
| 107187 | - sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4, | |
| 108065 | + assert( regOut!=regStat && regOut!=regStat+1 ); | |
| 108066 | + sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4, | |
| 107188 | 108067 | &statGetFuncdef, 0); |
| 107189 | 108068 | } |
| 107190 | 108069 | |
| 107191 | 108070 | /* |
| 107192 | 108071 | ** Generate code to do an analysis of all indices associated with |
| @@ -107208,16 +108087,15 @@ | ||
| 107208 | 108087 | int i; /* Loop counter */ |
| 107209 | 108088 | int jZeroRows = -1; /* Jump from here if number of rows is zero */ |
| 107210 | 108089 | int iDb; /* Index of database containing pTab */ |
| 107211 | 108090 | u8 needTableCnt = 1; /* True to count the table */ |
| 107212 | 108091 | int regNewRowid = iMem++; /* Rowid for the inserted record */ |
| 107213 | - int regStat4 = iMem++; /* Register to hold StatAccum object */ | |
| 108092 | + int regStat = iMem++; /* Register to hold StatAccum object */ | |
| 107214 | 108093 | int regChng = iMem++; /* Index of changed index field */ |
| 107215 | -#ifdef SQLITE_ENABLE_STAT4 | |
| 107216 | 108094 | int regRowid = iMem++; /* Rowid argument passed to stat_push() */ |
| 107217 | -#endif | |
| 107218 | 108095 | int regTemp = iMem++; /* Temporary use register */ |
| 108096 | + int regTemp2 = iMem++; /* Second temporary use register */ | |
| 107219 | 108097 | int regTabname = iMem++; /* Register containing table name */ |
| 107220 | 108098 | int regIdxname = iMem++; /* Register containing index name */ |
| 107221 | 108099 | int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ |
| 107222 | 108100 | int regPrev = iMem; /* MUST BE LAST (see below) */ |
| 107223 | 108101 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| @@ -107341,21 +108219,30 @@ | ||
| 107341 | 108219 | /* Invoke the stat_init() function. The arguments are: |
| 107342 | 108220 | ** |
| 107343 | 108221 | ** (1) the number of columns in the index including the rowid |
| 107344 | 108222 | ** (or for a WITHOUT ROWID table, the number of PK columns), |
| 107345 | 108223 | ** (2) the number of columns in the key without the rowid/pk |
| 107346 | - ** (3) the number of rows in the index, | |
| 107347 | - ** | |
| 107348 | - ** | |
| 107349 | - ** The third argument is only used for STAT4 | |
| 108224 | + ** (3) estimated number of rows in the index, | |
| 107350 | 108225 | */ |
| 108226 | + sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); | |
| 108227 | + assert( regRowid==regStat+2 ); | |
| 108228 | + sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); | |
| 107351 | 108229 | #ifdef SQLITE_ENABLE_STAT4 |
| 107352 | - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3); | |
| 108230 | + if( OptimizationEnabled(db, SQLITE_Stat4) ){ | |
| 108231 | + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp); | |
| 108232 | + addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); | |
| 108233 | + VdbeCoverage(v); | |
| 108234 | + }else | |
| 107353 | 108235 | #endif |
| 107354 | - sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1); | |
| 107355 | - sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2); | |
| 107356 | - sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4, | |
| 108236 | + { | |
| 108237 | + addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); | |
| 108238 | + VdbeCoverage(v); | |
| 108239 | + sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1); | |
| 108240 | + } | |
| 108241 | + assert( regTemp2==regStat+4 ); | |
| 108242 | + sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); | |
| 108243 | + sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, | |
| 107357 | 108244 | &statInitFuncdef, 0); |
| 107358 | 108245 | |
| 107359 | 108246 | /* Implementation of the following: |
| 107360 | 108247 | ** |
| 107361 | 108248 | ** Rewind csr |
| @@ -107362,12 +108249,10 @@ | ||
| 107362 | 108249 | ** if eof(csr) goto end_of_scan; |
| 107363 | 108250 | ** regChng = 0 |
| 107364 | 108251 | ** goto next_push_0; |
| 107365 | 108252 | ** |
| 107366 | 108253 | */ |
| 107367 | - addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); | |
| 107368 | - VdbeCoverage(v); | |
| 107369 | 108254 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); |
| 107370 | 108255 | addrNextRow = sqlite3VdbeCurrentAddr(v); |
| 107371 | 108256 | |
| 107372 | 108257 | if( nColTest>0 ){ |
| 107373 | 108258 | int endDistinctTest = sqlite3VdbeMakeLabel(pParse); |
| @@ -107396,10 +108281,11 @@ | ||
| 107396 | 108281 | } |
| 107397 | 108282 | for(i=0; i<nColTest; i++){ |
| 107398 | 108283 | char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); |
| 107399 | 108284 | sqlite3VdbeAddOp2(v, OP_Integer, i, regChng); |
| 107400 | 108285 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp); |
| 108286 | + VdbeComment((v, "%s.column(%d)", pIdx->zName, i)); | |
| 107401 | 108287 | aGotoChng[i] = |
| 107402 | 108288 | sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ); |
| 107403 | 108289 | sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); |
| 107404 | 108290 | VdbeCoverage(v); |
| 107405 | 108291 | } |
| @@ -107416,10 +108302,11 @@ | ||
| 107416 | 108302 | */ |
| 107417 | 108303 | sqlite3VdbeJumpHere(v, addrNextRow-1); |
| 107418 | 108304 | for(i=0; i<nColTest; i++){ |
| 107419 | 108305 | sqlite3VdbeJumpHere(v, aGotoChng[i]); |
| 107420 | 108306 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i); |
| 108307 | + VdbeComment((v, "%s.column(%d)", pIdx->zName, i)); | |
| 107421 | 108308 | } |
| 107422 | 108309 | sqlite3VdbeResolveLabel(v, endDistinctTest); |
| 107423 | 108310 | sqlite3DbFree(db, aGotoChng); |
| 107424 | 108311 | } |
| 107425 | 108312 | |
| @@ -107429,34 +108316,50 @@ | ||
| 107429 | 108316 | ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only |
| 107430 | 108317 | ** Next csr |
| 107431 | 108318 | ** if !eof(csr) goto next_row; |
| 107432 | 108319 | */ |
| 107433 | 108320 | #ifdef SQLITE_ENABLE_STAT4 |
| 107434 | - assert( regRowid==(regStat4+2) ); | |
| 107435 | - if( HasRowid(pTab) ){ | |
| 107436 | - sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); | |
| 107437 | - }else{ | |
| 107438 | - Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); | |
| 107439 | - int j, k, regKey; | |
| 107440 | - regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); | |
| 107441 | - for(j=0; j<pPk->nKeyCol; j++){ | |
| 107442 | - k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]); | |
| 107443 | - assert( k>=0 && k<pIdx->nColumn ); | |
| 107444 | - sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); | |
| 107445 | - VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName)); | |
| 107446 | - } | |
| 107447 | - sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); | |
| 107448 | - sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); | |
| 108321 | + if( OptimizationEnabled(db, SQLITE_Stat4) ){ | |
| 108322 | + assert( regRowid==(regStat+2) ); | |
| 108323 | + if( HasRowid(pTab) ){ | |
| 108324 | + sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); | |
| 108325 | + }else{ | |
| 108326 | + Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); | |
| 108327 | + int j, k, regKey; | |
| 108328 | + regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); | |
| 108329 | + for(j=0; j<pPk->nKeyCol; j++){ | |
| 108330 | + k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]); | |
| 108331 | + assert( k>=0 && k<pIdx->nColumn ); | |
| 108332 | + sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); | |
| 108333 | + VdbeComment((v, "%s.column(%d)", pIdx->zName, i)); | |
| 108334 | + } | |
| 108335 | + sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); | |
| 108336 | + sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); | |
| 108337 | + } | |
| 107449 | 108338 | } |
| 107450 | 108339 | #endif |
| 107451 | - assert( regChng==(regStat4+1) ); | |
| 107452 | - sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4, | |
| 107453 | - &statPushFuncdef, 0); | |
| 107454 | - sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); | |
| 108340 | + assert( regChng==(regStat+1) ); | |
| 108341 | + { | |
| 108342 | + sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4, | |
| 108343 | + &statPushFuncdef, 0); | |
| 108344 | + if( db->nAnalysisLimit ){ | |
| 108345 | + int j1, j2, j3; | |
| 108346 | + j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v); | |
| 108347 | + j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v); | |
| 108348 | + j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1); | |
| 108349 | + VdbeCoverage(v); | |
| 108350 | + sqlite3VdbeJumpHere(v, j1); | |
| 108351 | + sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); | |
| 108352 | + sqlite3VdbeJumpHere(v, j2); | |
| 108353 | + sqlite3VdbeJumpHere(v, j3); | |
| 108354 | + }else{ | |
| 108355 | + sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); | |
| 108356 | + } | |
| 108357 | + } | |
| 107455 | 108358 | |
| 107456 | 108359 | /* Add the entry to the stat1 table. */ |
| 107457 | - callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1); | |
| 108360 | + callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); | |
| 107458 | 108361 | assert( "BBB"[0]==SQLITE_AFF_TEXT ); |
| 107459 | 108362 | sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); |
| 107460 | 108363 | sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); |
| 107461 | 108364 | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); |
| 107462 | 108365 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| @@ -107464,11 +108367,11 @@ | ||
| 107464 | 108367 | #endif |
| 107465 | 108368 | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| 107466 | 108369 | |
| 107467 | 108370 | /* Add the entries to the stat4 table. */ |
| 107468 | 108371 | #ifdef SQLITE_ENABLE_STAT4 |
| 107469 | - { | |
| 108372 | + if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){ | |
| 107470 | 108373 | int regEq = regStat1; |
| 107471 | 108374 | int regLt = regStat1+1; |
| 107472 | 108375 | int regDLt = regStat1+2; |
| 107473 | 108376 | int regSample = regStat1+3; |
| 107474 | 108377 | int regCol = regStat1+4; |
| @@ -107478,16 +108381,16 @@ | ||
| 107478 | 108381 | u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; |
| 107479 | 108382 | |
| 107480 | 108383 | pParse->nMem = MAX(pParse->nMem, regCol+nCol); |
| 107481 | 108384 | |
| 107482 | 108385 | addrNext = sqlite3VdbeCurrentAddr(v); |
| 107483 | - callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid); | |
| 108386 | + callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid); | |
| 107484 | 108387 | addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); |
| 107485 | 108388 | VdbeCoverage(v); |
| 107486 | - callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq); | |
| 107487 | - callStatGet(pParse, regStat4, STAT_GET_NLT, regLt); | |
| 107488 | - callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt); | |
| 108389 | + callStatGet(pParse, regStat, STAT_GET_NEQ, regEq); | |
| 108390 | + callStatGet(pParse, regStat, STAT_GET_NLT, regLt); | |
| 108391 | + callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt); | |
| 107489 | 108392 | sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0); |
| 107490 | 108393 | VdbeCoverage(v); |
| 107491 | 108394 | for(i=0; i<nCol; i++){ |
| 107492 | 108395 | sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i); |
| 107493 | 108396 | } |
| @@ -109619,10 +110522,11 @@ | ||
| 109619 | 110522 | int i; |
| 109620 | 110523 | Column *pCol; |
| 109621 | 110524 | assert( pTable!=0 ); |
| 109622 | 110525 | if( (pCol = pTable->aCol)!=0 ){ |
| 109623 | 110526 | for(i=0; i<pTable->nCol; i++, pCol++){ |
| 110527 | + assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) ); | |
| 109624 | 110528 | sqlite3DbFree(db, pCol->zName); |
| 109625 | 110529 | sqlite3ExprDelete(db, pCol->pDflt); |
| 109626 | 110530 | sqlite3DbFree(db, pCol->zColl); |
| 109627 | 110531 | } |
| 109628 | 110532 | sqlite3DbFree(db, pTable->aCol); |
| @@ -110267,10 +111171,11 @@ | ||
| 110267 | 111171 | p->aCol = aNew; |
| 110268 | 111172 | } |
| 110269 | 111173 | pCol = &p->aCol[p->nCol]; |
| 110270 | 111174 | memset(pCol, 0, sizeof(p->aCol[0])); |
| 110271 | 111175 | pCol->zName = z; |
| 111176 | + pCol->hName = sqlite3StrIHash(z); | |
| 110272 | 111177 | sqlite3ColumnPropertiesFromName(p, pCol); |
| 110273 | 111178 | |
| 110274 | 111179 | if( pType->n==0 ){ |
| 110275 | 111180 | /* If there is no type specified, columns have the default affinity |
| 110276 | 111181 | ** 'BLOB' with a default size of 4 bytes. */ |
| @@ -113657,11 +114562,11 @@ | ||
| 113657 | 114562 | pParse->rc = rc; |
| 113658 | 114563 | return 1; |
| 113659 | 114564 | } |
| 113660 | 114565 | db->aDb[1].pBt = pBt; |
| 113661 | 114566 | assert( db->aDb[1].pSchema ); |
| 113662 | - if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ | |
| 114567 | + if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){ | |
| 113663 | 114568 | sqlite3OomFault(db); |
| 113664 | 114569 | return 1; |
| 113665 | 114570 | } |
| 113666 | 114571 | } |
| 113667 | 114572 | return 0; |
| @@ -113768,11 +114673,11 @@ | ||
| 113768 | 114673 | char *p4, /* Error message */ |
| 113769 | 114674 | i8 p4type, /* P4_STATIC or P4_TRANSIENT */ |
| 113770 | 114675 | u8 p5Errmsg /* P5_ErrMsg type */ |
| 113771 | 114676 | ){ |
| 113772 | 114677 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 113773 | - assert( (errCode&0xff)==SQLITE_CONSTRAINT ); | |
| 114678 | + assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested ); | |
| 113774 | 114679 | if( onError==OE_Abort ){ |
| 113775 | 114680 | sqlite3MayAbort(pParse); |
| 113776 | 114681 | } |
| 113777 | 114682 | sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type); |
| 113778 | 114683 | sqlite3VdbeChangeP5(v, p5Errmsg); |
| @@ -115481,10 +116386,11 @@ | ||
| 115481 | 116386 | VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); |
| 115482 | 116387 | r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, |
| 115483 | 116388 | &iPartIdxLabel, pPrior, r1); |
| 115484 | 116389 | sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, |
| 115485 | 116390 | pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
| 116391 | + sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */ | |
| 115486 | 116392 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 115487 | 116393 | pPrior = pIdx; |
| 115488 | 116394 | } |
| 115489 | 116395 | } |
| 115490 | 116396 | |
| @@ -122468,10 +123374,11 @@ | ||
| 122468 | 123374 | const char *(*filename_wal)(const char*); |
| 122469 | 123375 | /* Version 3.32.0 and later */ |
| 122470 | 123376 | char *(*create_filename)(const char*,const char*,const char*, |
| 122471 | 123377 | int,const char**); |
| 122472 | 123378 | void (*free_filename)(char*); |
| 123379 | + sqlite3_file *(*database_file_object)(const char*); | |
| 122473 | 123380 | }; |
| 122474 | 123381 | |
| 122475 | 123382 | /* |
| 122476 | 123383 | ** This is the function signature used for all extension entry points. It |
| 122477 | 123384 | ** is also defined in the file "loadext.c". |
| @@ -122771,10 +123678,11 @@ | ||
| 122771 | 123678 | #define sqlite3_filename_journal sqlite3_api->filename_journal |
| 122772 | 123679 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 122773 | 123680 | /* Version 3.32.0 and later */ |
| 122774 | 123681 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 122775 | 123682 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 123683 | +#define sqlite3_database_file_object sqlite3_api->database_file_object | |
| 122776 | 123684 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 122777 | 123685 | |
| 122778 | 123686 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 122779 | 123687 | /* This case when the file really is being compiled as a loadable |
| 122780 | 123688 | ** extension */ |
| @@ -123252,11 +124160,20 @@ | ||
| 123252 | 124160 | sqlite3_filename_journal, |
| 123253 | 124161 | sqlite3_filename_wal, |
| 123254 | 124162 | /* Version 3.32.0 and later */ |
| 123255 | 124163 | sqlite3_create_filename, |
| 123256 | 124164 | sqlite3_free_filename, |
| 124165 | + sqlite3_database_file_object, | |
| 123257 | 124166 | }; |
| 124167 | + | |
| 124168 | +/* True if x is the directory separator character | |
| 124169 | +*/ | |
| 124170 | +#if SQLITE_OS_WIN | |
| 124171 | +# define DirSep(X) ((X)=='/'||(X)=='\\') | |
| 124172 | +#else | |
| 124173 | +# define DirSep(X) ((X)=='/') | |
| 124174 | +#endif | |
| 123258 | 124175 | |
| 123259 | 124176 | /* |
| 123260 | 124177 | ** Attempt to load an SQLite extension library contained in the file |
| 123261 | 124178 | ** zFile. The entry point is zProc. zProc may be 0 in which case a |
| 123262 | 124179 | ** default entry point name (sqlite3_extension_init) is used. Use |
| @@ -123355,11 +124272,11 @@ | ||
| 123355 | 124272 | if( zAltEntry==0 ){ |
| 123356 | 124273 | sqlite3OsDlClose(pVfs, handle); |
| 123357 | 124274 | return SQLITE_NOMEM_BKPT; |
| 123358 | 124275 | } |
| 123359 | 124276 | memcpy(zAltEntry, "sqlite3_", 8); |
| 123360 | - for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){} | |
| 124277 | + for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} | |
| 123361 | 124278 | iFile++; |
| 123362 | 124279 | if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; |
| 123363 | 124280 | for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ |
| 123364 | 124281 | if( sqlite3Isalpha(c) ){ |
| 123365 | 124282 | zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; |
| @@ -123659,53 +124576,54 @@ | ||
| 123659 | 124576 | ** that script and rerun it. |
| 123660 | 124577 | */ |
| 123661 | 124578 | |
| 123662 | 124579 | /* The various pragma types */ |
| 123663 | 124580 | #define PragTyp_ACTIVATE_EXTENSIONS 0 |
| 123664 | -#define PragTyp_HEADER_VALUE 1 | |
| 123665 | -#define PragTyp_AUTO_VACUUM 2 | |
| 123666 | -#define PragTyp_FLAG 3 | |
| 123667 | -#define PragTyp_BUSY_TIMEOUT 4 | |
| 123668 | -#define PragTyp_CACHE_SIZE 5 | |
| 123669 | -#define PragTyp_CACHE_SPILL 6 | |
| 123670 | -#define PragTyp_CASE_SENSITIVE_LIKE 7 | |
| 123671 | -#define PragTyp_COLLATION_LIST 8 | |
| 123672 | -#define PragTyp_COMPILE_OPTIONS 9 | |
| 123673 | -#define PragTyp_DATA_STORE_DIRECTORY 10 | |
| 123674 | -#define PragTyp_DATABASE_LIST 11 | |
| 123675 | -#define PragTyp_DEFAULT_CACHE_SIZE 12 | |
| 123676 | -#define PragTyp_ENCODING 13 | |
| 123677 | -#define PragTyp_FOREIGN_KEY_CHECK 14 | |
| 123678 | -#define PragTyp_FOREIGN_KEY_LIST 15 | |
| 123679 | -#define PragTyp_FUNCTION_LIST 16 | |
| 123680 | -#define PragTyp_HARD_HEAP_LIMIT 17 | |
| 123681 | -#define PragTyp_INCREMENTAL_VACUUM 18 | |
| 123682 | -#define PragTyp_INDEX_INFO 19 | |
| 123683 | -#define PragTyp_INDEX_LIST 20 | |
| 123684 | -#define PragTyp_INTEGRITY_CHECK 21 | |
| 123685 | -#define PragTyp_JOURNAL_MODE 22 | |
| 123686 | -#define PragTyp_JOURNAL_SIZE_LIMIT 23 | |
| 123687 | -#define PragTyp_LOCK_PROXY_FILE 24 | |
| 123688 | -#define PragTyp_LOCKING_MODE 25 | |
| 123689 | -#define PragTyp_PAGE_COUNT 26 | |
| 123690 | -#define PragTyp_MMAP_SIZE 27 | |
| 123691 | -#define PragTyp_MODULE_LIST 28 | |
| 123692 | -#define PragTyp_OPTIMIZE 29 | |
| 123693 | -#define PragTyp_PAGE_SIZE 30 | |
| 123694 | -#define PragTyp_PRAGMA_LIST 31 | |
| 123695 | -#define PragTyp_SECURE_DELETE 32 | |
| 123696 | -#define PragTyp_SHRINK_MEMORY 33 | |
| 123697 | -#define PragTyp_SOFT_HEAP_LIMIT 34 | |
| 123698 | -#define PragTyp_SYNCHRONOUS 35 | |
| 123699 | -#define PragTyp_TABLE_INFO 36 | |
| 123700 | -#define PragTyp_TEMP_STORE 37 | |
| 123701 | -#define PragTyp_TEMP_STORE_DIRECTORY 38 | |
| 123702 | -#define PragTyp_THREADS 39 | |
| 123703 | -#define PragTyp_WAL_AUTOCHECKPOINT 40 | |
| 123704 | -#define PragTyp_WAL_CHECKPOINT 41 | |
| 123705 | -#define PragTyp_LOCK_STATUS 42 | |
| 123706 | -#define PragTyp_STATS 43 | |
| 124581 | +#define PragTyp_ANALYSIS_LIMIT 1 | |
| 124582 | +#define PragTyp_HEADER_VALUE 2 | |
| 124583 | +#define PragTyp_AUTO_VACUUM 3 | |
| 124584 | +#define PragTyp_FLAG 4 | |
| 124585 | +#define PragTyp_BUSY_TIMEOUT 5 | |
| 124586 | +#define PragTyp_CACHE_SIZE 6 | |
| 124587 | +#define PragTyp_CACHE_SPILL 7 | |
| 124588 | +#define PragTyp_CASE_SENSITIVE_LIKE 8 | |
| 124589 | +#define PragTyp_COLLATION_LIST 9 | |
| 124590 | +#define PragTyp_COMPILE_OPTIONS 10 | |
| 124591 | +#define PragTyp_DATA_STORE_DIRECTORY 11 | |
| 124592 | +#define PragTyp_DATABASE_LIST 12 | |
| 124593 | +#define PragTyp_DEFAULT_CACHE_SIZE 13 | |
| 124594 | +#define PragTyp_ENCODING 14 | |
| 124595 | +#define PragTyp_FOREIGN_KEY_CHECK 15 | |
| 124596 | +#define PragTyp_FOREIGN_KEY_LIST 16 | |
| 124597 | +#define PragTyp_FUNCTION_LIST 17 | |
| 124598 | +#define PragTyp_HARD_HEAP_LIMIT 18 | |
| 124599 | +#define PragTyp_INCREMENTAL_VACUUM 19 | |
| 124600 | +#define PragTyp_INDEX_INFO 20 | |
| 124601 | +#define PragTyp_INDEX_LIST 21 | |
| 124602 | +#define PragTyp_INTEGRITY_CHECK 22 | |
| 124603 | +#define PragTyp_JOURNAL_MODE 23 | |
| 124604 | +#define PragTyp_JOURNAL_SIZE_LIMIT 24 | |
| 124605 | +#define PragTyp_LOCK_PROXY_FILE 25 | |
| 124606 | +#define PragTyp_LOCKING_MODE 26 | |
| 124607 | +#define PragTyp_PAGE_COUNT 27 | |
| 124608 | +#define PragTyp_MMAP_SIZE 28 | |
| 124609 | +#define PragTyp_MODULE_LIST 29 | |
| 124610 | +#define PragTyp_OPTIMIZE 30 | |
| 124611 | +#define PragTyp_PAGE_SIZE 31 | |
| 124612 | +#define PragTyp_PRAGMA_LIST 32 | |
| 124613 | +#define PragTyp_SECURE_DELETE 33 | |
| 124614 | +#define PragTyp_SHRINK_MEMORY 34 | |
| 124615 | +#define PragTyp_SOFT_HEAP_LIMIT 35 | |
| 124616 | +#define PragTyp_SYNCHRONOUS 36 | |
| 124617 | +#define PragTyp_TABLE_INFO 37 | |
| 124618 | +#define PragTyp_TEMP_STORE 38 | |
| 124619 | +#define PragTyp_TEMP_STORE_DIRECTORY 39 | |
| 124620 | +#define PragTyp_THREADS 40 | |
| 124621 | +#define PragTyp_WAL_AUTOCHECKPOINT 41 | |
| 124622 | +#define PragTyp_WAL_CHECKPOINT 42 | |
| 124623 | +#define PragTyp_LOCK_STATUS 43 | |
| 124624 | +#define PragTyp_STATS 44 | |
| 123707 | 124625 | |
| 123708 | 124626 | /* Property flags associated with various pragma. */ |
| 123709 | 124627 | #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ |
| 123710 | 124628 | #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */ |
| 123711 | 124629 | #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */ |
| @@ -123792,10 +124710,15 @@ | ||
| 123792 | 124710 | /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS, |
| 123793 | 124711 | /* ePragFlg: */ 0, |
| 123794 | 124712 | /* ColNames: */ 0, 0, |
| 123795 | 124713 | /* iArg: */ 0 }, |
| 123796 | 124714 | #endif |
| 124715 | + {/* zName: */ "analysis_limit", | |
| 124716 | + /* ePragTyp: */ PragTyp_ANALYSIS_LIMIT, | |
| 124717 | + /* ePragFlg: */ PragFlg_Result0, | |
| 124718 | + /* ColNames: */ 0, 0, | |
| 124719 | + /* iArg: */ 0 }, | |
| 123797 | 124720 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 123798 | 124721 | {/* zName: */ "application_id", |
| 123799 | 124722 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 123800 | 124723 | /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0, |
| 123801 | 124724 | /* ColNames: */ 0, 0, |
| @@ -124292,11 +125215,11 @@ | ||
| 124292 | 125215 | /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, |
| 124293 | 125216 | /* ColNames: */ 0, 0, |
| 124294 | 125217 | /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, |
| 124295 | 125218 | #endif |
| 124296 | 125219 | }; |
| 124297 | -/* Number of pragmas: 66 on by default, 76 total. */ | |
| 125220 | +/* Number of pragmas: 67 on by default, 77 total. */ | |
| 124298 | 125221 | |
| 124299 | 125222 | /************** End of pragma.h **********************************************/ |
| 124300 | 125223 | /************** Continuing where we left off in pragma.c *********************/ |
| 124301 | 125224 | |
| 124302 | 125225 | /* |
| @@ -124822,11 +125745,11 @@ | ||
| 124822 | 125745 | }else{ |
| 124823 | 125746 | /* Malloc may fail when setting the page-size, as there is an internal |
| 124824 | 125747 | ** buffer that the pager module resizes using sqlite3_realloc(). |
| 124825 | 125748 | */ |
| 124826 | 125749 | db->nextPagesize = sqlite3Atoi(zRight); |
| 124827 | - if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){ | |
| 125750 | + if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,0,0) ){ | |
| 124828 | 125751 | sqlite3OomFault(db); |
| 124829 | 125752 | } |
| 124830 | 125753 | } |
| 124831 | 125754 | break; |
| 124832 | 125755 | } |
| @@ -125996,11 +126919,10 @@ | ||
| 125996 | 126919 | sqlite3ResolvePartIdxLabel(pParse, jmp3); |
| 125997 | 126920 | } |
| 125998 | 126921 | } |
| 125999 | 126922 | sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); |
| 126000 | 126923 | sqlite3VdbeJumpHere(v, loopTop-1); |
| 126001 | -#ifndef SQLITE_OMIT_BTREECOUNT | |
| 126002 | 126924 | if( !isQuick ){ |
| 126003 | 126925 | sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); |
| 126004 | 126926 | for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ |
| 126005 | 126927 | if( pPk==pIdx ) continue; |
| 126006 | 126928 | sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); |
| @@ -126010,11 +126932,10 @@ | ||
| 126010 | 126932 | sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); |
| 126011 | 126933 | integrityCheckResultRow(v); |
| 126012 | 126934 | sqlite3VdbeJumpHere(v, addr); |
| 126013 | 126935 | } |
| 126014 | 126936 | } |
| 126015 | -#endif /* SQLITE_OMIT_BTREECOUNT */ | |
| 126016 | 126937 | } |
| 126017 | 126938 | } |
| 126018 | 126939 | { |
| 126019 | 126940 | static const int iLn = VDBE_OFFSET_LINENO(2); |
| 126020 | 126941 | static const VdbeOpList endCode[] = { |
| @@ -126444,10 +127365,29 @@ | ||
| 126444 | 127365 | sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff)); |
| 126445 | 127366 | } |
| 126446 | 127367 | returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1)); |
| 126447 | 127368 | break; |
| 126448 | 127369 | } |
| 127370 | + | |
| 127371 | + /* | |
| 127372 | + ** PRAGMA analysis_limit | |
| 127373 | + ** PRAGMA analysis_limit = N | |
| 127374 | + ** | |
| 127375 | + ** Configure the maximum number of rows that ANALYZE will examine | |
| 127376 | + ** in each index that it looks at. Return the new limit. | |
| 127377 | + */ | |
| 127378 | + case PragTyp_ANALYSIS_LIMIT: { | |
| 127379 | + sqlite3_int64 N; | |
| 127380 | + if( zRight | |
| 127381 | + && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK | |
| 127382 | + && N>=0 | |
| 127383 | + ){ | |
| 127384 | + db->nAnalysisLimit = (int)(N&0x7fffffff); | |
| 127385 | + } | |
| 127386 | + returnSingleInt(v, db->nAnalysisLimit); | |
| 127387 | + break; | |
| 127388 | + } | |
| 126449 | 127389 | |
| 126450 | 127390 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 126451 | 127391 | /* |
| 126452 | 127392 | ** Report the current state of file logs for all databases |
| 126453 | 127393 | */ |
| @@ -129768,10 +130708,11 @@ | ||
| 129768 | 130708 | } |
| 129769 | 130709 | zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); |
| 129770 | 130710 | if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt); |
| 129771 | 130711 | } |
| 129772 | 130712 | pCol->zName = zName; |
| 130713 | + pCol->hName = sqlite3StrIHash(zName); | |
| 129773 | 130714 | sqlite3ColumnPropertiesFromName(0, pCol); |
| 129774 | 130715 | if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){ |
| 129775 | 130716 | sqlite3OomFault(db); |
| 129776 | 130717 | } |
| 129777 | 130718 | } |
| @@ -131221,11 +132162,14 @@ | ||
| 131221 | 132162 | if( ExprHasProperty(pExpr, EP_FromJoin) |
| 131222 | 132163 | && pExpr->iRightJoinTable==pSubst->iTable |
| 131223 | 132164 | ){ |
| 131224 | 132165 | pExpr->iRightJoinTable = pSubst->iNewTable; |
| 131225 | 132166 | } |
| 131226 | - if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){ | |
| 132167 | + if( pExpr->op==TK_COLUMN | |
| 132168 | + && pExpr->iTable==pSubst->iTable | |
| 132169 | + && !ExprHasProperty(pExpr, EP_FixedCol) | |
| 132170 | + ){ | |
| 131227 | 132171 | if( pExpr->iColumn<0 ){ |
| 131228 | 132172 | pExpr->op = TK_NULL; |
| 131229 | 132173 | }else{ |
| 131230 | 132174 | Expr *pNew; |
| 131231 | 132175 | Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; |
| @@ -131239,10 +132183,11 @@ | ||
| 131239 | 132183 | if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ |
| 131240 | 132184 | memset(&ifNullRow, 0, sizeof(ifNullRow)); |
| 131241 | 132185 | ifNullRow.op = TK_IF_NULL_ROW; |
| 131242 | 132186 | ifNullRow.pLeft = pCopy; |
| 131243 | 132187 | ifNullRow.iTable = pSubst->iNewTable; |
| 132188 | + ifNullRow.flags = EP_Skip; | |
| 131244 | 132189 | pCopy = &ifNullRow; |
| 131245 | 132190 | } |
| 131246 | 132191 | testcase( ExprHasProperty(pCopy, EP_Subquery) ); |
| 131247 | 132192 | pNew = sqlite3ExprDup(db, pCopy, 0); |
| 131248 | 132193 | if( pNew && pSubst->isLeftJoin ){ |
| @@ -133133,10 +134078,11 @@ | ||
| 133133 | 134078 | Vdbe *v = pParse->pVdbe; |
| 133134 | 134079 | int i; |
| 133135 | 134080 | struct AggInfo_func *pFunc; |
| 133136 | 134081 | int nReg = pAggInfo->nFunc + pAggInfo->nColumn; |
| 133137 | 134082 | if( nReg==0 ) return; |
| 134083 | + if( pParse->nErr ) return; | |
| 133138 | 134084 | #ifdef SQLITE_DEBUG |
| 133139 | 134085 | /* Verify that all AggInfo registers are within the range specified by |
| 133140 | 134086 | ** AggInfo.mnReg..AggInfo.mxReg */ |
| 133141 | 134087 | assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); |
| 133142 | 134088 | for(i=0; i<pAggInfo->nColumn; i++){ |
| @@ -134402,11 +135348,10 @@ | ||
| 134402 | 135348 | VdbeComment((v, "indicate accumulator empty")); |
| 134403 | 135349 | sqlite3VdbeAddOp1(v, OP_Return, regReset); |
| 134404 | 135350 | |
| 134405 | 135351 | } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ |
| 134406 | 135352 | else { |
| 134407 | -#ifndef SQLITE_OMIT_BTREECOUNT | |
| 134408 | 135353 | Table *pTab; |
| 134409 | 135354 | if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ |
| 134410 | 135355 | /* If isSimpleCount() returns a pointer to a Table structure, then |
| 134411 | 135356 | ** the SQL statement is of the form: |
| 134412 | 135357 | ** |
| @@ -134438,17 +135383,19 @@ | ||
| 134438 | 135383 | ** |
| 134439 | 135384 | ** In practice the KeyInfo structure will not be used. It is only |
| 134440 | 135385 | ** passed to keep OP_OpenRead happy. |
| 134441 | 135386 | */ |
| 134442 | 135387 | if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab); |
| 134443 | - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ | |
| 134444 | - if( pIdx->bUnordered==0 | |
| 134445 | - && pIdx->szIdxRow<pTab->szTabRow | |
| 134446 | - && pIdx->pPartIdxWhere==0 | |
| 134447 | - && (!pBest || pIdx->szIdxRow<pBest->szIdxRow) | |
| 134448 | - ){ | |
| 134449 | - pBest = pIdx; | |
| 135388 | + if( !p->pSrc->a[0].fg.notIndexed ){ | |
| 135389 | + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ | |
| 135390 | + if( pIdx->bUnordered==0 | |
| 135391 | + && pIdx->szIdxRow<pTab->szTabRow | |
| 135392 | + && pIdx->pPartIdxWhere==0 | |
| 135393 | + && (!pBest || pIdx->szIdxRow<pBest->szIdxRow) | |
| 135394 | + ){ | |
| 135395 | + pBest = pIdx; | |
| 135396 | + } | |
| 134450 | 135397 | } |
| 134451 | 135398 | } |
| 134452 | 135399 | if( pBest ){ |
| 134453 | 135400 | iRoot = pBest->tnum; |
| 134454 | 135401 | pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); |
| @@ -134460,13 +135407,11 @@ | ||
| 134460 | 135407 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); |
| 134461 | 135408 | } |
| 134462 | 135409 | sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); |
| 134463 | 135410 | sqlite3VdbeAddOp1(v, OP_Close, iCsr); |
| 134464 | 135411 | explainSimpleCount(pParse, pTab, pBest); |
| 134465 | - }else | |
| 134466 | -#endif /* SQLITE_OMIT_BTREECOUNT */ | |
| 134467 | - { | |
| 135412 | + }else{ | |
| 134468 | 135413 | int regAcc = 0; /* "populate accumulators" flag */ |
| 134469 | 135414 | |
| 134470 | 135415 | /* If there are accumulator registers but no min() or max() functions |
| 134471 | 135416 | ** without FILTER clauses, allocate register regAcc. Register regAcc |
| 134472 | 135417 | ** will contain 0 the first time the inner loop runs, and 1 thereafter. |
| @@ -137542,11 +138487,11 @@ | ||
| 137542 | 138487 | sqlite3SetString(pzErrMsg, db, "output file already exists"); |
| 137543 | 138488 | goto end_of_vacuum; |
| 137544 | 138489 | } |
| 137545 | 138490 | db->mDbFlags |= DBFLAG_VacuumInto; |
| 137546 | 138491 | } |
| 137547 | - nRes = sqlite3BtreeGetOptimalReserve(pMain); | |
| 138492 | + nRes = sqlite3BtreeGetRequestedReserve(pMain); | |
| 137548 | 138493 | |
| 137549 | 138494 | sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); |
| 137550 | 138495 | sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); |
| 137551 | 138496 | sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); |
| 137552 | 138497 | |
| @@ -137686,11 +138631,11 @@ | ||
| 137686 | 138631 | db->mDbFlags = saved_mDbFlags; |
| 137687 | 138632 | db->flags = saved_flags; |
| 137688 | 138633 | db->nChange = saved_nChange; |
| 137689 | 138634 | db->nTotalChange = saved_nTotalChange; |
| 137690 | 138635 | db->mTrace = saved_mTrace; |
| 137691 | - sqlite3BtreeSetPageSize(pMain, -1, -1, 1); | |
| 138636 | + sqlite3BtreeSetPageSize(pMain, -1, 0, 1); | |
| 137692 | 138637 | |
| 137693 | 138638 | /* Currently there is an SQL level transaction open on the vacuum |
| 137694 | 138639 | ** database. No locks are held on any other files (since the main file |
| 137695 | 138640 | ** was committed at the btree level). So it safe to end the transaction |
| 137696 | 138641 | ** by manually setting the autoCommit flag to true and detaching the |
| @@ -156491,14 +157436,15 @@ | ||
| 156491 | 157436 | ** simplify to constants 0 (false) and 1 (true), respectively, |
| 156492 | 157437 | ** regardless of the value of expr1. |
| 156493 | 157438 | */ |
| 156494 | 157439 | sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202); |
| 156495 | 157440 | yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0"); |
| 156496 | - }else if( 0 && yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){ | |
| 157441 | + }else if( yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){ | |
| 156497 | 157442 | Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr; |
| 156498 | 157443 | yymsp[-1].minor.yy242->a[0].pExpr = 0; |
| 156499 | 157444 | sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); |
| 157445 | + pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); | |
| 156500 | 157446 | yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS); |
| 156501 | 157447 | if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); |
| 156502 | 157448 | }else{ |
| 156503 | 157449 | yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); |
| 156504 | 157450 | if( yymsp[-4].minor.yy202 ){ |
| @@ -158376,11 +159322,11 @@ | ||
| 158376 | 159322 | VVA_ONLY( u8 startedWithOom = db->mallocFailed ); |
| 158377 | 159323 | |
| 158378 | 159324 | assert( zSql!=0 ); |
| 158379 | 159325 | mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; |
| 158380 | 159326 | if( db->nVdbeActive==0 ){ |
| 158381 | - db->u1.isInterrupted = 0; | |
| 159327 | + AtomicStore(&db->u1.isInterrupted, 0); | |
| 158382 | 159328 | } |
| 158383 | 159329 | pParse->rc = SQLITE_OK; |
| 158384 | 159330 | pParse->zTail = zSql; |
| 158385 | 159331 | assert( pzErrMsg!=0 ); |
| 158386 | 159332 | #ifdef SQLITE_DEBUG |
| @@ -158421,11 +159367,11 @@ | ||
| 158421 | 159367 | ); |
| 158422 | 159368 | #else |
| 158423 | 159369 | if( tokenType>=TK_SPACE ){ |
| 158424 | 159370 | assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); |
| 158425 | 159371 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 158426 | - if( db->u1.isInterrupted ){ | |
| 159372 | + if( AtomicLoad(&db->u1.isInterrupted) ){ | |
| 158427 | 159373 | pParse->rc = SQLITE_INTERRUPT; |
| 158428 | 159374 | break; |
| 158429 | 159375 | } |
| 158430 | 159376 | if( tokenType==TK_SPACE ){ |
| 158431 | 159377 | zSql += n; |
| @@ -159088,19 +160034,82 @@ | ||
| 159088 | 160034 | |
| 159089 | 160035 | |
| 159090 | 160036 | /************** End of sqliteicu.h *******************************************/ |
| 159091 | 160037 | /************** Continuing where we left off in main.c ***********************/ |
| 159092 | 160038 | #endif |
| 160039 | + | |
| 160040 | +/* | |
| 160041 | +** This is an extension initializer that is a no-op and always | |
| 160042 | +** succeeds, except that it fails if the fault-simulation is set | |
| 160043 | +** to 500. | |
| 160044 | +*/ | |
| 160045 | +static int sqlite3TestExtInit(sqlite3 *db){ | |
| 160046 | + (void)db; | |
| 160047 | + return sqlite3FaultSim(500); | |
| 160048 | +} | |
| 160049 | + | |
| 160050 | + | |
| 160051 | +/* | |
| 160052 | +** Forward declarations of external module initializer functions | |
| 160053 | +** for modules that need them. | |
| 160054 | +*/ | |
| 160055 | +#ifdef SQLITE_ENABLE_FTS1 | |
| 160056 | +SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*); | |
| 160057 | +#endif | |
| 160058 | +#ifdef SQLITE_ENABLE_FTS2 | |
| 160059 | +SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*); | |
| 160060 | +#endif | |
| 160061 | +#ifdef SQLITE_ENABLE_FTS5 | |
| 160062 | +SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); | |
| 160063 | +#endif | |
| 159093 | 160064 | #ifdef SQLITE_ENABLE_JSON1 |
| 159094 | 160065 | SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*); |
| 159095 | 160066 | #endif |
| 159096 | 160067 | #ifdef SQLITE_ENABLE_STMTVTAB |
| 159097 | 160068 | SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*); |
| 159098 | 160069 | #endif |
| 160070 | + | |
| 160071 | +/* | |
| 160072 | +** An array of pointers to extension initializer functions for | |
| 160073 | +** built-in extensions. | |
| 160074 | +*/ | |
| 160075 | +static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { | |
| 160076 | +#ifdef SQLITE_ENABLE_FTS1 | |
| 160077 | + sqlite3Fts1Init, | |
| 160078 | +#endif | |
| 160079 | +#ifdef SQLITE_ENABLE_FTS2 | |
| 160080 | + sqlite3Fts2Init, | |
| 160081 | +#endif | |
| 160082 | +#ifdef SQLITE_ENABLE_FTS3 | |
| 160083 | + sqlite3Fts3Init, | |
| 160084 | +#endif | |
| 159099 | 160085 | #ifdef SQLITE_ENABLE_FTS5 |
| 159100 | -SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); | |
| 160086 | + sqlite3Fts5Init, | |
| 159101 | 160087 | #endif |
| 160088 | +#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) | |
| 160089 | + sqlite3IcuInit, | |
| 160090 | +#endif | |
| 160091 | +#ifdef SQLITE_ENABLE_RTREE | |
| 160092 | + sqlite3RtreeInit, | |
| 160093 | +#endif | |
| 160094 | +#ifdef SQLITE_ENABLE_DBPAGE_VTAB | |
| 160095 | + sqlite3DbpageRegister, | |
| 160096 | +#endif | |
| 160097 | +#ifdef SQLITE_ENABLE_DBSTAT_VTAB | |
| 160098 | + sqlite3DbstatRegister, | |
| 160099 | +#endif | |
| 160100 | + sqlite3TestExtInit, | |
| 160101 | +#ifdef SQLITE_ENABLE_JSON1 | |
| 160102 | + sqlite3Json1Init, | |
| 160103 | +#endif | |
| 160104 | +#ifdef SQLITE_ENABLE_STMTVTAB | |
| 160105 | + sqlite3StmtVtabInit, | |
| 160106 | +#endif | |
| 160107 | +#ifdef SQLITE_ENABLE_BYTECODE_VTAB | |
| 160108 | + sqlite3VdbeBytecodeVtabInit, | |
| 160109 | +#endif | |
| 160110 | +}; | |
| 159102 | 160111 | |
| 159103 | 160112 | #ifndef SQLITE_AMALGAMATION |
| 159104 | 160113 | /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant |
| 159105 | 160114 | ** contains the text of SQLITE_VERSION macro. |
| 159106 | 160115 | */ |
| @@ -160614,12 +161623,11 @@ | ||
| 160614 | 161623 | ** Return non-zero to retry the lock. Return zero to stop trying |
| 160615 | 161624 | ** and cause SQLite to return SQLITE_BUSY. |
| 160616 | 161625 | */ |
| 160617 | 161626 | static int sqliteDefaultBusyCallback( |
| 160618 | 161627 | void *ptr, /* Database connection */ |
| 160619 | - int count, /* Number of times table has been busy */ | |
| 160620 | - sqlite3_file *pFile /* The file on which the lock occurred */ | |
| 161628 | + int count /* Number of times table has been busy */ | |
| 160621 | 161629 | ){ |
| 160622 | 161630 | #if SQLITE_OS_WIN || HAVE_USLEEP |
| 160623 | 161631 | /* This case is for systems that have support for sleeping for fractions of |
| 160624 | 161632 | ** a second. Examples: All windows systems, unix systems with usleep() */ |
| 160625 | 161633 | static const u8 delays[] = |
| @@ -160629,23 +161637,10 @@ | ||
| 160629 | 161637 | # define NDELAY ArraySize(delays) |
| 160630 | 161638 | sqlite3 *db = (sqlite3 *)ptr; |
| 160631 | 161639 | int tmout = db->busyTimeout; |
| 160632 | 161640 | int delay, prior; |
| 160633 | 161641 | |
| 160634 | -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 160635 | - if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){ | |
| 160636 | - if( count ){ | |
| 160637 | - tmout = 0; | |
| 160638 | - sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout); | |
| 160639 | - return 0; | |
| 160640 | - }else{ | |
| 160641 | - return 1; | |
| 160642 | - } | |
| 160643 | - } | |
| 160644 | -#else | |
| 160645 | - UNUSED_PARAMETER(pFile); | |
| 160646 | -#endif | |
| 160647 | 161642 | assert( count>=0 ); |
| 160648 | 161643 | if( count < NDELAY ){ |
| 160649 | 161644 | delay = delays[count]; |
| 160650 | 161645 | prior = totals[count]; |
| 160651 | 161646 | }else{ |
| @@ -160661,11 +161656,10 @@ | ||
| 160661 | 161656 | #else |
| 160662 | 161657 | /* This case for unix systems that lack usleep() support. Sleeping |
| 160663 | 161658 | ** must be done in increments of whole seconds */ |
| 160664 | 161659 | sqlite3 *db = (sqlite3 *)ptr; |
| 160665 | 161660 | int tmout = ((sqlite3 *)ptr)->busyTimeout; |
| 160666 | - UNUSED_PARAMETER(pFile); | |
| 160667 | 161661 | if( (count+1)*1000 > tmout ){ |
| 160668 | 161662 | return 0; |
| 160669 | 161663 | } |
| 160670 | 161664 | sqlite3OsSleep(db->pVfs, 1000000); |
| 160671 | 161665 | return 1; |
| @@ -160679,23 +161673,14 @@ | ||
| 160679 | 161673 | ** lock on VFS file pFile. |
| 160680 | 161674 | ** |
| 160681 | 161675 | ** If this routine returns non-zero, the lock is retried. If it |
| 160682 | 161676 | ** returns 0, the operation aborts with an SQLITE_BUSY error. |
| 160683 | 161677 | */ |
| 160684 | -SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){ | |
| 161678 | +SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){ | |
| 160685 | 161679 | int rc; |
| 160686 | 161680 | if( p->xBusyHandler==0 || p->nBusy<0 ) return 0; |
| 160687 | - if( p->bExtraFileArg ){ | |
| 160688 | - /* Add an extra parameter with the pFile pointer to the end of the | |
| 160689 | - ** callback argument list */ | |
| 160690 | - int (*xTra)(void*,int,sqlite3_file*); | |
| 160691 | - xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler; | |
| 160692 | - rc = xTra(p->pBusyArg, p->nBusy, pFile); | |
| 160693 | - }else{ | |
| 160694 | - /* Legacy style busy handler callback */ | |
| 160695 | - rc = p->xBusyHandler(p->pBusyArg, p->nBusy); | |
| 160696 | - } | |
| 161681 | + rc = p->xBusyHandler(p->pBusyArg, p->nBusy); | |
| 160697 | 161682 | if( rc==0 ){ |
| 160698 | 161683 | p->nBusy = -1; |
| 160699 | 161684 | }else{ |
| 160700 | 161685 | p->nBusy++; |
| 160701 | 161686 | } |
| @@ -160716,11 +161701,10 @@ | ||
| 160716 | 161701 | #endif |
| 160717 | 161702 | sqlite3_mutex_enter(db->mutex); |
| 160718 | 161703 | db->busyHandler.xBusyHandler = xBusy; |
| 160719 | 161704 | db->busyHandler.pBusyArg = pArg; |
| 160720 | 161705 | db->busyHandler.nBusy = 0; |
| 160721 | - db->busyHandler.bExtraFileArg = 0; | |
| 160722 | 161706 | db->busyTimeout = 0; |
| 160723 | 161707 | sqlite3_mutex_leave(db->mutex); |
| 160724 | 161708 | return SQLITE_OK; |
| 160725 | 161709 | } |
| 160726 | 161710 | |
| @@ -160767,11 +161751,10 @@ | ||
| 160767 | 161751 | #endif |
| 160768 | 161752 | if( ms>0 ){ |
| 160769 | 161753 | sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, |
| 160770 | 161754 | (void*)db); |
| 160771 | 161755 | db->busyTimeout = ms; |
| 160772 | - db->busyHandler.bExtraFileArg = 1; | |
| 160773 | 161756 | }else{ |
| 160774 | 161757 | sqlite3_busy_handler(db, 0, 0); |
| 160775 | 161758 | } |
| 160776 | 161759 | return SQLITE_OK; |
| 160777 | 161760 | } |
| @@ -160784,11 +161767,11 @@ | ||
| 160784 | 161767 | if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ |
| 160785 | 161768 | (void)SQLITE_MISUSE_BKPT; |
| 160786 | 161769 | return; |
| 160787 | 161770 | } |
| 160788 | 161771 | #endif |
| 160789 | - db->u1.isInterrupted = 1; | |
| 161772 | + AtomicStore(&db->u1.isInterrupted, 1); | |
| 160790 | 161773 | } |
| 160791 | 161774 | |
| 160792 | 161775 | |
| 160793 | 161776 | /* |
| 160794 | 161777 | ** This function is exactly the same as sqlite3_create_function(), except |
| @@ -161406,11 +162389,11 @@ | ||
| 161406 | 162389 | rc = sqlite3ApiExit(db, rc); |
| 161407 | 162390 | |
| 161408 | 162391 | /* If there are no active statements, clear the interrupt flag at this |
| 161409 | 162392 | ** point. */ |
| 161410 | 162393 | if( db->nVdbeActive==0 ){ |
| 161411 | - db->u1.isInterrupted = 0; | |
| 162394 | + AtomicStore(&db->u1.isInterrupted, 0); | |
| 161412 | 162395 | } |
| 161413 | 162396 | |
| 161414 | 162397 | sqlite3_mutex_leave(db->mutex); |
| 161415 | 162398 | return rc; |
| 161416 | 162399 | #endif |
| @@ -162093,10 +163076,11 @@ | ||
| 162093 | 163076 | sqlite3 *db; /* Store allocated handle here */ |
| 162094 | 163077 | int rc; /* Return code */ |
| 162095 | 163078 | int isThreadsafe; /* True for threadsafe connections */ |
| 162096 | 163079 | char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ |
| 162097 | 163080 | char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ |
| 163081 | + int i; /* Loop counter */ | |
| 162098 | 163082 | |
| 162099 | 163083 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 162100 | 163084 | if( ppDb==0 ) return SQLITE_MISUSE_BKPT; |
| 162101 | 163085 | #endif |
| 162102 | 163086 | *ppDb = 0; |
| @@ -162241,10 +163225,13 @@ | ||
| 162241 | 163225 | | SQLITE_EnableQPSG |
| 162242 | 163226 | #endif |
| 162243 | 163227 | #if defined(SQLITE_DEFAULT_DEFENSIVE) |
| 162244 | 163228 | | SQLITE_Defensive |
| 162245 | 163229 | #endif |
| 163230 | +#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE) | |
| 163231 | + | SQLITE_LegacyAlter | |
| 163232 | +#endif | |
| 162246 | 163233 | ; |
| 162247 | 163234 | sqlite3HashInit(&db->aCollSeq); |
| 162248 | 163235 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 162249 | 163236 | sqlite3HashInit(&db->aModule); |
| 162250 | 163237 | #endif |
| @@ -162333,18 +163320,15 @@ | ||
| 162333 | 163320 | */ |
| 162334 | 163321 | sqlite3Error(db, SQLITE_OK); |
| 162335 | 163322 | sqlite3RegisterPerConnectionBuiltinFunctions(db); |
| 162336 | 163323 | rc = sqlite3_errcode(db); |
| 162337 | 163324 | |
| 162338 | -#ifdef SQLITE_ENABLE_FTS5 | |
| 162339 | - /* Register any built-in FTS5 module before loading the automatic | |
| 162340 | - ** extensions. This allows automatic extensions to register FTS5 | |
| 162341 | - ** tokenizers and auxiliary functions. */ | |
| 162342 | - if( !db->mallocFailed && rc==SQLITE_OK ){ | |
| 162343 | - rc = sqlite3Fts5Init(db); | |
| 162344 | - } | |
| 162345 | -#endif | |
| 163325 | + | |
| 163326 | + /* Load compiled-in extensions */ | |
| 163327 | + for(i=0; rc==SQLITE_OK && i<ArraySize(sqlite3BuiltinExtensions); i++){ | |
| 163328 | + rc = sqlite3BuiltinExtensions[i](db); | |
| 163329 | + } | |
| 162346 | 163330 | |
| 162347 | 163331 | /* Load automatic extensions - extensions that have been registered |
| 162348 | 163332 | ** using the sqlite3_automatic_extension() API. |
| 162349 | 163333 | */ |
| 162350 | 163334 | if( rc==SQLITE_OK ){ |
| @@ -162353,66 +163337,10 @@ | ||
| 162353 | 163337 | if( rc!=SQLITE_OK ){ |
| 162354 | 163338 | goto opendb_out; |
| 162355 | 163339 | } |
| 162356 | 163340 | } |
| 162357 | 163341 | |
| 162358 | -#ifdef SQLITE_ENABLE_FTS1 | |
| 162359 | - if( !db->mallocFailed ){ | |
| 162360 | - extern int sqlite3Fts1Init(sqlite3*); | |
| 162361 | - rc = sqlite3Fts1Init(db); | |
| 162362 | - } | |
| 162363 | -#endif | |
| 162364 | - | |
| 162365 | -#ifdef SQLITE_ENABLE_FTS2 | |
| 162366 | - if( !db->mallocFailed && rc==SQLITE_OK ){ | |
| 162367 | - extern int sqlite3Fts2Init(sqlite3*); | |
| 162368 | - rc = sqlite3Fts2Init(db); | |
| 162369 | - } | |
| 162370 | -#endif | |
| 162371 | - | |
| 162372 | -#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */ | |
| 162373 | - if( !db->mallocFailed && rc==SQLITE_OK ){ | |
| 162374 | - rc = sqlite3Fts3Init(db); | |
| 162375 | - } | |
| 162376 | -#endif | |
| 162377 | - | |
| 162378 | -#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) | |
| 162379 | - if( !db->mallocFailed && rc==SQLITE_OK ){ | |
| 162380 | - rc = sqlite3IcuInit(db); | |
| 162381 | - } | |
| 162382 | -#endif | |
| 162383 | - | |
| 162384 | -#ifdef SQLITE_ENABLE_RTREE | |
| 162385 | - if( !db->mallocFailed && rc==SQLITE_OK){ | |
| 162386 | - rc = sqlite3RtreeInit(db); | |
| 162387 | - } | |
| 162388 | -#endif | |
| 162389 | - | |
| 162390 | -#ifdef SQLITE_ENABLE_DBPAGE_VTAB | |
| 162391 | - if( !db->mallocFailed && rc==SQLITE_OK){ | |
| 162392 | - rc = sqlite3DbpageRegister(db); | |
| 162393 | - } | |
| 162394 | -#endif | |
| 162395 | - | |
| 162396 | -#ifdef SQLITE_ENABLE_DBSTAT_VTAB | |
| 162397 | - if( !db->mallocFailed && rc==SQLITE_OK){ | |
| 162398 | - rc = sqlite3DbstatRegister(db); | |
| 162399 | - } | |
| 162400 | -#endif | |
| 162401 | - | |
| 162402 | -#ifdef SQLITE_ENABLE_JSON1 | |
| 162403 | - if( !db->mallocFailed && rc==SQLITE_OK){ | |
| 162404 | - rc = sqlite3Json1Init(db); | |
| 162405 | - } | |
| 162406 | -#endif | |
| 162407 | - | |
| 162408 | -#ifdef SQLITE_ENABLE_STMTVTAB | |
| 162409 | - if( !db->mallocFailed && rc==SQLITE_OK){ | |
| 162410 | - rc = sqlite3StmtVtabInit(db); | |
| 162411 | - } | |
| 162412 | -#endif | |
| 162413 | - | |
| 162414 | 163342 | #ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS |
| 162415 | 163343 | /* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time |
| 162416 | 163344 | ** option gives access to internal functions by default. |
| 162417 | 163345 | ** Testing use only!!! */ |
| 162418 | 163346 | db->mDbFlags |= DBFLAG_InternalFunc; |
| @@ -162893,10 +163821,17 @@ | ||
| 162893 | 163821 | }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ |
| 162894 | 163822 | *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); |
| 162895 | 163823 | rc = SQLITE_OK; |
| 162896 | 163824 | }else if( op==SQLITE_FCNTL_DATA_VERSION ){ |
| 162897 | 163825 | *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager); |
| 163826 | + rc = SQLITE_OK; | |
| 163827 | + }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){ | |
| 163828 | + int iNew = *(int*)pArg; | |
| 163829 | + *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree); | |
| 163830 | + if( iNew>=0 && iNew<=255 ){ | |
| 163831 | + sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); | |
| 163832 | + } | |
| 162898 | 163833 | rc = SQLITE_OK; |
| 162899 | 163834 | }else{ |
| 162900 | 163835 | rc = sqlite3OsFileControl(fd, op, pArg); |
| 162901 | 163836 | } |
| 162902 | 163837 | sqlite3BtreeLeave(pBtree); |
| @@ -163110,24 +164045,10 @@ | ||
| 163110 | 164045 | case SQLITE_TESTCTRL_BYTEORDER: { |
| 163111 | 164046 | rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN; |
| 163112 | 164047 | break; |
| 163113 | 164048 | } |
| 163114 | 164049 | |
| 163115 | - /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N) | |
| 163116 | - ** | |
| 163117 | - ** Set the nReserve size to N for the main database on the database | |
| 163118 | - ** connection db. | |
| 163119 | - */ | |
| 163120 | - case SQLITE_TESTCTRL_RESERVE: { | |
| 163121 | - sqlite3 *db = va_arg(ap, sqlite3*); | |
| 163122 | - int x = va_arg(ap,int); | |
| 163123 | - sqlite3_mutex_enter(db->mutex); | |
| 163124 | - sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0); | |
| 163125 | - sqlite3_mutex_leave(db->mutex); | |
| 163126 | - break; | |
| 163127 | - } | |
| 163128 | - | |
| 163129 | 164050 | /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N) |
| 163130 | 164051 | ** |
| 163131 | 164052 | ** Enable or disable various optimizations for testing purposes. The |
| 163132 | 164053 | ** argument N is a bitmask of optimizations to be disabled. For normal |
| 163133 | 164054 | ** operation N should be 0. The idea is that a test program (like the |
| @@ -165185,10 +166106,11 @@ | ||
| 165185 | 166106 | SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); |
| 165186 | 166107 | SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); |
| 165187 | 166108 | SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *); |
| 165188 | 166109 | SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*); |
| 165189 | 166110 | SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc); |
| 166111 | +SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut); | |
| 165190 | 166112 | |
| 165191 | 166113 | /* fts3_tokenizer.c */ |
| 165192 | 166114 | SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); |
| 165193 | 166115 | SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); |
| 165194 | 166116 | SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, |
| @@ -165916,10 +166838,26 @@ | ||
| 165916 | 166838 | fts3Appendf(pRc, &zRet, ", ?"); |
| 165917 | 166839 | } |
| 165918 | 166840 | sqlite3_free(zFree); |
| 165919 | 166841 | return zRet; |
| 165920 | 166842 | } |
| 166843 | + | |
| 166844 | +/* | |
| 166845 | +** Buffer z contains a positive integer value encoded as utf-8 text. | |
| 166846 | +** Decode this value and store it in *pnOut, returning the number of bytes | |
| 166847 | +** consumed. If an overflow error occurs return a negative value. | |
| 166848 | +*/ | |
| 166849 | +SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut){ | |
| 166850 | + u64 iVal = 0; | |
| 166851 | + int i; | |
| 166852 | + for(i=0; z[i]>='0' && z[i]<='9'; i++){ | |
| 166853 | + iVal = iVal*10 + (z[i] - '0'); | |
| 166854 | + if( iVal>0x7FFFFFFF ) return -1; | |
| 166855 | + } | |
| 166856 | + *pnOut = (int)iVal; | |
| 166857 | + return i; | |
| 166858 | +} | |
| 165921 | 166859 | |
| 165922 | 166860 | /* |
| 165923 | 166861 | ** This function interprets the string at (*pp) as a non-negative integer |
| 165924 | 166862 | ** value. It reads the integer and sets *pnOut to the value read, then |
| 165925 | 166863 | ** sets *pp to point to the byte immediately following the last byte of |
| @@ -165932,23 +166870,21 @@ | ||
| 165932 | 166870 | ** |
| 165933 | 166871 | ** This function is used when parsing the "prefix=" FTS4 parameter. |
| 165934 | 166872 | */ |
| 165935 | 166873 | static int fts3GobbleInt(const char **pp, int *pnOut){ |
| 165936 | 166874 | const int MAX_NPREFIX = 10000000; |
| 165937 | - const char *p; /* Iterator pointer */ | |
| 165938 | 166875 | int nInt = 0; /* Output value */ |
| 165939 | - | |
| 165940 | - for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ | |
| 165941 | - nInt = nInt * 10 + (p[0] - '0'); | |
| 165942 | - if( nInt>MAX_NPREFIX ){ | |
| 165943 | - nInt = 0; | |
| 165944 | - break; | |
| 165945 | - } | |
| 165946 | - } | |
| 165947 | - if( p==*pp ) return SQLITE_ERROR; | |
| 166876 | + int nByte; | |
| 166877 | + nByte = sqlite3Fts3ReadInt(*pp, &nInt); | |
| 166878 | + if( nInt>MAX_NPREFIX ){ | |
| 166879 | + nInt = 0; | |
| 166880 | + } | |
| 166881 | + if( nByte==0 ){ | |
| 166882 | + return SQLITE_ERROR; | |
| 166883 | + } | |
| 165948 | 166884 | *pnOut = nInt; |
| 165949 | - *pp = p; | |
| 166885 | + *pp += nByte; | |
| 165950 | 166886 | return SQLITE_OK; |
| 165951 | 166887 | } |
| 165952 | 166888 | |
| 165953 | 166889 | /* |
| 165954 | 166890 | ** This function is called to allocate an array of Fts3Index structures |
| @@ -167126,11 +168062,13 @@ | ||
| 167126 | 168062 | static void fts3ReadNextPos( |
| 167127 | 168063 | char **pp, /* IN/OUT: Pointer into position-list buffer */ |
| 167128 | 168064 | sqlite3_int64 *pi /* IN/OUT: Value read from position-list */ |
| 167129 | 168065 | ){ |
| 167130 | 168066 | if( (**pp)&0xFE ){ |
| 167131 | - fts3GetDeltaVarint(pp, pi); | |
| 168067 | + int iVal; | |
| 168068 | + *pp += fts3GetVarint32((*pp), &iVal); | |
| 168069 | + *pi += iVal; | |
| 167132 | 168070 | *pi -= 2; |
| 167133 | 168071 | }else{ |
| 167134 | 168072 | *pi = POSITION_LIST_END; |
| 167135 | 168073 | } |
| 167136 | 168074 | } |
| @@ -172026,14 +172964,11 @@ | ||
| 172026 | 172964 | |
| 172027 | 172965 | /* If this is a "NEAR" keyword, check for an explicit nearness. */ |
| 172028 | 172966 | if( pKey->eType==FTSQUERY_NEAR ){ |
| 172029 | 172967 | assert( nKey==4 ); |
| 172030 | 172968 | if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){ |
| 172031 | - nNear = 0; | |
| 172032 | - for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){ | |
| 172033 | - nNear = nNear * 10 + (zInput[nKey] - '0'); | |
| 172034 | - } | |
| 172969 | + nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear); | |
| 172035 | 172970 | } |
| 172036 | 172971 | } |
| 172037 | 172972 | |
| 172038 | 172973 | /* At this point this is probably a keyword. But for that to be true, |
| 172039 | 172974 | ** the next byte must contain either whitespace, an open or close |
| @@ -176558,10 +177493,11 @@ | ||
| 176558 | 177493 | ** b-tree node. And that the final byte of the doclist is 0x00. If either |
| 176559 | 177494 | ** of these statements is untrue, then the data structure is corrupt. |
| 176560 | 177495 | */ |
| 176561 | 177496 | if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode) |
| 176562 | 177497 | || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) |
| 177498 | + || pReader->nDoclist==0 | |
| 176563 | 177499 | ){ |
| 176564 | 177500 | return FTS_CORRUPT_VTAB; |
| 176565 | 177501 | } |
| 176566 | 177502 | return SQLITE_OK; |
| 176567 | 177503 | } |
| @@ -178211,25 +179147,25 @@ | ||
| 178211 | 179147 | ){ |
| 178212 | 179148 | const unsigned char *zText = sqlite3_column_text(pStmt, iCol); |
| 178213 | 179149 | if( zText ){ |
| 178214 | 179150 | int i; |
| 178215 | 179151 | int iMul = 1; |
| 178216 | - i64 iVal = 0; | |
| 179152 | + u64 iVal = 0; | |
| 178217 | 179153 | for(i=0; zText[i]>='0' && zText[i]<='9'; i++){ |
| 178218 | 179154 | iVal = iVal*10 + (zText[i] - '0'); |
| 178219 | 179155 | } |
| 178220 | - *piEndBlock = iVal; | |
| 179156 | + *piEndBlock = (i64)iVal; | |
| 178221 | 179157 | while( zText[i]==' ' ) i++; |
| 178222 | 179158 | iVal = 0; |
| 178223 | 179159 | if( zText[i]=='-' ){ |
| 178224 | 179160 | i++; |
| 178225 | 179161 | iMul = -1; |
| 178226 | 179162 | } |
| 178227 | 179163 | for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){ |
| 178228 | 179164 | iVal = iVal*10 + (zText[i] - '0'); |
| 178229 | 179165 | } |
| 178230 | - *pnByte = (iVal * (i64)iMul); | |
| 179166 | + *pnByte = ((i64)iVal * (i64)iMul); | |
| 178231 | 179167 | } |
| 178232 | 179168 | } |
| 178233 | 179169 | |
| 178234 | 179170 | |
| 178235 | 179171 | /* |
| @@ -223728,11 +224664,11 @@ | ||
| 223728 | 224664 | int nArg, /* Number of args */ |
| 223729 | 224665 | sqlite3_value **apUnused /* Function arguments */ |
| 223730 | 224666 | ){ |
| 223731 | 224667 | assert( nArg==0 ); |
| 223732 | 224668 | UNUSED_PARAM2(nArg, apUnused); |
| 223733 | - sqlite3_result_text(pCtx, "fts5: 2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525", -1, SQLITE_TRANSIENT); | |
| 224669 | + sqlite3_result_text(pCtx, "fts5: 2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff", -1, SQLITE_TRANSIENT); | |
| 223734 | 224670 | } |
| 223735 | 224671 | |
| 223736 | 224672 | /* |
| 223737 | 224673 | ** Return true if zName is the extension on one of the shadow tables used |
| 223738 | 224674 | ** by this module. |
| @@ -228379,11 +229315,12 @@ | ||
| 228379 | 229315 | } |
| 228380 | 229316 | case STMT_COLUMN_BUSY: { |
| 228381 | 229317 | sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt)); |
| 228382 | 229318 | break; |
| 228383 | 229319 | } |
| 228384 | - case STMT_COLUMN_MEM: { | |
| 229320 | + default: { | |
| 229321 | + assert( i==STMT_COLUMN_MEM ); | |
| 228385 | 229322 | i = SQLITE_STMTSTATUS_MEMUSED + |
| 228386 | 229323 | STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; |
| 228387 | 229324 | /* Fall thru */ |
| 228388 | 229325 | } |
| 228389 | 229326 | case STMT_COLUMN_NSCAN: |
| @@ -228510,12 +229447,12 @@ | ||
| 228510 | 229447 | } |
| 228511 | 229448 | #endif /* SQLITE_CORE */ |
| 228512 | 229449 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 228513 | 229450 | |
| 228514 | 229451 | /************** End of stmt.c ************************************************/ |
| 228515 | -#if __LINE__!=228515 | |
| 229452 | +#if __LINE__!=229452 | |
| 228516 | 229453 | #undef SQLITE_SOURCE_ID |
| 228517 | -#define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84alt2" | |
| 229454 | +#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f96alt2" | |
| 228518 | 229455 | #endif |
| 228519 | 229456 | /* Return the source-id for this library */ |
| 228520 | 229457 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 228521 | 229458 | /************************** End of sqlite3.c ******************************/ |
| 228522 | 229459 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -216,10 +216,13 @@ | |
| 216 | "ENABLE_ATOMIC_WRITE", |
| 217 | #endif |
| 218 | #if SQLITE_ENABLE_BATCH_ATOMIC_WRITE |
| 219 | "ENABLE_BATCH_ATOMIC_WRITE", |
| 220 | #endif |
| 221 | #if SQLITE_ENABLE_CEROD |
| 222 | "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), |
| 223 | #endif |
| 224 | #if SQLITE_ENABLE_COLUMN_METADATA |
| 225 | "ENABLE_COLUMN_METADATA", |
| @@ -534,13 +537,10 @@ | |
| 534 | "OMIT_BETWEEN_OPTIMIZATION", |
| 535 | #endif |
| 536 | #if SQLITE_OMIT_BLOB_LITERAL |
| 537 | "OMIT_BLOB_LITERAL", |
| 538 | #endif |
| 539 | #if SQLITE_OMIT_BTREECOUNT |
| 540 | "OMIT_BTREECOUNT", |
| 541 | #endif |
| 542 | #if SQLITE_OMIT_CAST |
| 543 | "OMIT_CAST", |
| 544 | #endif |
| 545 | #if SQLITE_OMIT_CHECK |
| 546 | "OMIT_CHECK", |
| @@ -1162,11 +1162,11 @@ | |
| 1162 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | */ |
| 1165 | #define SQLITE_VERSION "3.32.0" |
| 1166 | #define SQLITE_VERSION_NUMBER 3032000 |
| 1167 | #define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525" |
| 1168 | |
| 1169 | /* |
| 1170 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | ** |
| @@ -1336,30 +1336,26 @@ | |
| 1336 | ** for the [sqlite3] object. |
| 1337 | ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if |
| 1338 | ** the [sqlite3] object is successfully destroyed and all associated |
| 1339 | ** resources are deallocated. |
| 1340 | ** |
| 1341 | ** ^If the database connection is associated with unfinalized prepared |
| 1342 | ** statements or unfinished sqlite3_backup objects then sqlite3_close() |
| 1343 | ** will leave the database connection open and return [SQLITE_BUSY]. |
| 1344 | ** ^If sqlite3_close_v2() is called with unfinalized prepared statements |
| 1345 | ** and/or unfinished sqlite3_backups, then the database connection becomes |
| 1346 | ** an unusable "zombie" which will automatically be deallocated when the |
| 1347 | ** last prepared statement is finalized or the last sqlite3_backup is |
| 1348 | ** finished. The sqlite3_close_v2() interface is intended for use with |
| 1349 | ** host languages that are garbage collected, and where the order in which |
| 1350 | ** destructors are called is arbitrary. |
| 1351 | ** |
| 1352 | ** Applications should [sqlite3_finalize | finalize] all [prepared statements], |
| 1353 | ** [sqlite3_blob_close | close] all [BLOB handles], and |
| 1354 | ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated |
| 1355 | ** with the [sqlite3] object prior to attempting to close the object. ^If |
| 1356 | ** sqlite3_close_v2() is called on a [database connection] that still has |
| 1357 | ** outstanding [prepared statements], [BLOB handles], and/or |
| 1358 | ** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation |
| 1359 | ** of resources is deferred until all [prepared statements], [BLOB handles], |
| 1360 | ** and [sqlite3_backup] objects are also destroyed. |
| 1361 | ** |
| 1362 | ** ^If an [sqlite3] object is destroyed while a transaction is open, |
| 1363 | ** the transaction is automatically rolled back. |
| 1364 | ** |
| 1365 | ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] |
| @@ -1544,22 +1540,25 @@ | |
| 1544 | #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) |
| 1545 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1546 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1547 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1548 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1549 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1550 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1551 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1552 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1553 | #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) |
| 1554 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| 1555 | #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) |
| 1556 | #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) |
| 1557 | #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ |
| 1558 | #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8)) |
| 1559 | #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) |
| 1560 | #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) |
| 1561 | #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) |
| 1562 | #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) |
| 1563 | #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) |
| 1564 | #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) |
| 1565 | #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) |
| @@ -2124,14 +2123,16 @@ | |
| 2124 | ** so that all subsequent write operations are independent. |
| 2125 | ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without |
| 2126 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. |
| 2127 | ** |
| 2128 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] |
| 2129 | ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain |
| 2130 | ** a file lock using the xLock or xShmLock methods of the VFS to wait |
| 2131 | ** for up to M milliseconds before failing, where M is the single |
| 2132 | ** unsigned integer parameter. |
| 2133 | ** |
| 2134 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 2135 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 2136 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 2137 | ** The "data version" for the pager is written into the pointer. The |
| @@ -2148,10 +2149,15 @@ | |
| 2148 | ** a single attached database that occur due to other database connections, |
| 2149 | ** but omits changes implemented by the database connection on which it is |
| 2150 | ** called. This file control is the only mechanism to detect changes that |
| 2151 | ** happen either internally or externally and that are associated with |
| 2152 | ** a particular attached database. |
| 2153 | ** |
| 2154 | ** <li>[[SQLITE_FCNTL_CKPT_DONE]] |
| 2155 | ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint |
| 2156 | ** in wal mode after the client has finished copying pages from the wal |
| 2157 | ** file to the database file, but before the *-shm file is updated to |
| @@ -2192,10 +2198,12 @@ | |
| 2192 | #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 |
| 2193 | #define SQLITE_FCNTL_LOCK_TIMEOUT 34 |
| 2194 | #define SQLITE_FCNTL_DATA_VERSION 35 |
| 2195 | #define SQLITE_FCNTL_SIZE_LIMIT 36 |
| 2196 | #define SQLITE_FCNTL_CKPT_DONE 37 |
| 2197 | |
| 2198 | /* deprecated names */ |
| 2199 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 2200 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 2201 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -4570,12 +4578,23 @@ | |
| 4570 | ** |
| 4571 | ** These are utility routines, useful to [VFS|custom VFS implementations], |
| 4572 | ** that check if a database file was a URI that contained a specific query |
| 4573 | ** parameter, and if so obtains the value of that query parameter. |
| 4574 | ** |
| 4575 | ** If F is the database filename pointer passed into the xOpen() method of |
| 4576 | ** a VFS implementation or it is the return value of [sqlite3_db_filename()] |
| 4577 | ** and if P is the name of the query parameter, then |
| 4578 | ** sqlite3_uri_parameter(F,P) returns the value of the P |
| 4579 | ** parameter if it exists or a NULL pointer if P does not appear as a |
| 4580 | ** query parameter on F. If P is a query parameter of F and it |
| 4581 | ** has no explicit value, then sqlite3_uri_parameter(F,P) returns |
| @@ -4654,10 +4673,29 @@ | |
| 4654 | */ |
| 4655 | SQLITE_API const char *sqlite3_filename_database(const char*); |
| 4656 | SQLITE_API const char *sqlite3_filename_journal(const char*); |
| 4657 | SQLITE_API const char *sqlite3_filename_wal(const char*); |
| 4658 | |
| 4659 | /* |
| 4660 | ** CAPI3REF: Create and Destroy VFS Filenames |
| 4661 | ** |
| 4662 | ** These interfces are provided for use by [VFS shim] implementations and |
| 4663 | ** are not useful outside of that context. |
| @@ -4688,11 +4726,11 @@ | |
| 4688 | ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may |
| 4689 | ** be NULL pointers, though they can be empty strings. |
| 4690 | ** |
| 4691 | ** The sqlite3_free_filename(Y) routine releases a memory allocation |
| 4692 | ** previously obtained from sqlite3_create_filename(). Invoking |
| 4693 | ** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op. |
| 4694 | ** |
| 4695 | ** If the Y parameter to sqlite3_free_filename(Y) is anything other |
| 4696 | ** than a NULL pointer or a pointer previously acquired from |
| 4697 | ** sqlite3_create_filename(), then bad things such as heap |
| 4698 | ** corruption or segfaults may occur. The value Y should be |
| @@ -5295,10 +5333,28 @@ | |
| 5295 | ** |
| 5296 | ** ^The third argument is the value to bind to the parameter. |
| 5297 | ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| 5298 | ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter |
| 5299 | ** is ignored and the end result is the same as sqlite3_bind_null(). |
| 5300 | ** |
| 5301 | ** ^(In those routines that have a fourth argument, its value is the |
| 5302 | ** number of bytes in the parameter. To be clear: the value is the |
| 5303 | ** number of <u>bytes</u> in the value, not the number of characters.)^ |
| 5304 | ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| @@ -5308,11 +5364,11 @@ | |
| 5308 | ** the behavior is undefined. |
| 5309 | ** If a non-negative fourth parameter is provided to sqlite3_bind_text() |
| 5310 | ** or sqlite3_bind_text16() or sqlite3_bind_text64() then |
| 5311 | ** that parameter must be the byte offset |
| 5312 | ** where the NUL terminator would occur assuming the string were NUL |
| 5313 | ** terminated. If any NUL characters occur at byte offsets less than |
| 5314 | ** the value of the fourth parameter then the resulting string value will |
| 5315 | ** contain embedded NULs. The result of expressions involving strings |
| 5316 | ** with embedded NULs is undefined. |
| 5317 | ** |
| 5318 | ** ^The fifth argument to the BLOB and string binding interfaces |
| @@ -6633,12 +6689,13 @@ | |
| 6633 | ** cause the implemented SQL function to throw an exception. |
| 6634 | ** ^SQLite uses the string pointed to by the |
| 6635 | ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() |
| 6636 | ** as the text of an error message. ^SQLite interprets the error |
| 6637 | ** message string from sqlite3_result_error() as UTF-8. ^SQLite |
| 6638 | ** interprets the string from sqlite3_result_error16() as UTF-16 in native |
| 6639 | ** byte order. ^If the third parameter to sqlite3_result_error() |
| 6640 | ** or sqlite3_result_error16() is negative then SQLite takes as the error |
| 6641 | ** message all text up through the first zero character. |
| 6642 | ** ^If the third parameter to sqlite3_result_error() or |
| 6643 | ** sqlite3_result_error16() is non-negative then SQLite takes that many |
| 6644 | ** bytes (not characters) from the 2nd parameter as the error message. |
| @@ -6701,10 +6758,29 @@ | |
| 6701 | ** when it has finished using that result. |
| 6702 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces |
| 6703 | ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT |
| 6704 | ** then SQLite makes a copy of the result into space obtained |
| 6705 | ** from [sqlite3_malloc()] before it returns. |
| 6706 | ** |
| 6707 | ** ^The sqlite3_result_value() interface sets the result of |
| 6708 | ** the application-defined function to be a copy of the |
| 6709 | ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The |
| 6710 | ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] |
| @@ -8649,11 +8725,11 @@ | |
| 8649 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8650 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8651 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8652 | #define SQLITE_TESTCTRL_ASSERT 12 |
| 8653 | #define SQLITE_TESTCTRL_ALWAYS 13 |
| 8654 | #define SQLITE_TESTCTRL_RESERVE 14 |
| 8655 | #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 |
| 8656 | #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ |
| 8657 | #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ |
| 8658 | #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 |
| 8659 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 |
| @@ -13369,10 +13445,25 @@ | |
| 13369 | #pragma warn -aus /* Assigned value is never used */ |
| 13370 | #pragma warn -csu /* Comparing signed and unsigned */ |
| 13371 | #pragma warn -spa /* Suspicious pointer arithmetic */ |
| 13372 | #endif |
| 13373 | |
| 13374 | /* |
| 13375 | ** Include standard header files as necessary |
| 13376 | */ |
| 13377 | #ifdef HAVE_STDINT_H |
| 13378 | #include <stdint.h> |
| @@ -14446,11 +14537,10 @@ | |
| 14446 | typedef struct BusyHandler BusyHandler; |
| 14447 | struct BusyHandler { |
| 14448 | int (*xBusyHandler)(void *,int); /* The busy callback */ |
| 14449 | void *pBusyArg; /* First arg to busy callback */ |
| 14450 | int nBusy; /* Incremented with each busy call */ |
| 14451 | u8 bExtraFileArg; /* Include sqlite3_file as callback arg */ |
| 14452 | }; |
| 14453 | |
| 14454 | /* |
| 14455 | ** Name of the master database table. The master database table |
| 14456 | ** is a special table that holds the names and attributes of all |
| @@ -14704,11 +14794,11 @@ | |
| 14704 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); |
| 14705 | SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); |
| 14706 | SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); |
| 14707 | SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); |
| 14708 | SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); |
| 14709 | SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*); |
| 14710 | SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); |
| 14711 | SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); |
| 14712 | SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); |
| 14713 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*); |
| 14714 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); |
| @@ -14966,13 +15056,11 @@ | |
| 14966 | #ifndef NDEBUG |
| 14967 | SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); |
| 14968 | #endif |
| 14969 | SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*); |
| 14970 | |
| 14971 | #ifndef SQLITE_OMIT_BTREECOUNT |
| 14972 | SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*); |
| 14973 | #endif |
| 14974 | |
| 14975 | #ifdef SQLITE_TEST |
| 14976 | SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); |
| 14977 | SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); |
| 14978 | #endif |
| @@ -15543,10 +15631,13 @@ | |
| 15543 | |
| 15544 | SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); |
| 15545 | SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*); |
| 15546 | |
| 15547 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 15548 | |
| 15549 | /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on |
| 15550 | ** each VDBE opcode. |
| 15551 | ** |
| 15552 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| @@ -15828,17 +15919,25 @@ | |
| 15828 | SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); |
| 15829 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); |
| 15830 | SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); |
| 15831 | SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); |
| 15832 | # ifdef SQLITE_ENABLE_SNAPSHOT |
| 15833 | SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); |
| 15834 | SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); |
| 15835 | SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); |
| 15836 | SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); |
| 15837 | SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); |
| 15838 | # endif |
| 15839 | #endif |
| 15840 | |
| 15841 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 15842 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); |
| 15843 | #endif |
| 15844 | |
| @@ -15861,15 +15960,10 @@ | |
| 15861 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 15862 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 15863 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 15864 | SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); |
| 15865 | SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); |
| 15866 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 15867 | SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager); |
| 15868 | #else |
| 15869 | # define sqlite3PagerResetLockTimeout(X) |
| 15870 | #endif |
| 15871 | |
| 15872 | /* Functions used to truncate the database file. */ |
| 15873 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 15874 | |
| 15875 | SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); |
| @@ -16799,10 +16893,11 @@ | |
| 16799 | Hash aFunc; /* Hash table of connection functions */ |
| 16800 | Hash aCollSeq; /* All collating sequences */ |
| 16801 | BusyHandler busyHandler; /* Busy callback */ |
| 16802 | Db aDbStatic[2]; /* Static space for the 2 default backends */ |
| 16803 | Savepoint *pSavepoint; /* List of active savepoints */ |
| 16804 | int busyTimeout; /* Busy handler timeout, in msec */ |
| 16805 | int nSavepoint; /* Number of non-transaction savepoints */ |
| 16806 | int nStatement; /* Number of nested statement-transactions */ |
| 16807 | i64 nDeferredCons; /* Net deferred constraints this transaction. */ |
| 16808 | i64 nDeferredImmCons; /* Net deferred immediate constraints */ |
| @@ -17209,10 +17304,11 @@ | |
| 17209 | Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */ |
| 17210 | char *zColl; /* Collating sequence. If NULL, use the default */ |
| 17211 | u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ |
| 17212 | char affinity; /* One of the SQLITE_AFF_... values */ |
| 17213 | u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ |
| 17214 | u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ |
| 17215 | }; |
| 17216 | |
| 17217 | /* Allowed values for Column.colFlags: |
| 17218 | */ |
| @@ -19827,10 +19923,11 @@ | |
| 19827 | const char*, |
| 19828 | const char*, |
| 19829 | const char* |
| 19830 | ); |
| 19831 | SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*); |
| 19832 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 19833 | SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*); |
| 19834 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 19835 | SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); |
| 19836 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| @@ -19842,11 +19939,11 @@ | |
| 19842 | SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*); |
| 19843 | SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*); |
| 19844 | SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); |
| 19845 | SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*); |
| 19846 | SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*); |
| 19847 | SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*); |
| 19848 | SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*); |
| 19849 | SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *); |
| 19850 | SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB); |
| 19851 | SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*); |
| 19852 | SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*); |
| @@ -20578,11 +20675,12 @@ | |
| 20578 | /* |
| 20579 | ** VDBE_DISPLAY_P4 is true or false depending on whether or not the |
| 20580 | ** "explain" P4 display logic is enabled. |
| 20581 | */ |
| 20582 | #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ |
| 20583 | || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) |
| 20584 | # define VDBE_DISPLAY_P4 1 |
| 20585 | #else |
| 20586 | # define VDBE_DISPLAY_P4 0 |
| 20587 | #endif |
| 20588 | |
| @@ -21043,11 +21141,18 @@ | |
| 21043 | |
| 21044 | int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); |
| 21045 | SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*); |
| 21046 | SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*); |
| 21047 | SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*); |
| 21048 | #ifndef SQLITE_OMIT_EXPLAIN |
| 21049 | SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*); |
| 21050 | #endif |
| 21051 | SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*); |
| 21052 | SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int); |
| 21053 | SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); |
| @@ -21085,11 +21190,11 @@ | |
| 21085 | SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); |
| 21086 | SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); |
| 21087 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 21088 | SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); |
| 21089 | #endif |
| 21090 | #ifndef SQLITE_OMIT_EXPLAIN |
| 21091 | SQLITE_PRIVATE const char *sqlite3OpcodeName(int); |
| 21092 | #endif |
| 21093 | SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); |
| 21094 | SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n); |
| 21095 | SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); |
| @@ -22165,16 +22270,16 @@ | |
| 22165 | u8 nName; /* Length of th name */ |
| 22166 | char *zName; /* Name of the transformation */ |
| 22167 | double rLimit; /* Maximum NNN value for this transform */ |
| 22168 | double rXform; /* Constant used for this transform */ |
| 22169 | } aXformType[] = { |
| 22170 | { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) }, |
| 22171 | { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) }, |
| 22172 | { 0, 4, "hour", 128963628.0, 86400000.0/24.0 }, |
| 22173 | { 0, 3, "day", 5373485.0, 86400000.0 }, |
| 22174 | { 1, 5, "month", 176546.0, 30.0*86400000.0 }, |
| 22175 | { 2, 4, "year", 14713.0, 365.0*86400000.0 }, |
| 22176 | }; |
| 22177 | |
| 22178 | /* |
| 22179 | ** Process a modifier to a date-time stamp. The modifiers are |
| 22180 | ** as follows: |
| @@ -27258,11 +27363,11 @@ | |
| 27258 | if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){ |
| 27259 | n = mem0.hardLimit; |
| 27260 | } |
| 27261 | mem0.alarmThreshold = n; |
| 27262 | nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27263 | mem0.nearlyFull = (n>0 && n<=nUsed); |
| 27264 | sqlite3_mutex_leave(mem0.mutex); |
| 27265 | excess = sqlite3_memory_used() - n; |
| 27266 | if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); |
| 27267 | return priorLimit; |
| 27268 | } |
| @@ -27326,11 +27431,11 @@ | |
| 27326 | ** Return true if the heap is currently under memory pressure - in other |
| 27327 | ** words if the amount of heap used is close to the limit set by |
| 27328 | ** sqlite3_soft_heap_limit(). |
| 27329 | */ |
| 27330 | SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){ |
| 27331 | return mem0.nearlyFull; |
| 27332 | } |
| 27333 | |
| 27334 | /* |
| 27335 | ** Deinitialize the memory allocation subsystem. |
| 27336 | */ |
| @@ -27390,21 +27495,21 @@ | |
| 27390 | |
| 27391 | sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); |
| 27392 | if( mem0.alarmThreshold>0 ){ |
| 27393 | sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27394 | if( nUsed >= mem0.alarmThreshold - nFull ){ |
| 27395 | mem0.nearlyFull = 1; |
| 27396 | sqlite3MallocAlarm(nFull); |
| 27397 | if( mem0.hardLimit ){ |
| 27398 | nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27399 | if( nUsed >= mem0.hardLimit - nFull ){ |
| 27400 | *pp = 0; |
| 27401 | return; |
| 27402 | } |
| 27403 | } |
| 27404 | }else{ |
| 27405 | mem0.nearlyFull = 0; |
| 27406 | } |
| 27407 | } |
| 27408 | p = sqlite3GlobalConfig.m.xMalloc(nFull); |
| 27409 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 27410 | if( p==0 && mem0.alarmThreshold>0 ){ |
| @@ -27629,14 +27734,16 @@ | |
| 27629 | if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= |
| 27630 | mem0.alarmThreshold-nDiff ){ |
| 27631 | sqlite3MallocAlarm(nDiff); |
| 27632 | } |
| 27633 | pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
| 27634 | if( pNew==0 && mem0.alarmThreshold>0 ){ |
| 27635 | sqlite3MallocAlarm((int)nBytes); |
| 27636 | pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
| 27637 | } |
| 27638 | if( pNew ){ |
| 27639 | nNew = sqlite3MallocSize(pNew); |
| 27640 | sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); |
| 27641 | } |
| 27642 | sqlite3_mutex_leave(mem0.mutex); |
| @@ -27907,11 +28014,11 @@ | |
| 27907 | */ |
| 27908 | SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ |
| 27909 | if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ |
| 27910 | db->mallocFailed = 1; |
| 27911 | if( db->nVdbeExec>0 ){ |
| 27912 | db->u1.isInterrupted = 1; |
| 27913 | } |
| 27914 | DisableLookaside; |
| 27915 | if( db->pParse ){ |
| 27916 | db->pParse->rc = SQLITE_NOMEM_BKPT; |
| 27917 | } |
| @@ -27926,11 +28033,11 @@ | |
| 27926 | ** VDBEs. |
| 27927 | */ |
| 27928 | SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){ |
| 27929 | if( db->mallocFailed && db->nVdbeExec==0 ){ |
| 27930 | db->mallocFailed = 0; |
| 27931 | db->u1.isInterrupted = 0; |
| 27932 | assert( db->lookaside.bDisable>0 ); |
| 27933 | EnableLookaside; |
| 27934 | } |
| 27935 | } |
| 27936 | |
| @@ -31300,10 +31407,23 @@ | |
| 31300 | a = (unsigned char *)zLeft; |
| 31301 | b = (unsigned char *)zRight; |
| 31302 | while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
| 31303 | return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; |
| 31304 | } |
| 31305 | |
| 31306 | /* |
| 31307 | ** Compute 10 to the E-th power. Examples: E==1 results in 10. |
| 31308 | ** E==2 results in 100. E==50 results in 1.0e50. |
| 31309 | ** |
| @@ -34857,20 +34977,21 @@ | |
| 34857 | static int osSetPosixAdvisoryLock( |
| 34858 | int h, /* The file descriptor on which to take the lock */ |
| 34859 | struct flock *pLock, /* The description of the lock */ |
| 34860 | unixFile *pFile /* Structure holding timeout value */ |
| 34861 | ){ |
| 34862 | int rc = osFcntl(h,F_SETLK,pLock); |
| 34863 | while( rc<0 && pFile->iBusyTimeout>0 ){ |
| 34864 | /* On systems that support some kind of blocking file lock with a timeout, |
| 34865 | ** make appropriate changes here to invoke that blocking file lock. On |
| 34866 | ** generic posix, however, there is no such API. So we simply try the |
| 34867 | ** lock once every millisecond until either the timeout expires, or until |
| 34868 | ** the lock is obtained. */ |
| 34869 | usleep(1000); |
| 34870 | rc = osFcntl(h,F_SETLK,pLock); |
| 34871 | pFile->iBusyTimeout--; |
| 34872 | } |
| 34873 | return rc; |
| 34874 | } |
| 34875 | #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ |
| 34876 | |
| @@ -36977,11 +37098,11 @@ | |
| 36977 | zDirname[ii] = '\0'; |
| 36978 | }else{ |
| 36979 | if( zDirname[0]!='/' ) zDirname[0] = '.'; |
| 36980 | zDirname[1] = 0; |
| 36981 | } |
| 36982 | fd = robust_open(zDirname, O_RDONLY|O_BINARY|O_NOFOLLOW, 0); |
| 36983 | if( fd>=0 ){ |
| 36984 | OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); |
| 36985 | } |
| 36986 | *pFd = fd; |
| 36987 | if( fd>=0 ) return SQLITE_OK; |
| @@ -37287,11 +37408,13 @@ | |
| 37287 | *(int*)pArg = fileHasMoved(pFile); |
| 37288 | return SQLITE_OK; |
| 37289 | } |
| 37290 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 37291 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 37292 | pFile->iBusyTimeout = *(int*)pArg; |
| 37293 | return SQLITE_OK; |
| 37294 | } |
| 37295 | #endif |
| 37296 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 37297 | case SQLITE_FCNTL_MMAP_SIZE: { |
| @@ -37606,17 +37729,24 @@ | |
| 37606 | |
| 37607 | /* Locks are within range */ |
| 37608 | assert( n>=1 && n<=SQLITE_SHM_NLOCK ); |
| 37609 | |
| 37610 | if( pShmNode->hShm>=0 ){ |
| 37611 | /* Initialize the locking parameters */ |
| 37612 | f.l_type = lockType; |
| 37613 | f.l_whence = SEEK_SET; |
| 37614 | f.l_start = ofst; |
| 37615 | f.l_len = n; |
| 37616 | rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); |
| 37617 | rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; |
| 37618 | } |
| 37619 | |
| 37620 | /* Update the global lock state and do debug tracing */ |
| 37621 | #ifdef SQLITE_DEBUG |
| 37622 | { u16 mask; |
| @@ -38108,10 +38238,29 @@ | |
| 38108 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
| 38109 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
| 38110 | assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
| 38111 | assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); |
| 38112 | assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); |
| 38113 | |
| 38114 | mask = (1<<(ofst+n)) - (1<<ofst); |
| 38115 | assert( n>1 || mask==(1<<ofst) ); |
| 38116 | sqlite3_mutex_enter(pShmNode->pShmMutex); |
| 38117 | if( flags & SQLITE_SHM_UNLOCK ){ |
| @@ -51414,10 +51563,15 @@ | |
| 51414 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal); |
| 51415 | #endif |
| 51416 | |
| 51417 | /* Return the sqlite3_file object for the WAL file */ |
| 51418 | SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal); |
| 51419 | |
| 51420 | #endif /* ifndef SQLITE_OMIT_WAL */ |
| 51421 | #endif /* SQLITE_WAL_H */ |
| 51422 | |
| 51423 | /************** End of wal.h *************************************************/ |
| @@ -53935,13 +54089,16 @@ | |
| 53935 | } |
| 53936 | if( exists ){ |
| 53937 | /* One of the journals pointed to by the master journal exists. |
| 53938 | ** Open it and check if it points at the master journal. If |
| 53939 | ** so, return without deleting the master journal file. |
| 53940 | */ |
| 53941 | int c; |
| 53942 | int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL); |
| 53943 | rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0); |
| 53944 | if( rc!=SQLITE_OK ){ |
| 53945 | goto delmaster_out; |
| 53946 | } |
| 53947 | |
| @@ -56141,10 +56298,11 @@ | |
| 56141 | ** Pager object (sizeof(Pager) bytes) |
| 56142 | ** PCache object (sqlite3PcacheSize() bytes) |
| 56143 | ** Database file handle (pVfs->szOsFile bytes) |
| 56144 | ** Sub-journal file handle (journalFileSize bytes) |
| 56145 | ** Main journal file handle (journalFileSize bytes) |
| 56146 | ** \0\0\0\0 database prefix (4 bytes) |
| 56147 | ** Database file name (nPathname+1 bytes) |
| 56148 | ** URI query parameters (nUriByte bytes) |
| 56149 | ** Journal filename (nPathname+8+1 bytes) |
| 56150 | ** WAL filename (nPathname+4+1 bytes) |
| @@ -56180,10 +56338,11 @@ | |
| 56180 | pPtr = (u8 *)sqlite3MallocZero( |
| 56181 | ROUND8(sizeof(*pPager)) + /* Pager structure */ |
| 56182 | ROUND8(pcacheSize) + /* PCache object */ |
| 56183 | ROUND8(pVfs->szOsFile) + /* The main db file */ |
| 56184 | journalFileSize * 2 + /* The two journal files */ |
| 56185 | 4 + /* Database prefix */ |
| 56186 | nPathname + 1 + /* database filename */ |
| 56187 | nUriByte + /* query parameters */ |
| 56188 | nPathname + 8 + 1 + /* Journal filename */ |
| 56189 | #ifndef SQLITE_OMIT_WAL |
| @@ -56200,10 +56359,11 @@ | |
| 56200 | pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize); |
| 56201 | pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile); |
| 56202 | pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; |
| 56203 | pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; |
| 56204 | assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); |
| 56205 | |
| 56206 | /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ |
| 56207 | pPtr += 4; /* Skip zero prefix */ |
| 56208 | pPager->zFilename = (char*)pPtr; |
| 56209 | if( nPathname>0 ){ |
| @@ -56400,10 +56560,23 @@ | |
| 56400 | |
| 56401 | *ppPager = pPager; |
| 56402 | return SQLITE_OK; |
| 56403 | } |
| 56404 | |
| 56405 | |
| 56406 | |
| 56407 | /* |
| 56408 | ** This function is called after transitioning from PAGER_UNLOCK to |
| 56409 | ** PAGER_SHARED state. It tests if there is a hot journal present in |
| @@ -57085,11 +57258,10 @@ | |
| 57085 | Pager *pPager; |
| 57086 | assert( pPg!=0 ); |
| 57087 | assert( pPg->pgno==1 ); |
| 57088 | assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ |
| 57089 | pPager = pPg->pPager; |
| 57090 | sqlite3PagerResetLockTimeout(pPager); |
| 57091 | sqlite3PcacheRelease(pPg); |
| 57092 | pagerUnlockIfUnused(pPager); |
| 57093 | } |
| 57094 | |
| 57095 | /* |
| @@ -58378,20 +58550,10 @@ | |
| 58378 | */ |
| 58379 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ |
| 58380 | return pPager->fd; |
| 58381 | } |
| 58382 | |
| 58383 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 58384 | /* |
| 58385 | ** Reset the lock timeout for pager. |
| 58386 | */ |
| 58387 | SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){ |
| 58388 | int x = 0; |
| 58389 | sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x); |
| 58390 | } |
| 58391 | #endif |
| 58392 | |
| 58393 | /* |
| 58394 | ** Return the file handle for the journal file (if it exists). |
| 58395 | ** This will be either the rollback journal or the WAL file. |
| 58396 | */ |
| 58397 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ |
| @@ -58801,11 +58963,10 @@ | |
| 58801 | (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), |
| 58802 | pPager->pBusyHandlerArg, |
| 58803 | pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, |
| 58804 | pnLog, pnCkpt |
| 58805 | ); |
| 58806 | sqlite3PagerResetLockTimeout(pPager); |
| 58807 | } |
| 58808 | return rc; |
| 58809 | } |
| 58810 | |
| 58811 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){ |
| @@ -58966,11 +59127,35 @@ | |
| 58966 | } |
| 58967 | } |
| 58968 | return rc; |
| 58969 | } |
| 58970 | |
| 58971 | |
| 58972 | |
| 58973 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 58974 | /* |
| 58975 | ** If this is a WAL database, obtain a snapshot handle for the snapshot |
| 58976 | ** currently open. Otherwise, return an error. |
| @@ -58986,11 +59171,14 @@ | |
| 58986 | /* |
| 58987 | ** If this is a WAL database, store a pointer to pSnapshot. Next time a |
| 58988 | ** read transaction is opened, attempt to read from the snapshot it |
| 58989 | ** identifies. If this is not a WAL database, return an error. |
| 58990 | */ |
| 58991 | SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){ |
| 58992 | int rc = SQLITE_OK; |
| 58993 | if( pPager->pWal ){ |
| 58994 | sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); |
| 58995 | }else{ |
| 58996 | rc = SQLITE_ERROR; |
| @@ -59322,22 +59510,10 @@ | |
| 59322 | # define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X |
| 59323 | #else |
| 59324 | # define WALTRACE(X) |
| 59325 | #endif |
| 59326 | |
| 59327 | /* |
| 59328 | ** WAL mode depends on atomic aligned 32-bit loads and stores in a few |
| 59329 | ** places. The following macros try to make this explicit. |
| 59330 | */ |
| 59331 | #if GCC_VESRION>=5004000 |
| 59332 | # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) |
| 59333 | # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) |
| 59334 | #else |
| 59335 | # define AtomicLoad(PTR) (*(PTR)) |
| 59336 | # define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) |
| 59337 | #endif |
| 59338 | |
| 59339 | /* |
| 59340 | ** The maximum (and only) versions of the wal and wal-index formats |
| 59341 | ** that may be interpreted by this version of SQLite. |
| 59342 | ** |
| 59343 | ** If a client begins recovering a WAL file and finds that (a) the checksum |
| @@ -59542,10 +59718,13 @@ | |
| 59542 | #ifdef SQLITE_DEBUG |
| 59543 | u8 lockError; /* True if a locking error has occurred */ |
| 59544 | #endif |
| 59545 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 59546 | WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ |
| 59547 | #endif |
| 59548 | }; |
| 59549 | |
| 59550 | /* |
| 59551 | ** Candidate values for Wal.exclusiveMode. |
| @@ -59916,11 +60095,11 @@ | |
| 59916 | if( pWal->exclusiveMode ) return SQLITE_OK; |
| 59917 | rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, |
| 59918 | SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); |
| 59919 | WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, |
| 59920 | walLockName(lockIdx), rc ? "failed" : "ok")); |
| 59921 | VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) |
| 59922 | return rc; |
| 59923 | } |
| 59924 | static void walUnlockShared(Wal *pWal, int lockIdx){ |
| 59925 | if( pWal->exclusiveMode ) return; |
| 59926 | (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, |
| @@ -59932,11 +60111,11 @@ | |
| 59932 | if( pWal->exclusiveMode ) return SQLITE_OK; |
| 59933 | rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, |
| 59934 | SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); |
| 59935 | WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, |
| 59936 | walLockName(lockIdx), n, rc ? "failed" : "ok")); |
| 59937 | VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) |
| 59938 | return rc; |
| 59939 | } |
| 59940 | static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ |
| 59941 | if( pWal->exclusiveMode ) return; |
| 59942 | (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, |
| @@ -60751,10 +60930,93 @@ | |
| 60751 | p = 0; |
| 60752 | } |
| 60753 | *pp = p; |
| 60754 | return rc; |
| 60755 | } |
| 60756 | |
| 60757 | /* |
| 60758 | ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and |
| 60759 | ** n. If the attempt fails and parameter xBusy is not NULL, then it is a |
| 60760 | ** busy-handler function. Invoke it and retry the lock until either the |
| @@ -60769,10 +61031,16 @@ | |
| 60769 | ){ |
| 60770 | int rc; |
| 60771 | do { |
| 60772 | rc = walLockExclusive(pWal, lockIdx, n); |
| 60773 | }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); |
| 60774 | return rc; |
| 60775 | } |
| 60776 | |
| 60777 | /* |
| 60778 | ** The cache of the wal-index header must be valid to call this function. |
| @@ -60939,10 +61207,11 @@ | |
| 60939 | ** about the eventual size of the db file to the VFS layer. |
| 60940 | */ |
| 60941 | if( rc==SQLITE_OK ){ |
| 60942 | i64 nReq = ((i64)mxPage * szPage); |
| 60943 | i64 nSize; /* Current size of database file */ |
| 60944 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 60945 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 60946 | sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); |
| 60947 | } |
| 60948 | } |
| @@ -60950,11 +61219,11 @@ | |
| 60950 | |
| 60951 | /* Iterate through the contents of the WAL, copying data to the db file */ |
| 60952 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 60953 | i64 iOffset; |
| 60954 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| 60955 | if( db->u1.isInterrupted ){ |
| 60956 | rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; |
| 60957 | break; |
| 60958 | } |
| 60959 | if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ |
| 60960 | continue; |
| @@ -60966,10 +61235,11 @@ | |
| 60966 | iOffset = (iDbpage-1)*(i64)szPage; |
| 60967 | testcase( IS_BIG_INT(iOffset) ); |
| 60968 | rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); |
| 60969 | if( rc!=SQLITE_OK ) break; |
| 60970 | } |
| 60971 | |
| 60972 | /* If work was actually accomplished... */ |
| 60973 | if( rc==SQLITE_OK ){ |
| 60974 | if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ |
| 60975 | i64 szDb = pWal->hdr.nPage*(i64)szPage; |
| @@ -60977,14 +61247,10 @@ | |
| 60977 | rc = sqlite3OsTruncate(pWal->pDbFd, szDb); |
| 60978 | if( rc==SQLITE_OK ){ |
| 60979 | rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 60980 | } |
| 60981 | } |
| 60982 | if( rc==SQLITE_OK ){ |
| 60983 | rc = sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); |
| 60984 | if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; |
| 60985 | } |
| 60986 | if( rc==SQLITE_OK ){ |
| 60987 | pInfo->nBackfill = mxSafeFrame; |
| 60988 | } |
| 60989 | } |
| 60990 | |
| @@ -61250,32 +61516,36 @@ | |
| 61250 | badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); |
| 61251 | |
| 61252 | /* If the first attempt failed, it might have been due to a race |
| 61253 | ** with a writer. So get a WRITE lock and try again. |
| 61254 | */ |
| 61255 | assert( badHdr==0 || pWal->writeLock==0 ); |
| 61256 | if( badHdr ){ |
| 61257 | if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ |
| 61258 | if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ |
| 61259 | walUnlockShared(pWal, WAL_WRITE_LOCK); |
| 61260 | rc = SQLITE_READONLY_RECOVERY; |
| 61261 | } |
| 61262 | }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ |
| 61263 | pWal->writeLock = 1; |
| 61264 | if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ |
| 61265 | badHdr = walIndexTryHdr(pWal, pChanged); |
| 61266 | if( badHdr ){ |
| 61267 | /* If the wal-index header is still malformed even while holding |
| 61268 | ** a WRITE lock, it can only mean that the header is corrupted and |
| 61269 | ** needs to be reconstructed. So run recovery to do exactly that. |
| 61270 | */ |
| 61271 | rc = walIndexRecover(pWal); |
| 61272 | *pChanged = 1; |
| 61273 | } |
| 61274 | } |
| 61275 | pWal->writeLock = 0; |
| 61276 | walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 61277 | } |
| 61278 | } |
| 61279 | |
| 61280 | /* If the header is read successfully, check the version number to make |
| 61281 | ** sure the wal-index was not constructed with some future format that |
| @@ -61663,11 +61933,12 @@ | |
| 61663 | && (mxReadMark<mxFrame || mxI==0) |
| 61664 | ){ |
| 61665 | for(i=1; i<WAL_NREADER; i++){ |
| 61666 | rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61667 | if( rc==SQLITE_OK ){ |
| 61668 | mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame); |
| 61669 | mxI = i; |
| 61670 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61671 | break; |
| 61672 | }else if( rc!=SQLITE_BUSY ){ |
| 61673 | return rc; |
| @@ -61823,15 +62094,36 @@ | |
| 61823 | */ |
| 61824 | SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ |
| 61825 | int rc; /* Return code */ |
| 61826 | int cnt = 0; /* Number of TryBeginRead attempts */ |
| 61827 | |
| 61828 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 61829 | int bChanged = 0; |
| 61830 | WalIndexHdr *pSnapshot = pWal->pSnapshot; |
| 61831 | if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ |
| 61832 | bChanged = 1; |
| 61833 | } |
| 61834 | #endif |
| 61835 | |
| 61836 | do{ |
| 61837 | rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); |
| @@ -61860,52 +62152,46 @@ | |
| 61860 | volatile WalCkptInfo *pInfo = walCkptInfo(pWal); |
| 61861 | |
| 61862 | assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 ); |
| 61863 | assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); |
| 61864 | |
| 61865 | /* It is possible that there is a checkpointer thread running |
| 61866 | ** concurrent with this code. If this is the case, it may be that the |
| 61867 | ** checkpointer has already determined that it will checkpoint |
| 61868 | ** snapshot X, where X is later in the wal file than pSnapshot, but |
| 61869 | ** has not yet set the pInfo->nBackfillAttempted variable to indicate |
| 61870 | ** its intent. To avoid the race condition this leads to, ensure that |
| 61871 | ** there is no checkpointer process by taking a shared CKPT lock |
| 61872 | ** before checking pInfo->nBackfillAttempted. |
| 61873 | ** |
| 61874 | ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing |
| 61875 | ** this already? |
| 61876 | */ |
| 61877 | rc = walLockShared(pWal, WAL_CKPT_LOCK); |
| 61878 | |
| 61879 | if( rc==SQLITE_OK ){ |
| 61880 | /* Check that the wal file has not been wrapped. Assuming that it has |
| 61881 | ** not, also check that no checkpointer has attempted to checkpoint any |
| 61882 | ** frames beyond pSnapshot->mxFrame. If either of these conditions are |
| 61883 | ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr |
| 61884 | ** with *pSnapshot and set *pChanged as appropriate for opening the |
| 61885 | ** snapshot. */ |
| 61886 | if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) |
| 61887 | && pSnapshot->mxFrame>=pInfo->nBackfillAttempted |
| 61888 | ){ |
| 61889 | assert( pWal->readLock>0 ); |
| 61890 | memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); |
| 61891 | *pChanged = bChanged; |
| 61892 | }else{ |
| 61893 | rc = SQLITE_ERROR_SNAPSHOT; |
| 61894 | } |
| 61895 | |
| 61896 | /* Release the shared CKPT lock obtained above. */ |
| 61897 | walUnlockShared(pWal, WAL_CKPT_LOCK); |
| 61898 | pWal->minFrame = 1; |
| 61899 | } |
| 61900 | |
| 61901 | |
| 61902 | if( rc!=SQLITE_OK ){ |
| 61903 | sqlite3WalEndReadTransaction(pWal); |
| 61904 | } |
| 61905 | } |
| 61906 | } |
| 61907 | #endif |
| 61908 | return rc; |
| 61909 | } |
| 61910 | |
| 61911 | /* |
| @@ -62071,10 +62357,20 @@ | |
| 62071 | ** |
| 62072 | ** There can only be a single writer active at a time. |
| 62073 | */ |
| 62074 | SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ |
| 62075 | int rc; |
| 62076 | |
| 62077 | /* Cannot start a write transaction without first holding a read |
| 62078 | ** transaction. */ |
| 62079 | assert( pWal->readLock>=0 ); |
| 62080 | assert( pWal->writeLock==0 && pWal->iReCksum==0 ); |
| @@ -62647,50 +62943,57 @@ | |
| 62647 | ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ |
| 62648 | assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); |
| 62649 | |
| 62650 | if( pWal->readOnly ) return SQLITE_READONLY; |
| 62651 | WALTRACE(("WAL%p: checkpoint begins\n", pWal)); |
| 62652 | |
| 62653 | /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive |
| 62654 | ** "checkpoint" lock on the database file. */ |
| 62655 | rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); |
| 62656 | if( rc ){ |
| 62657 | /* EVIDENCE-OF: R-10421-19736 If any other process is running a |
| 62658 | ** checkpoint operation at the same time, the lock cannot be obtained and |
| 62659 | ** SQLITE_BUSY is returned. |
| 62660 | ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, |
| 62661 | ** it will not be invoked in this case. |
| 62662 | */ |
| 62663 | testcase( rc==SQLITE_BUSY ); |
| 62664 | testcase( xBusy!=0 ); |
| 62665 | return rc; |
| 62666 | } |
| 62667 | pWal->ckptLock = 1; |
| 62668 | |
| 62669 | /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and |
| 62670 | ** TRUNCATE modes also obtain the exclusive "writer" lock on the database |
| 62671 | ** file. |
| 62672 | ** |
| 62673 | ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained |
| 62674 | ** immediately, and a busy-handler is configured, it is invoked and the |
| 62675 | ** writer lock retried until either the busy-handler returns 0 or the |
| 62676 | ** lock is successfully obtained. |
| 62677 | */ |
| 62678 | if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ |
| 62679 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); |
| 62680 | if( rc==SQLITE_OK ){ |
| 62681 | pWal->writeLock = 1; |
| 62682 | }else if( rc==SQLITE_BUSY ){ |
| 62683 | eMode2 = SQLITE_CHECKPOINT_PASSIVE; |
| 62684 | xBusy2 = 0; |
| 62685 | rc = SQLITE_OK; |
| 62686 | } |
| 62687 | } |
| 62688 | |
| 62689 | /* Read the wal-index header. */ |
| 62690 | if( rc==SQLITE_OK ){ |
| 62691 | rc = walIndexReadHdr(pWal, &isChanged); |
| 62692 | if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ |
| 62693 | sqlite3OsUnfetch(pWal->pDbFd, 0, 0); |
| 62694 | } |
| 62695 | } |
| 62696 | |
| @@ -62717,16 +63020,24 @@ | |
| 62717 | ** next time the pager opens a snapshot on this database it knows that |
| 62718 | ** the cache needs to be reset. |
| 62719 | */ |
| 62720 | memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); |
| 62721 | } |
| 62722 | |
| 62723 | /* Release the locks. */ |
| 62724 | sqlite3WalEndWriteTransaction(pWal); |
| 62725 | walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); |
| 62726 | pWal->ckptLock = 0; |
| 62727 | WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); |
| 62728 | return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); |
| 62729 | } |
| 62730 | |
| 62731 | /* Return the value to pass to a sqlite3_wal_hook callback, the |
| 62732 | ** number of frames in the WAL at the point of the last commit since |
| @@ -62839,11 +63150,14 @@ | |
| 62839 | return rc; |
| 62840 | } |
| 62841 | |
| 62842 | /* Try to open on pSnapshot when the next read-transaction starts |
| 62843 | */ |
| 62844 | SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){ |
| 62845 | pWal->pSnapshot = (WalIndexHdr*)pSnapshot; |
| 62846 | } |
| 62847 | |
| 62848 | /* |
| 62849 | ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if |
| @@ -63358,10 +63672,11 @@ | |
| 63358 | u8 incrVacuum; /* True if incr-vacuum is enabled */ |
| 63359 | u8 bDoTruncate; /* True to truncate db on commit */ |
| 63360 | #endif |
| 63361 | u8 inTransaction; /* Transaction state */ |
| 63362 | u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ |
| 63363 | u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ |
| 63364 | u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ |
| 63365 | u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ |
| 63366 | u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ |
| 63367 | u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ |
| @@ -66251,12 +66566,11 @@ | |
| 66251 | */ |
| 66252 | static int btreeInvokeBusyHandler(void *pArg){ |
| 66253 | BtShared *pBt = (BtShared*)pArg; |
| 66254 | assert( pBt->db ); |
| 66255 | assert( sqlite3_mutex_held(pBt->db->mutex) ); |
| 66256 | return sqlite3InvokeBusyHandler(&pBt->db->busyHandler, |
| 66257 | sqlite3PagerFile(pBt->pPager)); |
| 66258 | } |
| 66259 | |
| 66260 | /* |
| 66261 | ** Open a database file. |
| 66262 | ** |
| @@ -66803,20 +67117,21 @@ | |
| 66803 | ** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size |
| 66804 | ** and autovacuum mode can no longer be changed. |
| 66805 | */ |
| 66806 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ |
| 66807 | int rc = SQLITE_OK; |
| 66808 | BtShared *pBt = p->pBt; |
| 66809 | assert( nReserve>=-1 && nReserve<=255 ); |
| 66810 | sqlite3BtreeEnter(p); |
| 66811 | if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ |
| 66812 | sqlite3BtreeLeave(p); |
| 66813 | return SQLITE_READONLY; |
| 66814 | } |
| 66815 | if( nReserve<0 ){ |
| 66816 | nReserve = pBt->pageSize - pBt->usableSize; |
| 66817 | } |
| 66818 | assert( nReserve>=0 && nReserve<=255 ); |
| 66819 | if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && |
| 66820 | ((pageSize-1)&pageSize)==0 ){ |
| 66821 | assert( (pageSize & 7)==0 ); |
| 66822 | assert( !pBt->pCursor ); |
| @@ -66858,20 +67173,21 @@ | |
| 66858 | /* |
| 66859 | ** Return the number of bytes of space at the end of every page that |
| 66860 | ** are intentually left unused. This is the "reserved" space that is |
| 66861 | ** sometimes used by extensions. |
| 66862 | ** |
| 66863 | ** If SQLITE_HAS_MUTEX is defined then the number returned is the |
| 66864 | ** greater of the current reserved space and the maximum requested |
| 66865 | ** reserve space. |
| 66866 | */ |
| 66867 | SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){ |
| 66868 | int n; |
| 66869 | sqlite3BtreeEnter(p); |
| 66870 | n = sqlite3BtreeGetReserveNoMutex(p); |
| 66871 | sqlite3BtreeLeave(p); |
| 66872 | return n; |
| 66873 | } |
| 66874 | |
| 66875 | |
| 66876 | /* |
| 66877 | ** Set the maximum page count for a database if mxPage is positive. |
| @@ -67317,10 +67633,11 @@ | |
| 67317 | ** when A already has a read lock, we encourage A to give up and let B |
| 67318 | ** proceed. |
| 67319 | */ |
| 67320 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ |
| 67321 | BtShared *pBt = p->pBt; |
| 67322 | int rc = SQLITE_OK; |
| 67323 | |
| 67324 | sqlite3BtreeEnter(p); |
| 67325 | btreeIntegrity(p); |
| 67326 | |
| @@ -67332,11 +67649,11 @@ | |
| 67332 | goto trans_begun; |
| 67333 | } |
| 67334 | assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 ); |
| 67335 | |
| 67336 | if( (p->db->flags & SQLITE_ResetDatabase) |
| 67337 | && sqlite3PagerIsreadonly(pBt->pPager)==0 |
| 67338 | ){ |
| 67339 | pBt->btsFlags &= ~BTS_READ_ONLY; |
| 67340 | } |
| 67341 | |
| 67342 | /* Write transactions are not possible on a read-only database */ |
| @@ -67380,10 +67697,22 @@ | |
| 67380 | if( SQLITE_OK!=rc ) goto trans_begun; |
| 67381 | |
| 67382 | pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; |
| 67383 | if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; |
| 67384 | do { |
| 67385 | /* Call lockBtree() until either pBt->pPage1 is populated or |
| 67386 | ** lockBtree() returns something other than SQLITE_OK. lockBtree() |
| 67387 | ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after |
| 67388 | ** reading page 1 it discovers that the page-size of the database |
| 67389 | ** file is not pBt->pageSize. In this case lockBtree() will update |
| @@ -67393,11 +67722,11 @@ | |
| 67393 | |
| 67394 | if( rc==SQLITE_OK && wrflag ){ |
| 67395 | if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ |
| 67396 | rc = SQLITE_READONLY; |
| 67397 | }else{ |
| 67398 | rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db)); |
| 67399 | if( rc==SQLITE_OK ){ |
| 67400 | rc = newDatabase(pBt); |
| 67401 | }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){ |
| 67402 | /* if there was no transaction opened when this function was |
| 67403 | ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error |
| @@ -67406,15 +67735,19 @@ | |
| 67406 | } |
| 67407 | } |
| 67408 | } |
| 67409 | |
| 67410 | if( rc!=SQLITE_OK ){ |
| 67411 | unlockBtreeIfUnused(pBt); |
| 67412 | } |
| 67413 | }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && |
| 67414 | btreeInvokeBusyHandler(pBt) ); |
| 67415 | sqlite3PagerResetLockTimeout(pBt->pPager); |
| 67416 | |
| 67417 | if( rc==SQLITE_OK ){ |
| 67418 | if( p->inTrans==TRANS_NONE ){ |
| 67419 | pBt->nTransaction++; |
| 67420 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| @@ -67462,11 +67795,11 @@ | |
| 67462 | if( wrflag ){ |
| 67463 | /* This call makes sure that the pager has the correct number of |
| 67464 | ** open savepoints. If the second parameter is greater than 0 and |
| 67465 | ** the sub-journal is not already open, then it will be opened here. |
| 67466 | */ |
| 67467 | rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); |
| 67468 | } |
| 67469 | } |
| 67470 | |
| 67471 | btreeIntegrity(p); |
| 67472 | sqlite3BtreeLeave(p); |
| @@ -71098,11 +71431,11 @@ | |
| 71098 | |
| 71099 | /* Remove cells from the start and end of the page */ |
| 71100 | assert( nCell>=0 ); |
| 71101 | if( iOld<iNew ){ |
| 71102 | int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray); |
| 71103 | if( nShift>nCell ) return SQLITE_CORRUPT_BKPT; |
| 71104 | memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2); |
| 71105 | nCell -= nShift; |
| 71106 | } |
| 71107 | if( iNewEnd < iOldEnd ){ |
| 71108 | int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); |
| @@ -73455,11 +73788,10 @@ | |
| 73455 | } |
| 73456 | sqlite3BtreeLeave(p); |
| 73457 | return rc; |
| 73458 | } |
| 73459 | |
| 73460 | #ifndef SQLITE_OMIT_BTREECOUNT |
| 73461 | /* |
| 73462 | ** The first argument, pCur, is a cursor opened on some b-tree. Count the |
| 73463 | ** number of entries in the b-tree and write the result to *pnEntry. |
| 73464 | ** |
| 73465 | ** SQLITE_OK is returned if the operation is successfully executed. |
| @@ -73477,11 +73809,11 @@ | |
| 73477 | } |
| 73478 | |
| 73479 | /* Unless an error occurs, the following loop runs one iteration for each |
| 73480 | ** page in the B-Tree structure (not including overflow pages). |
| 73481 | */ |
| 73482 | while( rc==SQLITE_OK && !db->u1.isInterrupted ){ |
| 73483 | int iIdx; /* Index of child node in parent */ |
| 73484 | MemPage *pPage; /* Current page of the b-tree */ |
| 73485 | |
| 73486 | /* If this is a leaf page or the tree is not an int-key tree, then |
| 73487 | ** this page contains countable entries. Increment the entry counter |
| @@ -73528,11 +73860,10 @@ | |
| 73528 | } |
| 73529 | |
| 73530 | /* An error has occurred. Return an error code. */ |
| 73531 | return rc; |
| 73532 | } |
| 73533 | #endif |
| 73534 | |
| 73535 | /* |
| 73536 | ** Return the pager associated with a BTree. This routine is used for |
| 73537 | ** testing and debugging only. |
| 73538 | */ |
| @@ -73603,11 +73934,11 @@ | |
| 73603 | } |
| 73604 | if( getPageReferenced(pCheck, iPage) ){ |
| 73605 | checkAppendMsg(pCheck, "2nd reference to page %d", iPage); |
| 73606 | return 1; |
| 73607 | } |
| 73608 | if( pCheck->db->u1.isInterrupted ) return 1; |
| 73609 | setPageReferenced(pCheck, iPage); |
| 73610 | return 0; |
| 73611 | } |
| 73612 | |
| 73613 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| @@ -74579,11 +74910,11 @@ | |
| 74579 | ** Attempt to set the page size of the destination to match the page size |
| 74580 | ** of the source. |
| 74581 | */ |
| 74582 | static int setDestPgsz(sqlite3_backup *p){ |
| 74583 | int rc; |
| 74584 | rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0); |
| 74585 | return rc; |
| 74586 | } |
| 74587 | |
| 74588 | /* |
| 74589 | ** Check that there is no open read-transaction on the b-tree passed as the |
| @@ -78604,24 +78935,23 @@ | |
| 78604 | ** "PX" -> "r[X]" |
| 78605 | ** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 |
| 78606 | ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 |
| 78607 | ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x |
| 78608 | */ |
| 78609 | static int displayComment( |
| 78610 | const Op *pOp, /* The opcode to be commented */ |
| 78611 | const char *zP4, /* Previously obtained value for P4 */ |
| 78612 | char *zTemp, /* Write result here */ |
| 78613 | int nTemp /* Space available in zTemp[] */ |
| 78614 | ){ |
| 78615 | const char *zOpName; |
| 78616 | const char *zSynopsis; |
| 78617 | int nOpName; |
| 78618 | int ii; |
| 78619 | char zAlt[50]; |
| 78620 | StrAccum x; |
| 78621 | sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0); |
| 78622 | |
| 78623 | zOpName = sqlite3OpcodeName(pOp->opcode); |
| 78624 | nOpName = sqlite3Strlen30(zOpName); |
| 78625 | if( zOpName[nOpName+1] ){ |
| 78626 | int seenCom = 0; |
| 78627 | char c; |
| @@ -78684,14 +79014,16 @@ | |
| 78684 | sqlite3_str_appendf(&x, "; %s", pOp->zComment); |
| 78685 | } |
| 78686 | }else if( pOp->zComment ){ |
| 78687 | sqlite3_str_appendall(&x, pOp->zComment); |
| 78688 | } |
| 78689 | sqlite3StrAccumFinish(&x); |
| 78690 | return x.nChar; |
| 78691 | } |
| 78692 | #endif /* SQLITE_DEBUG */ |
| 78693 | |
| 78694 | #if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) |
| 78695 | /* |
| 78696 | ** Translate the P4.pExpr value for an OP_CursorHint opcode into text |
| 78697 | ** that can be displayed in the P4 column of EXPLAIN output. |
| @@ -78768,15 +79100,15 @@ | |
| 78768 | #if VDBE_DISPLAY_P4 |
| 78769 | /* |
| 78770 | ** Compute a string that describes the P4 parameter for an opcode. |
| 78771 | ** Use zTemp for any required temporary buffer space. |
| 78772 | */ |
| 78773 | static char *displayP4(Op *pOp, char *zTemp, int nTemp){ |
| 78774 | char *zP4 = zTemp; |
| 78775 | StrAccum x; |
| 78776 | assert( nTemp>=20 ); |
| 78777 | sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0); |
| 78778 | switch( pOp->p4type ){ |
| 78779 | case P4_KEYINFO: { |
| 78780 | int j; |
| 78781 | KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; |
| 78782 | assert( pKeyInfo->aSortFlags!=0 ); |
| @@ -78856,40 +79188,36 @@ | |
| 78856 | int i; |
| 78857 | int *ai = pOp->p4.ai; |
| 78858 | int n = ai[0]; /* The first element of an INTARRAY is always the |
| 78859 | ** count of the number of elements to follow */ |
| 78860 | for(i=1; i<=n; i++){ |
| 78861 | sqlite3_str_appendf(&x, ",%d", ai[i]); |
| 78862 | } |
| 78863 | zTemp[0] = '['; |
| 78864 | sqlite3_str_append(&x, "]", 1); |
| 78865 | break; |
| 78866 | } |
| 78867 | case P4_SUBPROGRAM: { |
| 78868 | sqlite3_str_appendf(&x, "program"); |
| 78869 | break; |
| 78870 | } |
| 78871 | case P4_DYNBLOB: |
| 78872 | case P4_ADVANCE: { |
| 78873 | zTemp[0] = 0; |
| 78874 | break; |
| 78875 | } |
| 78876 | case P4_TABLE: { |
| 78877 | sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName); |
| 78878 | break; |
| 78879 | } |
| 78880 | default: { |
| 78881 | zP4 = pOp->p4.z; |
| 78882 | if( zP4==0 ){ |
| 78883 | zP4 = zTemp; |
| 78884 | zTemp[0] = 0; |
| 78885 | } |
| 78886 | } |
| 78887 | } |
| 78888 | sqlite3StrAccumFinish(&x); |
| 78889 | assert( zP4!=0 ); |
| 78890 | return zP4; |
| 78891 | } |
| 78892 | #endif /* VDBE_DISPLAY_P4 */ |
| 78893 | |
| 78894 | /* |
| 78895 | ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. |
| @@ -78975,28 +79303,32 @@ | |
| 78975 | /* |
| 78976 | ** Print a single opcode. This routine is used for debugging only. |
| 78977 | */ |
| 78978 | SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ |
| 78979 | char *zP4; |
| 78980 | char zPtr[50]; |
| 78981 | char zCom[100]; |
| 78982 | static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; |
| 78983 | if( pOut==0 ) pOut = stdout; |
| 78984 | zP4 = displayP4(pOp, zPtr, sizeof(zPtr)); |
| 78985 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 78986 | displayComment(pOp, zP4, zCom, sizeof(zCom)); |
| 78987 | #else |
| 78988 | zCom[0] = 0; |
| 78989 | #endif |
| 78990 | /* NB: The sqlite3OpcodeName() function is implemented by code created |
| 78991 | ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the |
| 78992 | ** information from the vdbe.c source text */ |
| 78993 | fprintf(pOut, zFormat1, pc, |
| 78994 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, |
| 78995 | zCom |
| 78996 | ); |
| 78997 | fflush(pOut); |
| 78998 | } |
| 78999 | #endif |
| 79000 | |
| 79001 | /* |
| 79002 | ** Initialize an array of N Mem element. |
| @@ -79083,10 +79415,125 @@ | |
| 79083 | assert( sqlite3VdbeFrameIsValid(pFrame) ); |
| 79084 | pFrame->pParent = pFrame->v->pDelFrame; |
| 79085 | pFrame->v->pDelFrame = pFrame; |
| 79086 | } |
| 79087 | |
| 79088 | |
| 79089 | /* |
| 79090 | ** Delete a VdbeFrame object and its contents. VdbeFrame objects are |
| 79091 | ** allocated by the OP_Program opcode in sqlite3VdbeExec(). |
| 79092 | */ |
| @@ -79123,20 +79570,18 @@ | |
| 79123 | ** the trigger subprograms are listed one by one. |
| 79124 | */ |
| 79125 | SQLITE_PRIVATE int sqlite3VdbeList( |
| 79126 | Vdbe *p /* The VDBE */ |
| 79127 | ){ |
| 79128 | int nRow; /* Stop when row count reaches this */ |
| 79129 | int nSub = 0; /* Number of sub-vdbes seen so far */ |
| 79130 | SubProgram **apSub = 0; /* Array of sub-vdbes */ |
| 79131 | Mem *pSub = 0; /* Memory cell hold array of subprogs */ |
| 79132 | sqlite3 *db = p->db; /* The database connection */ |
| 79133 | int i; /* Loop counter */ |
| 79134 | int rc = SQLITE_OK; /* Return code */ |
| 79135 | Mem *pMem = &p->aMem[1]; /* First Mem of result set */ |
| 79136 | int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0); |
| 79137 | Op *pOp = 0; |
| 79138 | |
| 79139 | assert( p->explain ); |
| 79140 | assert( p->magic==VDBE_MAGIC_RUN ); |
| 79141 | assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); |
| 79142 | |
| @@ -79152,166 +79597,66 @@ | |
| 79152 | ** sqlite3_column_text16() failed. */ |
| 79153 | sqlite3OomFault(db); |
| 79154 | return SQLITE_ERROR; |
| 79155 | } |
| 79156 | |
| 79157 | /* When the number of output rows reaches nRow, that means the |
| 79158 | ** listing has finished and sqlite3_step() should return SQLITE_DONE. |
| 79159 | ** nRow is the sum of the number of rows in the main program, plus |
| 79160 | ** the sum of the number of rows in all trigger subprograms encountered |
| 79161 | ** so far. The nRow value will increase as new trigger subprograms are |
| 79162 | ** encountered, but p->pc will eventually catch up to nRow. |
| 79163 | */ |
| 79164 | nRow = p->nOp; |
| 79165 | if( bListSubprogs ){ |
| 79166 | /* The first 8 memory cells are used for the result set. So we will |
| 79167 | ** commandeer the 9th cell to use as storage for an array of pointers |
| 79168 | ** to trigger subprograms. The VDBE is guaranteed to have at least 9 |
| 79169 | ** cells. */ |
| 79170 | assert( p->nMem>9 ); |
| 79171 | pSub = &p->aMem[9]; |
| 79172 | if( pSub->flags&MEM_Blob ){ |
| 79173 | /* On the first call to sqlite3_step(), pSub will hold a NULL. It is |
| 79174 | ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */ |
| 79175 | nSub = pSub->n/sizeof(Vdbe*); |
| 79176 | apSub = (SubProgram **)pSub->z; |
| 79177 | } |
| 79178 | for(i=0; i<nSub; i++){ |
| 79179 | nRow += apSub[i]->nOp; |
| 79180 | } |
| 79181 | } |
| 79182 | |
| 79183 | while(1){ /* Loop exits via break */ |
| 79184 | i = p->pc++; |
| 79185 | if( i>=nRow ){ |
| 79186 | p->rc = SQLITE_OK; |
| 79187 | rc = SQLITE_DONE; |
| 79188 | break; |
| 79189 | } |
| 79190 | if( i<p->nOp ){ |
| 79191 | /* The output line number is small enough that we are still in the |
| 79192 | ** main program. */ |
| 79193 | pOp = &p->aOp[i]; |
| 79194 | }else{ |
| 79195 | /* We are currently listing subprograms. Figure out which one and |
| 79196 | ** pick up the appropriate opcode. */ |
| 79197 | int j; |
| 79198 | i -= p->nOp; |
| 79199 | assert( apSub!=0 ); |
| 79200 | assert( nSub>0 ); |
| 79201 | for(j=0; i>=apSub[j]->nOp; j++){ |
| 79202 | i -= apSub[j]->nOp; |
| 79203 | assert( i<apSub[j]->nOp || j+1<nSub ); |
| 79204 | } |
| 79205 | pOp = &apSub[j]->aOp[i]; |
| 79206 | } |
| 79207 | |
| 79208 | /* When an OP_Program opcode is encounter (the only opcode that has |
| 79209 | ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms |
| 79210 | ** kept in p->aMem[9].z to hold the new program - assuming this subprogram |
| 79211 | ** has not already been seen. |
| 79212 | */ |
| 79213 | if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){ |
| 79214 | int nByte = (nSub+1)*sizeof(SubProgram*); |
| 79215 | int j; |
| 79216 | for(j=0; j<nSub; j++){ |
| 79217 | if( apSub[j]==pOp->p4.pProgram ) break; |
| 79218 | } |
| 79219 | if( j==nSub ){ |
| 79220 | p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); |
| 79221 | if( p->rc!=SQLITE_OK ){ |
| 79222 | rc = SQLITE_ERROR; |
| 79223 | break; |
| 79224 | } |
| 79225 | apSub = (SubProgram **)pSub->z; |
| 79226 | apSub[nSub++] = pOp->p4.pProgram; |
| 79227 | pSub->flags |= MEM_Blob; |
| 79228 | pSub->n = nSub*sizeof(SubProgram*); |
| 79229 | nRow += pOp->p4.pProgram->nOp; |
| 79230 | } |
| 79231 | } |
| 79232 | if( p->explain<2 ) break; |
| 79233 | if( pOp->opcode==OP_Explain ) break; |
| 79234 | if( pOp->opcode==OP_Init && p->pc>1 ) break; |
| 79235 | } |
| 79236 | |
| 79237 | if( rc==SQLITE_OK ){ |
| 79238 | if( db->u1.isInterrupted ){ |
| 79239 | p->rc = SQLITE_INTERRUPT; |
| 79240 | rc = SQLITE_ERROR; |
| 79241 | sqlite3VdbeError(p, sqlite3ErrStr(p->rc)); |
| 79242 | }else{ |
| 79243 | char *zP4; |
| 79244 | if( p->explain==1 ){ |
| 79245 | pMem->flags = MEM_Int; |
| 79246 | pMem->u.i = i; /* Program counter */ |
| 79247 | pMem++; |
| 79248 | |
| 79249 | pMem->flags = MEM_Static|MEM_Str|MEM_Term; |
| 79250 | pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ |
| 79251 | assert( pMem->z!=0 ); |
| 79252 | pMem->n = sqlite3Strlen30(pMem->z); |
| 79253 | pMem->enc = SQLITE_UTF8; |
| 79254 | pMem++; |
| 79255 | } |
| 79256 | |
| 79257 | pMem->flags = MEM_Int; |
| 79258 | pMem->u.i = pOp->p1; /* P1 */ |
| 79259 | pMem++; |
| 79260 | |
| 79261 | pMem->flags = MEM_Int; |
| 79262 | pMem->u.i = pOp->p2; /* P2 */ |
| 79263 | pMem++; |
| 79264 | |
| 79265 | pMem->flags = MEM_Int; |
| 79266 | pMem->u.i = pOp->p3; /* P3 */ |
| 79267 | pMem++; |
| 79268 | |
| 79269 | if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */ |
| 79270 | assert( p->db->mallocFailed ); |
| 79271 | return SQLITE_ERROR; |
| 79272 | } |
| 79273 | pMem->flags = MEM_Str|MEM_Term; |
| 79274 | zP4 = displayP4(pOp, pMem->z, pMem->szMalloc); |
| 79275 | if( zP4!=pMem->z ){ |
| 79276 | pMem->n = 0; |
| 79277 | sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0); |
| 79278 | }else{ |
| 79279 | assert( pMem->z!=0 ); |
| 79280 | pMem->n = sqlite3Strlen30(pMem->z); |
| 79281 | pMem->enc = SQLITE_UTF8; |
| 79282 | } |
| 79283 | pMem++; |
| 79284 | |
| 79285 | if( p->explain==1 ){ |
| 79286 | if( sqlite3VdbeMemClearAndResize(pMem, 4) ){ |
| 79287 | assert( p->db->mallocFailed ); |
| 79288 | return SQLITE_ERROR; |
| 79289 | } |
| 79290 | pMem->flags = MEM_Str|MEM_Term; |
| 79291 | pMem->n = 2; |
| 79292 | sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ |
| 79293 | pMem->enc = SQLITE_UTF8; |
| 79294 | pMem++; |
| 79295 | |
| 79296 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 79297 | if( sqlite3VdbeMemClearAndResize(pMem, 500) ){ |
| 79298 | assert( p->db->mallocFailed ); |
| 79299 | return SQLITE_ERROR; |
| 79300 | } |
| 79301 | pMem->flags = MEM_Str|MEM_Term; |
| 79302 | pMem->n = displayComment(pOp, zP4, pMem->z, 500); |
| 79303 | pMem->enc = SQLITE_UTF8; |
| 79304 | #else |
| 79305 | pMem->flags = MEM_Null; /* Comment */ |
| 79306 | #endif |
| 79307 | } |
| 79308 | |
| 79309 | p->nResColumn = 8 - 4*(p->explain-1); |
| 79310 | p->pResultSet = &p->aMem[1]; |
| 79311 | p->rc = SQLITE_OK; |
| 79312 | rc = SQLITE_ROW; |
| 79313 | } |
| 79314 | } |
| 79315 | return rc; |
| 79316 | } |
| 79317 | #endif /* SQLITE_OMIT_EXPLAIN */ |
| @@ -79877,12 +80222,13 @@ | |
| 79877 | int retryCount = 0; |
| 79878 | int nMainFile; |
| 79879 | |
| 79880 | /* Select a master journal file name */ |
| 79881 | nMainFile = sqlite3Strlen30(zMainFile); |
| 79882 | zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz%c%c", zMainFile, 0, 0); |
| 79883 | if( zMaster==0 ) return SQLITE_NOMEM_BKPT; |
| 79884 | do { |
| 79885 | u32 iRandom; |
| 79886 | if( retryCount ){ |
| 79887 | if( retryCount>100 ){ |
| 79888 | sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster); |
| @@ -79908,11 +80254,11 @@ | |
| 79908 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| |
| 79909 | SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0 |
| 79910 | ); |
| 79911 | } |
| 79912 | if( rc!=SQLITE_OK ){ |
| 79913 | sqlite3DbFree(db, zMaster); |
| 79914 | return rc; |
| 79915 | } |
| 79916 | |
| 79917 | /* Write the name of each database file in the transaction into the new |
| 79918 | ** master journal file. If an error occurs at this point close |
| @@ -79931,11 +80277,11 @@ | |
| 79931 | rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); |
| 79932 | offset += sqlite3Strlen30(zFile)+1; |
| 79933 | if( rc!=SQLITE_OK ){ |
| 79934 | sqlite3OsCloseFree(pMaster); |
| 79935 | sqlite3OsDelete(pVfs, zMaster, 0); |
| 79936 | sqlite3DbFree(db, zMaster); |
| 79937 | return rc; |
| 79938 | } |
| 79939 | } |
| 79940 | } |
| 79941 | |
| @@ -79945,11 +80291,11 @@ | |
| 79945 | if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) |
| 79946 | && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL)) |
| 79947 | ){ |
| 79948 | sqlite3OsCloseFree(pMaster); |
| 79949 | sqlite3OsDelete(pVfs, zMaster, 0); |
| 79950 | sqlite3DbFree(db, zMaster); |
| 79951 | return rc; |
| 79952 | } |
| 79953 | |
| 79954 | /* Sync all the db files involved in the transaction. The same call |
| 79955 | ** sets the master journal pointer in each individual journal. If |
| @@ -79968,20 +80314,20 @@ | |
| 79968 | } |
| 79969 | } |
| 79970 | sqlite3OsCloseFree(pMaster); |
| 79971 | assert( rc!=SQLITE_BUSY ); |
| 79972 | if( rc!=SQLITE_OK ){ |
| 79973 | sqlite3DbFree(db, zMaster); |
| 79974 | return rc; |
| 79975 | } |
| 79976 | |
| 79977 | /* Delete the master journal file. This commits the transaction. After |
| 79978 | ** doing this the directory is synced again before any individual |
| 79979 | ** transaction files are deleted. |
| 79980 | */ |
| 79981 | rc = sqlite3OsDelete(pVfs, zMaster, 1); |
| 79982 | sqlite3DbFree(db, zMaster); |
| 79983 | zMaster = 0; |
| 79984 | if( rc ){ |
| 79985 | return rc; |
| 79986 | } |
| 79987 | |
| @@ -83028,11 +83374,11 @@ | |
| 83028 | /* If there are no other statements currently running, then |
| 83029 | ** reset the interrupt flag. This prevents a call to sqlite3_interrupt |
| 83030 | ** from interrupting a statement that has not yet started. |
| 83031 | */ |
| 83032 | if( db->nVdbeActive==0 ){ |
| 83033 | db->u1.isInterrupted = 0; |
| 83034 | } |
| 83035 | |
| 83036 | assert( db->nVdbeWrite>0 || db->autoCommit==0 |
| 83037 | || (db->nDeferredCons==0 && db->nDeferredImmCons==0) |
| 83038 | ); |
| @@ -85413,11 +85759,11 @@ | |
| 85413 | assert( p->bIsReader || p->readOnly!=0 ); |
| 85414 | p->iCurrentTime = 0; |
| 85415 | assert( p->explain==0 ); |
| 85416 | p->pResultSet = 0; |
| 85417 | db->busyHandler.nBusy = 0; |
| 85418 | if( db->u1.isInterrupted ) goto abort_due_to_interrupt; |
| 85419 | sqlite3VdbeIOTraceSql(p); |
| 85420 | #ifdef SQLITE_DEBUG |
| 85421 | sqlite3BeginBenignMalloc(); |
| 85422 | if( p->pc==0 |
| 85423 | && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 |
| @@ -85597,11 +85943,11 @@ | |
| 85597 | ** But that is not due to sloppy coding habits. The code is written this |
| 85598 | ** way for performance, to avoid having to run the interrupt and progress |
| 85599 | ** checks on every opcode. This helps sqlite3_step() to run about 1.5% |
| 85600 | ** faster according to "valgrind --tool=cachegrind" */ |
| 85601 | check_for_interrupt: |
| 85602 | if( db->u1.isInterrupted ) goto abort_due_to_interrupt; |
| 85603 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 85604 | /* Call the progress callback if it is configured and the required number |
| 85605 | ** of VDBE ops have been executed (either since this invocation of |
| 85606 | ** sqlite3VdbeExec() or since last time the progress callback was called). |
| 85607 | ** If the progress callback returns non-zero, exit the virtual machine with |
| @@ -87893,32 +88239,38 @@ | |
| 87893 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 87894 | REGISTER_TRACE(pOp->p3, pOut); |
| 87895 | break; |
| 87896 | } |
| 87897 | |
| 87898 | /* Opcode: Count P1 P2 * * * |
| 87899 | ** Synopsis: r[P2]=count() |
| 87900 | ** |
| 87901 | ** Store the number of entries (an integer value) in the table or index |
| 87902 | ** opened by cursor P1 in register P2 |
| 87903 | */ |
| 87904 | #ifndef SQLITE_OMIT_BTREECOUNT |
| 87905 | case OP_Count: { /* out2 */ |
| 87906 | i64 nEntry; |
| 87907 | BtCursor *pCrsr; |
| 87908 | |
| 87909 | assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); |
| 87910 | pCrsr = p->apCsr[pOp->p1]->uc.pCursor; |
| 87911 | assert( pCrsr ); |
| 87912 | nEntry = 0; /* Not needed. Only used to silence a warning. */ |
| 87913 | rc = sqlite3BtreeCount(db, pCrsr, &nEntry); |
| 87914 | if( rc ) goto abort_due_to_error; |
| 87915 | pOut = out2Prerelease(p, pOp); |
| 87916 | pOut->u.i = nEntry; |
| 87917 | goto check_for_interrupt; |
| 87918 | } |
| 87919 | #endif |
| 87920 | |
| 87921 | /* Opcode: Savepoint P1 * * P4 * |
| 87922 | ** |
| 87923 | ** Open, release or rollback the savepoint named by parameter P4, depending |
| 87924 | ** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN). |
| @@ -90352,16 +90704,23 @@ | |
| 90352 | rc = sqlite3VdbeSorterWrite(pC, pIn2); |
| 90353 | if( rc) goto abort_due_to_error; |
| 90354 | break; |
| 90355 | } |
| 90356 | |
| 90357 | /* Opcode: IdxDelete P1 P2 P3 * * |
| 90358 | ** Synopsis: key=r[P2@P3] |
| 90359 | ** |
| 90360 | ** The content of P3 registers starting at register P2 form |
| 90361 | ** an unpacked index key. This opcode removes that entry from the |
| 90362 | ** index opened by cursor P1. |
| 90363 | */ |
| 90364 | case OP_IdxDelete: { |
| 90365 | VdbeCursor *pC; |
| 90366 | BtCursor *pCrsr; |
| 90367 | int res; |
| @@ -90374,20 +90733,22 @@ | |
| 90374 | assert( pC!=0 ); |
| 90375 | assert( pC->eCurType==CURTYPE_BTREE ); |
| 90376 | sqlite3VdbeIncrWriteCounter(p, pC); |
| 90377 | pCrsr = pC->uc.pCursor; |
| 90378 | assert( pCrsr!=0 ); |
| 90379 | assert( pOp->p5==0 ); |
| 90380 | r.pKeyInfo = pC->pKeyInfo; |
| 90381 | r.nField = (u16)pOp->p3; |
| 90382 | r.default_rc = 0; |
| 90383 | r.aMem = &aMem[pOp->p2]; |
| 90384 | rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); |
| 90385 | if( rc ) goto abort_due_to_error; |
| 90386 | if( res==0 ){ |
| 90387 | rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); |
| 90388 | if( rc ) goto abort_due_to_error; |
| 90389 | } |
| 90390 | assert( pC->deferredMoveto==0 ); |
| 90391 | pC->cacheStatus = CACHE_STALE; |
| 90392 | pC->seekResult = 0; |
| 90393 | break; |
| @@ -92705,11 +93066,11 @@ | |
| 92705 | |
| 92706 | /* Jump to here if the sqlite3_interrupt() API sets the interrupt |
| 92707 | ** flag. |
| 92708 | */ |
| 92709 | abort_due_to_interrupt: |
| 92710 | assert( db->u1.isInterrupted ); |
| 92711 | rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; |
| 92712 | p->rc = rc; |
| 92713 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 92714 | goto abort_due_to_error; |
| 92715 | } |
| @@ -95988,10 +96349,435 @@ | |
| 95988 | *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2); |
| 95989 | return SQLITE_OK; |
| 95990 | } |
| 95991 | |
| 95992 | /************** End of vdbesort.c ********************************************/ |
| 95993 | /************** Begin file memjournal.c **************************************/ |
| 95994 | /* |
| 95995 | ** 2008 October 7 |
| 95996 | ** |
| 95997 | ** The author disclaims copyright to this source code. In place of |
| @@ -96775,11 +97561,11 @@ | |
| 96775 | const char *zTab, |
| 96776 | const char *zDb |
| 96777 | ){ |
| 96778 | int n; |
| 96779 | const char *zSpan; |
| 96780 | if( NEVER(pItem->eEName!=ENAME_TAB) ) return 0; |
| 96781 | zSpan = pItem->zEName; |
| 96782 | for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} |
| 96783 | if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ |
| 96784 | return 0; |
| 96785 | } |
| @@ -96929,10 +97715,11 @@ | |
| 96929 | ExprList *pEList; |
| 96930 | SrcList *pSrcList = pNC->pSrcList; |
| 96931 | |
| 96932 | if( pSrcList ){ |
| 96933 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 96934 | pTab = pItem->pTab; |
| 96935 | assert( pTab!=0 && pTab->zName!=0 ); |
| 96936 | assert( pTab->nCol>0 ); |
| 96937 | if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ |
| 96938 | int hit = 0; |
| @@ -96962,12 +97749,13 @@ | |
| 96962 | } |
| 96963 | } |
| 96964 | if( 0==(cntTab++) ){ |
| 96965 | pMatch = pItem; |
| 96966 | } |
| 96967 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 96968 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 96969 | /* If there has been exactly one prior match and this match |
| 96970 | ** is for the right-hand table of a NATURAL JOIN or is in a |
| 96971 | ** USING clause, then skip this match. |
| 96972 | */ |
| 96973 | if( cnt==1 ){ |
| @@ -97024,14 +97812,15 @@ | |
| 97024 | } |
| 97025 | #endif /* SQLITE_OMIT_UPSERT */ |
| 97026 | |
| 97027 | if( pTab ){ |
| 97028 | int iCol; |
| 97029 | pSchema = pTab->pSchema; |
| 97030 | cntTab++; |
| 97031 | for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){ |
| 97032 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 97033 | if( iCol==pTab->iPKey ){ |
| 97034 | iCol = -1; |
| 97035 | } |
| 97036 | break; |
| 97037 | } |
| @@ -97828,11 +98617,11 @@ | |
| 97828 | nc.uNC.pEList = pEList; |
| 97829 | nc.ncFlags = NC_AllowAgg|NC_UEList; |
| 97830 | nc.nErr = 0; |
| 97831 | db = pParse->db; |
| 97832 | savedSuppErr = db->suppressErr; |
| 97833 | db->suppressErr = 1; |
| 97834 | rc = sqlite3ResolveExprNames(&nc, pE); |
| 97835 | db->suppressErr = savedSuppErr; |
| 97836 | if( rc ) return 0; |
| 97837 | |
| 97838 | /* Try to match the ORDER BY expression against an expression |
| @@ -98463,15 +99252,45 @@ | |
| 98463 | SQLITE_PRIVATE int sqlite3ResolveExprListNames( |
| 98464 | NameContext *pNC, /* Namespace to resolve expressions in. */ |
| 98465 | ExprList *pList /* The expression list to be analyzed. */ |
| 98466 | ){ |
| 98467 | int i; |
| 98468 | if( pList ){ |
| 98469 | for(i=0; i<pList->nExpr; i++){ |
| 98470 | if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort; |
| 98471 | } |
| 98472 | } |
| 98473 | return WRC_Continue; |
| 98474 | } |
| 98475 | |
| 98476 | /* |
| 98477 | ** Resolve all names in all expressions of a SELECT and in all |
| @@ -98601,11 +99420,11 @@ | |
| 98601 | ** SELECT * FROM t1 WHERE (select a from t1); |
| 98602 | */ |
| 98603 | SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ |
| 98604 | int op; |
| 98605 | while( ExprHasProperty(pExpr, EP_Skip) ){ |
| 98606 | assert( pExpr->op==TK_COLLATE ); |
| 98607 | pExpr = pExpr->pLeft; |
| 98608 | assert( pExpr!=0 ); |
| 98609 | } |
| 98610 | op = pExpr->op; |
| 98611 | if( op==TK_SELECT ){ |
| @@ -98668,11 +99487,11 @@ | |
| 98668 | /* |
| 98669 | ** Skip over any TK_COLLATE operators. |
| 98670 | */ |
| 98671 | SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ |
| 98672 | while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){ |
| 98673 | assert( pExpr->op==TK_COLLATE ); |
| 98674 | pExpr = pExpr->pLeft; |
| 98675 | } |
| 98676 | return pExpr; |
| 98677 | } |
| 98678 | |
| @@ -98687,11 +99506,11 @@ | |
| 98687 | assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); |
| 98688 | assert( pExpr->x.pList->nExpr>0 ); |
| 98689 | assert( pExpr->op==TK_FUNCTION ); |
| 98690 | pExpr = pExpr->x.pList->a[0].pExpr; |
| 98691 | }else{ |
| 98692 | assert( pExpr->op==TK_COLLATE ); |
| 98693 | pExpr = pExpr->pLeft; |
| 98694 | } |
| 98695 | } |
| 98696 | return pExpr; |
| 98697 | } |
| @@ -100343,20 +101162,26 @@ | |
| 100343 | ExprList *pList, /* List to which to add the span. */ |
| 100344 | Token *pName, /* Name to be added */ |
| 100345 | int dequote /* True to cause the name to be dequoted */ |
| 100346 | ){ |
| 100347 | assert( pList!=0 || pParse->db->mallocFailed!=0 ); |
| 100348 | if( pList ){ |
| 100349 | struct ExprList_item *pItem; |
| 100350 | assert( pList->nExpr>0 ); |
| 100351 | pItem = &pList->a[pList->nExpr-1]; |
| 100352 | assert( pItem->zEName==0 ); |
| 100353 | assert( pItem->eEName==ENAME_NAME ); |
| 100354 | pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); |
| 100355 | if( dequote ) sqlite3Dequote(pItem->zEName); |
| 100356 | if( IN_RENAME_OBJECT ){ |
| 100357 | sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); |
| 100358 | } |
| 100359 | } |
| 100360 | } |
| 100361 | |
| 100362 | /* |
| @@ -101497,10 +102322,12 @@ | |
| 101497 | struct ExprList_item *pItem; |
| 101498 | int r1, r2; |
| 101499 | affinity = sqlite3ExprAffinity(pLeft); |
| 101500 | if( affinity<=SQLITE_AFF_NONE ){ |
| 101501 | affinity = SQLITE_AFF_BLOB; |
| 101502 | } |
| 101503 | if( pKeyInfo ){ |
| 101504 | assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); |
| 101505 | pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); |
| 101506 | } |
| @@ -101735,10 +102562,11 @@ | |
| 101735 | int destStep6 = 0; /* Start of code for Step 6 */ |
| 101736 | int addrTruthOp; /* Address of opcode that determines the IN is true */ |
| 101737 | int destNotNull; /* Jump here if a comparison is not true in step 6 */ |
| 101738 | int addrTop; /* Top of the step-6 loop */ |
| 101739 | int iTab = 0; /* Index to use */ |
| 101740 | |
| 101741 | assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); |
| 101742 | pLeft = pExpr->pLeft; |
| 101743 | if( sqlite3ExprCheckIN(pParse, pExpr) ) return; |
| 101744 | zAff = exprINAffinity(pParse, pExpr); |
| @@ -101779,12 +102607,18 @@ | |
| 101779 | ** |
| 101780 | ** sqlite3FindInIndex() might have reordered the fields of the LHS vector |
| 101781 | ** so that the fields are in the same order as an existing index. The |
| 101782 | ** aiMap[] array contains a mapping from the original LHS field order to |
| 101783 | ** the field order that matches the RHS index. |
| 101784 | */ |
| 101785 | rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy); |
| 101786 | for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */ |
| 101787 | if( i==nVector ){ |
| 101788 | /* LHS fields are not reordered */ |
| 101789 | rLhs = rLhsOrig; |
| 101790 | }else{ |
| @@ -101806,25 +102640,17 @@ | |
| 101806 | CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); |
| 101807 | int labelOk = sqlite3VdbeMakeLabel(pParse); |
| 101808 | int r2, regToFree; |
| 101809 | int regCkNull = 0; |
| 101810 | int ii; |
| 101811 | int bLhsReal; /* True if the LHS of the IN has REAL affinity */ |
| 101812 | assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); |
| 101813 | if( destIfNull!=destIfFalse ){ |
| 101814 | regCkNull = sqlite3GetTempReg(pParse); |
| 101815 | sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); |
| 101816 | } |
| 101817 | bLhsReal = sqlite3ExprAffinity(pExpr->pLeft)==SQLITE_AFF_REAL; |
| 101818 | for(ii=0; ii<pList->nExpr; ii++){ |
| 101819 | if( bLhsReal ){ |
| 101820 | r2 = regToFree = sqlite3GetTempReg(pParse); |
| 101821 | sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2); |
| 101822 | sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC); |
| 101823 | }else{ |
| 101824 | r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree); |
| 101825 | } |
| 101826 | if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){ |
| 101827 | sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull); |
| 101828 | } |
| 101829 | sqlite3ReleaseTempReg(pParse, regToFree); |
| 101830 | if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){ |
| @@ -102394,14 +103220,10 @@ | |
| 102394 | } |
| 102395 | if( aff>SQLITE_AFF_BLOB ){ |
| 102396 | static const char zAff[] = "B\000C\000D\000E"; |
| 102397 | assert( SQLITE_AFF_BLOB=='A' ); |
| 102398 | assert( SQLITE_AFF_TEXT=='B' ); |
| 102399 | if( iReg!=target ){ |
| 102400 | sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target); |
| 102401 | iReg = target; |
| 102402 | } |
| 102403 | sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0, |
| 102404 | &zAff[(aff-'B')*2], P4_STATIC); |
| 102405 | } |
| 102406 | return iReg; |
| 102407 | } |
| @@ -103035,11 +103857,11 @@ | |
| 103035 | assert( pExpr->affExpr==OE_Rollback |
| 103036 | || pExpr->affExpr==OE_Abort |
| 103037 | || pExpr->affExpr==OE_Fail |
| 103038 | || pExpr->affExpr==OE_Ignore |
| 103039 | ); |
| 103040 | if( !pParse->pTriggerTab ){ |
| 103041 | sqlite3ErrorMsg(pParse, |
| 103042 | "RAISE() may only be used within a trigger-program"); |
| 103043 | return 0; |
| 103044 | } |
| 103045 | if( pExpr->affExpr==OE_Abort ){ |
| @@ -103049,12 +103871,13 @@ | |
| 103049 | if( pExpr->affExpr==OE_Ignore ){ |
| 103050 | sqlite3VdbeAddOp4( |
| 103051 | v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); |
| 103052 | VdbeCoverage(v); |
| 103053 | }else{ |
| 103054 | sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, |
| 103055 | pExpr->affExpr, pExpr->u.zToken, 0, 0); |
| 103056 | } |
| 103057 | |
| 103058 | break; |
| 103059 | } |
| 103060 | #endif |
| @@ -104805,10 +105628,26 @@ | |
| 104805 | exit_rename_table: |
| 104806 | sqlite3SrcListDelete(db, pSrc); |
| 104807 | sqlite3DbFree(db, zName); |
| 104808 | db->mDbFlags = savedDbFlags; |
| 104809 | } |
| 104810 | |
| 104811 | /* |
| 104812 | ** This function is called after an "ALTER TABLE ... ADD" statement |
| 104813 | ** has been parsed. Argument pColDef contains the text of the new |
| 104814 | ** column definition. |
| @@ -104858,11 +105697,12 @@ | |
| 104858 | if( pCol->colFlags & COLFLAG_PRIMKEY ){ |
| 104859 | sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column"); |
| 104860 | return; |
| 104861 | } |
| 104862 | if( pNew->pIndex ){ |
| 104863 | sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column"); |
| 104864 | return; |
| 104865 | } |
| 104866 | if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){ |
| 104867 | /* If the default value for the new column was specified with a |
| 104868 | ** literal NULL, then set pDflt to 0. This simplifies checking |
| @@ -104871,19 +105711,18 @@ | |
| 104871 | assert( pDflt==0 || pDflt->op==TK_SPAN ); |
| 104872 | if( pDflt && pDflt->pLeft->op==TK_NULL ){ |
| 104873 | pDflt = 0; |
| 104874 | } |
| 104875 | if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ |
| 104876 | sqlite3ErrorMsg(pParse, |
| 104877 | "Cannot add a REFERENCES column with non-NULL default value"); |
| 104878 | return; |
| 104879 | } |
| 104880 | if( pCol->notNull && !pDflt ){ |
| 104881 | sqlite3ErrorMsg(pParse, |
| 104882 | "Cannot add a NOT NULL column with default value NULL"); |
| 104883 | return; |
| 104884 | } |
| 104885 | |
| 104886 | /* Ensure the default expression is something that sqlite3ValueFromExpr() |
| 104887 | ** can handle (i.e. not CURRENT_TIME etc.) |
| 104888 | */ |
| 104889 | if( pDflt ){ |
| @@ -104894,18 +105733,17 @@ | |
| 104894 | if( rc!=SQLITE_OK ){ |
| 104895 | assert( db->mallocFailed == 1 ); |
| 104896 | return; |
| 104897 | } |
| 104898 | if( !pVal ){ |
| 104899 | sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default"); |
| 104900 | return; |
| 104901 | } |
| 104902 | sqlite3ValueFree(pVal); |
| 104903 | } |
| 104904 | }else if( pCol->colFlags & COLFLAG_STORED ){ |
| 104905 | sqlite3ErrorMsg(pParse, "cannot add a STORED column"); |
| 104906 | return; |
| 104907 | } |
| 104908 | |
| 104909 | |
| 104910 | /* Modify the CREATE TABLE statement. */ |
| 104911 | zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); |
| @@ -105020,10 +105858,11 @@ | |
| 105020 | } |
| 105021 | memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); |
| 105022 | for(i=0; i<pNew->nCol; i++){ |
| 105023 | Column *pCol = &pNew->aCol[i]; |
| 105024 | pCol->zName = sqlite3DbStrDup(db, pCol->zName); |
| 105025 | pCol->zColl = 0; |
| 105026 | pCol->pDflt = 0; |
| 105027 | } |
| 105028 | pNew->pSchema = db->aDb[iDb].pSchema; |
| 105029 | pNew->addColOffset = pTab->addColOffset; |
| @@ -105248,11 +106087,11 @@ | |
| 105248 | */ |
| 105249 | SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ |
| 105250 | RenameToken *pNew; |
| 105251 | assert( pPtr || pParse->db->mallocFailed ); |
| 105252 | renameTokenCheckAll(pParse, pPtr); |
| 105253 | if( pParse->eParseMode!=PARSE_MODE_UNMAP ){ |
| 105254 | pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken)); |
| 105255 | if( pNew ){ |
| 105256 | pNew->p = pPtr; |
| 105257 | pNew->t = *pToken; |
| 105258 | pNew->pNext = pParse->pRename; |
| @@ -105305,10 +106144,25 @@ | |
| 105305 | sqlite3WalkSelect(pWalker, p); |
| 105306 | sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); |
| 105307 | } |
| 105308 | } |
| 105309 | } |
| 105310 | |
| 105311 | /* |
| 105312 | ** Walker callback used by sqlite3RenameExprUnmap(). |
| 105313 | */ |
| 105314 | static int renameUnmapSelectCb(Walker *pWalker, Select *p){ |
| @@ -105327,10 +106181,11 @@ | |
| 105327 | if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */ |
| 105328 | SrcList *pSrc = p->pSrc; |
| 105329 | for(i=0; i<pSrc->nSrc; i++){ |
| 105330 | sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); |
| 105331 | if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; |
| 105332 | } |
| 105333 | } |
| 105334 | |
| 105335 | renameWalkWith(pWalker, p); |
| 105336 | return WRC_Continue; |
| @@ -105534,10 +106389,11 @@ | |
| 105534 | renameTokenFind(pParse, pCtx, (void*)zName); |
| 105535 | } |
| 105536 | } |
| 105537 | } |
| 105538 | } |
| 105539 | |
| 105540 | /* |
| 105541 | ** Parse the SQL statement zSql using Parse object (*p). The Parse object |
| 105542 | ** is initialized by this function before it is used. |
| 105543 | */ |
| @@ -106446,10 +107302,15 @@ | |
| 106446 | sqlite3 *db = pParse->db; |
| 106447 | Db *pDb; |
| 106448 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 106449 | int aRoot[ArraySize(aTable)]; |
| 106450 | u8 aCreateTbl[ArraySize(aTable)]; |
| 106451 | |
| 106452 | if( v==0 ) return; |
| 106453 | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| 106454 | assert( sqlite3VdbeDb(v)==db ); |
| 106455 | pDb = &db->aDb[iDb]; |
| @@ -106458,12 +107319,13 @@ | |
| 106458 | ** if they do already exist. |
| 106459 | */ |
| 106460 | for(i=0; i<ArraySize(aTable); i++){ |
| 106461 | const char *zTab = aTable[i].zName; |
| 106462 | Table *pStat; |
| 106463 | if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){ |
| 106464 | if( aTable[i].zCols ){ |
| 106465 | /* The sqlite_statN table does not exist. Create it. Note that a |
| 106466 | ** side-effect of the CREATE TABLE statement is to leave the rootpage |
| 106467 | ** of the new table in register pParse->regRoot. This is important |
| 106468 | ** because the OpenWrite opcode below will be needing it. */ |
| 106469 | sqlite3NestedParse(pParse, |
| @@ -106475,11 +107337,10 @@ | |
| 106475 | }else{ |
| 106476 | /* The table already exists. If zWhere is not NULL, delete all entries |
| 106477 | ** associated with the table zWhere. If zWhere is NULL, delete the |
| 106478 | ** entire contents of the table. */ |
| 106479 | aRoot[i] = pStat->tnum; |
| 106480 | aCreateTbl[i] = 0; |
| 106481 | sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); |
| 106482 | if( zWhere ){ |
| 106483 | sqlite3NestedParse(pParse, |
| 106484 | "DELETE FROM %Q.%s WHERE %s=%Q", |
| 106485 | pDb->zDbSName, zTab, zWhereType, zWhere |
| @@ -106494,11 +107355,11 @@ | |
| 106494 | } |
| 106495 | } |
| 106496 | } |
| 106497 | |
| 106498 | /* Open the sqlite_stat[134] tables for writing. */ |
| 106499 | for(i=0; aTable[i].zCols; i++){ |
| 106500 | assert( i<ArraySize(aTable) ); |
| 106501 | sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3); |
| 106502 | sqlite3VdbeChangeP5(v, aCreateTbl[i]); |
| 106503 | VdbeComment((v, aTable[i].zName)); |
| 106504 | } |
| @@ -106533,13 +107394,16 @@ | |
| 106533 | u32 iHash; /* Tiebreaker hash */ |
| 106534 | #endif |
| 106535 | }; |
| 106536 | struct StatAccum { |
| 106537 | sqlite3 *db; /* Database connection, for malloc() */ |
| 106538 | tRowcnt nRow; /* Number of rows in the entire table */ |
| 106539 | int nCol; /* Number of columns in index + pk/rowid */ |
| 106540 | int nKeyCol; /* Number of index columns w/o the pk/rowid */ |
| 106541 | StatSample current; /* Current row as a StatSample */ |
| 106542 | #ifdef SQLITE_ENABLE_STAT4 |
| 106543 | tRowcnt nPSample; /* How often to do a periodic sample */ |
| 106544 | int mxSample; /* Maximum number of samples to accumulate */ |
| 106545 | u32 iPrn; /* Pseudo-random number used for sampling */ |
| @@ -106615,31 +107479,32 @@ | |
| 106615 | ** Reclaim all memory of a StatAccum structure. |
| 106616 | */ |
| 106617 | static void statAccumDestructor(void *pOld){ |
| 106618 | StatAccum *p = (StatAccum*)pOld; |
| 106619 | #ifdef SQLITE_ENABLE_STAT4 |
| 106620 | int i; |
| 106621 | for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i); |
| 106622 | for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i); |
| 106623 | sampleClear(p->db, &p->current); |
| 106624 | #endif |
| 106625 | sqlite3DbFree(p->db, p); |
| 106626 | } |
| 106627 | |
| 106628 | /* |
| 106629 | ** Implementation of the stat_init(N,K,C) SQL function. The three parameters |
| 106630 | ** are: |
| 106631 | ** N: The number of columns in the index including the rowid/pk (note 1) |
| 106632 | ** K: The number of columns in the index excluding the rowid/pk. |
| 106633 | ** C: The number of rows in the index (note 2) |
| 106634 | ** |
| 106635 | ** Note 1: In the special case of the covering index that implements a |
| 106636 | ** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the |
| 106637 | ** total number of columns in the table. |
| 106638 | ** |
| 106639 | ** Note 2: C is only used for STAT4. |
| 106640 | ** |
| 106641 | ** For indexes on ordinary rowid tables, N==K+1. But for indexes on |
| 106642 | ** WITHOUT ROWID tables, N=K+P where P is the number of columns in the |
| 106643 | ** PRIMARY KEY of the table. The covering index that implements the |
| 106644 | ** original WITHOUT ROWID table as N==K as a special case. |
| 106645 | ** |
| @@ -106656,13 +107521,14 @@ | |
| 106656 | StatAccum *p; |
| 106657 | int nCol; /* Number of columns in index being sampled */ |
| 106658 | int nKeyCol; /* Number of key columns */ |
| 106659 | int nColUp; /* nCol rounded up for alignment */ |
| 106660 | int n; /* Bytes of space to allocate */ |
| 106661 | sqlite3 *db; /* Database connection */ |
| 106662 | #ifdef SQLITE_ENABLE_STAT4 |
| 106663 | int mxSample = SQLITE_STAT4_SAMPLES; |
| 106664 | #endif |
| 106665 | |
| 106666 | /* Decode the three function arguments */ |
| 106667 | UNUSED_PARAMETER(argc); |
| 106668 | nCol = sqlite3_value_int(argv[0]); |
| @@ -106673,39 +107539,43 @@ | |
| 106673 | assert( nKeyCol>0 ); |
| 106674 | |
| 106675 | /* Allocate the space required for the StatAccum object */ |
| 106676 | n = sizeof(*p) |
| 106677 | + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ |
| 106678 | + sizeof(tRowcnt)*nColUp /* StatAccum.anDLt */ |
| 106679 | #ifdef SQLITE_ENABLE_STAT4 |
| 106680 | + sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ |
| 106681 | + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ |
| 106682 | + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample) |
| 106683 | #endif |
| 106684 | ; |
| 106685 | db = sqlite3_context_db_handle(context); |
| 106686 | p = sqlite3DbMallocZero(db, n); |
| 106687 | if( p==0 ){ |
| 106688 | sqlite3_result_error_nomem(context); |
| 106689 | return; |
| 106690 | } |
| 106691 | |
| 106692 | p->db = db; |
| 106693 | p->nRow = 0; |
| 106694 | p->nCol = nCol; |
| 106695 | p->nKeyCol = nKeyCol; |
| 106696 | p->current.anDLt = (tRowcnt*)&p[1]; |
| 106697 | p->current.anEq = &p->current.anDLt[nColUp]; |
| 106698 | |
| 106699 | #ifdef SQLITE_ENABLE_STAT4 |
| 106700 | { |
| 106701 | u8 *pSpace; /* Allocated space not yet assigned */ |
| 106702 | int i; /* Used to iterate through p->aSample[] */ |
| 106703 | |
| 106704 | p->iGet = -1; |
| 106705 | p->mxSample = mxSample; |
| 106706 | p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1); |
| 106707 | p->current.anLt = &p->current.anEq[nColUp]; |
| 106708 | p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); |
| 106709 | |
| 106710 | /* Set up the StatAccum.a[] and aBest[] arrays */ |
| 106711 | p->a = (struct StatSample*)&p->current.anLt[nColUp]; |
| @@ -106729,11 +107599,11 @@ | |
| 106729 | ** (given by the 3rd parameter) is never used and can be any positive |
| 106730 | ** value. */ |
| 106731 | sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor); |
| 106732 | } |
| 106733 | static const FuncDef statInitFuncdef = { |
| 106734 | 2+IsStat4, /* nArg */ |
| 106735 | SQLITE_UTF8, /* funcFlags */ |
| 106736 | 0, /* pUserData */ |
| 106737 | 0, /* pNext */ |
| 106738 | statInit, /* xSFunc */ |
| 106739 | 0, /* xFinalize */ |
| @@ -106933,14 +107803,17 @@ | |
| 106933 | ** P Pointer to the StatAccum object created by stat_init() |
| 106934 | ** C Index of left-most column to differ from previous row |
| 106935 | ** R Rowid for the current row. Might be a key record for |
| 106936 | ** WITHOUT ROWID tables. |
| 106937 | ** |
| 106938 | ** This SQL function always returns NULL. It's purpose it to accumulate |
| 106939 | ** statistical data and/or samples in the StatAccum object about the |
| 106940 | ** index being analyzed. The stat_get() SQL function will later be used to |
| 106941 | ** extract relevant information for constructing the sqlite_statN tables. |
| 106942 | ** |
| 106943 | ** The R parameter is only used for STAT4 |
| 106944 | */ |
| 106945 | static void statPush( |
| 106946 | sqlite3_context *context, |
| @@ -106962,11 +107835,11 @@ | |
| 106962 | /* This is the first call to this function. Do initialization. */ |
| 106963 | for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1; |
| 106964 | }else{ |
| 106965 | /* Second and subsequent calls get processed here */ |
| 106966 | #ifdef SQLITE_ENABLE_STAT4 |
| 106967 | samplePushPrevious(p, iChng); |
| 106968 | #endif |
| 106969 | |
| 106970 | /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply |
| 106971 | ** to the current row of the index. */ |
| 106972 | for(i=0; i<iChng; i++){ |
| @@ -106973,30 +107846,29 @@ | |
| 106973 | p->current.anEq[i]++; |
| 106974 | } |
| 106975 | for(i=iChng; i<p->nCol; i++){ |
| 106976 | p->current.anDLt[i]++; |
| 106977 | #ifdef SQLITE_ENABLE_STAT4 |
| 106978 | p->current.anLt[i] += p->current.anEq[i]; |
| 106979 | #endif |
| 106980 | p->current.anEq[i] = 1; |
| 106981 | } |
| 106982 | } |
| 106983 | p->nRow++; |
| 106984 | #ifdef SQLITE_ENABLE_STAT4 |
| 106985 | if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ |
| 106986 | sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); |
| 106987 | }else{ |
| 106988 | sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), |
| 106989 | sqlite3_value_blob(argv[2])); |
| 106990 | } |
| 106991 | p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; |
| 106992 | #endif |
| 106993 | |
| 106994 | #ifdef SQLITE_ENABLE_STAT4 |
| 106995 | { |
| 106996 | tRowcnt nLt = p->current.anLt[p->nCol-1]; |
| 106997 | |
| 106998 | /* Check if this is to be a periodic sample. If so, add it. */ |
| 106999 | if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){ |
| 107000 | p->current.isPSample = 1; |
| 107001 | p->current.iCol = 0; |
| 107002 | sampleInsert(p, &p->current, p->nCol-1); |
| @@ -107008,13 +107880,18 @@ | |
| 107008 | p->current.iCol = i; |
| 107009 | if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){ |
| 107010 | sampleCopy(p, &p->aBest[i], &p->current); |
| 107011 | } |
| 107012 | } |
| 107013 | } |
| 107014 | #endif |
| 107015 | } |
| 107016 | static const FuncDef statPushFuncdef = { |
| 107017 | 2+IsStat4, /* nArg */ |
| 107018 | SQLITE_UTF8, /* funcFlags */ |
| 107019 | 0, /* pUserData */ |
| 107020 | 0, /* pNext */ |
| @@ -107062,10 +107939,11 @@ | |
| 107062 | assert( argc==2 ); |
| 107063 | assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ |
| 107064 | || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT |
| 107065 | || eCall==STAT_GET_NDLT |
| 107066 | ); |
| 107067 | if( eCall==STAT_GET_STAT1 ) |
| 107068 | #else |
| 107069 | assert( argc==1 ); |
| 107070 | #endif |
| 107071 | { |
| @@ -107097,11 +107975,12 @@ | |
| 107097 | if( zRet==0 ){ |
| 107098 | sqlite3_result_error_nomem(context); |
| 107099 | return; |
| 107100 | } |
| 107101 | |
| 107102 | sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow); |
| 107103 | z = zRet + sqlite3Strlen30(zRet); |
| 107104 | for(i=0; i<p->nKeyCol; i++){ |
| 107105 | u64 nDistinct = p->current.anDLt[i] + 1; |
| 107106 | u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; |
| 107107 | sqlite3_snprintf(24, z, " %llu", iVal); |
| @@ -107173,20 +108052,20 @@ | |
| 107173 | 0, 0, /* xValue, xInverse */ |
| 107174 | "stat_get", /* zName */ |
| 107175 | {0} |
| 107176 | }; |
| 107177 | |
| 107178 | static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){ |
| 107179 | #ifdef SQLITE_ENABLE_STAT4 |
| 107180 | sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1); |
| 107181 | #elif SQLITE_DEBUG |
| 107182 | assert( iParam==STAT_GET_STAT1 ); |
| 107183 | #else |
| 107184 | UNUSED_PARAMETER( iParam ); |
| 107185 | #endif |
| 107186 | assert( regOut!=regStat4 && regOut!=regStat4+1 ); |
| 107187 | sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4, |
| 107188 | &statGetFuncdef, 0); |
| 107189 | } |
| 107190 | |
| 107191 | /* |
| 107192 | ** Generate code to do an analysis of all indices associated with |
| @@ -107208,16 +108087,15 @@ | |
| 107208 | int i; /* Loop counter */ |
| 107209 | int jZeroRows = -1; /* Jump from here if number of rows is zero */ |
| 107210 | int iDb; /* Index of database containing pTab */ |
| 107211 | u8 needTableCnt = 1; /* True to count the table */ |
| 107212 | int regNewRowid = iMem++; /* Rowid for the inserted record */ |
| 107213 | int regStat4 = iMem++; /* Register to hold StatAccum object */ |
| 107214 | int regChng = iMem++; /* Index of changed index field */ |
| 107215 | #ifdef SQLITE_ENABLE_STAT4 |
| 107216 | int regRowid = iMem++; /* Rowid argument passed to stat_push() */ |
| 107217 | #endif |
| 107218 | int regTemp = iMem++; /* Temporary use register */ |
| 107219 | int regTabname = iMem++; /* Register containing table name */ |
| 107220 | int regIdxname = iMem++; /* Register containing index name */ |
| 107221 | int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ |
| 107222 | int regPrev = iMem; /* MUST BE LAST (see below) */ |
| 107223 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| @@ -107341,21 +108219,30 @@ | |
| 107341 | /* Invoke the stat_init() function. The arguments are: |
| 107342 | ** |
| 107343 | ** (1) the number of columns in the index including the rowid |
| 107344 | ** (or for a WITHOUT ROWID table, the number of PK columns), |
| 107345 | ** (2) the number of columns in the key without the rowid/pk |
| 107346 | ** (3) the number of rows in the index, |
| 107347 | ** |
| 107348 | ** |
| 107349 | ** The third argument is only used for STAT4 |
| 107350 | */ |
| 107351 | #ifdef SQLITE_ENABLE_STAT4 |
| 107352 | sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3); |
| 107353 | #endif |
| 107354 | sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1); |
| 107355 | sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2); |
| 107356 | sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4, |
| 107357 | &statInitFuncdef, 0); |
| 107358 | |
| 107359 | /* Implementation of the following: |
| 107360 | ** |
| 107361 | ** Rewind csr |
| @@ -107362,12 +108249,10 @@ | |
| 107362 | ** if eof(csr) goto end_of_scan; |
| 107363 | ** regChng = 0 |
| 107364 | ** goto next_push_0; |
| 107365 | ** |
| 107366 | */ |
| 107367 | addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); |
| 107368 | VdbeCoverage(v); |
| 107369 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); |
| 107370 | addrNextRow = sqlite3VdbeCurrentAddr(v); |
| 107371 | |
| 107372 | if( nColTest>0 ){ |
| 107373 | int endDistinctTest = sqlite3VdbeMakeLabel(pParse); |
| @@ -107396,10 +108281,11 @@ | |
| 107396 | } |
| 107397 | for(i=0; i<nColTest; i++){ |
| 107398 | char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); |
| 107399 | sqlite3VdbeAddOp2(v, OP_Integer, i, regChng); |
| 107400 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp); |
| 107401 | aGotoChng[i] = |
| 107402 | sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ); |
| 107403 | sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); |
| 107404 | VdbeCoverage(v); |
| 107405 | } |
| @@ -107416,10 +108302,11 @@ | |
| 107416 | */ |
| 107417 | sqlite3VdbeJumpHere(v, addrNextRow-1); |
| 107418 | for(i=0; i<nColTest; i++){ |
| 107419 | sqlite3VdbeJumpHere(v, aGotoChng[i]); |
| 107420 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i); |
| 107421 | } |
| 107422 | sqlite3VdbeResolveLabel(v, endDistinctTest); |
| 107423 | sqlite3DbFree(db, aGotoChng); |
| 107424 | } |
| 107425 | |
| @@ -107429,34 +108316,50 @@ | |
| 107429 | ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only |
| 107430 | ** Next csr |
| 107431 | ** if !eof(csr) goto next_row; |
| 107432 | */ |
| 107433 | #ifdef SQLITE_ENABLE_STAT4 |
| 107434 | assert( regRowid==(regStat4+2) ); |
| 107435 | if( HasRowid(pTab) ){ |
| 107436 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); |
| 107437 | }else{ |
| 107438 | Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); |
| 107439 | int j, k, regKey; |
| 107440 | regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); |
| 107441 | for(j=0; j<pPk->nKeyCol; j++){ |
| 107442 | k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]); |
| 107443 | assert( k>=0 && k<pIdx->nColumn ); |
| 107444 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); |
| 107445 | VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName)); |
| 107446 | } |
| 107447 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); |
| 107448 | sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); |
| 107449 | } |
| 107450 | #endif |
| 107451 | assert( regChng==(regStat4+1) ); |
| 107452 | sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4, |
| 107453 | &statPushFuncdef, 0); |
| 107454 | sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); |
| 107455 | |
| 107456 | /* Add the entry to the stat1 table. */ |
| 107457 | callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1); |
| 107458 | assert( "BBB"[0]==SQLITE_AFF_TEXT ); |
| 107459 | sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); |
| 107460 | sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); |
| 107461 | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); |
| 107462 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| @@ -107464,11 +108367,11 @@ | |
| 107464 | #endif |
| 107465 | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| 107466 | |
| 107467 | /* Add the entries to the stat4 table. */ |
| 107468 | #ifdef SQLITE_ENABLE_STAT4 |
| 107469 | { |
| 107470 | int regEq = regStat1; |
| 107471 | int regLt = regStat1+1; |
| 107472 | int regDLt = regStat1+2; |
| 107473 | int regSample = regStat1+3; |
| 107474 | int regCol = regStat1+4; |
| @@ -107478,16 +108381,16 @@ | |
| 107478 | u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; |
| 107479 | |
| 107480 | pParse->nMem = MAX(pParse->nMem, regCol+nCol); |
| 107481 | |
| 107482 | addrNext = sqlite3VdbeCurrentAddr(v); |
| 107483 | callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid); |
| 107484 | addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); |
| 107485 | VdbeCoverage(v); |
| 107486 | callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq); |
| 107487 | callStatGet(pParse, regStat4, STAT_GET_NLT, regLt); |
| 107488 | callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt); |
| 107489 | sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0); |
| 107490 | VdbeCoverage(v); |
| 107491 | for(i=0; i<nCol; i++){ |
| 107492 | sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i); |
| 107493 | } |
| @@ -109619,10 +110522,11 @@ | |
| 109619 | int i; |
| 109620 | Column *pCol; |
| 109621 | assert( pTable!=0 ); |
| 109622 | if( (pCol = pTable->aCol)!=0 ){ |
| 109623 | for(i=0; i<pTable->nCol; i++, pCol++){ |
| 109624 | sqlite3DbFree(db, pCol->zName); |
| 109625 | sqlite3ExprDelete(db, pCol->pDflt); |
| 109626 | sqlite3DbFree(db, pCol->zColl); |
| 109627 | } |
| 109628 | sqlite3DbFree(db, pTable->aCol); |
| @@ -110267,10 +111171,11 @@ | |
| 110267 | p->aCol = aNew; |
| 110268 | } |
| 110269 | pCol = &p->aCol[p->nCol]; |
| 110270 | memset(pCol, 0, sizeof(p->aCol[0])); |
| 110271 | pCol->zName = z; |
| 110272 | sqlite3ColumnPropertiesFromName(p, pCol); |
| 110273 | |
| 110274 | if( pType->n==0 ){ |
| 110275 | /* If there is no type specified, columns have the default affinity |
| 110276 | ** 'BLOB' with a default size of 4 bytes. */ |
| @@ -113657,11 +114562,11 @@ | |
| 113657 | pParse->rc = rc; |
| 113658 | return 1; |
| 113659 | } |
| 113660 | db->aDb[1].pBt = pBt; |
| 113661 | assert( db->aDb[1].pSchema ); |
| 113662 | if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ |
| 113663 | sqlite3OomFault(db); |
| 113664 | return 1; |
| 113665 | } |
| 113666 | } |
| 113667 | return 0; |
| @@ -113768,11 +114673,11 @@ | |
| 113768 | char *p4, /* Error message */ |
| 113769 | i8 p4type, /* P4_STATIC or P4_TRANSIENT */ |
| 113770 | u8 p5Errmsg /* P5_ErrMsg type */ |
| 113771 | ){ |
| 113772 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 113773 | assert( (errCode&0xff)==SQLITE_CONSTRAINT ); |
| 113774 | if( onError==OE_Abort ){ |
| 113775 | sqlite3MayAbort(pParse); |
| 113776 | } |
| 113777 | sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type); |
| 113778 | sqlite3VdbeChangeP5(v, p5Errmsg); |
| @@ -115481,10 +116386,11 @@ | |
| 115481 | VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); |
| 115482 | r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, |
| 115483 | &iPartIdxLabel, pPrior, r1); |
| 115484 | sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, |
| 115485 | pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
| 115486 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 115487 | pPrior = pIdx; |
| 115488 | } |
| 115489 | } |
| 115490 | |
| @@ -122468,10 +123374,11 @@ | |
| 122468 | const char *(*filename_wal)(const char*); |
| 122469 | /* Version 3.32.0 and later */ |
| 122470 | char *(*create_filename)(const char*,const char*,const char*, |
| 122471 | int,const char**); |
| 122472 | void (*free_filename)(char*); |
| 122473 | }; |
| 122474 | |
| 122475 | /* |
| 122476 | ** This is the function signature used for all extension entry points. It |
| 122477 | ** is also defined in the file "loadext.c". |
| @@ -122771,10 +123678,11 @@ | |
| 122771 | #define sqlite3_filename_journal sqlite3_api->filename_journal |
| 122772 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 122773 | /* Version 3.32.0 and later */ |
| 122774 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 122775 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 122776 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 122777 | |
| 122778 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 122779 | /* This case when the file really is being compiled as a loadable |
| 122780 | ** extension */ |
| @@ -123252,11 +124160,20 @@ | |
| 123252 | sqlite3_filename_journal, |
| 123253 | sqlite3_filename_wal, |
| 123254 | /* Version 3.32.0 and later */ |
| 123255 | sqlite3_create_filename, |
| 123256 | sqlite3_free_filename, |
| 123257 | }; |
| 123258 | |
| 123259 | /* |
| 123260 | ** Attempt to load an SQLite extension library contained in the file |
| 123261 | ** zFile. The entry point is zProc. zProc may be 0 in which case a |
| 123262 | ** default entry point name (sqlite3_extension_init) is used. Use |
| @@ -123355,11 +124272,11 @@ | |
| 123355 | if( zAltEntry==0 ){ |
| 123356 | sqlite3OsDlClose(pVfs, handle); |
| 123357 | return SQLITE_NOMEM_BKPT; |
| 123358 | } |
| 123359 | memcpy(zAltEntry, "sqlite3_", 8); |
| 123360 | for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){} |
| 123361 | iFile++; |
| 123362 | if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; |
| 123363 | for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ |
| 123364 | if( sqlite3Isalpha(c) ){ |
| 123365 | zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; |
| @@ -123659,53 +124576,54 @@ | |
| 123659 | ** that script and rerun it. |
| 123660 | */ |
| 123661 | |
| 123662 | /* The various pragma types */ |
| 123663 | #define PragTyp_ACTIVATE_EXTENSIONS 0 |
| 123664 | #define PragTyp_HEADER_VALUE 1 |
| 123665 | #define PragTyp_AUTO_VACUUM 2 |
| 123666 | #define PragTyp_FLAG 3 |
| 123667 | #define PragTyp_BUSY_TIMEOUT 4 |
| 123668 | #define PragTyp_CACHE_SIZE 5 |
| 123669 | #define PragTyp_CACHE_SPILL 6 |
| 123670 | #define PragTyp_CASE_SENSITIVE_LIKE 7 |
| 123671 | #define PragTyp_COLLATION_LIST 8 |
| 123672 | #define PragTyp_COMPILE_OPTIONS 9 |
| 123673 | #define PragTyp_DATA_STORE_DIRECTORY 10 |
| 123674 | #define PragTyp_DATABASE_LIST 11 |
| 123675 | #define PragTyp_DEFAULT_CACHE_SIZE 12 |
| 123676 | #define PragTyp_ENCODING 13 |
| 123677 | #define PragTyp_FOREIGN_KEY_CHECK 14 |
| 123678 | #define PragTyp_FOREIGN_KEY_LIST 15 |
| 123679 | #define PragTyp_FUNCTION_LIST 16 |
| 123680 | #define PragTyp_HARD_HEAP_LIMIT 17 |
| 123681 | #define PragTyp_INCREMENTAL_VACUUM 18 |
| 123682 | #define PragTyp_INDEX_INFO 19 |
| 123683 | #define PragTyp_INDEX_LIST 20 |
| 123684 | #define PragTyp_INTEGRITY_CHECK 21 |
| 123685 | #define PragTyp_JOURNAL_MODE 22 |
| 123686 | #define PragTyp_JOURNAL_SIZE_LIMIT 23 |
| 123687 | #define PragTyp_LOCK_PROXY_FILE 24 |
| 123688 | #define PragTyp_LOCKING_MODE 25 |
| 123689 | #define PragTyp_PAGE_COUNT 26 |
| 123690 | #define PragTyp_MMAP_SIZE 27 |
| 123691 | #define PragTyp_MODULE_LIST 28 |
| 123692 | #define PragTyp_OPTIMIZE 29 |
| 123693 | #define PragTyp_PAGE_SIZE 30 |
| 123694 | #define PragTyp_PRAGMA_LIST 31 |
| 123695 | #define PragTyp_SECURE_DELETE 32 |
| 123696 | #define PragTyp_SHRINK_MEMORY 33 |
| 123697 | #define PragTyp_SOFT_HEAP_LIMIT 34 |
| 123698 | #define PragTyp_SYNCHRONOUS 35 |
| 123699 | #define PragTyp_TABLE_INFO 36 |
| 123700 | #define PragTyp_TEMP_STORE 37 |
| 123701 | #define PragTyp_TEMP_STORE_DIRECTORY 38 |
| 123702 | #define PragTyp_THREADS 39 |
| 123703 | #define PragTyp_WAL_AUTOCHECKPOINT 40 |
| 123704 | #define PragTyp_WAL_CHECKPOINT 41 |
| 123705 | #define PragTyp_LOCK_STATUS 42 |
| 123706 | #define PragTyp_STATS 43 |
| 123707 | |
| 123708 | /* Property flags associated with various pragma. */ |
| 123709 | #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ |
| 123710 | #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */ |
| 123711 | #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */ |
| @@ -123792,10 +124710,15 @@ | |
| 123792 | /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS, |
| 123793 | /* ePragFlg: */ 0, |
| 123794 | /* ColNames: */ 0, 0, |
| 123795 | /* iArg: */ 0 }, |
| 123796 | #endif |
| 123797 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 123798 | {/* zName: */ "application_id", |
| 123799 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 123800 | /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0, |
| 123801 | /* ColNames: */ 0, 0, |
| @@ -124292,11 +125215,11 @@ | |
| 124292 | /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, |
| 124293 | /* ColNames: */ 0, 0, |
| 124294 | /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, |
| 124295 | #endif |
| 124296 | }; |
| 124297 | /* Number of pragmas: 66 on by default, 76 total. */ |
| 124298 | |
| 124299 | /************** End of pragma.h **********************************************/ |
| 124300 | /************** Continuing where we left off in pragma.c *********************/ |
| 124301 | |
| 124302 | /* |
| @@ -124822,11 +125745,11 @@ | |
| 124822 | }else{ |
| 124823 | /* Malloc may fail when setting the page-size, as there is an internal |
| 124824 | ** buffer that the pager module resizes using sqlite3_realloc(). |
| 124825 | */ |
| 124826 | db->nextPagesize = sqlite3Atoi(zRight); |
| 124827 | if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){ |
| 124828 | sqlite3OomFault(db); |
| 124829 | } |
| 124830 | } |
| 124831 | break; |
| 124832 | } |
| @@ -125996,11 +126919,10 @@ | |
| 125996 | sqlite3ResolvePartIdxLabel(pParse, jmp3); |
| 125997 | } |
| 125998 | } |
| 125999 | sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); |
| 126000 | sqlite3VdbeJumpHere(v, loopTop-1); |
| 126001 | #ifndef SQLITE_OMIT_BTREECOUNT |
| 126002 | if( !isQuick ){ |
| 126003 | sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); |
| 126004 | for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ |
| 126005 | if( pPk==pIdx ) continue; |
| 126006 | sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); |
| @@ -126010,11 +126932,10 @@ | |
| 126010 | sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); |
| 126011 | integrityCheckResultRow(v); |
| 126012 | sqlite3VdbeJumpHere(v, addr); |
| 126013 | } |
| 126014 | } |
| 126015 | #endif /* SQLITE_OMIT_BTREECOUNT */ |
| 126016 | } |
| 126017 | } |
| 126018 | { |
| 126019 | static const int iLn = VDBE_OFFSET_LINENO(2); |
| 126020 | static const VdbeOpList endCode[] = { |
| @@ -126444,10 +127365,29 @@ | |
| 126444 | sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff)); |
| 126445 | } |
| 126446 | returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1)); |
| 126447 | break; |
| 126448 | } |
| 126449 | |
| 126450 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 126451 | /* |
| 126452 | ** Report the current state of file logs for all databases |
| 126453 | */ |
| @@ -129768,10 +130708,11 @@ | |
| 129768 | } |
| 129769 | zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); |
| 129770 | if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt); |
| 129771 | } |
| 129772 | pCol->zName = zName; |
| 129773 | sqlite3ColumnPropertiesFromName(0, pCol); |
| 129774 | if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){ |
| 129775 | sqlite3OomFault(db); |
| 129776 | } |
| 129777 | } |
| @@ -131221,11 +132162,14 @@ | |
| 131221 | if( ExprHasProperty(pExpr, EP_FromJoin) |
| 131222 | && pExpr->iRightJoinTable==pSubst->iTable |
| 131223 | ){ |
| 131224 | pExpr->iRightJoinTable = pSubst->iNewTable; |
| 131225 | } |
| 131226 | if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){ |
| 131227 | if( pExpr->iColumn<0 ){ |
| 131228 | pExpr->op = TK_NULL; |
| 131229 | }else{ |
| 131230 | Expr *pNew; |
| 131231 | Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; |
| @@ -131239,10 +132183,11 @@ | |
| 131239 | if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ |
| 131240 | memset(&ifNullRow, 0, sizeof(ifNullRow)); |
| 131241 | ifNullRow.op = TK_IF_NULL_ROW; |
| 131242 | ifNullRow.pLeft = pCopy; |
| 131243 | ifNullRow.iTable = pSubst->iNewTable; |
| 131244 | pCopy = &ifNullRow; |
| 131245 | } |
| 131246 | testcase( ExprHasProperty(pCopy, EP_Subquery) ); |
| 131247 | pNew = sqlite3ExprDup(db, pCopy, 0); |
| 131248 | if( pNew && pSubst->isLeftJoin ){ |
| @@ -133133,10 +134078,11 @@ | |
| 133133 | Vdbe *v = pParse->pVdbe; |
| 133134 | int i; |
| 133135 | struct AggInfo_func *pFunc; |
| 133136 | int nReg = pAggInfo->nFunc + pAggInfo->nColumn; |
| 133137 | if( nReg==0 ) return; |
| 133138 | #ifdef SQLITE_DEBUG |
| 133139 | /* Verify that all AggInfo registers are within the range specified by |
| 133140 | ** AggInfo.mnReg..AggInfo.mxReg */ |
| 133141 | assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); |
| 133142 | for(i=0; i<pAggInfo->nColumn; i++){ |
| @@ -134402,11 +135348,10 @@ | |
| 134402 | VdbeComment((v, "indicate accumulator empty")); |
| 134403 | sqlite3VdbeAddOp1(v, OP_Return, regReset); |
| 134404 | |
| 134405 | } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ |
| 134406 | else { |
| 134407 | #ifndef SQLITE_OMIT_BTREECOUNT |
| 134408 | Table *pTab; |
| 134409 | if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ |
| 134410 | /* If isSimpleCount() returns a pointer to a Table structure, then |
| 134411 | ** the SQL statement is of the form: |
| 134412 | ** |
| @@ -134438,17 +135383,19 @@ | |
| 134438 | ** |
| 134439 | ** In practice the KeyInfo structure will not be used. It is only |
| 134440 | ** passed to keep OP_OpenRead happy. |
| 134441 | */ |
| 134442 | if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab); |
| 134443 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 134444 | if( pIdx->bUnordered==0 |
| 134445 | && pIdx->szIdxRow<pTab->szTabRow |
| 134446 | && pIdx->pPartIdxWhere==0 |
| 134447 | && (!pBest || pIdx->szIdxRow<pBest->szIdxRow) |
| 134448 | ){ |
| 134449 | pBest = pIdx; |
| 134450 | } |
| 134451 | } |
| 134452 | if( pBest ){ |
| 134453 | iRoot = pBest->tnum; |
| 134454 | pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); |
| @@ -134460,13 +135407,11 @@ | |
| 134460 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); |
| 134461 | } |
| 134462 | sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); |
| 134463 | sqlite3VdbeAddOp1(v, OP_Close, iCsr); |
| 134464 | explainSimpleCount(pParse, pTab, pBest); |
| 134465 | }else |
| 134466 | #endif /* SQLITE_OMIT_BTREECOUNT */ |
| 134467 | { |
| 134468 | int regAcc = 0; /* "populate accumulators" flag */ |
| 134469 | |
| 134470 | /* If there are accumulator registers but no min() or max() functions |
| 134471 | ** without FILTER clauses, allocate register regAcc. Register regAcc |
| 134472 | ** will contain 0 the first time the inner loop runs, and 1 thereafter. |
| @@ -137542,11 +138487,11 @@ | |
| 137542 | sqlite3SetString(pzErrMsg, db, "output file already exists"); |
| 137543 | goto end_of_vacuum; |
| 137544 | } |
| 137545 | db->mDbFlags |= DBFLAG_VacuumInto; |
| 137546 | } |
| 137547 | nRes = sqlite3BtreeGetOptimalReserve(pMain); |
| 137548 | |
| 137549 | sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); |
| 137550 | sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); |
| 137551 | sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); |
| 137552 | |
| @@ -137686,11 +138631,11 @@ | |
| 137686 | db->mDbFlags = saved_mDbFlags; |
| 137687 | db->flags = saved_flags; |
| 137688 | db->nChange = saved_nChange; |
| 137689 | db->nTotalChange = saved_nTotalChange; |
| 137690 | db->mTrace = saved_mTrace; |
| 137691 | sqlite3BtreeSetPageSize(pMain, -1, -1, 1); |
| 137692 | |
| 137693 | /* Currently there is an SQL level transaction open on the vacuum |
| 137694 | ** database. No locks are held on any other files (since the main file |
| 137695 | ** was committed at the btree level). So it safe to end the transaction |
| 137696 | ** by manually setting the autoCommit flag to true and detaching the |
| @@ -156491,14 +157436,15 @@ | |
| 156491 | ** simplify to constants 0 (false) and 1 (true), respectively, |
| 156492 | ** regardless of the value of expr1. |
| 156493 | */ |
| 156494 | sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202); |
| 156495 | yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0"); |
| 156496 | }else if( 0 && yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){ |
| 156497 | Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr; |
| 156498 | yymsp[-1].minor.yy242->a[0].pExpr = 0; |
| 156499 | sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); |
| 156500 | yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS); |
| 156501 | if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); |
| 156502 | }else{ |
| 156503 | yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); |
| 156504 | if( yymsp[-4].minor.yy202 ){ |
| @@ -158376,11 +159322,11 @@ | |
| 158376 | VVA_ONLY( u8 startedWithOom = db->mallocFailed ); |
| 158377 | |
| 158378 | assert( zSql!=0 ); |
| 158379 | mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; |
| 158380 | if( db->nVdbeActive==0 ){ |
| 158381 | db->u1.isInterrupted = 0; |
| 158382 | } |
| 158383 | pParse->rc = SQLITE_OK; |
| 158384 | pParse->zTail = zSql; |
| 158385 | assert( pzErrMsg!=0 ); |
| 158386 | #ifdef SQLITE_DEBUG |
| @@ -158421,11 +159367,11 @@ | |
| 158421 | ); |
| 158422 | #else |
| 158423 | if( tokenType>=TK_SPACE ){ |
| 158424 | assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); |
| 158425 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 158426 | if( db->u1.isInterrupted ){ |
| 158427 | pParse->rc = SQLITE_INTERRUPT; |
| 158428 | break; |
| 158429 | } |
| 158430 | if( tokenType==TK_SPACE ){ |
| 158431 | zSql += n; |
| @@ -159088,19 +160034,82 @@ | |
| 159088 | |
| 159089 | |
| 159090 | /************** End of sqliteicu.h *******************************************/ |
| 159091 | /************** Continuing where we left off in main.c ***********************/ |
| 159092 | #endif |
| 159093 | #ifdef SQLITE_ENABLE_JSON1 |
| 159094 | SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*); |
| 159095 | #endif |
| 159096 | #ifdef SQLITE_ENABLE_STMTVTAB |
| 159097 | SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*); |
| 159098 | #endif |
| 159099 | #ifdef SQLITE_ENABLE_FTS5 |
| 159100 | SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); |
| 159101 | #endif |
| 159102 | |
| 159103 | #ifndef SQLITE_AMALGAMATION |
| 159104 | /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant |
| 159105 | ** contains the text of SQLITE_VERSION macro. |
| 159106 | */ |
| @@ -160614,12 +161623,11 @@ | |
| 160614 | ** Return non-zero to retry the lock. Return zero to stop trying |
| 160615 | ** and cause SQLite to return SQLITE_BUSY. |
| 160616 | */ |
| 160617 | static int sqliteDefaultBusyCallback( |
| 160618 | void *ptr, /* Database connection */ |
| 160619 | int count, /* Number of times table has been busy */ |
| 160620 | sqlite3_file *pFile /* The file on which the lock occurred */ |
| 160621 | ){ |
| 160622 | #if SQLITE_OS_WIN || HAVE_USLEEP |
| 160623 | /* This case is for systems that have support for sleeping for fractions of |
| 160624 | ** a second. Examples: All windows systems, unix systems with usleep() */ |
| 160625 | static const u8 delays[] = |
| @@ -160629,23 +161637,10 @@ | |
| 160629 | # define NDELAY ArraySize(delays) |
| 160630 | sqlite3 *db = (sqlite3 *)ptr; |
| 160631 | int tmout = db->busyTimeout; |
| 160632 | int delay, prior; |
| 160633 | |
| 160634 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 160635 | if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){ |
| 160636 | if( count ){ |
| 160637 | tmout = 0; |
| 160638 | sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout); |
| 160639 | return 0; |
| 160640 | }else{ |
| 160641 | return 1; |
| 160642 | } |
| 160643 | } |
| 160644 | #else |
| 160645 | UNUSED_PARAMETER(pFile); |
| 160646 | #endif |
| 160647 | assert( count>=0 ); |
| 160648 | if( count < NDELAY ){ |
| 160649 | delay = delays[count]; |
| 160650 | prior = totals[count]; |
| 160651 | }else{ |
| @@ -160661,11 +161656,10 @@ | |
| 160661 | #else |
| 160662 | /* This case for unix systems that lack usleep() support. Sleeping |
| 160663 | ** must be done in increments of whole seconds */ |
| 160664 | sqlite3 *db = (sqlite3 *)ptr; |
| 160665 | int tmout = ((sqlite3 *)ptr)->busyTimeout; |
| 160666 | UNUSED_PARAMETER(pFile); |
| 160667 | if( (count+1)*1000 > tmout ){ |
| 160668 | return 0; |
| 160669 | } |
| 160670 | sqlite3OsSleep(db->pVfs, 1000000); |
| 160671 | return 1; |
| @@ -160679,23 +161673,14 @@ | |
| 160679 | ** lock on VFS file pFile. |
| 160680 | ** |
| 160681 | ** If this routine returns non-zero, the lock is retried. If it |
| 160682 | ** returns 0, the operation aborts with an SQLITE_BUSY error. |
| 160683 | */ |
| 160684 | SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){ |
| 160685 | int rc; |
| 160686 | if( p->xBusyHandler==0 || p->nBusy<0 ) return 0; |
| 160687 | if( p->bExtraFileArg ){ |
| 160688 | /* Add an extra parameter with the pFile pointer to the end of the |
| 160689 | ** callback argument list */ |
| 160690 | int (*xTra)(void*,int,sqlite3_file*); |
| 160691 | xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler; |
| 160692 | rc = xTra(p->pBusyArg, p->nBusy, pFile); |
| 160693 | }else{ |
| 160694 | /* Legacy style busy handler callback */ |
| 160695 | rc = p->xBusyHandler(p->pBusyArg, p->nBusy); |
| 160696 | } |
| 160697 | if( rc==0 ){ |
| 160698 | p->nBusy = -1; |
| 160699 | }else{ |
| 160700 | p->nBusy++; |
| 160701 | } |
| @@ -160716,11 +161701,10 @@ | |
| 160716 | #endif |
| 160717 | sqlite3_mutex_enter(db->mutex); |
| 160718 | db->busyHandler.xBusyHandler = xBusy; |
| 160719 | db->busyHandler.pBusyArg = pArg; |
| 160720 | db->busyHandler.nBusy = 0; |
| 160721 | db->busyHandler.bExtraFileArg = 0; |
| 160722 | db->busyTimeout = 0; |
| 160723 | sqlite3_mutex_leave(db->mutex); |
| 160724 | return SQLITE_OK; |
| 160725 | } |
| 160726 | |
| @@ -160767,11 +161751,10 @@ | |
| 160767 | #endif |
| 160768 | if( ms>0 ){ |
| 160769 | sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, |
| 160770 | (void*)db); |
| 160771 | db->busyTimeout = ms; |
| 160772 | db->busyHandler.bExtraFileArg = 1; |
| 160773 | }else{ |
| 160774 | sqlite3_busy_handler(db, 0, 0); |
| 160775 | } |
| 160776 | return SQLITE_OK; |
| 160777 | } |
| @@ -160784,11 +161767,11 @@ | |
| 160784 | if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ |
| 160785 | (void)SQLITE_MISUSE_BKPT; |
| 160786 | return; |
| 160787 | } |
| 160788 | #endif |
| 160789 | db->u1.isInterrupted = 1; |
| 160790 | } |
| 160791 | |
| 160792 | |
| 160793 | /* |
| 160794 | ** This function is exactly the same as sqlite3_create_function(), except |
| @@ -161406,11 +162389,11 @@ | |
| 161406 | rc = sqlite3ApiExit(db, rc); |
| 161407 | |
| 161408 | /* If there are no active statements, clear the interrupt flag at this |
| 161409 | ** point. */ |
| 161410 | if( db->nVdbeActive==0 ){ |
| 161411 | db->u1.isInterrupted = 0; |
| 161412 | } |
| 161413 | |
| 161414 | sqlite3_mutex_leave(db->mutex); |
| 161415 | return rc; |
| 161416 | #endif |
| @@ -162093,10 +163076,11 @@ | |
| 162093 | sqlite3 *db; /* Store allocated handle here */ |
| 162094 | int rc; /* Return code */ |
| 162095 | int isThreadsafe; /* True for threadsafe connections */ |
| 162096 | char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ |
| 162097 | char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ |
| 162098 | |
| 162099 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 162100 | if( ppDb==0 ) return SQLITE_MISUSE_BKPT; |
| 162101 | #endif |
| 162102 | *ppDb = 0; |
| @@ -162241,10 +163225,13 @@ | |
| 162241 | | SQLITE_EnableQPSG |
| 162242 | #endif |
| 162243 | #if defined(SQLITE_DEFAULT_DEFENSIVE) |
| 162244 | | SQLITE_Defensive |
| 162245 | #endif |
| 162246 | ; |
| 162247 | sqlite3HashInit(&db->aCollSeq); |
| 162248 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 162249 | sqlite3HashInit(&db->aModule); |
| 162250 | #endif |
| @@ -162333,18 +163320,15 @@ | |
| 162333 | */ |
| 162334 | sqlite3Error(db, SQLITE_OK); |
| 162335 | sqlite3RegisterPerConnectionBuiltinFunctions(db); |
| 162336 | rc = sqlite3_errcode(db); |
| 162337 | |
| 162338 | #ifdef SQLITE_ENABLE_FTS5 |
| 162339 | /* Register any built-in FTS5 module before loading the automatic |
| 162340 | ** extensions. This allows automatic extensions to register FTS5 |
| 162341 | ** tokenizers and auxiliary functions. */ |
| 162342 | if( !db->mallocFailed && rc==SQLITE_OK ){ |
| 162343 | rc = sqlite3Fts5Init(db); |
| 162344 | } |
| 162345 | #endif |
| 162346 | |
| 162347 | /* Load automatic extensions - extensions that have been registered |
| 162348 | ** using the sqlite3_automatic_extension() API. |
| 162349 | */ |
| 162350 | if( rc==SQLITE_OK ){ |
| @@ -162353,66 +163337,10 @@ | |
| 162353 | if( rc!=SQLITE_OK ){ |
| 162354 | goto opendb_out; |
| 162355 | } |
| 162356 | } |
| 162357 | |
| 162358 | #ifdef SQLITE_ENABLE_FTS1 |
| 162359 | if( !db->mallocFailed ){ |
| 162360 | extern int sqlite3Fts1Init(sqlite3*); |
| 162361 | rc = sqlite3Fts1Init(db); |
| 162362 | } |
| 162363 | #endif |
| 162364 | |
| 162365 | #ifdef SQLITE_ENABLE_FTS2 |
| 162366 | if( !db->mallocFailed && rc==SQLITE_OK ){ |
| 162367 | extern int sqlite3Fts2Init(sqlite3*); |
| 162368 | rc = sqlite3Fts2Init(db); |
| 162369 | } |
| 162370 | #endif |
| 162371 | |
| 162372 | #ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */ |
| 162373 | if( !db->mallocFailed && rc==SQLITE_OK ){ |
| 162374 | rc = sqlite3Fts3Init(db); |
| 162375 | } |
| 162376 | #endif |
| 162377 | |
| 162378 | #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) |
| 162379 | if( !db->mallocFailed && rc==SQLITE_OK ){ |
| 162380 | rc = sqlite3IcuInit(db); |
| 162381 | } |
| 162382 | #endif |
| 162383 | |
| 162384 | #ifdef SQLITE_ENABLE_RTREE |
| 162385 | if( !db->mallocFailed && rc==SQLITE_OK){ |
| 162386 | rc = sqlite3RtreeInit(db); |
| 162387 | } |
| 162388 | #endif |
| 162389 | |
| 162390 | #ifdef SQLITE_ENABLE_DBPAGE_VTAB |
| 162391 | if( !db->mallocFailed && rc==SQLITE_OK){ |
| 162392 | rc = sqlite3DbpageRegister(db); |
| 162393 | } |
| 162394 | #endif |
| 162395 | |
| 162396 | #ifdef SQLITE_ENABLE_DBSTAT_VTAB |
| 162397 | if( !db->mallocFailed && rc==SQLITE_OK){ |
| 162398 | rc = sqlite3DbstatRegister(db); |
| 162399 | } |
| 162400 | #endif |
| 162401 | |
| 162402 | #ifdef SQLITE_ENABLE_JSON1 |
| 162403 | if( !db->mallocFailed && rc==SQLITE_OK){ |
| 162404 | rc = sqlite3Json1Init(db); |
| 162405 | } |
| 162406 | #endif |
| 162407 | |
| 162408 | #ifdef SQLITE_ENABLE_STMTVTAB |
| 162409 | if( !db->mallocFailed && rc==SQLITE_OK){ |
| 162410 | rc = sqlite3StmtVtabInit(db); |
| 162411 | } |
| 162412 | #endif |
| 162413 | |
| 162414 | #ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS |
| 162415 | /* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time |
| 162416 | ** option gives access to internal functions by default. |
| 162417 | ** Testing use only!!! */ |
| 162418 | db->mDbFlags |= DBFLAG_InternalFunc; |
| @@ -162893,10 +163821,17 @@ | |
| 162893 | }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ |
| 162894 | *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); |
| 162895 | rc = SQLITE_OK; |
| 162896 | }else if( op==SQLITE_FCNTL_DATA_VERSION ){ |
| 162897 | *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager); |
| 162898 | rc = SQLITE_OK; |
| 162899 | }else{ |
| 162900 | rc = sqlite3OsFileControl(fd, op, pArg); |
| 162901 | } |
| 162902 | sqlite3BtreeLeave(pBtree); |
| @@ -163110,24 +164045,10 @@ | |
| 163110 | case SQLITE_TESTCTRL_BYTEORDER: { |
| 163111 | rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN; |
| 163112 | break; |
| 163113 | } |
| 163114 | |
| 163115 | /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N) |
| 163116 | ** |
| 163117 | ** Set the nReserve size to N for the main database on the database |
| 163118 | ** connection db. |
| 163119 | */ |
| 163120 | case SQLITE_TESTCTRL_RESERVE: { |
| 163121 | sqlite3 *db = va_arg(ap, sqlite3*); |
| 163122 | int x = va_arg(ap,int); |
| 163123 | sqlite3_mutex_enter(db->mutex); |
| 163124 | sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0); |
| 163125 | sqlite3_mutex_leave(db->mutex); |
| 163126 | break; |
| 163127 | } |
| 163128 | |
| 163129 | /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N) |
| 163130 | ** |
| 163131 | ** Enable or disable various optimizations for testing purposes. The |
| 163132 | ** argument N is a bitmask of optimizations to be disabled. For normal |
| 163133 | ** operation N should be 0. The idea is that a test program (like the |
| @@ -165185,10 +166106,11 @@ | |
| 165185 | SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); |
| 165186 | SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); |
| 165187 | SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *); |
| 165188 | SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*); |
| 165189 | SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc); |
| 165190 | |
| 165191 | /* fts3_tokenizer.c */ |
| 165192 | SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); |
| 165193 | SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); |
| 165194 | SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, |
| @@ -165916,10 +166838,26 @@ | |
| 165916 | fts3Appendf(pRc, &zRet, ", ?"); |
| 165917 | } |
| 165918 | sqlite3_free(zFree); |
| 165919 | return zRet; |
| 165920 | } |
| 165921 | |
| 165922 | /* |
| 165923 | ** This function interprets the string at (*pp) as a non-negative integer |
| 165924 | ** value. It reads the integer and sets *pnOut to the value read, then |
| 165925 | ** sets *pp to point to the byte immediately following the last byte of |
| @@ -165932,23 +166870,21 @@ | |
| 165932 | ** |
| 165933 | ** This function is used when parsing the "prefix=" FTS4 parameter. |
| 165934 | */ |
| 165935 | static int fts3GobbleInt(const char **pp, int *pnOut){ |
| 165936 | const int MAX_NPREFIX = 10000000; |
| 165937 | const char *p; /* Iterator pointer */ |
| 165938 | int nInt = 0; /* Output value */ |
| 165939 | |
| 165940 | for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ |
| 165941 | nInt = nInt * 10 + (p[0] - '0'); |
| 165942 | if( nInt>MAX_NPREFIX ){ |
| 165943 | nInt = 0; |
| 165944 | break; |
| 165945 | } |
| 165946 | } |
| 165947 | if( p==*pp ) return SQLITE_ERROR; |
| 165948 | *pnOut = nInt; |
| 165949 | *pp = p; |
| 165950 | return SQLITE_OK; |
| 165951 | } |
| 165952 | |
| 165953 | /* |
| 165954 | ** This function is called to allocate an array of Fts3Index structures |
| @@ -167126,11 +168062,13 @@ | |
| 167126 | static void fts3ReadNextPos( |
| 167127 | char **pp, /* IN/OUT: Pointer into position-list buffer */ |
| 167128 | sqlite3_int64 *pi /* IN/OUT: Value read from position-list */ |
| 167129 | ){ |
| 167130 | if( (**pp)&0xFE ){ |
| 167131 | fts3GetDeltaVarint(pp, pi); |
| 167132 | *pi -= 2; |
| 167133 | }else{ |
| 167134 | *pi = POSITION_LIST_END; |
| 167135 | } |
| 167136 | } |
| @@ -172026,14 +172964,11 @@ | |
| 172026 | |
| 172027 | /* If this is a "NEAR" keyword, check for an explicit nearness. */ |
| 172028 | if( pKey->eType==FTSQUERY_NEAR ){ |
| 172029 | assert( nKey==4 ); |
| 172030 | if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){ |
| 172031 | nNear = 0; |
| 172032 | for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){ |
| 172033 | nNear = nNear * 10 + (zInput[nKey] - '0'); |
| 172034 | } |
| 172035 | } |
| 172036 | } |
| 172037 | |
| 172038 | /* At this point this is probably a keyword. But for that to be true, |
| 172039 | ** the next byte must contain either whitespace, an open or close |
| @@ -176558,10 +177493,11 @@ | |
| 176558 | ** b-tree node. And that the final byte of the doclist is 0x00. If either |
| 176559 | ** of these statements is untrue, then the data structure is corrupt. |
| 176560 | */ |
| 176561 | if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode) |
| 176562 | || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) |
| 176563 | ){ |
| 176564 | return FTS_CORRUPT_VTAB; |
| 176565 | } |
| 176566 | return SQLITE_OK; |
| 176567 | } |
| @@ -178211,25 +179147,25 @@ | |
| 178211 | ){ |
| 178212 | const unsigned char *zText = sqlite3_column_text(pStmt, iCol); |
| 178213 | if( zText ){ |
| 178214 | int i; |
| 178215 | int iMul = 1; |
| 178216 | i64 iVal = 0; |
| 178217 | for(i=0; zText[i]>='0' && zText[i]<='9'; i++){ |
| 178218 | iVal = iVal*10 + (zText[i] - '0'); |
| 178219 | } |
| 178220 | *piEndBlock = iVal; |
| 178221 | while( zText[i]==' ' ) i++; |
| 178222 | iVal = 0; |
| 178223 | if( zText[i]=='-' ){ |
| 178224 | i++; |
| 178225 | iMul = -1; |
| 178226 | } |
| 178227 | for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){ |
| 178228 | iVal = iVal*10 + (zText[i] - '0'); |
| 178229 | } |
| 178230 | *pnByte = (iVal * (i64)iMul); |
| 178231 | } |
| 178232 | } |
| 178233 | |
| 178234 | |
| 178235 | /* |
| @@ -223728,11 +224664,11 @@ | |
| 223728 | int nArg, /* Number of args */ |
| 223729 | sqlite3_value **apUnused /* Function arguments */ |
| 223730 | ){ |
| 223731 | assert( nArg==0 ); |
| 223732 | UNUSED_PARAM2(nArg, apUnused); |
| 223733 | sqlite3_result_text(pCtx, "fts5: 2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525", -1, SQLITE_TRANSIENT); |
| 223734 | } |
| 223735 | |
| 223736 | /* |
| 223737 | ** Return true if zName is the extension on one of the shadow tables used |
| 223738 | ** by this module. |
| @@ -228379,11 +229315,12 @@ | |
| 228379 | } |
| 228380 | case STMT_COLUMN_BUSY: { |
| 228381 | sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt)); |
| 228382 | break; |
| 228383 | } |
| 228384 | case STMT_COLUMN_MEM: { |
| 228385 | i = SQLITE_STMTSTATUS_MEMUSED + |
| 228386 | STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; |
| 228387 | /* Fall thru */ |
| 228388 | } |
| 228389 | case STMT_COLUMN_NSCAN: |
| @@ -228510,12 +229447,12 @@ | |
| 228510 | } |
| 228511 | #endif /* SQLITE_CORE */ |
| 228512 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 228513 | |
| 228514 | /************** End of stmt.c ************************************************/ |
| 228515 | #if __LINE__!=228515 |
| 228516 | #undef SQLITE_SOURCE_ID |
| 228517 | #define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84alt2" |
| 228518 | #endif |
| 228519 | /* Return the source-id for this library */ |
| 228520 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 228521 | /************************** End of sqlite3.c ******************************/ |
| 228522 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -216,10 +216,13 @@ | |
| 216 | "ENABLE_ATOMIC_WRITE", |
| 217 | #endif |
| 218 | #if SQLITE_ENABLE_BATCH_ATOMIC_WRITE |
| 219 | "ENABLE_BATCH_ATOMIC_WRITE", |
| 220 | #endif |
| 221 | #if SQLITE_ENABLE_BYTECODE_VTAB |
| 222 | "ENABLE_BYTECODE_VTAB", |
| 223 | #endif |
| 224 | #if SQLITE_ENABLE_CEROD |
| 225 | "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), |
| 226 | #endif |
| 227 | #if SQLITE_ENABLE_COLUMN_METADATA |
| 228 | "ENABLE_COLUMN_METADATA", |
| @@ -534,13 +537,10 @@ | |
| 537 | "OMIT_BETWEEN_OPTIMIZATION", |
| 538 | #endif |
| 539 | #if SQLITE_OMIT_BLOB_LITERAL |
| 540 | "OMIT_BLOB_LITERAL", |
| 541 | #endif |
| 542 | #if SQLITE_OMIT_CAST |
| 543 | "OMIT_CAST", |
| 544 | #endif |
| 545 | #if SQLITE_OMIT_CHECK |
| 546 | "OMIT_CHECK", |
| @@ -1162,11 +1162,11 @@ | |
| 1162 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | */ |
| 1165 | #define SQLITE_VERSION "3.32.0" |
| 1166 | #define SQLITE_VERSION_NUMBER 3032000 |
| 1167 | #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" |
| 1168 | |
| 1169 | /* |
| 1170 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | ** |
| @@ -1336,30 +1336,26 @@ | |
| 1336 | ** for the [sqlite3] object. |
| 1337 | ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if |
| 1338 | ** the [sqlite3] object is successfully destroyed and all associated |
| 1339 | ** resources are deallocated. |
| 1340 | ** |
| 1341 | ** Ideally, applications should [sqlite3_finalize | finalize] all |
| 1342 | ** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and |
| 1343 | ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated |
| 1344 | ** with the [sqlite3] object prior to attempting to close the object. |
| 1345 | ** ^If the database connection is associated with unfinalized prepared |
| 1346 | ** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then |
| 1347 | ** sqlite3_close() will leave the database connection open and return |
| 1348 | ** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared |
| 1349 | ** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups, |
| 1350 | ** it returns [SQLITE_OK] regardless, but instead of deallocating the database |
| 1351 | ** connection immediately, it marks the database connection as an unusable |
| 1352 | ** "zombie" and makes arrangements to automatically deallocate the database |
| 1353 | ** connection after all prepared statements are finalized, all BLOB handles |
| 1354 | ** are closed, and all backups have finished. The sqlite3_close_v2() interface |
| 1355 | ** is intended for use with host languages that are garbage collected, and |
| 1356 | ** where the order in which destructors are called is arbitrary. |
| 1357 | ** |
| 1358 | ** ^If an [sqlite3] object is destroyed while a transaction is open, |
| 1359 | ** the transaction is automatically rolled back. |
| 1360 | ** |
| 1361 | ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] |
| @@ -1544,22 +1540,25 @@ | |
| 1540 | #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) |
| 1541 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1542 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1543 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1544 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1545 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 1546 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1547 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1548 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1549 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1550 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| 1551 | #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) |
| 1552 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| 1553 | #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) |
| 1554 | #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) |
| 1555 | #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ |
| 1556 | #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8)) |
| 1557 | #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) |
| 1558 | #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) |
| 1559 | #define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8)) |
| 1560 | #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) |
| 1561 | #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) |
| 1562 | #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) |
| 1563 | #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) |
| 1564 | #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) |
| @@ -2124,14 +2123,16 @@ | |
| 2123 | ** so that all subsequent write operations are independent. |
| 2124 | ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without |
| 2125 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. |
| 2126 | ** |
| 2127 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] |
| 2128 | ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS |
| 2129 | ** to block for up to M milliseconds before failing when attempting to |
| 2130 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 2131 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 2132 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 2133 | ** integer is overwritten with the previous value of M. |
| 2134 | ** |
| 2135 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 2136 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 2137 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 2138 | ** The "data version" for the pager is written into the pointer. The |
| @@ -2148,10 +2149,15 @@ | |
| 2149 | ** a single attached database that occur due to other database connections, |
| 2150 | ** but omits changes implemented by the database connection on which it is |
| 2151 | ** called. This file control is the only mechanism to detect changes that |
| 2152 | ** happen either internally or externally and that are associated with |
| 2153 | ** a particular attached database. |
| 2154 | ** |
| 2155 | ** <li>[[SQLITE_FCNTL_CKPT_START]] |
| 2156 | ** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint |
| 2157 | ** in wal mode before the client starts to copy pages from the wal |
| 2158 | ** file to the database file. |
| 2159 | ** |
| 2160 | ** <li>[[SQLITE_FCNTL_CKPT_DONE]] |
| 2161 | ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint |
| 2162 | ** in wal mode after the client has finished copying pages from the wal |
| 2163 | ** file to the database file, but before the *-shm file is updated to |
| @@ -2192,10 +2198,12 @@ | |
| 2198 | #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 |
| 2199 | #define SQLITE_FCNTL_LOCK_TIMEOUT 34 |
| 2200 | #define SQLITE_FCNTL_DATA_VERSION 35 |
| 2201 | #define SQLITE_FCNTL_SIZE_LIMIT 36 |
| 2202 | #define SQLITE_FCNTL_CKPT_DONE 37 |
| 2203 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 2204 | #define SQLITE_FCNTL_CKPT_START 39 |
| 2205 | |
| 2206 | /* deprecated names */ |
| 2207 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 2208 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 2209 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -4570,12 +4578,23 @@ | |
| 4578 | ** |
| 4579 | ** These are utility routines, useful to [VFS|custom VFS implementations], |
| 4580 | ** that check if a database file was a URI that contained a specific query |
| 4581 | ** parameter, and if so obtains the value of that query parameter. |
| 4582 | ** |
| 4583 | ** The first parameter to these interfaces (hereafter referred to |
| 4584 | ** as F) must be one of: |
| 4585 | ** <ul> |
| 4586 | ** <li> A database filename pointer created by the SQLite core and |
| 4587 | ** passed into the xOpen() method of a VFS implemention, or |
| 4588 | ** <li> A filename obtained from [sqlite3_db_filename()], or |
| 4589 | ** <li> A new filename constructed using [sqlite3_create_filename()]. |
| 4590 | ** </ul> |
| 4591 | ** If the F parameter is not one of the above, then the behavior is |
| 4592 | ** undefined and probably undesirable. Older versions of SQLite were |
| 4593 | ** more tolerant of invalid F parameters than newer versions. |
| 4594 | ** |
| 4595 | ** If F is a suitable filename (as described in the previous paragraph) |
| 4596 | ** and if P is the name of the query parameter, then |
| 4597 | ** sqlite3_uri_parameter(F,P) returns the value of the P |
| 4598 | ** parameter if it exists or a NULL pointer if P does not appear as a |
| 4599 | ** query parameter on F. If P is a query parameter of F and it |
| 4600 | ** has no explicit value, then sqlite3_uri_parameter(F,P) returns |
| @@ -4654,10 +4673,29 @@ | |
| 4673 | */ |
| 4674 | SQLITE_API const char *sqlite3_filename_database(const char*); |
| 4675 | SQLITE_API const char *sqlite3_filename_journal(const char*); |
| 4676 | SQLITE_API const char *sqlite3_filename_wal(const char*); |
| 4677 | |
| 4678 | /* |
| 4679 | ** CAPI3REF: Database File Corresponding To A Journal |
| 4680 | ** |
| 4681 | ** ^If X is the name of a rollback or WAL-mode journal file that is |
| 4682 | ** passed into the xOpen method of [sqlite3_vfs], then |
| 4683 | ** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file] |
| 4684 | ** object that represents the main database file. |
| 4685 | ** |
| 4686 | ** This routine is intended for use in custom [VFS] implementations |
| 4687 | ** only. It is not a general-purpose interface. |
| 4688 | ** The argument sqlite3_file_object(X) must be a filename pointer that |
| 4689 | ** has been passed into [sqlite3_vfs].xOpen method where the |
| 4690 | ** flags parameter to xOpen contains one of the bits |
| 4691 | ** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use |
| 4692 | ** of this routine results in undefined and probably undesirable |
| 4693 | ** behavior. |
| 4694 | */ |
| 4695 | SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); |
| 4696 | |
| 4697 | /* |
| 4698 | ** CAPI3REF: Create and Destroy VFS Filenames |
| 4699 | ** |
| 4700 | ** These interfces are provided for use by [VFS shim] implementations and |
| 4701 | ** are not useful outside of that context. |
| @@ -4688,11 +4726,11 @@ | |
| 4726 | ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may |
| 4727 | ** be NULL pointers, though they can be empty strings. |
| 4728 | ** |
| 4729 | ** The sqlite3_free_filename(Y) routine releases a memory allocation |
| 4730 | ** previously obtained from sqlite3_create_filename(). Invoking |
| 4731 | ** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op. |
| 4732 | ** |
| 4733 | ** If the Y parameter to sqlite3_free_filename(Y) is anything other |
| 4734 | ** than a NULL pointer or a pointer previously acquired from |
| 4735 | ** sqlite3_create_filename(), then bad things such as heap |
| 4736 | ** corruption or segfaults may occur. The value Y should be |
| @@ -5295,10 +5333,28 @@ | |
| 5333 | ** |
| 5334 | ** ^The third argument is the value to bind to the parameter. |
| 5335 | ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| 5336 | ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter |
| 5337 | ** is ignored and the end result is the same as sqlite3_bind_null(). |
| 5338 | ** ^If the third parameter to sqlite3_bind_text() is not NULL, then |
| 5339 | ** it should be a pointer to well-formed UTF8 text. |
| 5340 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 5341 | ** it should be a pointer to well-formed UTF16 text. |
| 5342 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 5343 | ** it should be a pointer to a well-formed unicode string that is |
| 5344 | ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 |
| 5345 | ** otherwise. |
| 5346 | ** |
| 5347 | ** [[byte-order determination rules]] ^The byte-order of |
| 5348 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 5349 | ** found in first character, which is removed, or in the absence of a BOM |
| 5350 | ** the byte order is the native byte order of the host |
| 5351 | ** machine for sqlite3_bind_text16() or the byte order specified in |
| 5352 | ** the 6th parameter for sqlite3_bind_text64().)^ |
| 5353 | ** ^If UTF16 input text contains invalid unicode |
| 5354 | ** characters, then SQLite might change those invalid characters |
| 5355 | ** into the unicode replacement character: U+FFFD. |
| 5356 | ** |
| 5357 | ** ^(In those routines that have a fourth argument, its value is the |
| 5358 | ** number of bytes in the parameter. To be clear: the value is the |
| 5359 | ** number of <u>bytes</u> in the value, not the number of characters.)^ |
| 5360 | ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| @@ -5308,11 +5364,11 @@ | |
| 5364 | ** the behavior is undefined. |
| 5365 | ** If a non-negative fourth parameter is provided to sqlite3_bind_text() |
| 5366 | ** or sqlite3_bind_text16() or sqlite3_bind_text64() then |
| 5367 | ** that parameter must be the byte offset |
| 5368 | ** where the NUL terminator would occur assuming the string were NUL |
| 5369 | ** terminated. If any NUL characters occurs at byte offsets less than |
| 5370 | ** the value of the fourth parameter then the resulting string value will |
| 5371 | ** contain embedded NULs. The result of expressions involving strings |
| 5372 | ** with embedded NULs is undefined. |
| 5373 | ** |
| 5374 | ** ^The fifth argument to the BLOB and string binding interfaces |
| @@ -6633,12 +6689,13 @@ | |
| 6689 | ** cause the implemented SQL function to throw an exception. |
| 6690 | ** ^SQLite uses the string pointed to by the |
| 6691 | ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() |
| 6692 | ** as the text of an error message. ^SQLite interprets the error |
| 6693 | ** message string from sqlite3_result_error() as UTF-8. ^SQLite |
| 6694 | ** interprets the string from sqlite3_result_error16() as UTF-16 using |
| 6695 | ** the same [byte-order determination rules] as [sqlite3_bind_text16()]. |
| 6696 | ** ^If the third parameter to sqlite3_result_error() |
| 6697 | ** or sqlite3_result_error16() is negative then SQLite takes as the error |
| 6698 | ** message all text up through the first zero character. |
| 6699 | ** ^If the third parameter to sqlite3_result_error() or |
| 6700 | ** sqlite3_result_error16() is non-negative then SQLite takes that many |
| 6701 | ** bytes (not characters) from the 2nd parameter as the error message. |
| @@ -6701,10 +6758,29 @@ | |
| 6758 | ** when it has finished using that result. |
| 6759 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces |
| 6760 | ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT |
| 6761 | ** then SQLite makes a copy of the result into space obtained |
| 6762 | ** from [sqlite3_malloc()] before it returns. |
| 6763 | ** |
| 6764 | ** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and |
| 6765 | ** sqlite3_result_text16be() routines, and for sqlite3_result_text64() |
| 6766 | ** when the encoding is not UTF8, if the input UTF16 begins with a |
| 6767 | ** byte-order mark (BOM, U+FEFF) then the BOM is removed from the |
| 6768 | ** string and the rest of the string is interpreted according to the |
| 6769 | ** byte-order specified by the BOM. ^The byte-order specified by |
| 6770 | ** the BOM at the beginning of the text overrides the byte-order |
| 6771 | ** specified by the interface procedure. ^So, for example, if |
| 6772 | ** sqlite3_result_text16le() is invoked with text that begins |
| 6773 | ** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the |
| 6774 | ** first two bytes of input are skipped and the remaining input |
| 6775 | ** is interpreted as UTF16BE text. |
| 6776 | ** |
| 6777 | ** ^For UTF16 input text to the sqlite3_result_text16(), |
| 6778 | ** sqlite3_result_text16be(), sqlite3_result_text16le(), and |
| 6779 | ** sqlite3_result_text64() routines, if the text contains invalid |
| 6780 | ** UTF16 characters, the invalid characters might be converted |
| 6781 | ** into the unicode replacement character, U+FFFD. |
| 6782 | ** |
| 6783 | ** ^The sqlite3_result_value() interface sets the result of |
| 6784 | ** the application-defined function to be a copy of the |
| 6785 | ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The |
| 6786 | ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] |
| @@ -8649,11 +8725,11 @@ | |
| 8725 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8726 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8727 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8728 | #define SQLITE_TESTCTRL_ASSERT 12 |
| 8729 | #define SQLITE_TESTCTRL_ALWAYS 13 |
| 8730 | #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ |
| 8731 | #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 |
| 8732 | #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ |
| 8733 | #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ |
| 8734 | #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 |
| 8735 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 |
| @@ -13369,10 +13445,25 @@ | |
| 13445 | #pragma warn -aus /* Assigned value is never used */ |
| 13446 | #pragma warn -csu /* Comparing signed and unsigned */ |
| 13447 | #pragma warn -spa /* Suspicious pointer arithmetic */ |
| 13448 | #endif |
| 13449 | |
| 13450 | /* |
| 13451 | ** WAL mode depends on atomic aligned 32-bit loads and stores in a few |
| 13452 | ** places. The following macros try to make this explicit. |
| 13453 | */ |
| 13454 | #ifndef __has_feature |
| 13455 | # define __has_feature(x) 0 /* compatibility with non-clang compilers */ |
| 13456 | #endif |
| 13457 | #if GCC_VERSION>=4007000 || __has_feature(c_atomic) |
| 13458 | # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) |
| 13459 | # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) |
| 13460 | #else |
| 13461 | # define AtomicLoad(PTR) (*(PTR)) |
| 13462 | # define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) |
| 13463 | #endif |
| 13464 | |
| 13465 | /* |
| 13466 | ** Include standard header files as necessary |
| 13467 | */ |
| 13468 | #ifdef HAVE_STDINT_H |
| 13469 | #include <stdint.h> |
| @@ -14446,11 +14537,10 @@ | |
| 14537 | typedef struct BusyHandler BusyHandler; |
| 14538 | struct BusyHandler { |
| 14539 | int (*xBusyHandler)(void *,int); /* The busy callback */ |
| 14540 | void *pBusyArg; /* First arg to busy callback */ |
| 14541 | int nBusy; /* Incremented with each busy call */ |
| 14542 | }; |
| 14543 | |
| 14544 | /* |
| 14545 | ** Name of the master database table. The master database table |
| 14546 | ** is a special table that holds the names and attributes of all |
| @@ -14704,11 +14794,11 @@ | |
| 14794 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); |
| 14795 | SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); |
| 14796 | SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); |
| 14797 | SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); |
| 14798 | SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); |
| 14799 | SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*); |
| 14800 | SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); |
| 14801 | SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); |
| 14802 | SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); |
| 14803 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*); |
| 14804 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); |
| @@ -14966,13 +15056,11 @@ | |
| 15056 | #ifndef NDEBUG |
| 15057 | SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); |
| 15058 | #endif |
| 15059 | SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*); |
| 15060 | |
| 15061 | SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*); |
| 15062 | |
| 15063 | #ifdef SQLITE_TEST |
| 15064 | SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); |
| 15065 | SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); |
| 15066 | #endif |
| @@ -15543,10 +15631,13 @@ | |
| 15631 | |
| 15632 | SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); |
| 15633 | SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*); |
| 15634 | |
| 15635 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 15636 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 15637 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); |
| 15638 | #endif |
| 15639 | |
| 15640 | /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on |
| 15641 | ** each VDBE opcode. |
| 15642 | ** |
| 15643 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| @@ -15828,17 +15919,25 @@ | |
| 15919 | SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); |
| 15920 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); |
| 15921 | SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); |
| 15922 | SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); |
| 15923 | # ifdef SQLITE_ENABLE_SNAPSHOT |
| 15924 | SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); |
| 15925 | SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); |
| 15926 | SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); |
| 15927 | SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); |
| 15928 | SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); |
| 15929 | # endif |
| 15930 | #endif |
| 15931 | |
| 15932 | #if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) |
| 15933 | SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int); |
| 15934 | SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*); |
| 15935 | #else |
| 15936 | # define sqlite3PagerWalWriteLock(y,z) SQLITE_OK |
| 15937 | # define sqlite3PagerWalDb(x,y) |
| 15938 | #endif |
| 15939 | |
| 15940 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 15941 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); |
| 15942 | #endif |
| 15943 | |
| @@ -15861,15 +15960,10 @@ | |
| 15960 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 15961 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 15962 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 15963 | SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); |
| 15964 | SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); |
| 15965 | |
| 15966 | /* Functions used to truncate the database file. */ |
| 15967 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 15968 | |
| 15969 | SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); |
| @@ -16799,10 +16893,11 @@ | |
| 16893 | Hash aFunc; /* Hash table of connection functions */ |
| 16894 | Hash aCollSeq; /* All collating sequences */ |
| 16895 | BusyHandler busyHandler; /* Busy callback */ |
| 16896 | Db aDbStatic[2]; /* Static space for the 2 default backends */ |
| 16897 | Savepoint *pSavepoint; /* List of active savepoints */ |
| 16898 | int nAnalysisLimit; /* Number of index rows to ANALYZE */ |
| 16899 | int busyTimeout; /* Busy handler timeout, in msec */ |
| 16900 | int nSavepoint; /* Number of non-transaction savepoints */ |
| 16901 | int nStatement; /* Number of nested statement-transactions */ |
| 16902 | i64 nDeferredCons; /* Net deferred constraints this transaction. */ |
| 16903 | i64 nDeferredImmCons; /* Net deferred immediate constraints */ |
| @@ -17209,10 +17304,11 @@ | |
| 17304 | Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */ |
| 17305 | char *zColl; /* Collating sequence. If NULL, use the default */ |
| 17306 | u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ |
| 17307 | char affinity; /* One of the SQLITE_AFF_... values */ |
| 17308 | u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ |
| 17309 | u8 hName; /* Column name hash for faster lookup */ |
| 17310 | u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ |
| 17311 | }; |
| 17312 | |
| 17313 | /* Allowed values for Column.colFlags: |
| 17314 | */ |
| @@ -19827,10 +19923,11 @@ | |
| 19923 | const char*, |
| 19924 | const char*, |
| 19925 | const char* |
| 19926 | ); |
| 19927 | SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*); |
| 19928 | SQLITE_PRIVATE u8 sqlite3StrIHash(const char*); |
| 19929 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 19930 | SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*); |
| 19931 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 19932 | SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); |
| 19933 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| @@ -19842,11 +19939,11 @@ | |
| 19939 | SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*); |
| 19940 | SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*); |
| 19941 | SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); |
| 19942 | SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*); |
| 19943 | SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*); |
| 19944 | SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*); |
| 19945 | SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*); |
| 19946 | SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *); |
| 19947 | SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB); |
| 19948 | SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*); |
| 19949 | SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*); |
| @@ -20578,11 +20675,12 @@ | |
| 20675 | /* |
| 20676 | ** VDBE_DISPLAY_P4 is true or false depending on whether or not the |
| 20677 | ** "explain" P4 display logic is enabled. |
| 20678 | */ |
| 20679 | #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ |
| 20680 | || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ |
| 20681 | || defined(SQLITE_ENABLE_BYTECODE_VTAB) |
| 20682 | # define VDBE_DISPLAY_P4 1 |
| 20683 | #else |
| 20684 | # define VDBE_DISPLAY_P4 0 |
| 20685 | #endif |
| 20686 | |
| @@ -21043,11 +21141,18 @@ | |
| 21141 | |
| 21142 | int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); |
| 21143 | SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*); |
| 21144 | SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*); |
| 21145 | SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*); |
| 21146 | #if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) |
| 21147 | SQLITE_PRIVATE int sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**); |
| 21148 | SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3*,Op*); |
| 21149 | #endif |
| 21150 | #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) |
| 21151 | SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*); |
| 21152 | #endif |
| 21153 | #if !defined(SQLITE_OMIT_EXPLAIN) |
| 21154 | SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*); |
| 21155 | #endif |
| 21156 | SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*); |
| 21157 | SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int); |
| 21158 | SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); |
| @@ -21085,11 +21190,11 @@ | |
| 21190 | SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); |
| 21191 | SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); |
| 21192 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 21193 | SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); |
| 21194 | #endif |
| 21195 | #if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) |
| 21196 | SQLITE_PRIVATE const char *sqlite3OpcodeName(int); |
| 21197 | #endif |
| 21198 | SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); |
| 21199 | SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n); |
| 21200 | SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); |
| @@ -22165,16 +22270,16 @@ | |
| 22270 | u8 nName; /* Length of th name */ |
| 22271 | char *zName; /* Name of the transformation */ |
| 22272 | double rLimit; /* Maximum NNN value for this transform */ |
| 22273 | double rXform; /* Constant used for this transform */ |
| 22274 | } aXformType[] = { |
| 22275 | { 0, 6, "second", 464269060800.0, 1000.0 }, |
| 22276 | { 0, 6, "minute", 7737817680.0, 60000.0 }, |
| 22277 | { 0, 4, "hour", 128963628.0, 3600000.0 }, |
| 22278 | { 0, 3, "day", 5373485.0, 86400000.0 }, |
| 22279 | { 1, 5, "month", 176546.0, 2592000000.0 }, |
| 22280 | { 2, 4, "year", 14713.0, 31536000000.0 }, |
| 22281 | }; |
| 22282 | |
| 22283 | /* |
| 22284 | ** Process a modifier to a date-time stamp. The modifiers are |
| 22285 | ** as follows: |
| @@ -27258,11 +27363,11 @@ | |
| 27363 | if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){ |
| 27364 | n = mem0.hardLimit; |
| 27365 | } |
| 27366 | mem0.alarmThreshold = n; |
| 27367 | nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27368 | AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed); |
| 27369 | sqlite3_mutex_leave(mem0.mutex); |
| 27370 | excess = sqlite3_memory_used() - n; |
| 27371 | if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); |
| 27372 | return priorLimit; |
| 27373 | } |
| @@ -27326,11 +27431,11 @@ | |
| 27431 | ** Return true if the heap is currently under memory pressure - in other |
| 27432 | ** words if the amount of heap used is close to the limit set by |
| 27433 | ** sqlite3_soft_heap_limit(). |
| 27434 | */ |
| 27435 | SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){ |
| 27436 | return AtomicLoad(&mem0.nearlyFull); |
| 27437 | } |
| 27438 | |
| 27439 | /* |
| 27440 | ** Deinitialize the memory allocation subsystem. |
| 27441 | */ |
| @@ -27390,21 +27495,21 @@ | |
| 27495 | |
| 27496 | sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); |
| 27497 | if( mem0.alarmThreshold>0 ){ |
| 27498 | sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27499 | if( nUsed >= mem0.alarmThreshold - nFull ){ |
| 27500 | AtomicStore(&mem0.nearlyFull, 1); |
| 27501 | sqlite3MallocAlarm(nFull); |
| 27502 | if( mem0.hardLimit ){ |
| 27503 | nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
| 27504 | if( nUsed >= mem0.hardLimit - nFull ){ |
| 27505 | *pp = 0; |
| 27506 | return; |
| 27507 | } |
| 27508 | } |
| 27509 | }else{ |
| 27510 | AtomicStore(&mem0.nearlyFull, 0); |
| 27511 | } |
| 27512 | } |
| 27513 | p = sqlite3GlobalConfig.m.xMalloc(nFull); |
| 27514 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 27515 | if( p==0 && mem0.alarmThreshold>0 ){ |
| @@ -27629,14 +27734,16 @@ | |
| 27734 | if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= |
| 27735 | mem0.alarmThreshold-nDiff ){ |
| 27736 | sqlite3MallocAlarm(nDiff); |
| 27737 | } |
| 27738 | pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
| 27739 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 27740 | if( pNew==0 && mem0.alarmThreshold>0 ){ |
| 27741 | sqlite3MallocAlarm((int)nBytes); |
| 27742 | pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
| 27743 | } |
| 27744 | #endif |
| 27745 | if( pNew ){ |
| 27746 | nNew = sqlite3MallocSize(pNew); |
| 27747 | sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); |
| 27748 | } |
| 27749 | sqlite3_mutex_leave(mem0.mutex); |
| @@ -27907,11 +28014,11 @@ | |
| 28014 | */ |
| 28015 | SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ |
| 28016 | if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ |
| 28017 | db->mallocFailed = 1; |
| 28018 | if( db->nVdbeExec>0 ){ |
| 28019 | AtomicStore(&db->u1.isInterrupted, 1); |
| 28020 | } |
| 28021 | DisableLookaside; |
| 28022 | if( db->pParse ){ |
| 28023 | db->pParse->rc = SQLITE_NOMEM_BKPT; |
| 28024 | } |
| @@ -27926,11 +28033,11 @@ | |
| 28033 | ** VDBEs. |
| 28034 | */ |
| 28035 | SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){ |
| 28036 | if( db->mallocFailed && db->nVdbeExec==0 ){ |
| 28037 | db->mallocFailed = 0; |
| 28038 | AtomicStore(&db->u1.isInterrupted, 0); |
| 28039 | assert( db->lookaside.bDisable>0 ); |
| 28040 | EnableLookaside; |
| 28041 | } |
| 28042 | } |
| 28043 | |
| @@ -31300,10 +31407,23 @@ | |
| 31407 | a = (unsigned char *)zLeft; |
| 31408 | b = (unsigned char *)zRight; |
| 31409 | while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
| 31410 | return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; |
| 31411 | } |
| 31412 | |
| 31413 | /* |
| 31414 | ** Compute an 8-bit hash on a string that is insensitive to case differences |
| 31415 | */ |
| 31416 | SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){ |
| 31417 | u8 h = 0; |
| 31418 | if( z==0 ) return 0; |
| 31419 | while( z[0] ){ |
| 31420 | h += UpperToLower[(unsigned char)z[0]]; |
| 31421 | z++; |
| 31422 | } |
| 31423 | return h; |
| 31424 | } |
| 31425 | |
| 31426 | /* |
| 31427 | ** Compute 10 to the E-th power. Examples: E==1 results in 10. |
| 31428 | ** E==2 results in 100. E==50 results in 1.0e50. |
| 31429 | ** |
| @@ -34857,20 +34977,21 @@ | |
| 34977 | static int osSetPosixAdvisoryLock( |
| 34978 | int h, /* The file descriptor on which to take the lock */ |
| 34979 | struct flock *pLock, /* The description of the lock */ |
| 34980 | unixFile *pFile /* Structure holding timeout value */ |
| 34981 | ){ |
| 34982 | int tm = pFile->iBusyTimeout; |
| 34983 | int rc = osFcntl(h,F_SETLK,pLock); |
| 34984 | while( rc<0 && tm>0 ){ |
| 34985 | /* On systems that support some kind of blocking file lock with a timeout, |
| 34986 | ** make appropriate changes here to invoke that blocking file lock. On |
| 34987 | ** generic posix, however, there is no such API. So we simply try the |
| 34988 | ** lock once every millisecond until either the timeout expires, or until |
| 34989 | ** the lock is obtained. */ |
| 34990 | usleep(1000); |
| 34991 | rc = osFcntl(h,F_SETLK,pLock); |
| 34992 | tm--; |
| 34993 | } |
| 34994 | return rc; |
| 34995 | } |
| 34996 | #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ |
| 34997 | |
| @@ -36977,11 +37098,11 @@ | |
| 37098 | zDirname[ii] = '\0'; |
| 37099 | }else{ |
| 37100 | if( zDirname[0]!='/' ) zDirname[0] = '.'; |
| 37101 | zDirname[1] = 0; |
| 37102 | } |
| 37103 | fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); |
| 37104 | if( fd>=0 ){ |
| 37105 | OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); |
| 37106 | } |
| 37107 | *pFd = fd; |
| 37108 | if( fd>=0 ) return SQLITE_OK; |
| @@ -37287,11 +37408,13 @@ | |
| 37408 | *(int*)pArg = fileHasMoved(pFile); |
| 37409 | return SQLITE_OK; |
| 37410 | } |
| 37411 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 37412 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 37413 | int iOld = pFile->iBusyTimeout; |
| 37414 | pFile->iBusyTimeout = *(int*)pArg; |
| 37415 | *(int*)pArg = iOld; |
| 37416 | return SQLITE_OK; |
| 37417 | } |
| 37418 | #endif |
| 37419 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 37420 | case SQLITE_FCNTL_MMAP_SIZE: { |
| @@ -37606,17 +37729,24 @@ | |
| 37729 | |
| 37730 | /* Locks are within range */ |
| 37731 | assert( n>=1 && n<=SQLITE_SHM_NLOCK ); |
| 37732 | |
| 37733 | if( pShmNode->hShm>=0 ){ |
| 37734 | int res; |
| 37735 | /* Initialize the locking parameters */ |
| 37736 | f.l_type = lockType; |
| 37737 | f.l_whence = SEEK_SET; |
| 37738 | f.l_start = ofst; |
| 37739 | f.l_len = n; |
| 37740 | res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); |
| 37741 | if( res==-1 ){ |
| 37742 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 37743 | rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); |
| 37744 | #else |
| 37745 | rc = SQLITE_BUSY; |
| 37746 | #endif |
| 37747 | } |
| 37748 | } |
| 37749 | |
| 37750 | /* Update the global lock state and do debug tracing */ |
| 37751 | #ifdef SQLITE_DEBUG |
| 37752 | { u16 mask; |
| @@ -38108,10 +38238,29 @@ | |
| 38238 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
| 38239 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
| 38240 | assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
| 38241 | assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); |
| 38242 | assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); |
| 38243 | |
| 38244 | /* Check that, if this to be a blocking lock, no locks that occur later |
| 38245 | ** in the following list than the lock being obtained are already held: |
| 38246 | ** |
| 38247 | ** 1. Checkpointer lock (ofst==1). |
| 38248 | ** 2. Write lock (ofst==0). |
| 38249 | ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK). |
| 38250 | ** |
| 38251 | ** In other words, if this is a blocking lock, none of the locks that |
| 38252 | ** occur later in the above list than the lock being obtained may be |
| 38253 | ** held. */ |
| 38254 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 38255 | assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( |
| 38256 | (ofst!=2) /* not RECOVER */ |
| 38257 | && (ofst!=1 || (p->exclMask|p->sharedMask)==0) |
| 38258 | && (ofst!=0 || (p->exclMask|p->sharedMask)<3) |
| 38259 | && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst)) |
| 38260 | )); |
| 38261 | #endif |
| 38262 | |
| 38263 | mask = (1<<(ofst+n)) - (1<<ofst); |
| 38264 | assert( n>1 || mask==(1<<ofst) ); |
| 38265 | sqlite3_mutex_enter(pShmNode->pShmMutex); |
| 38266 | if( flags & SQLITE_SHM_UNLOCK ){ |
| @@ -51414,10 +51563,15 @@ | |
| 51563 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal); |
| 51564 | #endif |
| 51565 | |
| 51566 | /* Return the sqlite3_file object for the WAL file */ |
| 51567 | SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal); |
| 51568 | |
| 51569 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 51570 | SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock); |
| 51571 | SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db); |
| 51572 | #endif |
| 51573 | |
| 51574 | #endif /* ifndef SQLITE_OMIT_WAL */ |
| 51575 | #endif /* SQLITE_WAL_H */ |
| 51576 | |
| 51577 | /************** End of wal.h *************************************************/ |
| @@ -53935,13 +54089,16 @@ | |
| 54089 | } |
| 54090 | if( exists ){ |
| 54091 | /* One of the journals pointed to by the master journal exists. |
| 54092 | ** Open it and check if it points at the master journal. If |
| 54093 | ** so, return without deleting the master journal file. |
| 54094 | ** NB: zJournal is really a MAIN_JOURNAL. But call it a |
| 54095 | ** MASTER_JOURNAL here so that the VFS will not send the zJournal |
| 54096 | ** name into sqlite3_database_file_object(). |
| 54097 | */ |
| 54098 | int c; |
| 54099 | int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); |
| 54100 | rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0); |
| 54101 | if( rc!=SQLITE_OK ){ |
| 54102 | goto delmaster_out; |
| 54103 | } |
| 54104 | |
| @@ -56141,10 +56298,11 @@ | |
| 56298 | ** Pager object (sizeof(Pager) bytes) |
| 56299 | ** PCache object (sqlite3PcacheSize() bytes) |
| 56300 | ** Database file handle (pVfs->szOsFile bytes) |
| 56301 | ** Sub-journal file handle (journalFileSize bytes) |
| 56302 | ** Main journal file handle (journalFileSize bytes) |
| 56303 | ** Ptr back to the Pager (sizeof(Pager*) bytes) |
| 56304 | ** \0\0\0\0 database prefix (4 bytes) |
| 56305 | ** Database file name (nPathname+1 bytes) |
| 56306 | ** URI query parameters (nUriByte bytes) |
| 56307 | ** Journal filename (nPathname+8+1 bytes) |
| 56308 | ** WAL filename (nPathname+4+1 bytes) |
| @@ -56180,10 +56338,11 @@ | |
| 56338 | pPtr = (u8 *)sqlite3MallocZero( |
| 56339 | ROUND8(sizeof(*pPager)) + /* Pager structure */ |
| 56340 | ROUND8(pcacheSize) + /* PCache object */ |
| 56341 | ROUND8(pVfs->szOsFile) + /* The main db file */ |
| 56342 | journalFileSize * 2 + /* The two journal files */ |
| 56343 | sizeof(pPager) + /* Space to hold a pointer */ |
| 56344 | 4 + /* Database prefix */ |
| 56345 | nPathname + 1 + /* database filename */ |
| 56346 | nUriByte + /* query parameters */ |
| 56347 | nPathname + 8 + 1 + /* Journal filename */ |
| 56348 | #ifndef SQLITE_OMIT_WAL |
| @@ -56200,10 +56359,11 @@ | |
| 56359 | pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize); |
| 56360 | pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile); |
| 56361 | pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; |
| 56362 | pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; |
| 56363 | assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); |
| 56364 | memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); |
| 56365 | |
| 56366 | /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ |
| 56367 | pPtr += 4; /* Skip zero prefix */ |
| 56368 | pPager->zFilename = (char*)pPtr; |
| 56369 | if( nPathname>0 ){ |
| @@ -56400,10 +56560,23 @@ | |
| 56560 | |
| 56561 | *ppPager = pPager; |
| 56562 | return SQLITE_OK; |
| 56563 | } |
| 56564 | |
| 56565 | /* |
| 56566 | ** Return the sqlite3_file for the main database given the name |
| 56567 | ** of the corresonding WAL or Journal name as passed into |
| 56568 | ** xOpen. |
| 56569 | */ |
| 56570 | SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){ |
| 56571 | Pager *pPager; |
| 56572 | while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ |
| 56573 | zName--; |
| 56574 | } |
| 56575 | pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); |
| 56576 | return pPager->fd; |
| 56577 | } |
| 56578 | |
| 56579 | |
| 56580 | /* |
| 56581 | ** This function is called after transitioning from PAGER_UNLOCK to |
| 56582 | ** PAGER_SHARED state. It tests if there is a hot journal present in |
| @@ -57085,11 +57258,10 @@ | |
| 57258 | Pager *pPager; |
| 57259 | assert( pPg!=0 ); |
| 57260 | assert( pPg->pgno==1 ); |
| 57261 | assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ |
| 57262 | pPager = pPg->pPager; |
| 57263 | sqlite3PcacheRelease(pPg); |
| 57264 | pagerUnlockIfUnused(pPager); |
| 57265 | } |
| 57266 | |
| 57267 | /* |
| @@ -58378,20 +58550,10 @@ | |
| 58550 | */ |
| 58551 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ |
| 58552 | return pPager->fd; |
| 58553 | } |
| 58554 | |
| 58555 | /* |
| 58556 | ** Return the file handle for the journal file (if it exists). |
| 58557 | ** This will be either the rollback journal or the WAL file. |
| 58558 | */ |
| 58559 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ |
| @@ -58801,11 +58963,10 @@ | |
| 58963 | (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), |
| 58964 | pPager->pBusyHandlerArg, |
| 58965 | pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, |
| 58966 | pnLog, pnCkpt |
| 58967 | ); |
| 58968 | } |
| 58969 | return rc; |
| 58970 | } |
| 58971 | |
| 58972 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){ |
| @@ -58966,11 +59127,35 @@ | |
| 59127 | } |
| 59128 | } |
| 59129 | return rc; |
| 59130 | } |
| 59131 | |
| 59132 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 59133 | /* |
| 59134 | ** If pager pPager is a wal-mode database not in exclusive locking mode, |
| 59135 | ** invoke the sqlite3WalWriteLock() function on the associated Wal object |
| 59136 | ** with the same db and bLock parameters as were passed to this function. |
| 59137 | ** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise. |
| 59138 | */ |
| 59139 | SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){ |
| 59140 | int rc = SQLITE_OK; |
| 59141 | if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){ |
| 59142 | rc = sqlite3WalWriteLock(pPager->pWal, bLock); |
| 59143 | } |
| 59144 | return rc; |
| 59145 | } |
| 59146 | |
| 59147 | /* |
| 59148 | ** Set the database handle used by the wal layer to determine if |
| 59149 | ** blocking locks are required. |
| 59150 | */ |
| 59151 | SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){ |
| 59152 | if( pagerUseWal(pPager) ){ |
| 59153 | sqlite3WalDb(pPager->pWal, db); |
| 59154 | } |
| 59155 | } |
| 59156 | #endif |
| 59157 | |
| 59158 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 59159 | /* |
| 59160 | ** If this is a WAL database, obtain a snapshot handle for the snapshot |
| 59161 | ** currently open. Otherwise, return an error. |
| @@ -58986,11 +59171,14 @@ | |
| 59171 | /* |
| 59172 | ** If this is a WAL database, store a pointer to pSnapshot. Next time a |
| 59173 | ** read transaction is opened, attempt to read from the snapshot it |
| 59174 | ** identifies. If this is not a WAL database, return an error. |
| 59175 | */ |
| 59176 | SQLITE_PRIVATE int sqlite3PagerSnapshotOpen( |
| 59177 | Pager *pPager, |
| 59178 | sqlite3_snapshot *pSnapshot |
| 59179 | ){ |
| 59180 | int rc = SQLITE_OK; |
| 59181 | if( pPager->pWal ){ |
| 59182 | sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); |
| 59183 | }else{ |
| 59184 | rc = SQLITE_ERROR; |
| @@ -59322,22 +59510,10 @@ | |
| 59510 | # define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X |
| 59511 | #else |
| 59512 | # define WALTRACE(X) |
| 59513 | #endif |
| 59514 | |
| 59515 | /* |
| 59516 | ** The maximum (and only) versions of the wal and wal-index formats |
| 59517 | ** that may be interpreted by this version of SQLite. |
| 59518 | ** |
| 59519 | ** If a client begins recovering a WAL file and finds that (a) the checksum |
| @@ -59542,10 +59718,13 @@ | |
| 59718 | #ifdef SQLITE_DEBUG |
| 59719 | u8 lockError; /* True if a locking error has occurred */ |
| 59720 | #endif |
| 59721 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 59722 | WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ |
| 59723 | #endif |
| 59724 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 59725 | sqlite3 *db; |
| 59726 | #endif |
| 59727 | }; |
| 59728 | |
| 59729 | /* |
| 59730 | ** Candidate values for Wal.exclusiveMode. |
| @@ -59916,11 +60095,11 @@ | |
| 60095 | if( pWal->exclusiveMode ) return SQLITE_OK; |
| 60096 | rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, |
| 60097 | SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); |
| 60098 | WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, |
| 60099 | walLockName(lockIdx), rc ? "failed" : "ok")); |
| 60100 | VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) |
| 60101 | return rc; |
| 60102 | } |
| 60103 | static void walUnlockShared(Wal *pWal, int lockIdx){ |
| 60104 | if( pWal->exclusiveMode ) return; |
| 60105 | (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, |
| @@ -59932,11 +60111,11 @@ | |
| 60111 | if( pWal->exclusiveMode ) return SQLITE_OK; |
| 60112 | rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, |
| 60113 | SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); |
| 60114 | WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, |
| 60115 | walLockName(lockIdx), n, rc ? "failed" : "ok")); |
| 60116 | VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) |
| 60117 | return rc; |
| 60118 | } |
| 60119 | static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ |
| 60120 | if( pWal->exclusiveMode ) return; |
| 60121 | (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, |
| @@ -60751,10 +60930,93 @@ | |
| 60930 | p = 0; |
| 60931 | } |
| 60932 | *pp = p; |
| 60933 | return rc; |
| 60934 | } |
| 60935 | |
| 60936 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 60937 | /* |
| 60938 | ** Attempt to enable blocking locks. Blocking locks are enabled only if (a) |
| 60939 | ** they are supported by the VFS, and (b) the database handle is configured |
| 60940 | ** with a busy-timeout. Return 1 if blocking locks are successfully enabled, |
| 60941 | ** or 0 otherwise. |
| 60942 | */ |
| 60943 | static int walEnableBlocking(Wal *pWal){ |
| 60944 | int res = 0; |
| 60945 | if( pWal->db ){ |
| 60946 | int tmout = pWal->db->busyTimeout; |
| 60947 | if( tmout ){ |
| 60948 | int rc; |
| 60949 | rc = sqlite3OsFileControl( |
| 60950 | pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout |
| 60951 | ); |
| 60952 | res = (rc==SQLITE_OK); |
| 60953 | } |
| 60954 | } |
| 60955 | return res; |
| 60956 | } |
| 60957 | |
| 60958 | /* |
| 60959 | ** Disable blocking locks. |
| 60960 | */ |
| 60961 | static void walDisableBlocking(Wal *pWal){ |
| 60962 | int tmout = 0; |
| 60963 | sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout); |
| 60964 | } |
| 60965 | |
| 60966 | /* |
| 60967 | ** If parameter bLock is true, attempt to enable blocking locks, take |
| 60968 | ** the WRITER lock, and then disable blocking locks. If blocking locks |
| 60969 | ** cannot be enabled, no attempt to obtain the WRITER lock is made. Return |
| 60970 | ** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not |
| 60971 | ** an error if blocking locks can not be enabled. |
| 60972 | ** |
| 60973 | ** If the bLock parameter is false and the WRITER lock is held, release it. |
| 60974 | */ |
| 60975 | SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock){ |
| 60976 | int rc = SQLITE_OK; |
| 60977 | assert( pWal->readLock<0 || bLock==0 ); |
| 60978 | if( bLock ){ |
| 60979 | assert( pWal->db ); |
| 60980 | if( walEnableBlocking(pWal) ){ |
| 60981 | rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 60982 | if( rc==SQLITE_OK ){ |
| 60983 | pWal->writeLock = 1; |
| 60984 | } |
| 60985 | walDisableBlocking(pWal); |
| 60986 | } |
| 60987 | }else if( pWal->writeLock ){ |
| 60988 | walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 60989 | pWal->writeLock = 0; |
| 60990 | } |
| 60991 | return rc; |
| 60992 | } |
| 60993 | |
| 60994 | /* |
| 60995 | ** Set the database handle used to determine if blocking locks are required. |
| 60996 | */ |
| 60997 | SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){ |
| 60998 | pWal->db = db; |
| 60999 | } |
| 61000 | |
| 61001 | /* |
| 61002 | ** Take an exclusive WRITE lock. Blocking if so configured. |
| 61003 | */ |
| 61004 | static int walLockWriter(Wal *pWal){ |
| 61005 | int rc; |
| 61006 | walEnableBlocking(pWal); |
| 61007 | rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 61008 | walDisableBlocking(pWal); |
| 61009 | return rc; |
| 61010 | } |
| 61011 | #else |
| 61012 | # define walEnableBlocking(x) 0 |
| 61013 | # define walDisableBlocking(x) |
| 61014 | # define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) |
| 61015 | # define sqlite3WalDb(pWal, db) |
| 61016 | #endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ |
| 61017 | |
| 61018 | |
| 61019 | /* |
| 61020 | ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and |
| 61021 | ** n. If the attempt fails and parameter xBusy is not NULL, then it is a |
| 61022 | ** busy-handler function. Invoke it and retry the lock until either the |
| @@ -60769,10 +61031,16 @@ | |
| 61031 | ){ |
| 61032 | int rc; |
| 61033 | do { |
| 61034 | rc = walLockExclusive(pWal, lockIdx, n); |
| 61035 | }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); |
| 61036 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 61037 | if( rc==SQLITE_BUSY_TIMEOUT ){ |
| 61038 | walDisableBlocking(pWal); |
| 61039 | rc = SQLITE_BUSY; |
| 61040 | } |
| 61041 | #endif |
| 61042 | return rc; |
| 61043 | } |
| 61044 | |
| 61045 | /* |
| 61046 | ** The cache of the wal-index header must be valid to call this function. |
| @@ -60939,10 +61207,11 @@ | |
| 61207 | ** about the eventual size of the db file to the VFS layer. |
| 61208 | */ |
| 61209 | if( rc==SQLITE_OK ){ |
| 61210 | i64 nReq = ((i64)mxPage * szPage); |
| 61211 | i64 nSize; /* Current size of database file */ |
| 61212 | sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); |
| 61213 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 61214 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 61215 | sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); |
| 61216 | } |
| 61217 | } |
| @@ -60950,11 +61219,11 @@ | |
| 61219 | |
| 61220 | /* Iterate through the contents of the WAL, copying data to the db file */ |
| 61221 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 61222 | i64 iOffset; |
| 61223 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| 61224 | if( AtomicLoad(&db->u1.isInterrupted) ){ |
| 61225 | rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; |
| 61226 | break; |
| 61227 | } |
| 61228 | if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ |
| 61229 | continue; |
| @@ -60966,10 +61235,11 @@ | |
| 61235 | iOffset = (iDbpage-1)*(i64)szPage; |
| 61236 | testcase( IS_BIG_INT(iOffset) ); |
| 61237 | rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); |
| 61238 | if( rc!=SQLITE_OK ) break; |
| 61239 | } |
| 61240 | sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); |
| 61241 | |
| 61242 | /* If work was actually accomplished... */ |
| 61243 | if( rc==SQLITE_OK ){ |
| 61244 | if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ |
| 61245 | i64 szDb = pWal->hdr.nPage*(i64)szPage; |
| @@ -60977,14 +61247,10 @@ | |
| 61247 | rc = sqlite3OsTruncate(pWal->pDbFd, szDb); |
| 61248 | if( rc==SQLITE_OK ){ |
| 61249 | rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 61250 | } |
| 61251 | } |
| 61252 | if( rc==SQLITE_OK ){ |
| 61253 | pInfo->nBackfill = mxSafeFrame; |
| 61254 | } |
| 61255 | } |
| 61256 | |
| @@ -61250,32 +61516,36 @@ | |
| 61516 | badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); |
| 61517 | |
| 61518 | /* If the first attempt failed, it might have been due to a race |
| 61519 | ** with a writer. So get a WRITE lock and try again. |
| 61520 | */ |
| 61521 | if( badHdr ){ |
| 61522 | if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ |
| 61523 | if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ |
| 61524 | walUnlockShared(pWal, WAL_WRITE_LOCK); |
| 61525 | rc = SQLITE_READONLY_RECOVERY; |
| 61526 | } |
| 61527 | }else{ |
| 61528 | int bWriteLock = pWal->writeLock; |
| 61529 | if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ |
| 61530 | pWal->writeLock = 1; |
| 61531 | if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ |
| 61532 | badHdr = walIndexTryHdr(pWal, pChanged); |
| 61533 | if( badHdr ){ |
| 61534 | /* If the wal-index header is still malformed even while holding |
| 61535 | ** a WRITE lock, it can only mean that the header is corrupted and |
| 61536 | ** needs to be reconstructed. So run recovery to do exactly that. |
| 61537 | */ |
| 61538 | rc = walIndexRecover(pWal); |
| 61539 | *pChanged = 1; |
| 61540 | } |
| 61541 | } |
| 61542 | if( bWriteLock==0 ){ |
| 61543 | pWal->writeLock = 0; |
| 61544 | walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 61545 | } |
| 61546 | } |
| 61547 | } |
| 61548 | } |
| 61549 | |
| 61550 | /* If the header is read successfully, check the version number to make |
| 61551 | ** sure the wal-index was not constructed with some future format that |
| @@ -61663,11 +61933,12 @@ | |
| 61933 | && (mxReadMark<mxFrame || mxI==0) |
| 61934 | ){ |
| 61935 | for(i=1; i<WAL_NREADER; i++){ |
| 61936 | rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61937 | if( rc==SQLITE_OK ){ |
| 61938 | AtomicStore(pInfo->aReadMark+i,mxFrame); |
| 61939 | mxReadMark = mxFrame; |
| 61940 | mxI = i; |
| 61941 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 61942 | break; |
| 61943 | }else if( rc!=SQLITE_BUSY ){ |
| 61944 | return rc; |
| @@ -61823,15 +62094,36 @@ | |
| 62094 | */ |
| 62095 | SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ |
| 62096 | int rc; /* Return code */ |
| 62097 | int cnt = 0; /* Number of TryBeginRead attempts */ |
| 62098 | |
| 62099 | assert( pWal->ckptLock==0 ); |
| 62100 | |
| 62101 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 62102 | int bChanged = 0; |
| 62103 | WalIndexHdr *pSnapshot = pWal->pSnapshot; |
| 62104 | if( pSnapshot ){ |
| 62105 | if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ |
| 62106 | bChanged = 1; |
| 62107 | } |
| 62108 | |
| 62109 | /* It is possible that there is a checkpointer thread running |
| 62110 | ** concurrent with this code. If this is the case, it may be that the |
| 62111 | ** checkpointer has already determined that it will checkpoint |
| 62112 | ** snapshot X, where X is later in the wal file than pSnapshot, but |
| 62113 | ** has not yet set the pInfo->nBackfillAttempted variable to indicate |
| 62114 | ** its intent. To avoid the race condition this leads to, ensure that |
| 62115 | ** there is no checkpointer process by taking a shared CKPT lock |
| 62116 | ** before checking pInfo->nBackfillAttempted. */ |
| 62117 | (void)walEnableBlocking(pWal); |
| 62118 | rc = walLockShared(pWal, WAL_CKPT_LOCK); |
| 62119 | walDisableBlocking(pWal); |
| 62120 | |
| 62121 | if( rc!=SQLITE_OK ){ |
| 62122 | return rc; |
| 62123 | } |
| 62124 | pWal->ckptLock = 1; |
| 62125 | } |
| 62126 | #endif |
| 62127 | |
| 62128 | do{ |
| 62129 | rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); |
| @@ -61860,52 +62152,46 @@ | |
| 62152 | volatile WalCkptInfo *pInfo = walCkptInfo(pWal); |
| 62153 | |
| 62154 | assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 ); |
| 62155 | assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); |
| 62156 | |
| 62157 | /* Check that the wal file has not been wrapped. Assuming that it has |
| 62158 | ** not, also check that no checkpointer has attempted to checkpoint any |
| 62159 | ** frames beyond pSnapshot->mxFrame. If either of these conditions are |
| 62160 | ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr |
| 62161 | ** with *pSnapshot and set *pChanged as appropriate for opening the |
| 62162 | ** snapshot. */ |
| 62163 | if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) |
| 62164 | && pSnapshot->mxFrame>=pInfo->nBackfillAttempted |
| 62165 | ){ |
| 62166 | assert( pWal->readLock>0 ); |
| 62167 | memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); |
| 62168 | *pChanged = bChanged; |
| 62169 | }else{ |
| 62170 | rc = SQLITE_ERROR_SNAPSHOT; |
| 62171 | } |
| 62172 | |
| 62173 | /* A client using a non-current snapshot may not ignore any frames |
| 62174 | ** from the start of the wal file. This is because, for a system |
| 62175 | ** where (minFrame < iSnapshot < maxFrame), a checkpointer may |
| 62176 | ** have omitted to checkpoint a frame earlier than minFrame in |
| 62177 | ** the file because there exists a frame after iSnapshot that |
| 62178 | ** is the same database page. */ |
| 62179 | pWal->minFrame = 1; |
| 62180 | |
| 62181 | if( rc!=SQLITE_OK ){ |
| 62182 | sqlite3WalEndReadTransaction(pWal); |
| 62183 | } |
| 62184 | } |
| 62185 | } |
| 62186 | |
| 62187 | /* Release the shared CKPT lock obtained above. */ |
| 62188 | if( pWal->ckptLock ){ |
| 62189 | assert( pSnapshot ); |
| 62190 | walUnlockShared(pWal, WAL_CKPT_LOCK); |
| 62191 | pWal->ckptLock = 0; |
| 62192 | } |
| 62193 | #endif |
| 62194 | return rc; |
| 62195 | } |
| 62196 | |
| 62197 | /* |
| @@ -62071,10 +62357,20 @@ | |
| 62357 | ** |
| 62358 | ** There can only be a single writer active at a time. |
| 62359 | */ |
| 62360 | SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ |
| 62361 | int rc; |
| 62362 | |
| 62363 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 62364 | /* If the write-lock is already held, then it was obtained before the |
| 62365 | ** read-transaction was even opened, making this call a no-op. |
| 62366 | ** Return early. */ |
| 62367 | if( pWal->writeLock ){ |
| 62368 | assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); |
| 62369 | return SQLITE_OK; |
| 62370 | } |
| 62371 | #endif |
| 62372 | |
| 62373 | /* Cannot start a write transaction without first holding a read |
| 62374 | ** transaction. */ |
| 62375 | assert( pWal->readLock>=0 ); |
| 62376 | assert( pWal->writeLock==0 && pWal->iReCksum==0 ); |
| @@ -62647,50 +62943,57 @@ | |
| 62943 | ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ |
| 62944 | assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); |
| 62945 | |
| 62946 | if( pWal->readOnly ) return SQLITE_READONLY; |
| 62947 | WALTRACE(("WAL%p: checkpoint begins\n", pWal)); |
| 62948 | |
| 62949 | /* Enable blocking locks, if possible. If blocking locks are successfully |
| 62950 | ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ |
| 62951 | sqlite3WalDb(pWal, db); |
| 62952 | (void)walEnableBlocking(pWal); |
| 62953 | |
| 62954 | /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive |
| 62955 | ** "checkpoint" lock on the database file. |
| 62956 | ** EVIDENCE-OF: R-10421-19736 If any other process is running a |
| 62957 | ** checkpoint operation at the same time, the lock cannot be obtained and |
| 62958 | ** SQLITE_BUSY is returned. |
| 62959 | ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, |
| 62960 | ** it will not be invoked in this case. |
| 62961 | */ |
| 62962 | rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); |
| 62963 | testcase( rc==SQLITE_BUSY ); |
| 62964 | testcase( rc!=SQLITE_OK && xBusy2!=0 ); |
| 62965 | if( rc==SQLITE_OK ){ |
| 62966 | pWal->ckptLock = 1; |
| 62967 | |
| 62968 | /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and |
| 62969 | ** TRUNCATE modes also obtain the exclusive "writer" lock on the database |
| 62970 | ** file. |
| 62971 | ** |
| 62972 | ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained |
| 62973 | ** immediately, and a busy-handler is configured, it is invoked and the |
| 62974 | ** writer lock retried until either the busy-handler returns 0 or the |
| 62975 | ** lock is successfully obtained. |
| 62976 | */ |
| 62977 | if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ |
| 62978 | rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1); |
| 62979 | if( rc==SQLITE_OK ){ |
| 62980 | pWal->writeLock = 1; |
| 62981 | }else if( rc==SQLITE_BUSY ){ |
| 62982 | eMode2 = SQLITE_CHECKPOINT_PASSIVE; |
| 62983 | xBusy2 = 0; |
| 62984 | rc = SQLITE_OK; |
| 62985 | } |
| 62986 | } |
| 62987 | } |
| 62988 | |
| 62989 | |
| 62990 | /* Read the wal-index header. */ |
| 62991 | if( rc==SQLITE_OK ){ |
| 62992 | walDisableBlocking(pWal); |
| 62993 | rc = walIndexReadHdr(pWal, &isChanged); |
| 62994 | (void)walEnableBlocking(pWal); |
| 62995 | if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ |
| 62996 | sqlite3OsUnfetch(pWal->pDbFd, 0, 0); |
| 62997 | } |
| 62998 | } |
| 62999 | |
| @@ -62717,16 +63020,24 @@ | |
| 63020 | ** next time the pager opens a snapshot on this database it knows that |
| 63021 | ** the cache needs to be reset. |
| 63022 | */ |
| 63023 | memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); |
| 63024 | } |
| 63025 | |
| 63026 | walDisableBlocking(pWal); |
| 63027 | sqlite3WalDb(pWal, 0); |
| 63028 | |
| 63029 | /* Release the locks. */ |
| 63030 | sqlite3WalEndWriteTransaction(pWal); |
| 63031 | if( pWal->ckptLock ){ |
| 63032 | walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); |
| 63033 | pWal->ckptLock = 0; |
| 63034 | } |
| 63035 | WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); |
| 63036 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 63037 | if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; |
| 63038 | #endif |
| 63039 | return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); |
| 63040 | } |
| 63041 | |
| 63042 | /* Return the value to pass to a sqlite3_wal_hook callback, the |
| 63043 | ** number of frames in the WAL at the point of the last commit since |
| @@ -62839,11 +63150,14 @@ | |
| 63150 | return rc; |
| 63151 | } |
| 63152 | |
| 63153 | /* Try to open on pSnapshot when the next read-transaction starts |
| 63154 | */ |
| 63155 | SQLITE_PRIVATE void sqlite3WalSnapshotOpen( |
| 63156 | Wal *pWal, |
| 63157 | sqlite3_snapshot *pSnapshot |
| 63158 | ){ |
| 63159 | pWal->pSnapshot = (WalIndexHdr*)pSnapshot; |
| 63160 | } |
| 63161 | |
| 63162 | /* |
| 63163 | ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if |
| @@ -63358,10 +63672,11 @@ | |
| 63672 | u8 incrVacuum; /* True if incr-vacuum is enabled */ |
| 63673 | u8 bDoTruncate; /* True to truncate db on commit */ |
| 63674 | #endif |
| 63675 | u8 inTransaction; /* Transaction state */ |
| 63676 | u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ |
| 63677 | u8 nReserveWanted; /* Desired number of extra bytes per page */ |
| 63678 | u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ |
| 63679 | u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ |
| 63680 | u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ |
| 63681 | u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ |
| 63682 | u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ |
| @@ -66251,12 +66566,11 @@ | |
| 66566 | */ |
| 66567 | static int btreeInvokeBusyHandler(void *pArg){ |
| 66568 | BtShared *pBt = (BtShared*)pArg; |
| 66569 | assert( pBt->db ); |
| 66570 | assert( sqlite3_mutex_held(pBt->db->mutex) ); |
| 66571 | return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); |
| 66572 | } |
| 66573 | |
| 66574 | /* |
| 66575 | ** Open a database file. |
| 66576 | ** |
| @@ -66803,20 +67117,21 @@ | |
| 67117 | ** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size |
| 67118 | ** and autovacuum mode can no longer be changed. |
| 67119 | */ |
| 67120 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ |
| 67121 | int rc = SQLITE_OK; |
| 67122 | int x; |
| 67123 | BtShared *pBt = p->pBt; |
| 67124 | assert( nReserve>=0 && nReserve<=255 ); |
| 67125 | sqlite3BtreeEnter(p); |
| 67126 | pBt->nReserveWanted = nReserve; |
| 67127 | x = pBt->pageSize - pBt->usableSize; |
| 67128 | if( nReserve<x ) nReserve = x; |
| 67129 | if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ |
| 67130 | sqlite3BtreeLeave(p); |
| 67131 | return SQLITE_READONLY; |
| 67132 | } |
| 67133 | assert( nReserve>=0 && nReserve<=255 ); |
| 67134 | if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && |
| 67135 | ((pageSize-1)&pageSize)==0 ){ |
| 67136 | assert( (pageSize & 7)==0 ); |
| 67137 | assert( !pBt->pCursor ); |
| @@ -66858,20 +67173,21 @@ | |
| 67173 | /* |
| 67174 | ** Return the number of bytes of space at the end of every page that |
| 67175 | ** are intentually left unused. This is the "reserved" space that is |
| 67176 | ** sometimes used by extensions. |
| 67177 | ** |
| 67178 | ** The value returned is the larger of the current reserve size and |
| 67179 | ** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES. |
| 67180 | ** The amount of reserve can only grow - never shrink. |
| 67181 | */ |
| 67182 | SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree *p){ |
| 67183 | int n1, n2; |
| 67184 | sqlite3BtreeEnter(p); |
| 67185 | n1 = (int)p->pBt->nReserveWanted; |
| 67186 | n2 = sqlite3BtreeGetReserveNoMutex(p); |
| 67187 | sqlite3BtreeLeave(p); |
| 67188 | return n1>n2 ? n1 : n2; |
| 67189 | } |
| 67190 | |
| 67191 | |
| 67192 | /* |
| 67193 | ** Set the maximum page count for a database if mxPage is positive. |
| @@ -67317,10 +67633,11 @@ | |
| 67633 | ** when A already has a read lock, we encourage A to give up and let B |
| 67634 | ** proceed. |
| 67635 | */ |
| 67636 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ |
| 67637 | BtShared *pBt = p->pBt; |
| 67638 | Pager *pPager = pBt->pPager; |
| 67639 | int rc = SQLITE_OK; |
| 67640 | |
| 67641 | sqlite3BtreeEnter(p); |
| 67642 | btreeIntegrity(p); |
| 67643 | |
| @@ -67332,11 +67649,11 @@ | |
| 67649 | goto trans_begun; |
| 67650 | } |
| 67651 | assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 ); |
| 67652 | |
| 67653 | if( (p->db->flags & SQLITE_ResetDatabase) |
| 67654 | && sqlite3PagerIsreadonly(pPager)==0 |
| 67655 | ){ |
| 67656 | pBt->btsFlags &= ~BTS_READ_ONLY; |
| 67657 | } |
| 67658 | |
| 67659 | /* Write transactions are not possible on a read-only database */ |
| @@ -67380,10 +67697,22 @@ | |
| 67697 | if( SQLITE_OK!=rc ) goto trans_begun; |
| 67698 | |
| 67699 | pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; |
| 67700 | if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY; |
| 67701 | do { |
| 67702 | sqlite3PagerWalDb(pPager, p->db); |
| 67703 | |
| 67704 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 67705 | /* If transitioning from no transaction directly to a write transaction, |
| 67706 | ** block for the WRITER lock first if possible. */ |
| 67707 | if( pBt->pPage1==0 && wrflag ){ |
| 67708 | assert( pBt->inTransaction==TRANS_NONE ); |
| 67709 | rc = sqlite3PagerWalWriteLock(pPager, 1); |
| 67710 | if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break; |
| 67711 | } |
| 67712 | #endif |
| 67713 | |
| 67714 | /* Call lockBtree() until either pBt->pPage1 is populated or |
| 67715 | ** lockBtree() returns something other than SQLITE_OK. lockBtree() |
| 67716 | ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after |
| 67717 | ** reading page 1 it discovers that the page-size of the database |
| 67718 | ** file is not pBt->pageSize. In this case lockBtree() will update |
| @@ -67393,11 +67722,11 @@ | |
| 67722 | |
| 67723 | if( rc==SQLITE_OK && wrflag ){ |
| 67724 | if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ |
| 67725 | rc = SQLITE_READONLY; |
| 67726 | }else{ |
| 67727 | rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db)); |
| 67728 | if( rc==SQLITE_OK ){ |
| 67729 | rc = newDatabase(pBt); |
| 67730 | }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){ |
| 67731 | /* if there was no transaction opened when this function was |
| 67732 | ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error |
| @@ -67406,15 +67735,19 @@ | |
| 67735 | } |
| 67736 | } |
| 67737 | } |
| 67738 | |
| 67739 | if( rc!=SQLITE_OK ){ |
| 67740 | (void)sqlite3PagerWalWriteLock(pPager, 0); |
| 67741 | unlockBtreeIfUnused(pBt); |
| 67742 | } |
| 67743 | }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && |
| 67744 | btreeInvokeBusyHandler(pBt) ); |
| 67745 | sqlite3PagerWalDb(pPager, 0); |
| 67746 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 67747 | if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; |
| 67748 | #endif |
| 67749 | |
| 67750 | if( rc==SQLITE_OK ){ |
| 67751 | if( p->inTrans==TRANS_NONE ){ |
| 67752 | pBt->nTransaction++; |
| 67753 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| @@ -67462,11 +67795,11 @@ | |
| 67795 | if( wrflag ){ |
| 67796 | /* This call makes sure that the pager has the correct number of |
| 67797 | ** open savepoints. If the second parameter is greater than 0 and |
| 67798 | ** the sub-journal is not already open, then it will be opened here. |
| 67799 | */ |
| 67800 | rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint); |
| 67801 | } |
| 67802 | } |
| 67803 | |
| 67804 | btreeIntegrity(p); |
| 67805 | sqlite3BtreeLeave(p); |
| @@ -71098,11 +71431,11 @@ | |
| 71431 | |
| 71432 | /* Remove cells from the start and end of the page */ |
| 71433 | assert( nCell>=0 ); |
| 71434 | if( iOld<iNew ){ |
| 71435 | int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray); |
| 71436 | if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT; |
| 71437 | memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2); |
| 71438 | nCell -= nShift; |
| 71439 | } |
| 71440 | if( iNewEnd < iOldEnd ){ |
| 71441 | int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); |
| @@ -73455,11 +73788,10 @@ | |
| 73788 | } |
| 73789 | sqlite3BtreeLeave(p); |
| 73790 | return rc; |
| 73791 | } |
| 73792 | |
| 73793 | /* |
| 73794 | ** The first argument, pCur, is a cursor opened on some b-tree. Count the |
| 73795 | ** number of entries in the b-tree and write the result to *pnEntry. |
| 73796 | ** |
| 73797 | ** SQLITE_OK is returned if the operation is successfully executed. |
| @@ -73477,11 +73809,11 @@ | |
| 73809 | } |
| 73810 | |
| 73811 | /* Unless an error occurs, the following loop runs one iteration for each |
| 73812 | ** page in the B-Tree structure (not including overflow pages). |
| 73813 | */ |
| 73814 | while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){ |
| 73815 | int iIdx; /* Index of child node in parent */ |
| 73816 | MemPage *pPage; /* Current page of the b-tree */ |
| 73817 | |
| 73818 | /* If this is a leaf page or the tree is not an int-key tree, then |
| 73819 | ** this page contains countable entries. Increment the entry counter |
| @@ -73528,11 +73860,10 @@ | |
| 73860 | } |
| 73861 | |
| 73862 | /* An error has occurred. Return an error code. */ |
| 73863 | return rc; |
| 73864 | } |
| 73865 | |
| 73866 | /* |
| 73867 | ** Return the pager associated with a BTree. This routine is used for |
| 73868 | ** testing and debugging only. |
| 73869 | */ |
| @@ -73603,11 +73934,11 @@ | |
| 73934 | } |
| 73935 | if( getPageReferenced(pCheck, iPage) ){ |
| 73936 | checkAppendMsg(pCheck, "2nd reference to page %d", iPage); |
| 73937 | return 1; |
| 73938 | } |
| 73939 | if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1; |
| 73940 | setPageReferenced(pCheck, iPage); |
| 73941 | return 0; |
| 73942 | } |
| 73943 | |
| 73944 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| @@ -74579,11 +74910,11 @@ | |
| 74910 | ** Attempt to set the page size of the destination to match the page size |
| 74911 | ** of the source. |
| 74912 | */ |
| 74913 | static int setDestPgsz(sqlite3_backup *p){ |
| 74914 | int rc; |
| 74915 | rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0); |
| 74916 | return rc; |
| 74917 | } |
| 74918 | |
| 74919 | /* |
| 74920 | ** Check that there is no open read-transaction on the b-tree passed as the |
| @@ -78604,24 +78935,23 @@ | |
| 78935 | ** "PX" -> "r[X]" |
| 78936 | ** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 |
| 78937 | ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 |
| 78938 | ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x |
| 78939 | */ |
| 78940 | SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( |
| 78941 | sqlite3 *db, /* Optional - Oom error reporting only */ |
| 78942 | const Op *pOp, /* The opcode to be commented */ |
| 78943 | const char *zP4 /* Previously obtained value for P4 */ |
| 78944 | ){ |
| 78945 | const char *zOpName; |
| 78946 | const char *zSynopsis; |
| 78947 | int nOpName; |
| 78948 | int ii; |
| 78949 | char zAlt[50]; |
| 78950 | StrAccum x; |
| 78951 | |
| 78952 | sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); |
| 78953 | zOpName = sqlite3OpcodeName(pOp->opcode); |
| 78954 | nOpName = sqlite3Strlen30(zOpName); |
| 78955 | if( zOpName[nOpName+1] ){ |
| 78956 | int seenCom = 0; |
| 78957 | char c; |
| @@ -78684,14 +79014,16 @@ | |
| 79014 | sqlite3_str_appendf(&x, "; %s", pOp->zComment); |
| 79015 | } |
| 79016 | }else if( pOp->zComment ){ |
| 79017 | sqlite3_str_appendall(&x, pOp->zComment); |
| 79018 | } |
| 79019 | if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){ |
| 79020 | sqlite3OomFault(db); |
| 79021 | } |
| 79022 | return sqlite3StrAccumFinish(&x); |
| 79023 | } |
| 79024 | #endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */ |
| 79025 | |
| 79026 | #if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) |
| 79027 | /* |
| 79028 | ** Translate the P4.pExpr value for an OP_CursorHint opcode into text |
| 79029 | ** that can be displayed in the P4 column of EXPLAIN output. |
| @@ -78768,15 +79100,15 @@ | |
| 79100 | #if VDBE_DISPLAY_P4 |
| 79101 | /* |
| 79102 | ** Compute a string that describes the P4 parameter for an opcode. |
| 79103 | ** Use zTemp for any required temporary buffer space. |
| 79104 | */ |
| 79105 | SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ |
| 79106 | char *zP4 = 0; |
| 79107 | StrAccum x; |
| 79108 | |
| 79109 | sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); |
| 79110 | switch( pOp->p4type ){ |
| 79111 | case P4_KEYINFO: { |
| 79112 | int j; |
| 79113 | KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; |
| 79114 | assert( pKeyInfo->aSortFlags!=0 ); |
| @@ -78856,40 +79188,36 @@ | |
| 79188 | int i; |
| 79189 | int *ai = pOp->p4.ai; |
| 79190 | int n = ai[0]; /* The first element of an INTARRAY is always the |
| 79191 | ** count of the number of elements to follow */ |
| 79192 | for(i=1; i<=n; i++){ |
| 79193 | sqlite3_str_appendf(&x, "%c%d", (i==1 ? '[' : ','), ai[i]); |
| 79194 | } |
| 79195 | sqlite3_str_append(&x, "]", 1); |
| 79196 | break; |
| 79197 | } |
| 79198 | case P4_SUBPROGRAM: { |
| 79199 | zP4 = "program"; |
| 79200 | break; |
| 79201 | } |
| 79202 | case P4_DYNBLOB: |
| 79203 | case P4_ADVANCE: { |
| 79204 | break; |
| 79205 | } |
| 79206 | case P4_TABLE: { |
| 79207 | zP4 = pOp->p4.pTab->zName; |
| 79208 | break; |
| 79209 | } |
| 79210 | default: { |
| 79211 | zP4 = pOp->p4.z; |
| 79212 | } |
| 79213 | } |
| 79214 | if( zP4 ) sqlite3_str_appendall(&x, zP4); |
| 79215 | if( (x.accError & SQLITE_NOMEM)!=0 ){ |
| 79216 | sqlite3OomFault(db); |
| 79217 | } |
| 79218 | return sqlite3StrAccumFinish(&x); |
| 79219 | } |
| 79220 | #endif /* VDBE_DISPLAY_P4 */ |
| 79221 | |
| 79222 | /* |
| 79223 | ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. |
| @@ -78975,28 +79303,32 @@ | |
| 79303 | /* |
| 79304 | ** Print a single opcode. This routine is used for debugging only. |
| 79305 | */ |
| 79306 | SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ |
| 79307 | char *zP4; |
| 79308 | char *zCom; |
| 79309 | sqlite3 dummyDb; |
| 79310 | static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; |
| 79311 | if( pOut==0 ) pOut = stdout; |
| 79312 | dummyDb.mallocFailed = 1; |
| 79313 | zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp); |
| 79314 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 79315 | zCom = sqlite3VdbeDisplayComment(0, pOp, zP4); |
| 79316 | #else |
| 79317 | zCom = 0; |
| 79318 | #endif |
| 79319 | /* NB: The sqlite3OpcodeName() function is implemented by code created |
| 79320 | ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the |
| 79321 | ** information from the vdbe.c source text */ |
| 79322 | fprintf(pOut, zFormat1, pc, |
| 79323 | sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, |
| 79324 | zP4 ? zP4 : "", pOp->p5, |
| 79325 | zCom ? zCom : "" |
| 79326 | ); |
| 79327 | fflush(pOut); |
| 79328 | sqlite3_free(zP4); |
| 79329 | sqlite3_free(zCom); |
| 79330 | } |
| 79331 | #endif |
| 79332 | |
| 79333 | /* |
| 79334 | ** Initialize an array of N Mem element. |
| @@ -79083,10 +79415,125 @@ | |
| 79415 | assert( sqlite3VdbeFrameIsValid(pFrame) ); |
| 79416 | pFrame->pParent = pFrame->v->pDelFrame; |
| 79417 | pFrame->v->pDelFrame = pFrame; |
| 79418 | } |
| 79419 | |
| 79420 | #if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN) |
| 79421 | /* |
| 79422 | ** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN |
| 79423 | ** QUERY PLAN output. |
| 79424 | ** |
| 79425 | ** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no |
| 79426 | ** more opcodes to be displayed. |
| 79427 | */ |
| 79428 | SQLITE_PRIVATE int sqlite3VdbeNextOpcode( |
| 79429 | Vdbe *p, /* The statement being explained */ |
| 79430 | Mem *pSub, /* Storage for keeping track of subprogram nesting */ |
| 79431 | int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */ |
| 79432 | int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */ |
| 79433 | int *piAddr, /* OUT: Write index into (*paOp)[] here */ |
| 79434 | Op **paOp /* OUT: Write the opcode array here */ |
| 79435 | ){ |
| 79436 | int nRow; /* Stop when row count reaches this */ |
| 79437 | int nSub = 0; /* Number of sub-vdbes seen so far */ |
| 79438 | SubProgram **apSub = 0; /* Array of sub-vdbes */ |
| 79439 | int i; /* Next instruction address */ |
| 79440 | int rc = SQLITE_OK; /* Result code */ |
| 79441 | Op *aOp = 0; /* Opcode array */ |
| 79442 | int iPc; /* Rowid. Copy of value in *piPc */ |
| 79443 | |
| 79444 | /* When the number of output rows reaches nRow, that means the |
| 79445 | ** listing has finished and sqlite3_step() should return SQLITE_DONE. |
| 79446 | ** nRow is the sum of the number of rows in the main program, plus |
| 79447 | ** the sum of the number of rows in all trigger subprograms encountered |
| 79448 | ** so far. The nRow value will increase as new trigger subprograms are |
| 79449 | ** encountered, but p->pc will eventually catch up to nRow. |
| 79450 | */ |
| 79451 | nRow = p->nOp; |
| 79452 | if( pSub!=0 ){ |
| 79453 | if( pSub->flags&MEM_Blob ){ |
| 79454 | /* pSub is initiallly NULL. It is initialized to a BLOB by |
| 79455 | ** the P4_SUBPROGRAM processing logic below */ |
| 79456 | nSub = pSub->n/sizeof(Vdbe*); |
| 79457 | apSub = (SubProgram **)pSub->z; |
| 79458 | } |
| 79459 | for(i=0; i<nSub; i++){ |
| 79460 | nRow += apSub[i]->nOp; |
| 79461 | } |
| 79462 | } |
| 79463 | iPc = *piPc; |
| 79464 | while(1){ /* Loop exits via break */ |
| 79465 | i = iPc++; |
| 79466 | if( i>=nRow ){ |
| 79467 | p->rc = SQLITE_OK; |
| 79468 | rc = SQLITE_DONE; |
| 79469 | break; |
| 79470 | } |
| 79471 | if( i<p->nOp ){ |
| 79472 | /* The rowid is small enough that we are still in the |
| 79473 | ** main program. */ |
| 79474 | aOp = p->aOp; |
| 79475 | }else{ |
| 79476 | /* We are currently listing subprograms. Figure out which one and |
| 79477 | ** pick up the appropriate opcode. */ |
| 79478 | int j; |
| 79479 | i -= p->nOp; |
| 79480 | assert( apSub!=0 ); |
| 79481 | assert( nSub>0 ); |
| 79482 | for(j=0; i>=apSub[j]->nOp; j++){ |
| 79483 | i -= apSub[j]->nOp; |
| 79484 | assert( i<apSub[j]->nOp || j+1<nSub ); |
| 79485 | } |
| 79486 | aOp = apSub[j]->aOp; |
| 79487 | } |
| 79488 | |
| 79489 | /* When an OP_Program opcode is encounter (the only opcode that has |
| 79490 | ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms |
| 79491 | ** kept in p->aMem[9].z to hold the new program - assuming this subprogram |
| 79492 | ** has not already been seen. |
| 79493 | */ |
| 79494 | if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){ |
| 79495 | int nByte = (nSub+1)*sizeof(SubProgram*); |
| 79496 | int j; |
| 79497 | for(j=0; j<nSub; j++){ |
| 79498 | if( apSub[j]==aOp[i].p4.pProgram ) break; |
| 79499 | } |
| 79500 | if( j==nSub ){ |
| 79501 | p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); |
| 79502 | if( p->rc!=SQLITE_OK ){ |
| 79503 | rc = SQLITE_ERROR; |
| 79504 | break; |
| 79505 | } |
| 79506 | apSub = (SubProgram **)pSub->z; |
| 79507 | apSub[nSub++] = aOp[i].p4.pProgram; |
| 79508 | MemSetTypeFlag(pSub, MEM_Blob); |
| 79509 | pSub->n = nSub*sizeof(SubProgram*); |
| 79510 | nRow += aOp[i].p4.pProgram->nOp; |
| 79511 | } |
| 79512 | } |
| 79513 | if( eMode==0 ) break; |
| 79514 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 79515 | if( eMode==2 ){ |
| 79516 | Op *pOp = aOp + i; |
| 79517 | if( pOp->opcode==OP_OpenRead ) break; |
| 79518 | if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break; |
| 79519 | if( pOp->opcode==OP_ReopenIdx ) break; |
| 79520 | }else |
| 79521 | #endif |
| 79522 | { |
| 79523 | assert( eMode==1 ); |
| 79524 | if( aOp[i].opcode==OP_Explain ) break; |
| 79525 | if( aOp[i].opcode==OP_Init && iPc>1 ) break; |
| 79526 | } |
| 79527 | } |
| 79528 | *piPc = iPc; |
| 79529 | *piAddr = i; |
| 79530 | *paOp = aOp; |
| 79531 | return rc; |
| 79532 | } |
| 79533 | #endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */ |
| 79534 | |
| 79535 | |
| 79536 | /* |
| 79537 | ** Delete a VdbeFrame object and its contents. VdbeFrame objects are |
| 79538 | ** allocated by the OP_Program opcode in sqlite3VdbeExec(). |
| 79539 | */ |
| @@ -79123,20 +79570,18 @@ | |
| 79570 | ** the trigger subprograms are listed one by one. |
| 79571 | */ |
| 79572 | SQLITE_PRIVATE int sqlite3VdbeList( |
| 79573 | Vdbe *p /* The VDBE */ |
| 79574 | ){ |
| 79575 | Mem *pSub = 0; /* Memory cell hold array of subprogs */ |
| 79576 | sqlite3 *db = p->db; /* The database connection */ |
| 79577 | int i; /* Loop counter */ |
| 79578 | int rc = SQLITE_OK; /* Return code */ |
| 79579 | Mem *pMem = &p->aMem[1]; /* First Mem of result set */ |
| 79580 | int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0); |
| 79581 | Op *aOp; /* Array of opcodes */ |
| 79582 | Op *pOp; /* Current opcode */ |
| 79583 | |
| 79584 | assert( p->explain ); |
| 79585 | assert( p->magic==VDBE_MAGIC_RUN ); |
| 79586 | assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); |
| 79587 | |
| @@ -79152,166 +79597,66 @@ | |
| 79597 | ** sqlite3_column_text16() failed. */ |
| 79598 | sqlite3OomFault(db); |
| 79599 | return SQLITE_ERROR; |
| 79600 | } |
| 79601 | |
| 79602 | if( bListSubprogs ){ |
| 79603 | /* The first 8 memory cells are used for the result set. So we will |
| 79604 | ** commandeer the 9th cell to use as storage for an array of pointers |
| 79605 | ** to trigger subprograms. The VDBE is guaranteed to have at least 9 |
| 79606 | ** cells. */ |
| 79607 | assert( p->nMem>9 ); |
| 79608 | pSub = &p->aMem[9]; |
| 79609 | }else{ |
| 79610 | pSub = 0; |
| 79611 | } |
| 79612 | |
| 79613 | /* Figure out which opcode is next to display */ |
| 79614 | rc = sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp); |
| 79615 | |
| 79616 | if( rc==SQLITE_OK ){ |
| 79617 | pOp = aOp + i; |
| 79618 | if( AtomicLoad(&db->u1.isInterrupted) ){ |
| 79619 | p->rc = SQLITE_INTERRUPT; |
| 79620 | rc = SQLITE_ERROR; |
| 79621 | sqlite3VdbeError(p, sqlite3ErrStr(p->rc)); |
| 79622 | }else{ |
| 79623 | char *zP4 = sqlite3VdbeDisplayP4(db, pOp); |
| 79624 | if( p->explain==2 ){ |
| 79625 | sqlite3VdbeMemSetInt64(pMem, pOp->p1); |
| 79626 | sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); |
| 79627 | sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); |
| 79628 | sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free); |
| 79629 | p->nResColumn = 4; |
| 79630 | }else{ |
| 79631 | sqlite3VdbeMemSetInt64(pMem+0, i); |
| 79632 | sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode), |
| 79633 | -1, SQLITE_UTF8, SQLITE_STATIC); |
| 79634 | sqlite3VdbeMemSetInt64(pMem+2, pOp->p1); |
| 79635 | sqlite3VdbeMemSetInt64(pMem+3, pOp->p2); |
| 79636 | sqlite3VdbeMemSetInt64(pMem+4, pOp->p3); |
| 79637 | /* pMem+5 for p4 is done last */ |
| 79638 | sqlite3VdbeMemSetInt64(pMem+6, pOp->p5); |
| 79639 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 79640 | { |
| 79641 | char *zCom = sqlite3VdbeDisplayComment(db, pOp, zP4); |
| 79642 | sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlite3_free); |
| 79643 | } |
| 79644 | #else |
| 79645 | sqlite3VdbeMemSetNull(pMem+7); |
| 79646 | #endif |
| 79647 | sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free); |
| 79648 | p->nResColumn = 8; |
| 79649 | } |
| 79650 | p->pResultSet = pMem; |
| 79651 | if( db->mallocFailed ){ |
| 79652 | p->rc = SQLITE_NOMEM; |
| 79653 | rc = SQLITE_ERROR; |
| 79654 | }else{ |
| 79655 | p->rc = SQLITE_OK; |
| 79656 | rc = SQLITE_ROW; |
| 79657 | } |
| 79658 | } |
| 79659 | } |
| 79660 | return rc; |
| 79661 | } |
| 79662 | #endif /* SQLITE_OMIT_EXPLAIN */ |
| @@ -79877,12 +80222,13 @@ | |
| 80222 | int retryCount = 0; |
| 80223 | int nMainFile; |
| 80224 | |
| 80225 | /* Select a master journal file name */ |
| 80226 | nMainFile = sqlite3Strlen30(zMainFile); |
| 80227 | zMaster = sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0); |
| 80228 | if( zMaster==0 ) return SQLITE_NOMEM_BKPT; |
| 80229 | zMaster += 4; |
| 80230 | do { |
| 80231 | u32 iRandom; |
| 80232 | if( retryCount ){ |
| 80233 | if( retryCount>100 ){ |
| 80234 | sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster); |
| @@ -79908,11 +80254,11 @@ | |
| 80254 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| |
| 80255 | SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0 |
| 80256 | ); |
| 80257 | } |
| 80258 | if( rc!=SQLITE_OK ){ |
| 80259 | sqlite3DbFree(db, zMaster-4); |
| 80260 | return rc; |
| 80261 | } |
| 80262 | |
| 80263 | /* Write the name of each database file in the transaction into the new |
| 80264 | ** master journal file. If an error occurs at this point close |
| @@ -79931,11 +80277,11 @@ | |
| 80277 | rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); |
| 80278 | offset += sqlite3Strlen30(zFile)+1; |
| 80279 | if( rc!=SQLITE_OK ){ |
| 80280 | sqlite3OsCloseFree(pMaster); |
| 80281 | sqlite3OsDelete(pVfs, zMaster, 0); |
| 80282 | sqlite3DbFree(db, zMaster-4); |
| 80283 | return rc; |
| 80284 | } |
| 80285 | } |
| 80286 | } |
| 80287 | |
| @@ -79945,11 +80291,11 @@ | |
| 80291 | if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) |
| 80292 | && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL)) |
| 80293 | ){ |
| 80294 | sqlite3OsCloseFree(pMaster); |
| 80295 | sqlite3OsDelete(pVfs, zMaster, 0); |
| 80296 | sqlite3DbFree(db, zMaster-4); |
| 80297 | return rc; |
| 80298 | } |
| 80299 | |
| 80300 | /* Sync all the db files involved in the transaction. The same call |
| 80301 | ** sets the master journal pointer in each individual journal. If |
| @@ -79968,20 +80314,20 @@ | |
| 80314 | } |
| 80315 | } |
| 80316 | sqlite3OsCloseFree(pMaster); |
| 80317 | assert( rc!=SQLITE_BUSY ); |
| 80318 | if( rc!=SQLITE_OK ){ |
| 80319 | sqlite3DbFree(db, zMaster-4); |
| 80320 | return rc; |
| 80321 | } |
| 80322 | |
| 80323 | /* Delete the master journal file. This commits the transaction. After |
| 80324 | ** doing this the directory is synced again before any individual |
| 80325 | ** transaction files are deleted. |
| 80326 | */ |
| 80327 | rc = sqlite3OsDelete(pVfs, zMaster, 1); |
| 80328 | sqlite3DbFree(db, zMaster-4); |
| 80329 | zMaster = 0; |
| 80330 | if( rc ){ |
| 80331 | return rc; |
| 80332 | } |
| 80333 | |
| @@ -83028,11 +83374,11 @@ | |
| 83374 | /* If there are no other statements currently running, then |
| 83375 | ** reset the interrupt flag. This prevents a call to sqlite3_interrupt |
| 83376 | ** from interrupting a statement that has not yet started. |
| 83377 | */ |
| 83378 | if( db->nVdbeActive==0 ){ |
| 83379 | AtomicStore(&db->u1.isInterrupted, 0); |
| 83380 | } |
| 83381 | |
| 83382 | assert( db->nVdbeWrite>0 || db->autoCommit==0 |
| 83383 | || (db->nDeferredCons==0 && db->nDeferredImmCons==0) |
| 83384 | ); |
| @@ -85413,11 +85759,11 @@ | |
| 85759 | assert( p->bIsReader || p->readOnly!=0 ); |
| 85760 | p->iCurrentTime = 0; |
| 85761 | assert( p->explain==0 ); |
| 85762 | p->pResultSet = 0; |
| 85763 | db->busyHandler.nBusy = 0; |
| 85764 | if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; |
| 85765 | sqlite3VdbeIOTraceSql(p); |
| 85766 | #ifdef SQLITE_DEBUG |
| 85767 | sqlite3BeginBenignMalloc(); |
| 85768 | if( p->pc==0 |
| 85769 | && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 |
| @@ -85597,11 +85943,11 @@ | |
| 85943 | ** But that is not due to sloppy coding habits. The code is written this |
| 85944 | ** way for performance, to avoid having to run the interrupt and progress |
| 85945 | ** checks on every opcode. This helps sqlite3_step() to run about 1.5% |
| 85946 | ** faster according to "valgrind --tool=cachegrind" */ |
| 85947 | check_for_interrupt: |
| 85948 | if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; |
| 85949 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 85950 | /* Call the progress callback if it is configured and the required number |
| 85951 | ** of VDBE ops have been executed (either since this invocation of |
| 85952 | ** sqlite3VdbeExec() or since last time the progress callback was called). |
| 85953 | ** If the progress callback returns non-zero, exit the virtual machine with |
| @@ -87893,32 +88239,38 @@ | |
| 88239 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 88240 | REGISTER_TRACE(pOp->p3, pOut); |
| 88241 | break; |
| 88242 | } |
| 88243 | |
| 88244 | /* Opcode: Count P1 P2 p3 * * |
| 88245 | ** Synopsis: r[P2]=count() |
| 88246 | ** |
| 88247 | ** Store the number of entries (an integer value) in the table or index |
| 88248 | ** opened by cursor P1 in register P2. |
| 88249 | ** |
| 88250 | ** If P3==0, then an exact count is obtained, which involves visiting |
| 88251 | ** every btree page of the table. But if P3 is non-zero, an estimate |
| 88252 | ** is returned based on the current cursor position. |
| 88253 | */ |
| 88254 | case OP_Count: { /* out2 */ |
| 88255 | i64 nEntry; |
| 88256 | BtCursor *pCrsr; |
| 88257 | |
| 88258 | assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); |
| 88259 | pCrsr = p->apCsr[pOp->p1]->uc.pCursor; |
| 88260 | assert( pCrsr ); |
| 88261 | if( pOp->p3 ){ |
| 88262 | nEntry = sqlite3BtreeRowCountEst(pCrsr); |
| 88263 | }else{ |
| 88264 | nEntry = 0; /* Not needed. Only used to silence a warning. */ |
| 88265 | rc = sqlite3BtreeCount(db, pCrsr, &nEntry); |
| 88266 | if( rc ) goto abort_due_to_error; |
| 88267 | } |
| 88268 | pOut = out2Prerelease(p, pOp); |
| 88269 | pOut->u.i = nEntry; |
| 88270 | goto check_for_interrupt; |
| 88271 | } |
| 88272 | |
| 88273 | /* Opcode: Savepoint P1 * * P4 * |
| 88274 | ** |
| 88275 | ** Open, release or rollback the savepoint named by parameter P4, depending |
| 88276 | ** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN). |
| @@ -90352,16 +90704,23 @@ | |
| 90704 | rc = sqlite3VdbeSorterWrite(pC, pIn2); |
| 90705 | if( rc) goto abort_due_to_error; |
| 90706 | break; |
| 90707 | } |
| 90708 | |
| 90709 | /* Opcode: IdxDelete P1 P2 P3 * P5 |
| 90710 | ** Synopsis: key=r[P2@P3] |
| 90711 | ** |
| 90712 | ** The content of P3 registers starting at register P2 form |
| 90713 | ** an unpacked index key. This opcode removes that entry from the |
| 90714 | ** index opened by cursor P1. |
| 90715 | ** |
| 90716 | ** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error |
| 90717 | ** if no matching index entry is found. This happens when running |
| 90718 | ** an UPDATE or DELETE statement and the index entry to be updated |
| 90719 | ** or deleted is not found. For some uses of IdxDelete |
| 90720 | ** (example: the EXCEPT operator) it does not matter that no matching |
| 90721 | ** entry is found. For those cases, P5 is zero. |
| 90722 | */ |
| 90723 | case OP_IdxDelete: { |
| 90724 | VdbeCursor *pC; |
| 90725 | BtCursor *pCrsr; |
| 90726 | int res; |
| @@ -90374,20 +90733,22 @@ | |
| 90733 | assert( pC!=0 ); |
| 90734 | assert( pC->eCurType==CURTYPE_BTREE ); |
| 90735 | sqlite3VdbeIncrWriteCounter(p, pC); |
| 90736 | pCrsr = pC->uc.pCursor; |
| 90737 | assert( pCrsr!=0 ); |
| 90738 | r.pKeyInfo = pC->pKeyInfo; |
| 90739 | r.nField = (u16)pOp->p3; |
| 90740 | r.default_rc = 0; |
| 90741 | r.aMem = &aMem[pOp->p2]; |
| 90742 | rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); |
| 90743 | if( rc ) goto abort_due_to_error; |
| 90744 | if( res==0 ){ |
| 90745 | rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); |
| 90746 | if( rc ) goto abort_due_to_error; |
| 90747 | }else if( pOp->p5 ){ |
| 90748 | rc = SQLITE_CORRUPT_INDEX; |
| 90749 | goto abort_due_to_error; |
| 90750 | } |
| 90751 | assert( pC->deferredMoveto==0 ); |
| 90752 | pC->cacheStatus = CACHE_STALE; |
| 90753 | pC->seekResult = 0; |
| 90754 | break; |
| @@ -92705,11 +93066,11 @@ | |
| 93066 | |
| 93067 | /* Jump to here if the sqlite3_interrupt() API sets the interrupt |
| 93068 | ** flag. |
| 93069 | */ |
| 93070 | abort_due_to_interrupt: |
| 93071 | assert( AtomicLoad(&db->u1.isInterrupted) ); |
| 93072 | rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; |
| 93073 | p->rc = rc; |
| 93074 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 93075 | goto abort_due_to_error; |
| 93076 | } |
| @@ -95988,10 +96349,435 @@ | |
| 96349 | *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2); |
| 96350 | return SQLITE_OK; |
| 96351 | } |
| 96352 | |
| 96353 | /************** End of vdbesort.c ********************************************/ |
| 96354 | /************** Begin file vdbevtab.c ****************************************/ |
| 96355 | /* |
| 96356 | ** 2020-03-23 |
| 96357 | ** |
| 96358 | ** The author disclaims copyright to this source code. In place of |
| 96359 | ** a legal notice, here is a blessing: |
| 96360 | ** |
| 96361 | ** May you do good and not evil. |
| 96362 | ** May you find forgiveness for yourself and forgive others. |
| 96363 | ** May you share freely, never taking more than you give. |
| 96364 | ** |
| 96365 | ************************************************************************* |
| 96366 | ** |
| 96367 | ** This file implements virtual-tables for examining the bytecode content |
| 96368 | ** of a prepared statement. |
| 96369 | */ |
| 96370 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 96371 | /* #include "sqliteInt.h" */ |
| 96372 | /* #include "vdbeInt.h" */ |
| 96373 | |
| 96374 | /* An instance of the bytecode() table-valued function. |
| 96375 | */ |
| 96376 | typedef struct bytecodevtab bytecodevtab; |
| 96377 | struct bytecodevtab { |
| 96378 | sqlite3_vtab base; /* Base class - must be first */ |
| 96379 | sqlite3 *db; /* Database connection */ |
| 96380 | int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */ |
| 96381 | }; |
| 96382 | |
| 96383 | /* A cursor for scanning through the bytecode |
| 96384 | */ |
| 96385 | typedef struct bytecodevtab_cursor bytecodevtab_cursor; |
| 96386 | struct bytecodevtab_cursor { |
| 96387 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 96388 | sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */ |
| 96389 | int iRowid; /* The rowid of the output table */ |
| 96390 | int iAddr; /* Address */ |
| 96391 | int needFinalize; /* Cursors owns pStmt and must finalize it */ |
| 96392 | int showSubprograms; /* Provide a listing of subprograms */ |
| 96393 | Op *aOp; /* Operand array */ |
| 96394 | char *zP4; /* Rendered P4 value */ |
| 96395 | const char *zType; /* tables_used.type */ |
| 96396 | const char *zSchema; /* tables_used.schema */ |
| 96397 | const char *zName; /* tables_used.name */ |
| 96398 | Mem sub; /* Subprograms */ |
| 96399 | }; |
| 96400 | |
| 96401 | /* |
| 96402 | ** Create a new bytecode() table-valued function. |
| 96403 | */ |
| 96404 | static int bytecodevtabConnect( |
| 96405 | sqlite3 *db, |
| 96406 | void *pAux, |
| 96407 | int argc, const char *const*argv, |
| 96408 | sqlite3_vtab **ppVtab, |
| 96409 | char **pzErr |
| 96410 | ){ |
| 96411 | bytecodevtab *pNew; |
| 96412 | int rc; |
| 96413 | int isTabUsed = pAux!=0; |
| 96414 | const char *azSchema[2] = { |
| 96415 | /* bytecode() schema */ |
| 96416 | "CREATE TABLE x(" |
| 96417 | "addr INT," |
| 96418 | "opcode TEXT," |
| 96419 | "p1 INT," |
| 96420 | "p2 INT," |
| 96421 | "p3 INT," |
| 96422 | "p4 TEXT," |
| 96423 | "p5 INT," |
| 96424 | "comment TEXT," |
| 96425 | "subprog TEXT," |
| 96426 | "stmt HIDDEN" |
| 96427 | ");", |
| 96428 | |
| 96429 | /* Tables_used() schema */ |
| 96430 | "CREATE TABLE x(" |
| 96431 | "type TEXT," |
| 96432 | "schema TEXT," |
| 96433 | "name TEXT," |
| 96434 | "wr INT," |
| 96435 | "subprog TEXT," |
| 96436 | "stmt HIDDEN" |
| 96437 | ");" |
| 96438 | }; |
| 96439 | |
| 96440 | rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]); |
| 96441 | if( rc==SQLITE_OK ){ |
| 96442 | pNew = sqlite3_malloc( sizeof(*pNew) ); |
| 96443 | *ppVtab = (sqlite3_vtab*)pNew; |
| 96444 | if( pNew==0 ) return SQLITE_NOMEM; |
| 96445 | memset(pNew, 0, sizeof(*pNew)); |
| 96446 | pNew->db = db; |
| 96447 | pNew->bTablesUsed = isTabUsed*2; |
| 96448 | } |
| 96449 | return rc; |
| 96450 | } |
| 96451 | |
| 96452 | /* |
| 96453 | ** This method is the destructor for bytecodevtab objects. |
| 96454 | */ |
| 96455 | static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){ |
| 96456 | bytecodevtab *p = (bytecodevtab*)pVtab; |
| 96457 | sqlite3_free(p); |
| 96458 | return SQLITE_OK; |
| 96459 | } |
| 96460 | |
| 96461 | /* |
| 96462 | ** Constructor for a new bytecodevtab_cursor object. |
| 96463 | */ |
| 96464 | static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ |
| 96465 | bytecodevtab *pVTab = (bytecodevtab*)p; |
| 96466 | bytecodevtab_cursor *pCur; |
| 96467 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 96468 | if( pCur==0 ) return SQLITE_NOMEM; |
| 96469 | memset(pCur, 0, sizeof(*pCur)); |
| 96470 | sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1); |
| 96471 | *ppCursor = &pCur->base; |
| 96472 | return SQLITE_OK; |
| 96473 | } |
| 96474 | |
| 96475 | /* |
| 96476 | ** Clear all internal content from a bytecodevtab cursor. |
| 96477 | */ |
| 96478 | static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){ |
| 96479 | sqlite3_free(pCur->zP4); |
| 96480 | pCur->zP4 = 0; |
| 96481 | sqlite3VdbeMemRelease(&pCur->sub); |
| 96482 | sqlite3VdbeMemSetNull(&pCur->sub); |
| 96483 | if( pCur->needFinalize ){ |
| 96484 | sqlite3_finalize(pCur->pStmt); |
| 96485 | } |
| 96486 | pCur->pStmt = 0; |
| 96487 | pCur->needFinalize = 0; |
| 96488 | pCur->zType = 0; |
| 96489 | pCur->zSchema = 0; |
| 96490 | pCur->zName = 0; |
| 96491 | } |
| 96492 | |
| 96493 | /* |
| 96494 | ** Destructor for a bytecodevtab_cursor. |
| 96495 | */ |
| 96496 | static int bytecodevtabClose(sqlite3_vtab_cursor *cur){ |
| 96497 | bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; |
| 96498 | bytecodevtabCursorClear(pCur); |
| 96499 | sqlite3_free(pCur); |
| 96500 | return SQLITE_OK; |
| 96501 | } |
| 96502 | |
| 96503 | |
| 96504 | /* |
| 96505 | ** Advance a bytecodevtab_cursor to its next row of output. |
| 96506 | */ |
| 96507 | static int bytecodevtabNext(sqlite3_vtab_cursor *cur){ |
| 96508 | bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; |
| 96509 | bytecodevtab *pTab = (bytecodevtab*)cur->pVtab; |
| 96510 | int rc; |
| 96511 | if( pCur->zP4 ){ |
| 96512 | sqlite3_free(pCur->zP4); |
| 96513 | pCur->zP4 = 0; |
| 96514 | } |
| 96515 | if( pCur->zName ){ |
| 96516 | pCur->zName = 0; |
| 96517 | pCur->zType = 0; |
| 96518 | pCur->zSchema = 0; |
| 96519 | } |
| 96520 | rc = sqlite3VdbeNextOpcode( |
| 96521 | (Vdbe*)pCur->pStmt, |
| 96522 | pCur->showSubprograms ? &pCur->sub : 0, |
| 96523 | pTab->bTablesUsed, |
| 96524 | &pCur->iRowid, |
| 96525 | &pCur->iAddr, |
| 96526 | &pCur->aOp); |
| 96527 | if( rc!=SQLITE_OK ){ |
| 96528 | sqlite3VdbeMemSetNull(&pCur->sub); |
| 96529 | pCur->aOp = 0; |
| 96530 | } |
| 96531 | return SQLITE_OK; |
| 96532 | } |
| 96533 | |
| 96534 | /* |
| 96535 | ** Return TRUE if the cursor has been moved off of the last |
| 96536 | ** row of output. |
| 96537 | */ |
| 96538 | static int bytecodevtabEof(sqlite3_vtab_cursor *cur){ |
| 96539 | bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; |
| 96540 | return pCur->aOp==0; |
| 96541 | } |
| 96542 | |
| 96543 | /* |
| 96544 | ** Return values of columns for the row at which the bytecodevtab_cursor |
| 96545 | ** is currently pointing. |
| 96546 | */ |
| 96547 | static int bytecodevtabColumn( |
| 96548 | sqlite3_vtab_cursor *cur, /* The cursor */ |
| 96549 | sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ |
| 96550 | int i /* Which column to return */ |
| 96551 | ){ |
| 96552 | bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; |
| 96553 | bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab; |
| 96554 | Op *pOp = pCur->aOp + pCur->iAddr; |
| 96555 | if( pVTab->bTablesUsed ){ |
| 96556 | if( i==4 ){ |
| 96557 | i = 8; |
| 96558 | }else{ |
| 96559 | if( i<=2 && pCur->zType==0 ){ |
| 96560 | Schema *pSchema; |
| 96561 | HashElem *k; |
| 96562 | int iDb = pOp->p3; |
| 96563 | int iRoot = pOp->p2; |
| 96564 | sqlite3 *db = pVTab->db; |
| 96565 | pSchema = db->aDb[iDb].pSchema; |
| 96566 | pCur->zSchema = db->aDb[iDb].zDbSName; |
| 96567 | for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ |
| 96568 | Table *pTab = (Table*)sqliteHashData(k); |
| 96569 | if( !IsVirtual(pTab) && pTab->tnum==iRoot ){ |
| 96570 | pCur->zName = pTab->zName; |
| 96571 | pCur->zType = "table"; |
| 96572 | break; |
| 96573 | } |
| 96574 | } |
| 96575 | if( pCur->zName==0 ){ |
| 96576 | for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){ |
| 96577 | Index *pIdx = (Index*)sqliteHashData(k); |
| 96578 | if( pIdx->tnum==iRoot ){ |
| 96579 | pCur->zName = pIdx->zName; |
| 96580 | pCur->zType = "index"; |
| 96581 | } |
| 96582 | } |
| 96583 | } |
| 96584 | } |
| 96585 | i += 10; |
| 96586 | } |
| 96587 | } |
| 96588 | switch( i ){ |
| 96589 | case 0: /* addr */ |
| 96590 | sqlite3_result_int(ctx, pCur->iAddr); |
| 96591 | break; |
| 96592 | case 1: /* opcode */ |
| 96593 | sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode), |
| 96594 | -1, SQLITE_STATIC); |
| 96595 | break; |
| 96596 | case 2: /* p1 */ |
| 96597 | sqlite3_result_int(ctx, pOp->p1); |
| 96598 | break; |
| 96599 | case 3: /* p2 */ |
| 96600 | sqlite3_result_int(ctx, pOp->p2); |
| 96601 | break; |
| 96602 | case 4: /* p3 */ |
| 96603 | sqlite3_result_int(ctx, pOp->p3); |
| 96604 | break; |
| 96605 | case 5: /* p4 */ |
| 96606 | case 7: /* comment */ |
| 96607 | if( pCur->zP4==0 ){ |
| 96608 | pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp); |
| 96609 | } |
| 96610 | if( i==5 ){ |
| 96611 | sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC); |
| 96612 | }else{ |
| 96613 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 96614 | char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4); |
| 96615 | sqlite3_result_text(ctx, zCom, -1, sqlite3_free); |
| 96616 | #endif |
| 96617 | } |
| 96618 | break; |
| 96619 | case 6: /* p5 */ |
| 96620 | sqlite3_result_int(ctx, pOp->p5); |
| 96621 | break; |
| 96622 | case 8: { /* subprog */ |
| 96623 | Op *aOp = pCur->aOp; |
| 96624 | assert( aOp[0].opcode==OP_Init ); |
| 96625 | assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 ); |
| 96626 | if( pCur->iRowid==pCur->iAddr+1 ){ |
| 96627 | break; /* Result is NULL for the main program */ |
| 96628 | }else if( aOp[0].p4.z!=0 ){ |
| 96629 | sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC); |
| 96630 | }else{ |
| 96631 | sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); |
| 96632 | } |
| 96633 | break; |
| 96634 | } |
| 96635 | case 10: /* tables_used.type */ |
| 96636 | sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); |
| 96637 | break; |
| 96638 | case 11: /* tables_used.schema */ |
| 96639 | sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); |
| 96640 | break; |
| 96641 | case 12: /* tables_used.name */ |
| 96642 | sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); |
| 96643 | break; |
| 96644 | case 13: /* tables_used.wr */ |
| 96645 | sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); |
| 96646 | break; |
| 96647 | } |
| 96648 | return SQLITE_OK; |
| 96649 | } |
| 96650 | |
| 96651 | /* |
| 96652 | ** Return the rowid for the current row. In this implementation, the |
| 96653 | ** rowid is the same as the output value. |
| 96654 | */ |
| 96655 | static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
| 96656 | bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; |
| 96657 | *pRowid = pCur->iRowid; |
| 96658 | return SQLITE_OK; |
| 96659 | } |
| 96660 | |
| 96661 | /* |
| 96662 | ** Initialize a cursor. |
| 96663 | ** |
| 96664 | ** idxNum==0 means show all subprograms |
| 96665 | ** idxNum==1 means show only the main bytecode and omit subprograms. |
| 96666 | */ |
| 96667 | static int bytecodevtabFilter( |
| 96668 | sqlite3_vtab_cursor *pVtabCursor, |
| 96669 | int idxNum, const char *idxStr, |
| 96670 | int argc, sqlite3_value **argv |
| 96671 | ){ |
| 96672 | bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; |
| 96673 | bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; |
| 96674 | int rc = SQLITE_OK; |
| 96675 | |
| 96676 | bytecodevtabCursorClear(pCur); |
| 96677 | pCur->iRowid = 0; |
| 96678 | pCur->iAddr = 0; |
| 96679 | pCur->showSubprograms = idxNum==0; |
| 96680 | assert( argc==1 ); |
| 96681 | if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){ |
| 96682 | const char *zSql = (const char*)sqlite3_value_text(argv[0]); |
| 96683 | if( zSql==0 ){ |
| 96684 | rc = SQLITE_NOMEM; |
| 96685 | }else{ |
| 96686 | rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0); |
| 96687 | pCur->needFinalize = 1; |
| 96688 | } |
| 96689 | }else{ |
| 96690 | pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer"); |
| 96691 | } |
| 96692 | if( pCur->pStmt==0 ){ |
| 96693 | pVTab->base.zErrMsg = sqlite3_mprintf( |
| 96694 | "argument to %s() is not a valid SQL statement", |
| 96695 | pVTab->bTablesUsed ? "tables_used" : "bytecode" |
| 96696 | ); |
| 96697 | rc = SQLITE_ERROR; |
| 96698 | }else{ |
| 96699 | bytecodevtabNext(pVtabCursor); |
| 96700 | } |
| 96701 | return rc; |
| 96702 | } |
| 96703 | |
| 96704 | /* |
| 96705 | ** We must have a single stmt=? constraint that will be passed through |
| 96706 | ** into the xFilter method. If there is no valid stmt=? constraint, |
| 96707 | ** then return an SQLITE_CONSTRAINT error. |
| 96708 | */ |
| 96709 | static int bytecodevtabBestIndex( |
| 96710 | sqlite3_vtab *tab, |
| 96711 | sqlite3_index_info *pIdxInfo |
| 96712 | ){ |
| 96713 | int i; |
| 96714 | int rc = SQLITE_CONSTRAINT; |
| 96715 | struct sqlite3_index_constraint *p; |
| 96716 | bytecodevtab *pVTab = (bytecodevtab*)tab; |
| 96717 | int iBaseCol = pVTab->bTablesUsed ? 4 : 8; |
| 96718 | pIdxInfo->estimatedCost = (double)100; |
| 96719 | pIdxInfo->estimatedRows = 100; |
| 96720 | pIdxInfo->idxNum = 0; |
| 96721 | for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){ |
| 96722 | if( p->usable==0 ) continue; |
| 96723 | if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){ |
| 96724 | rc = SQLITE_OK; |
| 96725 | pIdxInfo->aConstraintUsage[i].omit = 1; |
| 96726 | pIdxInfo->aConstraintUsage[i].argvIndex = 1; |
| 96727 | } |
| 96728 | if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){ |
| 96729 | pIdxInfo->aConstraintUsage[i].omit = 1; |
| 96730 | pIdxInfo->idxNum = 1; |
| 96731 | } |
| 96732 | } |
| 96733 | return rc; |
| 96734 | } |
| 96735 | |
| 96736 | /* |
| 96737 | ** This following structure defines all the methods for the |
| 96738 | ** virtual table. |
| 96739 | */ |
| 96740 | static sqlite3_module bytecodevtabModule = { |
| 96741 | /* iVersion */ 0, |
| 96742 | /* xCreate */ 0, |
| 96743 | /* xConnect */ bytecodevtabConnect, |
| 96744 | /* xBestIndex */ bytecodevtabBestIndex, |
| 96745 | /* xDisconnect */ bytecodevtabDisconnect, |
| 96746 | /* xDestroy */ 0, |
| 96747 | /* xOpen */ bytecodevtabOpen, |
| 96748 | /* xClose */ bytecodevtabClose, |
| 96749 | /* xFilter */ bytecodevtabFilter, |
| 96750 | /* xNext */ bytecodevtabNext, |
| 96751 | /* xEof */ bytecodevtabEof, |
| 96752 | /* xColumn */ bytecodevtabColumn, |
| 96753 | /* xRowid */ bytecodevtabRowid, |
| 96754 | /* xUpdate */ 0, |
| 96755 | /* xBegin */ 0, |
| 96756 | /* xSync */ 0, |
| 96757 | /* xCommit */ 0, |
| 96758 | /* xRollback */ 0, |
| 96759 | /* xFindMethod */ 0, |
| 96760 | /* xRename */ 0, |
| 96761 | /* xSavepoint */ 0, |
| 96762 | /* xRelease */ 0, |
| 96763 | /* xRollbackTo */ 0, |
| 96764 | /* xShadowName */ 0 |
| 96765 | }; |
| 96766 | |
| 96767 | |
| 96768 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ |
| 96769 | int rc; |
| 96770 | rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0); |
| 96771 | if( rc==SQLITE_OK ){ |
| 96772 | rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); |
| 96773 | } |
| 96774 | return rc; |
| 96775 | } |
| 96776 | #endif /* SQLITE_ENABLE_BYTECODE_VTAB */ |
| 96777 | |
| 96778 | /************** End of vdbevtab.c ********************************************/ |
| 96779 | /************** Begin file memjournal.c **************************************/ |
| 96780 | /* |
| 96781 | ** 2008 October 7 |
| 96782 | ** |
| 96783 | ** The author disclaims copyright to this source code. In place of |
| @@ -96775,11 +97561,11 @@ | |
| 97561 | const char *zTab, |
| 97562 | const char *zDb |
| 97563 | ){ |
| 97564 | int n; |
| 97565 | const char *zSpan; |
| 97566 | if( pItem->eEName!=ENAME_TAB ) return 0; |
| 97567 | zSpan = pItem->zEName; |
| 97568 | for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} |
| 97569 | if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ |
| 97570 | return 0; |
| 97571 | } |
| @@ -96929,10 +97715,11 @@ | |
| 97715 | ExprList *pEList; |
| 97716 | SrcList *pSrcList = pNC->pSrcList; |
| 97717 | |
| 97718 | if( pSrcList ){ |
| 97719 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 97720 | u8 hCol; |
| 97721 | pTab = pItem->pTab; |
| 97722 | assert( pTab!=0 && pTab->zName!=0 ); |
| 97723 | assert( pTab->nCol>0 ); |
| 97724 | if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ |
| 97725 | int hit = 0; |
| @@ -96962,12 +97749,13 @@ | |
| 97749 | } |
| 97750 | } |
| 97751 | if( 0==(cntTab++) ){ |
| 97752 | pMatch = pItem; |
| 97753 | } |
| 97754 | hCol = sqlite3StrIHash(zCol); |
| 97755 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 97756 | if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 97757 | /* If there has been exactly one prior match and this match |
| 97758 | ** is for the right-hand table of a NATURAL JOIN or is in a |
| 97759 | ** USING clause, then skip this match. |
| 97760 | */ |
| 97761 | if( cnt==1 ){ |
| @@ -97024,14 +97812,15 @@ | |
| 97812 | } |
| 97813 | #endif /* SQLITE_OMIT_UPSERT */ |
| 97814 | |
| 97815 | if( pTab ){ |
| 97816 | int iCol; |
| 97817 | u8 hCol = sqlite3StrIHash(zCol); |
| 97818 | pSchema = pTab->pSchema; |
| 97819 | cntTab++; |
| 97820 | for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){ |
| 97821 | if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 97822 | if( iCol==pTab->iPKey ){ |
| 97823 | iCol = -1; |
| 97824 | } |
| 97825 | break; |
| 97826 | } |
| @@ -97828,11 +98617,11 @@ | |
| 98617 | nc.uNC.pEList = pEList; |
| 98618 | nc.ncFlags = NC_AllowAgg|NC_UEList; |
| 98619 | nc.nErr = 0; |
| 98620 | db = pParse->db; |
| 98621 | savedSuppErr = db->suppressErr; |
| 98622 | if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1; |
| 98623 | rc = sqlite3ResolveExprNames(&nc, pE); |
| 98624 | db->suppressErr = savedSuppErr; |
| 98625 | if( rc ) return 0; |
| 98626 | |
| 98627 | /* Try to match the ORDER BY expression against an expression |
| @@ -98463,15 +99252,45 @@ | |
| 99252 | SQLITE_PRIVATE int sqlite3ResolveExprListNames( |
| 99253 | NameContext *pNC, /* Namespace to resolve expressions in. */ |
| 99254 | ExprList *pList /* The expression list to be analyzed. */ |
| 99255 | ){ |
| 99256 | int i; |
| 99257 | int savedHasAgg = 0; |
| 99258 | Walker w; |
| 99259 | if( pList==0 ) return WRC_Continue; |
| 99260 | w.pParse = pNC->pParse; |
| 99261 | w.xExprCallback = resolveExprStep; |
| 99262 | w.xSelectCallback = resolveSelectStep; |
| 99263 | w.xSelectCallback2 = 0; |
| 99264 | w.u.pNC = pNC; |
| 99265 | savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); |
| 99266 | pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); |
| 99267 | for(i=0; i<pList->nExpr; i++){ |
| 99268 | Expr *pExpr = pList->a[i].pExpr; |
| 99269 | if( pExpr==0 ) continue; |
| 99270 | #if SQLITE_MAX_EXPR_DEPTH>0 |
| 99271 | w.pParse->nHeight += pExpr->nHeight; |
| 99272 | if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){ |
| 99273 | return WRC_Abort; |
| 99274 | } |
| 99275 | #endif |
| 99276 | sqlite3WalkExpr(&w, pExpr); |
| 99277 | #if SQLITE_MAX_EXPR_DEPTH>0 |
| 99278 | w.pParse->nHeight -= pExpr->nHeight; |
| 99279 | #endif |
| 99280 | assert( EP_Agg==NC_HasAgg ); |
| 99281 | assert( EP_Win==NC_HasWin ); |
| 99282 | testcase( pNC->ncFlags & NC_HasAgg ); |
| 99283 | testcase( pNC->ncFlags & NC_HasWin ); |
| 99284 | if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){ |
| 99285 | ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); |
| 99286 | savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); |
| 99287 | pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); |
| 99288 | } |
| 99289 | if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort; |
| 99290 | } |
| 99291 | pNC->ncFlags |= savedHasAgg; |
| 99292 | return WRC_Continue; |
| 99293 | } |
| 99294 | |
| 99295 | /* |
| 99296 | ** Resolve all names in all expressions of a SELECT and in all |
| @@ -98601,11 +99420,11 @@ | |
| 99420 | ** SELECT * FROM t1 WHERE (select a from t1); |
| 99421 | */ |
| 99422 | SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ |
| 99423 | int op; |
| 99424 | while( ExprHasProperty(pExpr, EP_Skip) ){ |
| 99425 | assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); |
| 99426 | pExpr = pExpr->pLeft; |
| 99427 | assert( pExpr!=0 ); |
| 99428 | } |
| 99429 | op = pExpr->op; |
| 99430 | if( op==TK_SELECT ){ |
| @@ -98668,11 +99487,11 @@ | |
| 99487 | /* |
| 99488 | ** Skip over any TK_COLLATE operators. |
| 99489 | */ |
| 99490 | SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ |
| 99491 | while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){ |
| 99492 | assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); |
| 99493 | pExpr = pExpr->pLeft; |
| 99494 | } |
| 99495 | return pExpr; |
| 99496 | } |
| 99497 | |
| @@ -98687,11 +99506,11 @@ | |
| 99506 | assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); |
| 99507 | assert( pExpr->x.pList->nExpr>0 ); |
| 99508 | assert( pExpr->op==TK_FUNCTION ); |
| 99509 | pExpr = pExpr->x.pList->a[0].pExpr; |
| 99510 | }else{ |
| 99511 | assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); |
| 99512 | pExpr = pExpr->pLeft; |
| 99513 | } |
| 99514 | } |
| 99515 | return pExpr; |
| 99516 | } |
| @@ -100343,20 +101162,26 @@ | |
| 101162 | ExprList *pList, /* List to which to add the span. */ |
| 101163 | Token *pName, /* Name to be added */ |
| 101164 | int dequote /* True to cause the name to be dequoted */ |
| 101165 | ){ |
| 101166 | assert( pList!=0 || pParse->db->mallocFailed!=0 ); |
| 101167 | assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 ); |
| 101168 | if( pList ){ |
| 101169 | struct ExprList_item *pItem; |
| 101170 | assert( pList->nExpr>0 ); |
| 101171 | pItem = &pList->a[pList->nExpr-1]; |
| 101172 | assert( pItem->zEName==0 ); |
| 101173 | assert( pItem->eEName==ENAME_NAME ); |
| 101174 | pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); |
| 101175 | if( dequote ){ |
| 101176 | /* If dequote==0, then pName->z does not point to part of a DDL |
| 101177 | ** statement handled by the parser. And so no token need be added |
| 101178 | ** to the token-map. */ |
| 101179 | sqlite3Dequote(pItem->zEName); |
| 101180 | if( IN_RENAME_OBJECT ){ |
| 101181 | sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); |
| 101182 | } |
| 101183 | } |
| 101184 | } |
| 101185 | } |
| 101186 | |
| 101187 | /* |
| @@ -101497,10 +102322,12 @@ | |
| 102322 | struct ExprList_item *pItem; |
| 102323 | int r1, r2; |
| 102324 | affinity = sqlite3ExprAffinity(pLeft); |
| 102325 | if( affinity<=SQLITE_AFF_NONE ){ |
| 102326 | affinity = SQLITE_AFF_BLOB; |
| 102327 | }else if( affinity==SQLITE_AFF_REAL ){ |
| 102328 | affinity = SQLITE_AFF_NUMERIC; |
| 102329 | } |
| 102330 | if( pKeyInfo ){ |
| 102331 | assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); |
| 102332 | pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); |
| 102333 | } |
| @@ -101735,10 +102562,11 @@ | |
| 102562 | int destStep6 = 0; /* Start of code for Step 6 */ |
| 102563 | int addrTruthOp; /* Address of opcode that determines the IN is true */ |
| 102564 | int destNotNull; /* Jump here if a comparison is not true in step 6 */ |
| 102565 | int addrTop; /* Top of the step-6 loop */ |
| 102566 | int iTab = 0; /* Index to use */ |
| 102567 | u8 okConstFactor = pParse->okConstFactor; |
| 102568 | |
| 102569 | assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); |
| 102570 | pLeft = pExpr->pLeft; |
| 102571 | if( sqlite3ExprCheckIN(pParse, pExpr) ) return; |
| 102572 | zAff = exprINAffinity(pParse, pExpr); |
| @@ -101779,12 +102607,18 @@ | |
| 102607 | ** |
| 102608 | ** sqlite3FindInIndex() might have reordered the fields of the LHS vector |
| 102609 | ** so that the fields are in the same order as an existing index. The |
| 102610 | ** aiMap[] array contains a mapping from the original LHS field order to |
| 102611 | ** the field order that matches the RHS index. |
| 102612 | ** |
| 102613 | ** Avoid factoring the LHS of the IN(...) expression out of the loop, |
| 102614 | ** even if it is constant, as OP_Affinity may be used on the register |
| 102615 | ** by code generated below. */ |
| 102616 | assert( pParse->okConstFactor==okConstFactor ); |
| 102617 | pParse->okConstFactor = 0; |
| 102618 | rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy); |
| 102619 | pParse->okConstFactor = okConstFactor; |
| 102620 | for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */ |
| 102621 | if( i==nVector ){ |
| 102622 | /* LHS fields are not reordered */ |
| 102623 | rLhs = rLhsOrig; |
| 102624 | }else{ |
| @@ -101806,25 +102640,17 @@ | |
| 102640 | CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); |
| 102641 | int labelOk = sqlite3VdbeMakeLabel(pParse); |
| 102642 | int r2, regToFree; |
| 102643 | int regCkNull = 0; |
| 102644 | int ii; |
| 102645 | assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); |
| 102646 | if( destIfNull!=destIfFalse ){ |
| 102647 | regCkNull = sqlite3GetTempReg(pParse); |
| 102648 | sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); |
| 102649 | } |
| 102650 | for(ii=0; ii<pList->nExpr; ii++){ |
| 102651 | r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree); |
| 102652 | if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){ |
| 102653 | sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull); |
| 102654 | } |
| 102655 | sqlite3ReleaseTempReg(pParse, regToFree); |
| 102656 | if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){ |
| @@ -102394,14 +103220,10 @@ | |
| 103220 | } |
| 103221 | if( aff>SQLITE_AFF_BLOB ){ |
| 103222 | static const char zAff[] = "B\000C\000D\000E"; |
| 103223 | assert( SQLITE_AFF_BLOB=='A' ); |
| 103224 | assert( SQLITE_AFF_TEXT=='B' ); |
| 103225 | sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0, |
| 103226 | &zAff[(aff-'B')*2], P4_STATIC); |
| 103227 | } |
| 103228 | return iReg; |
| 103229 | } |
| @@ -103035,11 +103857,11 @@ | |
| 103857 | assert( pExpr->affExpr==OE_Rollback |
| 103858 | || pExpr->affExpr==OE_Abort |
| 103859 | || pExpr->affExpr==OE_Fail |
| 103860 | || pExpr->affExpr==OE_Ignore |
| 103861 | ); |
| 103862 | if( !pParse->pTriggerTab && !pParse->nested ){ |
| 103863 | sqlite3ErrorMsg(pParse, |
| 103864 | "RAISE() may only be used within a trigger-program"); |
| 103865 | return 0; |
| 103866 | } |
| 103867 | if( pExpr->affExpr==OE_Abort ){ |
| @@ -103049,12 +103871,13 @@ | |
| 103871 | if( pExpr->affExpr==OE_Ignore ){ |
| 103872 | sqlite3VdbeAddOp4( |
| 103873 | v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); |
| 103874 | VdbeCoverage(v); |
| 103875 | }else{ |
| 103876 | sqlite3HaltConstraint(pParse, |
| 103877 | pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR, |
| 103878 | pExpr->affExpr, pExpr->u.zToken, 0, 0); |
| 103879 | } |
| 103880 | |
| 103881 | break; |
| 103882 | } |
| 103883 | #endif |
| @@ -104805,10 +105628,26 @@ | |
| 105628 | exit_rename_table: |
| 105629 | sqlite3SrcListDelete(db, pSrc); |
| 105630 | sqlite3DbFree(db, zName); |
| 105631 | db->mDbFlags = savedDbFlags; |
| 105632 | } |
| 105633 | |
| 105634 | /* |
| 105635 | ** Write code that will raise an error if the table described by |
| 105636 | ** zDb and zTab is not empty. |
| 105637 | */ |
| 105638 | static void sqlite3ErrorIfNotEmpty( |
| 105639 | Parse *pParse, /* Parsing context */ |
| 105640 | const char *zDb, /* Schema holding the table */ |
| 105641 | const char *zTab, /* Table to check for empty */ |
| 105642 | const char *zErr /* Error message text */ |
| 105643 | ){ |
| 105644 | sqlite3NestedParse(pParse, |
| 105645 | "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"", |
| 105646 | zErr, zDb, zTab |
| 105647 | ); |
| 105648 | } |
| 105649 | |
| 105650 | /* |
| 105651 | ** This function is called after an "ALTER TABLE ... ADD" statement |
| 105652 | ** has been parsed. Argument pColDef contains the text of the new |
| 105653 | ** column definition. |
| @@ -104858,11 +105697,12 @@ | |
| 105697 | if( pCol->colFlags & COLFLAG_PRIMKEY ){ |
| 105698 | sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column"); |
| 105699 | return; |
| 105700 | } |
| 105701 | if( pNew->pIndex ){ |
| 105702 | sqlite3ErrorMsg(pParse, |
| 105703 | "Cannot add a UNIQUE column"); |
| 105704 | return; |
| 105705 | } |
| 105706 | if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){ |
| 105707 | /* If the default value for the new column was specified with a |
| 105708 | ** literal NULL, then set pDflt to 0. This simplifies checking |
| @@ -104871,19 +105711,18 @@ | |
| 105711 | assert( pDflt==0 || pDflt->op==TK_SPAN ); |
| 105712 | if( pDflt && pDflt->pLeft->op==TK_NULL ){ |
| 105713 | pDflt = 0; |
| 105714 | } |
| 105715 | if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ |
| 105716 | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, |
| 105717 | "Cannot add a REFERENCES column with non-NULL default value"); |
| 105718 | } |
| 105719 | if( pCol->notNull && !pDflt ){ |
| 105720 | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, |
| 105721 | "Cannot add a NOT NULL column with default value NULL"); |
| 105722 | } |
| 105723 | |
| 105724 | |
| 105725 | /* Ensure the default expression is something that sqlite3ValueFromExpr() |
| 105726 | ** can handle (i.e. not CURRENT_TIME etc.) |
| 105727 | */ |
| 105728 | if( pDflt ){ |
| @@ -104894,18 +105733,17 @@ | |
| 105733 | if( rc!=SQLITE_OK ){ |
| 105734 | assert( db->mallocFailed == 1 ); |
| 105735 | return; |
| 105736 | } |
| 105737 | if( !pVal ){ |
| 105738 | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, |
| 105739 | "Cannot add a column with non-constant default"); |
| 105740 | } |
| 105741 | sqlite3ValueFree(pVal); |
| 105742 | } |
| 105743 | }else if( pCol->colFlags & COLFLAG_STORED ){ |
| 105744 | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column"); |
| 105745 | } |
| 105746 | |
| 105747 | |
| 105748 | /* Modify the CREATE TABLE statement. */ |
| 105749 | zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); |
| @@ -105020,10 +105858,11 @@ | |
| 105858 | } |
| 105859 | memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); |
| 105860 | for(i=0; i<pNew->nCol; i++){ |
| 105861 | Column *pCol = &pNew->aCol[i]; |
| 105862 | pCol->zName = sqlite3DbStrDup(db, pCol->zName); |
| 105863 | pCol->hName = sqlite3StrIHash(pCol->zName); |
| 105864 | pCol->zColl = 0; |
| 105865 | pCol->pDflt = 0; |
| 105866 | } |
| 105867 | pNew->pSchema = db->aDb[iDb].pSchema; |
| 105868 | pNew->addColOffset = pTab->addColOffset; |
| @@ -105248,11 +106087,11 @@ | |
| 106087 | */ |
| 106088 | SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ |
| 106089 | RenameToken *pNew; |
| 106090 | assert( pPtr || pParse->db->mallocFailed ); |
| 106091 | renameTokenCheckAll(pParse, pPtr); |
| 106092 | if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){ |
| 106093 | pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken)); |
| 106094 | if( pNew ){ |
| 106095 | pNew->p = pPtr; |
| 106096 | pNew->t = *pToken; |
| 106097 | pNew->pNext = pParse->pRename; |
| @@ -105305,10 +106144,25 @@ | |
| 106144 | sqlite3WalkSelect(pWalker, p); |
| 106145 | sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); |
| 106146 | } |
| 106147 | } |
| 106148 | } |
| 106149 | |
| 106150 | /* |
| 106151 | ** Unmap all tokens in the IdList object passed as the second argument. |
| 106152 | */ |
| 106153 | static void unmapColumnIdlistNames( |
| 106154 | Parse *pParse, |
| 106155 | IdList *pIdList |
| 106156 | ){ |
| 106157 | if( pIdList ){ |
| 106158 | int ii; |
| 106159 | for(ii=0; ii<pIdList->nId; ii++){ |
| 106160 | sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName); |
| 106161 | } |
| 106162 | } |
| 106163 | } |
| 106164 | |
| 106165 | /* |
| 106166 | ** Walker callback used by sqlite3RenameExprUnmap(). |
| 106167 | */ |
| 106168 | static int renameUnmapSelectCb(Walker *pWalker, Select *p){ |
| @@ -105327,10 +106181,11 @@ | |
| 106181 | if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */ |
| 106182 | SrcList *pSrc = p->pSrc; |
| 106183 | for(i=0; i<pSrc->nSrc; i++){ |
| 106184 | sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); |
| 106185 | if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; |
| 106186 | unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); |
| 106187 | } |
| 106188 | } |
| 106189 | |
| 106190 | renameWalkWith(pWalker, p); |
| 106191 | return WRC_Continue; |
| @@ -105534,10 +106389,11 @@ | |
| 106389 | renameTokenFind(pParse, pCtx, (void*)zName); |
| 106390 | } |
| 106391 | } |
| 106392 | } |
| 106393 | } |
| 106394 | |
| 106395 | |
| 106396 | /* |
| 106397 | ** Parse the SQL statement zSql using Parse object (*p). The Parse object |
| 106398 | ** is initialized by this function before it is used. |
| 106399 | */ |
| @@ -106446,10 +107302,15 @@ | |
| 107302 | sqlite3 *db = pParse->db; |
| 107303 | Db *pDb; |
| 107304 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 107305 | int aRoot[ArraySize(aTable)]; |
| 107306 | u8 aCreateTbl[ArraySize(aTable)]; |
| 107307 | #ifdef SQLITE_ENABLE_STAT4 |
| 107308 | const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; |
| 107309 | #else |
| 107310 | const int nToOpen = 1; |
| 107311 | #endif |
| 107312 | |
| 107313 | if( v==0 ) return; |
| 107314 | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| 107315 | assert( sqlite3VdbeDb(v)==db ); |
| 107316 | pDb = &db->aDb[iDb]; |
| @@ -106458,12 +107319,13 @@ | |
| 107319 | ** if they do already exist. |
| 107320 | */ |
| 107321 | for(i=0; i<ArraySize(aTable); i++){ |
| 107322 | const char *zTab = aTable[i].zName; |
| 107323 | Table *pStat; |
| 107324 | aCreateTbl[i] = 0; |
| 107325 | if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){ |
| 107326 | if( i<nToOpen ){ |
| 107327 | /* The sqlite_statN table does not exist. Create it. Note that a |
| 107328 | ** side-effect of the CREATE TABLE statement is to leave the rootpage |
| 107329 | ** of the new table in register pParse->regRoot. This is important |
| 107330 | ** because the OpenWrite opcode below will be needing it. */ |
| 107331 | sqlite3NestedParse(pParse, |
| @@ -106475,11 +107337,10 @@ | |
| 107337 | }else{ |
| 107338 | /* The table already exists. If zWhere is not NULL, delete all entries |
| 107339 | ** associated with the table zWhere. If zWhere is NULL, delete the |
| 107340 | ** entire contents of the table. */ |
| 107341 | aRoot[i] = pStat->tnum; |
| 107342 | sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); |
| 107343 | if( zWhere ){ |
| 107344 | sqlite3NestedParse(pParse, |
| 107345 | "DELETE FROM %Q.%s WHERE %s=%Q", |
| 107346 | pDb->zDbSName, zTab, zWhereType, zWhere |
| @@ -106494,11 +107355,11 @@ | |
| 107355 | } |
| 107356 | } |
| 107357 | } |
| 107358 | |
| 107359 | /* Open the sqlite_stat[134] tables for writing. */ |
| 107360 | for(i=0; i<nToOpen; i++){ |
| 107361 | assert( i<ArraySize(aTable) ); |
| 107362 | sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3); |
| 107363 | sqlite3VdbeChangeP5(v, aCreateTbl[i]); |
| 107364 | VdbeComment((v, aTable[i].zName)); |
| 107365 | } |
| @@ -106533,13 +107394,16 @@ | |
| 107394 | u32 iHash; /* Tiebreaker hash */ |
| 107395 | #endif |
| 107396 | }; |
| 107397 | struct StatAccum { |
| 107398 | sqlite3 *db; /* Database connection, for malloc() */ |
| 107399 | tRowcnt nEst; /* Estimated number of rows */ |
| 107400 | tRowcnt nRow; /* Number of rows visited so far */ |
| 107401 | int nLimit; /* Analysis row-scan limit */ |
| 107402 | int nCol; /* Number of columns in index + pk/rowid */ |
| 107403 | int nKeyCol; /* Number of index columns w/o the pk/rowid */ |
| 107404 | u8 nSkipAhead; /* Number of times of skip-ahead */ |
| 107405 | StatSample current; /* Current row as a StatSample */ |
| 107406 | #ifdef SQLITE_ENABLE_STAT4 |
| 107407 | tRowcnt nPSample; /* How often to do a periodic sample */ |
| 107408 | int mxSample; /* Maximum number of samples to accumulate */ |
| 107409 | u32 iPrn; /* Pseudo-random number used for sampling */ |
| @@ -106615,31 +107479,32 @@ | |
| 107479 | ** Reclaim all memory of a StatAccum structure. |
| 107480 | */ |
| 107481 | static void statAccumDestructor(void *pOld){ |
| 107482 | StatAccum *p = (StatAccum*)pOld; |
| 107483 | #ifdef SQLITE_ENABLE_STAT4 |
| 107484 | if( p->mxSample ){ |
| 107485 | int i; |
| 107486 | for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i); |
| 107487 | for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i); |
| 107488 | sampleClear(p->db, &p->current); |
| 107489 | } |
| 107490 | #endif |
| 107491 | sqlite3DbFree(p->db, p); |
| 107492 | } |
| 107493 | |
| 107494 | /* |
| 107495 | ** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters |
| 107496 | ** are: |
| 107497 | ** N: The number of columns in the index including the rowid/pk (note 1) |
| 107498 | ** K: The number of columns in the index excluding the rowid/pk. |
| 107499 | ** C: Estimated number of rows in the index |
| 107500 | ** L: A limit on the number of rows to scan, or 0 for no-limit |
| 107501 | ** |
| 107502 | ** Note 1: In the special case of the covering index that implements a |
| 107503 | ** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the |
| 107504 | ** total number of columns in the table. |
| 107505 | ** |
| 107506 | ** For indexes on ordinary rowid tables, N==K+1. But for indexes on |
| 107507 | ** WITHOUT ROWID tables, N=K+P where P is the number of columns in the |
| 107508 | ** PRIMARY KEY of the table. The covering index that implements the |
| 107509 | ** original WITHOUT ROWID table as N==K as a special case. |
| 107510 | ** |
| @@ -106656,13 +107521,14 @@ | |
| 107521 | StatAccum *p; |
| 107522 | int nCol; /* Number of columns in index being sampled */ |
| 107523 | int nKeyCol; /* Number of key columns */ |
| 107524 | int nColUp; /* nCol rounded up for alignment */ |
| 107525 | int n; /* Bytes of space to allocate */ |
| 107526 | sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */ |
| 107527 | #ifdef SQLITE_ENABLE_STAT4 |
| 107528 | /* Maximum number of samples. 0 if STAT4 data is not collected */ |
| 107529 | int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0; |
| 107530 | #endif |
| 107531 | |
| 107532 | /* Decode the three function arguments */ |
| 107533 | UNUSED_PARAMETER(argc); |
| 107534 | nCol = sqlite3_value_int(argv[0]); |
| @@ -106673,39 +107539,43 @@ | |
| 107539 | assert( nKeyCol>0 ); |
| 107540 | |
| 107541 | /* Allocate the space required for the StatAccum object */ |
| 107542 | n = sizeof(*p) |
| 107543 | + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ |
| 107544 | + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ |
| 107545 | #ifdef SQLITE_ENABLE_STAT4 |
| 107546 | if( mxSample ){ |
| 107547 | n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ |
| 107548 | + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ |
| 107549 | + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); |
| 107550 | } |
| 107551 | #endif |
| 107552 | db = sqlite3_context_db_handle(context); |
| 107553 | p = sqlite3DbMallocZero(db, n); |
| 107554 | if( p==0 ){ |
| 107555 | sqlite3_result_error_nomem(context); |
| 107556 | return; |
| 107557 | } |
| 107558 | |
| 107559 | p->db = db; |
| 107560 | p->nEst = sqlite3_value_int64(argv[2]); |
| 107561 | p->nRow = 0; |
| 107562 | p->nLimit = sqlite3_value_int64(argv[3]); |
| 107563 | p->nCol = nCol; |
| 107564 | p->nKeyCol = nKeyCol; |
| 107565 | p->nSkipAhead = 0; |
| 107566 | p->current.anDLt = (tRowcnt*)&p[1]; |
| 107567 | p->current.anEq = &p->current.anDLt[nColUp]; |
| 107568 | |
| 107569 | #ifdef SQLITE_ENABLE_STAT4 |
| 107570 | p->mxSample = p->nLimit==0 ? mxSample : 0; |
| 107571 | if( mxSample ){ |
| 107572 | u8 *pSpace; /* Allocated space not yet assigned */ |
| 107573 | int i; /* Used to iterate through p->aSample[] */ |
| 107574 | |
| 107575 | p->iGet = -1; |
| 107576 | p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1); |
| 107577 | p->current.anLt = &p->current.anEq[nColUp]; |
| 107578 | p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); |
| 107579 | |
| 107580 | /* Set up the StatAccum.a[] and aBest[] arrays */ |
| 107581 | p->a = (struct StatSample*)&p->current.anLt[nColUp]; |
| @@ -106729,11 +107599,11 @@ | |
| 107599 | ** (given by the 3rd parameter) is never used and can be any positive |
| 107600 | ** value. */ |
| 107601 | sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor); |
| 107602 | } |
| 107603 | static const FuncDef statInitFuncdef = { |
| 107604 | 4, /* nArg */ |
| 107605 | SQLITE_UTF8, /* funcFlags */ |
| 107606 | 0, /* pUserData */ |
| 107607 | 0, /* pNext */ |
| 107608 | statInit, /* xSFunc */ |
| 107609 | 0, /* xFinalize */ |
| @@ -106933,14 +107803,17 @@ | |
| 107803 | ** P Pointer to the StatAccum object created by stat_init() |
| 107804 | ** C Index of left-most column to differ from previous row |
| 107805 | ** R Rowid for the current row. Might be a key record for |
| 107806 | ** WITHOUT ROWID tables. |
| 107807 | ** |
| 107808 | ** The purpose of this routine is to collect statistical data and/or |
| 107809 | ** samples from the index being analyzed into the StatAccum object. |
| 107810 | ** The stat_get() SQL function will be used afterwards to |
| 107811 | ** retrieve the information gathered. |
| 107812 | ** |
| 107813 | ** This SQL function usually returns NULL, but might return an integer |
| 107814 | ** if it wants the byte-code to do special processing. |
| 107815 | ** |
| 107816 | ** The R parameter is only used for STAT4 |
| 107817 | */ |
| 107818 | static void statPush( |
| 107819 | sqlite3_context *context, |
| @@ -106962,11 +107835,11 @@ | |
| 107835 | /* This is the first call to this function. Do initialization. */ |
| 107836 | for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1; |
| 107837 | }else{ |
| 107838 | /* Second and subsequent calls get processed here */ |
| 107839 | #ifdef SQLITE_ENABLE_STAT4 |
| 107840 | if( p->mxSample ) samplePushPrevious(p, iChng); |
| 107841 | #endif |
| 107842 | |
| 107843 | /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply |
| 107844 | ** to the current row of the index. */ |
| 107845 | for(i=0; i<iChng; i++){ |
| @@ -106973,30 +107846,29 @@ | |
| 107846 | p->current.anEq[i]++; |
| 107847 | } |
| 107848 | for(i=iChng; i<p->nCol; i++){ |
| 107849 | p->current.anDLt[i]++; |
| 107850 | #ifdef SQLITE_ENABLE_STAT4 |
| 107851 | if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; |
| 107852 | #endif |
| 107853 | p->current.anEq[i] = 1; |
| 107854 | } |
| 107855 | } |
| 107856 | |
| 107857 | p->nRow++; |
| 107858 | #ifdef SQLITE_ENABLE_STAT4 |
| 107859 | if( p->mxSample ){ |
| 107860 | tRowcnt nLt; |
| 107861 | if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ |
| 107862 | sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); |
| 107863 | }else{ |
| 107864 | sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), |
| 107865 | sqlite3_value_blob(argv[2])); |
| 107866 | } |
| 107867 | p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; |
| 107868 | |
| 107869 | nLt = p->current.anLt[p->nCol-1]; |
| 107870 | /* Check if this is to be a periodic sample. If so, add it. */ |
| 107871 | if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){ |
| 107872 | p->current.isPSample = 1; |
| 107873 | p->current.iCol = 0; |
| 107874 | sampleInsert(p, &p->current, p->nCol-1); |
| @@ -107008,13 +107880,18 @@ | |
| 107880 | p->current.iCol = i; |
| 107881 | if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){ |
| 107882 | sampleCopy(p, &p->aBest[i], &p->current); |
| 107883 | } |
| 107884 | } |
| 107885 | }else |
| 107886 | #endif |
| 107887 | if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){ |
| 107888 | p->nSkipAhead++; |
| 107889 | sqlite3_result_int(context, p->current.anDLt[0]>0); |
| 107890 | } |
| 107891 | } |
| 107892 | |
| 107893 | static const FuncDef statPushFuncdef = { |
| 107894 | 2+IsStat4, /* nArg */ |
| 107895 | SQLITE_UTF8, /* funcFlags */ |
| 107896 | 0, /* pUserData */ |
| 107897 | 0, /* pNext */ |
| @@ -107062,10 +107939,11 @@ | |
| 107939 | assert( argc==2 ); |
| 107940 | assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ |
| 107941 | || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT |
| 107942 | || eCall==STAT_GET_NDLT |
| 107943 | ); |
| 107944 | assert( eCall==STAT_GET_STAT1 || p->mxSample ); |
| 107945 | if( eCall==STAT_GET_STAT1 ) |
| 107946 | #else |
| 107947 | assert( argc==1 ); |
| 107948 | #endif |
| 107949 | { |
| @@ -107097,11 +107975,12 @@ | |
| 107975 | if( zRet==0 ){ |
| 107976 | sqlite3_result_error_nomem(context); |
| 107977 | return; |
| 107978 | } |
| 107979 | |
| 107980 | sqlite3_snprintf(24, zRet, "%llu", |
| 107981 | p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); |
| 107982 | z = zRet + sqlite3Strlen30(zRet); |
| 107983 | for(i=0; i<p->nKeyCol; i++){ |
| 107984 | u64 nDistinct = p->current.anDLt[i] + 1; |
| 107985 | u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; |
| 107986 | sqlite3_snprintf(24, z, " %llu", iVal); |
| @@ -107173,20 +108052,20 @@ | |
| 108052 | 0, 0, /* xValue, xInverse */ |
| 108053 | "stat_get", /* zName */ |
| 108054 | {0} |
| 108055 | }; |
| 108056 | |
| 108057 | static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){ |
| 108058 | #ifdef SQLITE_ENABLE_STAT4 |
| 108059 | sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1); |
| 108060 | #elif SQLITE_DEBUG |
| 108061 | assert( iParam==STAT_GET_STAT1 ); |
| 108062 | #else |
| 108063 | UNUSED_PARAMETER( iParam ); |
| 108064 | #endif |
| 108065 | assert( regOut!=regStat && regOut!=regStat+1 ); |
| 108066 | sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4, |
| 108067 | &statGetFuncdef, 0); |
| 108068 | } |
| 108069 | |
| 108070 | /* |
| 108071 | ** Generate code to do an analysis of all indices associated with |
| @@ -107208,16 +108087,15 @@ | |
| 108087 | int i; /* Loop counter */ |
| 108088 | int jZeroRows = -1; /* Jump from here if number of rows is zero */ |
| 108089 | int iDb; /* Index of database containing pTab */ |
| 108090 | u8 needTableCnt = 1; /* True to count the table */ |
| 108091 | int regNewRowid = iMem++; /* Rowid for the inserted record */ |
| 108092 | int regStat = iMem++; /* Register to hold StatAccum object */ |
| 108093 | int regChng = iMem++; /* Index of changed index field */ |
| 108094 | int regRowid = iMem++; /* Rowid argument passed to stat_push() */ |
| 108095 | int regTemp = iMem++; /* Temporary use register */ |
| 108096 | int regTemp2 = iMem++; /* Second temporary use register */ |
| 108097 | int regTabname = iMem++; /* Register containing table name */ |
| 108098 | int regIdxname = iMem++; /* Register containing index name */ |
| 108099 | int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ |
| 108100 | int regPrev = iMem; /* MUST BE LAST (see below) */ |
| 108101 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| @@ -107341,21 +108219,30 @@ | |
| 108219 | /* Invoke the stat_init() function. The arguments are: |
| 108220 | ** |
| 108221 | ** (1) the number of columns in the index including the rowid |
| 108222 | ** (or for a WITHOUT ROWID table, the number of PK columns), |
| 108223 | ** (2) the number of columns in the key without the rowid/pk |
| 108224 | ** (3) estimated number of rows in the index, |
| 108225 | */ |
| 108226 | sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); |
| 108227 | assert( regRowid==regStat+2 ); |
| 108228 | sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); |
| 108229 | #ifdef SQLITE_ENABLE_STAT4 |
| 108230 | if( OptimizationEnabled(db, SQLITE_Stat4) ){ |
| 108231 | sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp); |
| 108232 | addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); |
| 108233 | VdbeCoverage(v); |
| 108234 | }else |
| 108235 | #endif |
| 108236 | { |
| 108237 | addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); |
| 108238 | VdbeCoverage(v); |
| 108239 | sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1); |
| 108240 | } |
| 108241 | assert( regTemp2==regStat+4 ); |
| 108242 | sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); |
| 108243 | sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, |
| 108244 | &statInitFuncdef, 0); |
| 108245 | |
| 108246 | /* Implementation of the following: |
| 108247 | ** |
| 108248 | ** Rewind csr |
| @@ -107362,12 +108249,10 @@ | |
| 108249 | ** if eof(csr) goto end_of_scan; |
| 108250 | ** regChng = 0 |
| 108251 | ** goto next_push_0; |
| 108252 | ** |
| 108253 | */ |
| 108254 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); |
| 108255 | addrNextRow = sqlite3VdbeCurrentAddr(v); |
| 108256 | |
| 108257 | if( nColTest>0 ){ |
| 108258 | int endDistinctTest = sqlite3VdbeMakeLabel(pParse); |
| @@ -107396,10 +108281,11 @@ | |
| 108281 | } |
| 108282 | for(i=0; i<nColTest; i++){ |
| 108283 | char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); |
| 108284 | sqlite3VdbeAddOp2(v, OP_Integer, i, regChng); |
| 108285 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp); |
| 108286 | VdbeComment((v, "%s.column(%d)", pIdx->zName, i)); |
| 108287 | aGotoChng[i] = |
| 108288 | sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ); |
| 108289 | sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); |
| 108290 | VdbeCoverage(v); |
| 108291 | } |
| @@ -107416,10 +108302,11 @@ | |
| 108302 | */ |
| 108303 | sqlite3VdbeJumpHere(v, addrNextRow-1); |
| 108304 | for(i=0; i<nColTest; i++){ |
| 108305 | sqlite3VdbeJumpHere(v, aGotoChng[i]); |
| 108306 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i); |
| 108307 | VdbeComment((v, "%s.column(%d)", pIdx->zName, i)); |
| 108308 | } |
| 108309 | sqlite3VdbeResolveLabel(v, endDistinctTest); |
| 108310 | sqlite3DbFree(db, aGotoChng); |
| 108311 | } |
| 108312 | |
| @@ -107429,34 +108316,50 @@ | |
| 108316 | ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only |
| 108317 | ** Next csr |
| 108318 | ** if !eof(csr) goto next_row; |
| 108319 | */ |
| 108320 | #ifdef SQLITE_ENABLE_STAT4 |
| 108321 | if( OptimizationEnabled(db, SQLITE_Stat4) ){ |
| 108322 | assert( regRowid==(regStat+2) ); |
| 108323 | if( HasRowid(pTab) ){ |
| 108324 | sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); |
| 108325 | }else{ |
| 108326 | Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); |
| 108327 | int j, k, regKey; |
| 108328 | regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); |
| 108329 | for(j=0; j<pPk->nKeyCol; j++){ |
| 108330 | k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]); |
| 108331 | assert( k>=0 && k<pIdx->nColumn ); |
| 108332 | sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); |
| 108333 | VdbeComment((v, "%s.column(%d)", pIdx->zName, i)); |
| 108334 | } |
| 108335 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); |
| 108336 | sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); |
| 108337 | } |
| 108338 | } |
| 108339 | #endif |
| 108340 | assert( regChng==(regStat+1) ); |
| 108341 | { |
| 108342 | sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4, |
| 108343 | &statPushFuncdef, 0); |
| 108344 | if( db->nAnalysisLimit ){ |
| 108345 | int j1, j2, j3; |
| 108346 | j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v); |
| 108347 | j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v); |
| 108348 | j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1); |
| 108349 | VdbeCoverage(v); |
| 108350 | sqlite3VdbeJumpHere(v, j1); |
| 108351 | sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); |
| 108352 | sqlite3VdbeJumpHere(v, j2); |
| 108353 | sqlite3VdbeJumpHere(v, j3); |
| 108354 | }else{ |
| 108355 | sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); |
| 108356 | } |
| 108357 | } |
| 108358 | |
| 108359 | /* Add the entry to the stat1 table. */ |
| 108360 | callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); |
| 108361 | assert( "BBB"[0]==SQLITE_AFF_TEXT ); |
| 108362 | sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); |
| 108363 | sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); |
| 108364 | sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); |
| 108365 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| @@ -107464,11 +108367,11 @@ | |
| 108367 | #endif |
| 108368 | sqlite3VdbeChangeP5(v, OPFLAG_APPEND); |
| 108369 | |
| 108370 | /* Add the entries to the stat4 table. */ |
| 108371 | #ifdef SQLITE_ENABLE_STAT4 |
| 108372 | if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){ |
| 108373 | int regEq = regStat1; |
| 108374 | int regLt = regStat1+1; |
| 108375 | int regDLt = regStat1+2; |
| 108376 | int regSample = regStat1+3; |
| 108377 | int regCol = regStat1+4; |
| @@ -107478,16 +108381,16 @@ | |
| 108381 | u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; |
| 108382 | |
| 108383 | pParse->nMem = MAX(pParse->nMem, regCol+nCol); |
| 108384 | |
| 108385 | addrNext = sqlite3VdbeCurrentAddr(v); |
| 108386 | callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid); |
| 108387 | addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); |
| 108388 | VdbeCoverage(v); |
| 108389 | callStatGet(pParse, regStat, STAT_GET_NEQ, regEq); |
| 108390 | callStatGet(pParse, regStat, STAT_GET_NLT, regLt); |
| 108391 | callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt); |
| 108392 | sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0); |
| 108393 | VdbeCoverage(v); |
| 108394 | for(i=0; i<nCol; i++){ |
| 108395 | sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i); |
| 108396 | } |
| @@ -109619,10 +110522,11 @@ | |
| 110522 | int i; |
| 110523 | Column *pCol; |
| 110524 | assert( pTable!=0 ); |
| 110525 | if( (pCol = pTable->aCol)!=0 ){ |
| 110526 | for(i=0; i<pTable->nCol; i++, pCol++){ |
| 110527 | assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) ); |
| 110528 | sqlite3DbFree(db, pCol->zName); |
| 110529 | sqlite3ExprDelete(db, pCol->pDflt); |
| 110530 | sqlite3DbFree(db, pCol->zColl); |
| 110531 | } |
| 110532 | sqlite3DbFree(db, pTable->aCol); |
| @@ -110267,10 +111171,11 @@ | |
| 111171 | p->aCol = aNew; |
| 111172 | } |
| 111173 | pCol = &p->aCol[p->nCol]; |
| 111174 | memset(pCol, 0, sizeof(p->aCol[0])); |
| 111175 | pCol->zName = z; |
| 111176 | pCol->hName = sqlite3StrIHash(z); |
| 111177 | sqlite3ColumnPropertiesFromName(p, pCol); |
| 111178 | |
| 111179 | if( pType->n==0 ){ |
| 111180 | /* If there is no type specified, columns have the default affinity |
| 111181 | ** 'BLOB' with a default size of 4 bytes. */ |
| @@ -113657,11 +114562,11 @@ | |
| 114562 | pParse->rc = rc; |
| 114563 | return 1; |
| 114564 | } |
| 114565 | db->aDb[1].pBt = pBt; |
| 114566 | assert( db->aDb[1].pSchema ); |
| 114567 | if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){ |
| 114568 | sqlite3OomFault(db); |
| 114569 | return 1; |
| 114570 | } |
| 114571 | } |
| 114572 | return 0; |
| @@ -113768,11 +114673,11 @@ | |
| 114673 | char *p4, /* Error message */ |
| 114674 | i8 p4type, /* P4_STATIC or P4_TRANSIENT */ |
| 114675 | u8 p5Errmsg /* P5_ErrMsg type */ |
| 114676 | ){ |
| 114677 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 114678 | assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested ); |
| 114679 | if( onError==OE_Abort ){ |
| 114680 | sqlite3MayAbort(pParse); |
| 114681 | } |
| 114682 | sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type); |
| 114683 | sqlite3VdbeChangeP5(v, p5Errmsg); |
| @@ -115481,10 +116386,11 @@ | |
| 116386 | VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); |
| 116387 | r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, |
| 116388 | &iPartIdxLabel, pPrior, r1); |
| 116389 | sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, |
| 116390 | pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
| 116391 | sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */ |
| 116392 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 116393 | pPrior = pIdx; |
| 116394 | } |
| 116395 | } |
| 116396 | |
| @@ -122468,10 +123374,11 @@ | |
| 123374 | const char *(*filename_wal)(const char*); |
| 123375 | /* Version 3.32.0 and later */ |
| 123376 | char *(*create_filename)(const char*,const char*,const char*, |
| 123377 | int,const char**); |
| 123378 | void (*free_filename)(char*); |
| 123379 | sqlite3_file *(*database_file_object)(const char*); |
| 123380 | }; |
| 123381 | |
| 123382 | /* |
| 123383 | ** This is the function signature used for all extension entry points. It |
| 123384 | ** is also defined in the file "loadext.c". |
| @@ -122771,10 +123678,11 @@ | |
| 123678 | #define sqlite3_filename_journal sqlite3_api->filename_journal |
| 123679 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 123680 | /* Version 3.32.0 and later */ |
| 123681 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 123682 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 123683 | #define sqlite3_database_file_object sqlite3_api->database_file_object |
| 123684 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 123685 | |
| 123686 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 123687 | /* This case when the file really is being compiled as a loadable |
| 123688 | ** extension */ |
| @@ -123252,11 +124160,20 @@ | |
| 124160 | sqlite3_filename_journal, |
| 124161 | sqlite3_filename_wal, |
| 124162 | /* Version 3.32.0 and later */ |
| 124163 | sqlite3_create_filename, |
| 124164 | sqlite3_free_filename, |
| 124165 | sqlite3_database_file_object, |
| 124166 | }; |
| 124167 | |
| 124168 | /* True if x is the directory separator character |
| 124169 | */ |
| 124170 | #if SQLITE_OS_WIN |
| 124171 | # define DirSep(X) ((X)=='/'||(X)=='\\') |
| 124172 | #else |
| 124173 | # define DirSep(X) ((X)=='/') |
| 124174 | #endif |
| 124175 | |
| 124176 | /* |
| 124177 | ** Attempt to load an SQLite extension library contained in the file |
| 124178 | ** zFile. The entry point is zProc. zProc may be 0 in which case a |
| 124179 | ** default entry point name (sqlite3_extension_init) is used. Use |
| @@ -123355,11 +124272,11 @@ | |
| 124272 | if( zAltEntry==0 ){ |
| 124273 | sqlite3OsDlClose(pVfs, handle); |
| 124274 | return SQLITE_NOMEM_BKPT; |
| 124275 | } |
| 124276 | memcpy(zAltEntry, "sqlite3_", 8); |
| 124277 | for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} |
| 124278 | iFile++; |
| 124279 | if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; |
| 124280 | for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ |
| 124281 | if( sqlite3Isalpha(c) ){ |
| 124282 | zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; |
| @@ -123659,53 +124576,54 @@ | |
| 124576 | ** that script and rerun it. |
| 124577 | */ |
| 124578 | |
| 124579 | /* The various pragma types */ |
| 124580 | #define PragTyp_ACTIVATE_EXTENSIONS 0 |
| 124581 | #define PragTyp_ANALYSIS_LIMIT 1 |
| 124582 | #define PragTyp_HEADER_VALUE 2 |
| 124583 | #define PragTyp_AUTO_VACUUM 3 |
| 124584 | #define PragTyp_FLAG 4 |
| 124585 | #define PragTyp_BUSY_TIMEOUT 5 |
| 124586 | #define PragTyp_CACHE_SIZE 6 |
| 124587 | #define PragTyp_CACHE_SPILL 7 |
| 124588 | #define PragTyp_CASE_SENSITIVE_LIKE 8 |
| 124589 | #define PragTyp_COLLATION_LIST 9 |
| 124590 | #define PragTyp_COMPILE_OPTIONS 10 |
| 124591 | #define PragTyp_DATA_STORE_DIRECTORY 11 |
| 124592 | #define PragTyp_DATABASE_LIST 12 |
| 124593 | #define PragTyp_DEFAULT_CACHE_SIZE 13 |
| 124594 | #define PragTyp_ENCODING 14 |
| 124595 | #define PragTyp_FOREIGN_KEY_CHECK 15 |
| 124596 | #define PragTyp_FOREIGN_KEY_LIST 16 |
| 124597 | #define PragTyp_FUNCTION_LIST 17 |
| 124598 | #define PragTyp_HARD_HEAP_LIMIT 18 |
| 124599 | #define PragTyp_INCREMENTAL_VACUUM 19 |
| 124600 | #define PragTyp_INDEX_INFO 20 |
| 124601 | #define PragTyp_INDEX_LIST 21 |
| 124602 | #define PragTyp_INTEGRITY_CHECK 22 |
| 124603 | #define PragTyp_JOURNAL_MODE 23 |
| 124604 | #define PragTyp_JOURNAL_SIZE_LIMIT 24 |
| 124605 | #define PragTyp_LOCK_PROXY_FILE 25 |
| 124606 | #define PragTyp_LOCKING_MODE 26 |
| 124607 | #define PragTyp_PAGE_COUNT 27 |
| 124608 | #define PragTyp_MMAP_SIZE 28 |
| 124609 | #define PragTyp_MODULE_LIST 29 |
| 124610 | #define PragTyp_OPTIMIZE 30 |
| 124611 | #define PragTyp_PAGE_SIZE 31 |
| 124612 | #define PragTyp_PRAGMA_LIST 32 |
| 124613 | #define PragTyp_SECURE_DELETE 33 |
| 124614 | #define PragTyp_SHRINK_MEMORY 34 |
| 124615 | #define PragTyp_SOFT_HEAP_LIMIT 35 |
| 124616 | #define PragTyp_SYNCHRONOUS 36 |
| 124617 | #define PragTyp_TABLE_INFO 37 |
| 124618 | #define PragTyp_TEMP_STORE 38 |
| 124619 | #define PragTyp_TEMP_STORE_DIRECTORY 39 |
| 124620 | #define PragTyp_THREADS 40 |
| 124621 | #define PragTyp_WAL_AUTOCHECKPOINT 41 |
| 124622 | #define PragTyp_WAL_CHECKPOINT 42 |
| 124623 | #define PragTyp_LOCK_STATUS 43 |
| 124624 | #define PragTyp_STATS 44 |
| 124625 | |
| 124626 | /* Property flags associated with various pragma. */ |
| 124627 | #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ |
| 124628 | #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */ |
| 124629 | #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */ |
| @@ -123792,10 +124710,15 @@ | |
| 124710 | /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS, |
| 124711 | /* ePragFlg: */ 0, |
| 124712 | /* ColNames: */ 0, 0, |
| 124713 | /* iArg: */ 0 }, |
| 124714 | #endif |
| 124715 | {/* zName: */ "analysis_limit", |
| 124716 | /* ePragTyp: */ PragTyp_ANALYSIS_LIMIT, |
| 124717 | /* ePragFlg: */ PragFlg_Result0, |
| 124718 | /* ColNames: */ 0, 0, |
| 124719 | /* iArg: */ 0 }, |
| 124720 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 124721 | {/* zName: */ "application_id", |
| 124722 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 124723 | /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0, |
| 124724 | /* ColNames: */ 0, 0, |
| @@ -124292,11 +125215,11 @@ | |
| 125215 | /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, |
| 125216 | /* ColNames: */ 0, 0, |
| 125217 | /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, |
| 125218 | #endif |
| 125219 | }; |
| 125220 | /* Number of pragmas: 67 on by default, 77 total. */ |
| 125221 | |
| 125222 | /************** End of pragma.h **********************************************/ |
| 125223 | /************** Continuing where we left off in pragma.c *********************/ |
| 125224 | |
| 125225 | /* |
| @@ -124822,11 +125745,11 @@ | |
| 125745 | }else{ |
| 125746 | /* Malloc may fail when setting the page-size, as there is an internal |
| 125747 | ** buffer that the pager module resizes using sqlite3_realloc(). |
| 125748 | */ |
| 125749 | db->nextPagesize = sqlite3Atoi(zRight); |
| 125750 | if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,0,0) ){ |
| 125751 | sqlite3OomFault(db); |
| 125752 | } |
| 125753 | } |
| 125754 | break; |
| 125755 | } |
| @@ -125996,11 +126919,10 @@ | |
| 126919 | sqlite3ResolvePartIdxLabel(pParse, jmp3); |
| 126920 | } |
| 126921 | } |
| 126922 | sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); |
| 126923 | sqlite3VdbeJumpHere(v, loopTop-1); |
| 126924 | if( !isQuick ){ |
| 126925 | sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); |
| 126926 | for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ |
| 126927 | if( pPk==pIdx ) continue; |
| 126928 | sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); |
| @@ -126010,11 +126932,10 @@ | |
| 126932 | sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); |
| 126933 | integrityCheckResultRow(v); |
| 126934 | sqlite3VdbeJumpHere(v, addr); |
| 126935 | } |
| 126936 | } |
| 126937 | } |
| 126938 | } |
| 126939 | { |
| 126940 | static const int iLn = VDBE_OFFSET_LINENO(2); |
| 126941 | static const VdbeOpList endCode[] = { |
| @@ -126444,10 +127365,29 @@ | |
| 127365 | sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff)); |
| 127366 | } |
| 127367 | returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1)); |
| 127368 | break; |
| 127369 | } |
| 127370 | |
| 127371 | /* |
| 127372 | ** PRAGMA analysis_limit |
| 127373 | ** PRAGMA analysis_limit = N |
| 127374 | ** |
| 127375 | ** Configure the maximum number of rows that ANALYZE will examine |
| 127376 | ** in each index that it looks at. Return the new limit. |
| 127377 | */ |
| 127378 | case PragTyp_ANALYSIS_LIMIT: { |
| 127379 | sqlite3_int64 N; |
| 127380 | if( zRight |
| 127381 | && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK |
| 127382 | && N>=0 |
| 127383 | ){ |
| 127384 | db->nAnalysisLimit = (int)(N&0x7fffffff); |
| 127385 | } |
| 127386 | returnSingleInt(v, db->nAnalysisLimit); |
| 127387 | break; |
| 127388 | } |
| 127389 | |
| 127390 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 127391 | /* |
| 127392 | ** Report the current state of file logs for all databases |
| 127393 | */ |
| @@ -129768,10 +130708,11 @@ | |
| 130708 | } |
| 130709 | zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); |
| 130710 | if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt); |
| 130711 | } |
| 130712 | pCol->zName = zName; |
| 130713 | pCol->hName = sqlite3StrIHash(zName); |
| 130714 | sqlite3ColumnPropertiesFromName(0, pCol); |
| 130715 | if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){ |
| 130716 | sqlite3OomFault(db); |
| 130717 | } |
| 130718 | } |
| @@ -131221,11 +132162,14 @@ | |
| 132162 | if( ExprHasProperty(pExpr, EP_FromJoin) |
| 132163 | && pExpr->iRightJoinTable==pSubst->iTable |
| 132164 | ){ |
| 132165 | pExpr->iRightJoinTable = pSubst->iNewTable; |
| 132166 | } |
| 132167 | if( pExpr->op==TK_COLUMN |
| 132168 | && pExpr->iTable==pSubst->iTable |
| 132169 | && !ExprHasProperty(pExpr, EP_FixedCol) |
| 132170 | ){ |
| 132171 | if( pExpr->iColumn<0 ){ |
| 132172 | pExpr->op = TK_NULL; |
| 132173 | }else{ |
| 132174 | Expr *pNew; |
| 132175 | Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; |
| @@ -131239,10 +132183,11 @@ | |
| 132183 | if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ |
| 132184 | memset(&ifNullRow, 0, sizeof(ifNullRow)); |
| 132185 | ifNullRow.op = TK_IF_NULL_ROW; |
| 132186 | ifNullRow.pLeft = pCopy; |
| 132187 | ifNullRow.iTable = pSubst->iNewTable; |
| 132188 | ifNullRow.flags = EP_Skip; |
| 132189 | pCopy = &ifNullRow; |
| 132190 | } |
| 132191 | testcase( ExprHasProperty(pCopy, EP_Subquery) ); |
| 132192 | pNew = sqlite3ExprDup(db, pCopy, 0); |
| 132193 | if( pNew && pSubst->isLeftJoin ){ |
| @@ -133133,10 +134078,11 @@ | |
| 134078 | Vdbe *v = pParse->pVdbe; |
| 134079 | int i; |
| 134080 | struct AggInfo_func *pFunc; |
| 134081 | int nReg = pAggInfo->nFunc + pAggInfo->nColumn; |
| 134082 | if( nReg==0 ) return; |
| 134083 | if( pParse->nErr ) return; |
| 134084 | #ifdef SQLITE_DEBUG |
| 134085 | /* Verify that all AggInfo registers are within the range specified by |
| 134086 | ** AggInfo.mnReg..AggInfo.mxReg */ |
| 134087 | assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); |
| 134088 | for(i=0; i<pAggInfo->nColumn; i++){ |
| @@ -134402,11 +135348,10 @@ | |
| 135348 | VdbeComment((v, "indicate accumulator empty")); |
| 135349 | sqlite3VdbeAddOp1(v, OP_Return, regReset); |
| 135350 | |
| 135351 | } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ |
| 135352 | else { |
| 135353 | Table *pTab; |
| 135354 | if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ |
| 135355 | /* If isSimpleCount() returns a pointer to a Table structure, then |
| 135356 | ** the SQL statement is of the form: |
| 135357 | ** |
| @@ -134438,17 +135383,19 @@ | |
| 135383 | ** |
| 135384 | ** In practice the KeyInfo structure will not be used. It is only |
| 135385 | ** passed to keep OP_OpenRead happy. |
| 135386 | */ |
| 135387 | if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab); |
| 135388 | if( !p->pSrc->a[0].fg.notIndexed ){ |
| 135389 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 135390 | if( pIdx->bUnordered==0 |
| 135391 | && pIdx->szIdxRow<pTab->szTabRow |
| 135392 | && pIdx->pPartIdxWhere==0 |
| 135393 | && (!pBest || pIdx->szIdxRow<pBest->szIdxRow) |
| 135394 | ){ |
| 135395 | pBest = pIdx; |
| 135396 | } |
| 135397 | } |
| 135398 | } |
| 135399 | if( pBest ){ |
| 135400 | iRoot = pBest->tnum; |
| 135401 | pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); |
| @@ -134460,13 +135407,11 @@ | |
| 135407 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); |
| 135408 | } |
| 135409 | sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); |
| 135410 | sqlite3VdbeAddOp1(v, OP_Close, iCsr); |
| 135411 | explainSimpleCount(pParse, pTab, pBest); |
| 135412 | }else{ |
| 135413 | int regAcc = 0; /* "populate accumulators" flag */ |
| 135414 | |
| 135415 | /* If there are accumulator registers but no min() or max() functions |
| 135416 | ** without FILTER clauses, allocate register regAcc. Register regAcc |
| 135417 | ** will contain 0 the first time the inner loop runs, and 1 thereafter. |
| @@ -137542,11 +138487,11 @@ | |
| 138487 | sqlite3SetString(pzErrMsg, db, "output file already exists"); |
| 138488 | goto end_of_vacuum; |
| 138489 | } |
| 138490 | db->mDbFlags |= DBFLAG_VacuumInto; |
| 138491 | } |
| 138492 | nRes = sqlite3BtreeGetRequestedReserve(pMain); |
| 138493 | |
| 138494 | sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); |
| 138495 | sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); |
| 138496 | sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); |
| 138497 | |
| @@ -137686,11 +138631,11 @@ | |
| 138631 | db->mDbFlags = saved_mDbFlags; |
| 138632 | db->flags = saved_flags; |
| 138633 | db->nChange = saved_nChange; |
| 138634 | db->nTotalChange = saved_nTotalChange; |
| 138635 | db->mTrace = saved_mTrace; |
| 138636 | sqlite3BtreeSetPageSize(pMain, -1, 0, 1); |
| 138637 | |
| 138638 | /* Currently there is an SQL level transaction open on the vacuum |
| 138639 | ** database. No locks are held on any other files (since the main file |
| 138640 | ** was committed at the btree level). So it safe to end the transaction |
| 138641 | ** by manually setting the autoCommit flag to true and detaching the |
| @@ -156491,14 +157436,15 @@ | |
| 157436 | ** simplify to constants 0 (false) and 1 (true), respectively, |
| 157437 | ** regardless of the value of expr1. |
| 157438 | */ |
| 157439 | sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202); |
| 157440 | yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0"); |
| 157441 | }else if( yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){ |
| 157442 | Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr; |
| 157443 | yymsp[-1].minor.yy242->a[0].pExpr = 0; |
| 157444 | sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); |
| 157445 | pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); |
| 157446 | yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS); |
| 157447 | if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); |
| 157448 | }else{ |
| 157449 | yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); |
| 157450 | if( yymsp[-4].minor.yy202 ){ |
| @@ -158376,11 +159322,11 @@ | |
| 159322 | VVA_ONLY( u8 startedWithOom = db->mallocFailed ); |
| 159323 | |
| 159324 | assert( zSql!=0 ); |
| 159325 | mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; |
| 159326 | if( db->nVdbeActive==0 ){ |
| 159327 | AtomicStore(&db->u1.isInterrupted, 0); |
| 159328 | } |
| 159329 | pParse->rc = SQLITE_OK; |
| 159330 | pParse->zTail = zSql; |
| 159331 | assert( pzErrMsg!=0 ); |
| 159332 | #ifdef SQLITE_DEBUG |
| @@ -158421,11 +159367,11 @@ | |
| 159367 | ); |
| 159368 | #else |
| 159369 | if( tokenType>=TK_SPACE ){ |
| 159370 | assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); |
| 159371 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 159372 | if( AtomicLoad(&db->u1.isInterrupted) ){ |
| 159373 | pParse->rc = SQLITE_INTERRUPT; |
| 159374 | break; |
| 159375 | } |
| 159376 | if( tokenType==TK_SPACE ){ |
| 159377 | zSql += n; |
| @@ -159088,19 +160034,82 @@ | |
| 160034 | |
| 160035 | |
| 160036 | /************** End of sqliteicu.h *******************************************/ |
| 160037 | /************** Continuing where we left off in main.c ***********************/ |
| 160038 | #endif |
| 160039 | |
| 160040 | /* |
| 160041 | ** This is an extension initializer that is a no-op and always |
| 160042 | ** succeeds, except that it fails if the fault-simulation is set |
| 160043 | ** to 500. |
| 160044 | */ |
| 160045 | static int sqlite3TestExtInit(sqlite3 *db){ |
| 160046 | (void)db; |
| 160047 | return sqlite3FaultSim(500); |
| 160048 | } |
| 160049 | |
| 160050 | |
| 160051 | /* |
| 160052 | ** Forward declarations of external module initializer functions |
| 160053 | ** for modules that need them. |
| 160054 | */ |
| 160055 | #ifdef SQLITE_ENABLE_FTS1 |
| 160056 | SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*); |
| 160057 | #endif |
| 160058 | #ifdef SQLITE_ENABLE_FTS2 |
| 160059 | SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*); |
| 160060 | #endif |
| 160061 | #ifdef SQLITE_ENABLE_FTS5 |
| 160062 | SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); |
| 160063 | #endif |
| 160064 | #ifdef SQLITE_ENABLE_JSON1 |
| 160065 | SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*); |
| 160066 | #endif |
| 160067 | #ifdef SQLITE_ENABLE_STMTVTAB |
| 160068 | SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*); |
| 160069 | #endif |
| 160070 | |
| 160071 | /* |
| 160072 | ** An array of pointers to extension initializer functions for |
| 160073 | ** built-in extensions. |
| 160074 | */ |
| 160075 | static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { |
| 160076 | #ifdef SQLITE_ENABLE_FTS1 |
| 160077 | sqlite3Fts1Init, |
| 160078 | #endif |
| 160079 | #ifdef SQLITE_ENABLE_FTS2 |
| 160080 | sqlite3Fts2Init, |
| 160081 | #endif |
| 160082 | #ifdef SQLITE_ENABLE_FTS3 |
| 160083 | sqlite3Fts3Init, |
| 160084 | #endif |
| 160085 | #ifdef SQLITE_ENABLE_FTS5 |
| 160086 | sqlite3Fts5Init, |
| 160087 | #endif |
| 160088 | #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) |
| 160089 | sqlite3IcuInit, |
| 160090 | #endif |
| 160091 | #ifdef SQLITE_ENABLE_RTREE |
| 160092 | sqlite3RtreeInit, |
| 160093 | #endif |
| 160094 | #ifdef SQLITE_ENABLE_DBPAGE_VTAB |
| 160095 | sqlite3DbpageRegister, |
| 160096 | #endif |
| 160097 | #ifdef SQLITE_ENABLE_DBSTAT_VTAB |
| 160098 | sqlite3DbstatRegister, |
| 160099 | #endif |
| 160100 | sqlite3TestExtInit, |
| 160101 | #ifdef SQLITE_ENABLE_JSON1 |
| 160102 | sqlite3Json1Init, |
| 160103 | #endif |
| 160104 | #ifdef SQLITE_ENABLE_STMTVTAB |
| 160105 | sqlite3StmtVtabInit, |
| 160106 | #endif |
| 160107 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 160108 | sqlite3VdbeBytecodeVtabInit, |
| 160109 | #endif |
| 160110 | }; |
| 160111 | |
| 160112 | #ifndef SQLITE_AMALGAMATION |
| 160113 | /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant |
| 160114 | ** contains the text of SQLITE_VERSION macro. |
| 160115 | */ |
| @@ -160614,12 +161623,11 @@ | |
| 161623 | ** Return non-zero to retry the lock. Return zero to stop trying |
| 161624 | ** and cause SQLite to return SQLITE_BUSY. |
| 161625 | */ |
| 161626 | static int sqliteDefaultBusyCallback( |
| 161627 | void *ptr, /* Database connection */ |
| 161628 | int count /* Number of times table has been busy */ |
| 161629 | ){ |
| 161630 | #if SQLITE_OS_WIN || HAVE_USLEEP |
| 161631 | /* This case is for systems that have support for sleeping for fractions of |
| 161632 | ** a second. Examples: All windows systems, unix systems with usleep() */ |
| 161633 | static const u8 delays[] = |
| @@ -160629,23 +161637,10 @@ | |
| 161637 | # define NDELAY ArraySize(delays) |
| 161638 | sqlite3 *db = (sqlite3 *)ptr; |
| 161639 | int tmout = db->busyTimeout; |
| 161640 | int delay, prior; |
| 161641 | |
| 161642 | assert( count>=0 ); |
| 161643 | if( count < NDELAY ){ |
| 161644 | delay = delays[count]; |
| 161645 | prior = totals[count]; |
| 161646 | }else{ |
| @@ -160661,11 +161656,10 @@ | |
| 161656 | #else |
| 161657 | /* This case for unix systems that lack usleep() support. Sleeping |
| 161658 | ** must be done in increments of whole seconds */ |
| 161659 | sqlite3 *db = (sqlite3 *)ptr; |
| 161660 | int tmout = ((sqlite3 *)ptr)->busyTimeout; |
| 161661 | if( (count+1)*1000 > tmout ){ |
| 161662 | return 0; |
| 161663 | } |
| 161664 | sqlite3OsSleep(db->pVfs, 1000000); |
| 161665 | return 1; |
| @@ -160679,23 +161673,14 @@ | |
| 161673 | ** lock on VFS file pFile. |
| 161674 | ** |
| 161675 | ** If this routine returns non-zero, the lock is retried. If it |
| 161676 | ** returns 0, the operation aborts with an SQLITE_BUSY error. |
| 161677 | */ |
| 161678 | SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){ |
| 161679 | int rc; |
| 161680 | if( p->xBusyHandler==0 || p->nBusy<0 ) return 0; |
| 161681 | rc = p->xBusyHandler(p->pBusyArg, p->nBusy); |
| 161682 | if( rc==0 ){ |
| 161683 | p->nBusy = -1; |
| 161684 | }else{ |
| 161685 | p->nBusy++; |
| 161686 | } |
| @@ -160716,11 +161701,10 @@ | |
| 161701 | #endif |
| 161702 | sqlite3_mutex_enter(db->mutex); |
| 161703 | db->busyHandler.xBusyHandler = xBusy; |
| 161704 | db->busyHandler.pBusyArg = pArg; |
| 161705 | db->busyHandler.nBusy = 0; |
| 161706 | db->busyTimeout = 0; |
| 161707 | sqlite3_mutex_leave(db->mutex); |
| 161708 | return SQLITE_OK; |
| 161709 | } |
| 161710 | |
| @@ -160767,11 +161751,10 @@ | |
| 161751 | #endif |
| 161752 | if( ms>0 ){ |
| 161753 | sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, |
| 161754 | (void*)db); |
| 161755 | db->busyTimeout = ms; |
| 161756 | }else{ |
| 161757 | sqlite3_busy_handler(db, 0, 0); |
| 161758 | } |
| 161759 | return SQLITE_OK; |
| 161760 | } |
| @@ -160784,11 +161767,11 @@ | |
| 161767 | if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ |
| 161768 | (void)SQLITE_MISUSE_BKPT; |
| 161769 | return; |
| 161770 | } |
| 161771 | #endif |
| 161772 | AtomicStore(&db->u1.isInterrupted, 1); |
| 161773 | } |
| 161774 | |
| 161775 | |
| 161776 | /* |
| 161777 | ** This function is exactly the same as sqlite3_create_function(), except |
| @@ -161406,11 +162389,11 @@ | |
| 162389 | rc = sqlite3ApiExit(db, rc); |
| 162390 | |
| 162391 | /* If there are no active statements, clear the interrupt flag at this |
| 162392 | ** point. */ |
| 162393 | if( db->nVdbeActive==0 ){ |
| 162394 | AtomicStore(&db->u1.isInterrupted, 0); |
| 162395 | } |
| 162396 | |
| 162397 | sqlite3_mutex_leave(db->mutex); |
| 162398 | return rc; |
| 162399 | #endif |
| @@ -162093,10 +163076,11 @@ | |
| 163076 | sqlite3 *db; /* Store allocated handle here */ |
| 163077 | int rc; /* Return code */ |
| 163078 | int isThreadsafe; /* True for threadsafe connections */ |
| 163079 | char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ |
| 163080 | char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ |
| 163081 | int i; /* Loop counter */ |
| 163082 | |
| 163083 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 163084 | if( ppDb==0 ) return SQLITE_MISUSE_BKPT; |
| 163085 | #endif |
| 163086 | *ppDb = 0; |
| @@ -162241,10 +163225,13 @@ | |
| 163225 | | SQLITE_EnableQPSG |
| 163226 | #endif |
| 163227 | #if defined(SQLITE_DEFAULT_DEFENSIVE) |
| 163228 | | SQLITE_Defensive |
| 163229 | #endif |
| 163230 | #if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE) |
| 163231 | | SQLITE_LegacyAlter |
| 163232 | #endif |
| 163233 | ; |
| 163234 | sqlite3HashInit(&db->aCollSeq); |
| 163235 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 163236 | sqlite3HashInit(&db->aModule); |
| 163237 | #endif |
| @@ -162333,18 +163320,15 @@ | |
| 163320 | */ |
| 163321 | sqlite3Error(db, SQLITE_OK); |
| 163322 | sqlite3RegisterPerConnectionBuiltinFunctions(db); |
| 163323 | rc = sqlite3_errcode(db); |
| 163324 | |
| 163325 | |
| 163326 | /* Load compiled-in extensions */ |
| 163327 | for(i=0; rc==SQLITE_OK && i<ArraySize(sqlite3BuiltinExtensions); i++){ |
| 163328 | rc = sqlite3BuiltinExtensions[i](db); |
| 163329 | } |
| 163330 | |
| 163331 | /* Load automatic extensions - extensions that have been registered |
| 163332 | ** using the sqlite3_automatic_extension() API. |
| 163333 | */ |
| 163334 | if( rc==SQLITE_OK ){ |
| @@ -162353,66 +163337,10 @@ | |
| 163337 | if( rc!=SQLITE_OK ){ |
| 163338 | goto opendb_out; |
| 163339 | } |
| 163340 | } |
| 163341 | |
| 163342 | #ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS |
| 163343 | /* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time |
| 163344 | ** option gives access to internal functions by default. |
| 163345 | ** Testing use only!!! */ |
| 163346 | db->mDbFlags |= DBFLAG_InternalFunc; |
| @@ -162893,10 +163821,17 @@ | |
| 163821 | }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ |
| 163822 | *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); |
| 163823 | rc = SQLITE_OK; |
| 163824 | }else if( op==SQLITE_FCNTL_DATA_VERSION ){ |
| 163825 | *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager); |
| 163826 | rc = SQLITE_OK; |
| 163827 | }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){ |
| 163828 | int iNew = *(int*)pArg; |
| 163829 | *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree); |
| 163830 | if( iNew>=0 && iNew<=255 ){ |
| 163831 | sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); |
| 163832 | } |
| 163833 | rc = SQLITE_OK; |
| 163834 | }else{ |
| 163835 | rc = sqlite3OsFileControl(fd, op, pArg); |
| 163836 | } |
| 163837 | sqlite3BtreeLeave(pBtree); |
| @@ -163110,24 +164045,10 @@ | |
| 164045 | case SQLITE_TESTCTRL_BYTEORDER: { |
| 164046 | rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN; |
| 164047 | break; |
| 164048 | } |
| 164049 | |
| 164050 | /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N) |
| 164051 | ** |
| 164052 | ** Enable or disable various optimizations for testing purposes. The |
| 164053 | ** argument N is a bitmask of optimizations to be disabled. For normal |
| 164054 | ** operation N should be 0. The idea is that a test program (like the |
| @@ -165185,10 +166106,11 @@ | |
| 166106 | SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); |
| 166107 | SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); |
| 166108 | SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *); |
| 166109 | SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*); |
| 166110 | SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc); |
| 166111 | SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut); |
| 166112 | |
| 166113 | /* fts3_tokenizer.c */ |
| 166114 | SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); |
| 166115 | SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); |
| 166116 | SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, |
| @@ -165916,10 +166838,26 @@ | |
| 166838 | fts3Appendf(pRc, &zRet, ", ?"); |
| 166839 | } |
| 166840 | sqlite3_free(zFree); |
| 166841 | return zRet; |
| 166842 | } |
| 166843 | |
| 166844 | /* |
| 166845 | ** Buffer z contains a positive integer value encoded as utf-8 text. |
| 166846 | ** Decode this value and store it in *pnOut, returning the number of bytes |
| 166847 | ** consumed. If an overflow error occurs return a negative value. |
| 166848 | */ |
| 166849 | SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut){ |
| 166850 | u64 iVal = 0; |
| 166851 | int i; |
| 166852 | for(i=0; z[i]>='0' && z[i]<='9'; i++){ |
| 166853 | iVal = iVal*10 + (z[i] - '0'); |
| 166854 | if( iVal>0x7FFFFFFF ) return -1; |
| 166855 | } |
| 166856 | *pnOut = (int)iVal; |
| 166857 | return i; |
| 166858 | } |
| 166859 | |
| 166860 | /* |
| 166861 | ** This function interprets the string at (*pp) as a non-negative integer |
| 166862 | ** value. It reads the integer and sets *pnOut to the value read, then |
| 166863 | ** sets *pp to point to the byte immediately following the last byte of |
| @@ -165932,23 +166870,21 @@ | |
| 166870 | ** |
| 166871 | ** This function is used when parsing the "prefix=" FTS4 parameter. |
| 166872 | */ |
| 166873 | static int fts3GobbleInt(const char **pp, int *pnOut){ |
| 166874 | const int MAX_NPREFIX = 10000000; |
| 166875 | int nInt = 0; /* Output value */ |
| 166876 | int nByte; |
| 166877 | nByte = sqlite3Fts3ReadInt(*pp, &nInt); |
| 166878 | if( nInt>MAX_NPREFIX ){ |
| 166879 | nInt = 0; |
| 166880 | } |
| 166881 | if( nByte==0 ){ |
| 166882 | return SQLITE_ERROR; |
| 166883 | } |
| 166884 | *pnOut = nInt; |
| 166885 | *pp += nByte; |
| 166886 | return SQLITE_OK; |
| 166887 | } |
| 166888 | |
| 166889 | /* |
| 166890 | ** This function is called to allocate an array of Fts3Index structures |
| @@ -167126,11 +168062,13 @@ | |
| 168062 | static void fts3ReadNextPos( |
| 168063 | char **pp, /* IN/OUT: Pointer into position-list buffer */ |
| 168064 | sqlite3_int64 *pi /* IN/OUT: Value read from position-list */ |
| 168065 | ){ |
| 168066 | if( (**pp)&0xFE ){ |
| 168067 | int iVal; |
| 168068 | *pp += fts3GetVarint32((*pp), &iVal); |
| 168069 | *pi += iVal; |
| 168070 | *pi -= 2; |
| 168071 | }else{ |
| 168072 | *pi = POSITION_LIST_END; |
| 168073 | } |
| 168074 | } |
| @@ -172026,14 +172964,11 @@ | |
| 172964 | |
| 172965 | /* If this is a "NEAR" keyword, check for an explicit nearness. */ |
| 172966 | if( pKey->eType==FTSQUERY_NEAR ){ |
| 172967 | assert( nKey==4 ); |
| 172968 | if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){ |
| 172969 | nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear); |
| 172970 | } |
| 172971 | } |
| 172972 | |
| 172973 | /* At this point this is probably a keyword. But for that to be true, |
| 172974 | ** the next byte must contain either whitespace, an open or close |
| @@ -176558,10 +177493,11 @@ | |
| 177493 | ** b-tree node. And that the final byte of the doclist is 0x00. If either |
| 177494 | ** of these statements is untrue, then the data structure is corrupt. |
| 177495 | */ |
| 177496 | if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode) |
| 177497 | || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) |
| 177498 | || pReader->nDoclist==0 |
| 177499 | ){ |
| 177500 | return FTS_CORRUPT_VTAB; |
| 177501 | } |
| 177502 | return SQLITE_OK; |
| 177503 | } |
| @@ -178211,25 +179147,25 @@ | |
| 179147 | ){ |
| 179148 | const unsigned char *zText = sqlite3_column_text(pStmt, iCol); |
| 179149 | if( zText ){ |
| 179150 | int i; |
| 179151 | int iMul = 1; |
| 179152 | u64 iVal = 0; |
| 179153 | for(i=0; zText[i]>='0' && zText[i]<='9'; i++){ |
| 179154 | iVal = iVal*10 + (zText[i] - '0'); |
| 179155 | } |
| 179156 | *piEndBlock = (i64)iVal; |
| 179157 | while( zText[i]==' ' ) i++; |
| 179158 | iVal = 0; |
| 179159 | if( zText[i]=='-' ){ |
| 179160 | i++; |
| 179161 | iMul = -1; |
| 179162 | } |
| 179163 | for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){ |
| 179164 | iVal = iVal*10 + (zText[i] - '0'); |
| 179165 | } |
| 179166 | *pnByte = ((i64)iVal * (i64)iMul); |
| 179167 | } |
| 179168 | } |
| 179169 | |
| 179170 | |
| 179171 | /* |
| @@ -223728,11 +224664,11 @@ | |
| 224664 | int nArg, /* Number of args */ |
| 224665 | sqlite3_value **apUnused /* Function arguments */ |
| 224666 | ){ |
| 224667 | assert( nArg==0 ); |
| 224668 | UNUSED_PARAM2(nArg, apUnused); |
| 224669 | sqlite3_result_text(pCtx, "fts5: 2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff", -1, SQLITE_TRANSIENT); |
| 224670 | } |
| 224671 | |
| 224672 | /* |
| 224673 | ** Return true if zName is the extension on one of the shadow tables used |
| 224674 | ** by this module. |
| @@ -228379,11 +229315,12 @@ | |
| 229315 | } |
| 229316 | case STMT_COLUMN_BUSY: { |
| 229317 | sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt)); |
| 229318 | break; |
| 229319 | } |
| 229320 | default: { |
| 229321 | assert( i==STMT_COLUMN_MEM ); |
| 229322 | i = SQLITE_STMTSTATUS_MEMUSED + |
| 229323 | STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; |
| 229324 | /* Fall thru */ |
| 229325 | } |
| 229326 | case STMT_COLUMN_NSCAN: |
| @@ -228510,12 +229447,12 @@ | |
| 229447 | } |
| 229448 | #endif /* SQLITE_CORE */ |
| 229449 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 229450 | |
| 229451 | /************** End of stmt.c ************************************************/ |
| 229452 | #if __LINE__!=229452 |
| 229453 | #undef SQLITE_SOURCE_ID |
| 229454 | #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f96alt2" |
| 229455 | #endif |
| 229456 | /* Return the source-id for this library */ |
| 229457 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 229458 | /************************** End of sqlite3.c ******************************/ |
| 229459 |
+107
-31
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -123,11 +123,11 @@ | ||
| 123 | 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | 125 | */ |
| 126 | 126 | #define SQLITE_VERSION "3.32.0" |
| 127 | 127 | #define SQLITE_VERSION_NUMBER 3032000 |
| 128 | -#define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525" | |
| 128 | +#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| @@ -297,30 +297,26 @@ | ||
| 297 | 297 | ** for the [sqlite3] object. |
| 298 | 298 | ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if |
| 299 | 299 | ** the [sqlite3] object is successfully destroyed and all associated |
| 300 | 300 | ** resources are deallocated. |
| 301 | 301 | ** |
| 302 | +** Ideally, applications should [sqlite3_finalize | finalize] all | |
| 303 | +** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and | |
| 304 | +** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated | |
| 305 | +** with the [sqlite3] object prior to attempting to close the object. | |
| 302 | 306 | ** ^If the database connection is associated with unfinalized prepared |
| 303 | -** statements or unfinished sqlite3_backup objects then sqlite3_close() | |
| 304 | -** will leave the database connection open and return [SQLITE_BUSY]. | |
| 305 | -** ^If sqlite3_close_v2() is called with unfinalized prepared statements | |
| 306 | -** and/or unfinished sqlite3_backups, then the database connection becomes | |
| 307 | -** an unusable "zombie" which will automatically be deallocated when the | |
| 308 | -** last prepared statement is finalized or the last sqlite3_backup is | |
| 309 | -** finished. The sqlite3_close_v2() interface is intended for use with | |
| 310 | -** host languages that are garbage collected, and where the order in which | |
| 311 | -** destructors are called is arbitrary. | |
| 312 | -** | |
| 313 | -** Applications should [sqlite3_finalize | finalize] all [prepared statements], | |
| 314 | -** [sqlite3_blob_close | close] all [BLOB handles], and | |
| 315 | -** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated | |
| 316 | -** with the [sqlite3] object prior to attempting to close the object. ^If | |
| 317 | -** sqlite3_close_v2() is called on a [database connection] that still has | |
| 318 | -** outstanding [prepared statements], [BLOB handles], and/or | |
| 319 | -** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation | |
| 320 | -** of resources is deferred until all [prepared statements], [BLOB handles], | |
| 321 | -** and [sqlite3_backup] objects are also destroyed. | |
| 307 | +** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then | |
| 308 | +** sqlite3_close() will leave the database connection open and return | |
| 309 | +** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared | |
| 310 | +** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups, | |
| 311 | +** it returns [SQLITE_OK] regardless, but instead of deallocating the database | |
| 312 | +** connection immediately, it marks the database connection as an unusable | |
| 313 | +** "zombie" and makes arrangements to automatically deallocate the database | |
| 314 | +** connection after all prepared statements are finalized, all BLOB handles | |
| 315 | +** are closed, and all backups have finished. The sqlite3_close_v2() interface | |
| 316 | +** is intended for use with host languages that are garbage collected, and | |
| 317 | +** where the order in which destructors are called is arbitrary. | |
| 322 | 318 | ** |
| 323 | 319 | ** ^If an [sqlite3] object is destroyed while a transaction is open, |
| 324 | 320 | ** the transaction is automatically rolled back. |
| 325 | 321 | ** |
| 326 | 322 | ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] |
| @@ -505,22 +501,25 @@ | ||
| 505 | 501 | #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) |
| 506 | 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 507 | 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 508 | 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 509 | 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | +#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) | |
| 510 | 507 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 511 | 508 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 512 | 509 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 513 | 510 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 511 | +#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) | |
| 514 | 512 | #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) |
| 515 | 513 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| 516 | 514 | #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) |
| 517 | 515 | #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) |
| 518 | 516 | #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ |
| 519 | 517 | #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8)) |
| 520 | 518 | #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) |
| 521 | 519 | #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) |
| 520 | +#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8)) | |
| 522 | 521 | #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) |
| 523 | 522 | #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) |
| 524 | 523 | #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) |
| 525 | 524 | #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) |
| 526 | 525 | #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) |
| @@ -1085,14 +1084,16 @@ | ||
| 1085 | 1084 | ** so that all subsequent write operations are independent. |
| 1086 | 1085 | ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without |
| 1087 | 1086 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. |
| 1088 | 1087 | ** |
| 1089 | 1088 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] |
| 1090 | -** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain | |
| 1091 | -** a file lock using the xLock or xShmLock methods of the VFS to wait | |
| 1092 | -** for up to M milliseconds before failing, where M is the single | |
| 1093 | -** unsigned integer parameter. | |
| 1089 | +** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS | |
| 1090 | +** to block for up to M milliseconds before failing when attempting to | |
| 1091 | +** obtain a file lock using the xLock or xShmLock methods of the VFS. | |
| 1092 | +** The parameter is a pointer to a 32-bit signed integer that contains | |
| 1093 | +** the value that M is to be set to. Before returning, the 32-bit signed | |
| 1094 | +** integer is overwritten with the previous value of M. | |
| 1094 | 1095 | ** |
| 1095 | 1096 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1096 | 1097 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1097 | 1098 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1098 | 1099 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1109,10 +1110,15 @@ | ||
| 1109 | 1110 | ** a single attached database that occur due to other database connections, |
| 1110 | 1111 | ** but omits changes implemented by the database connection on which it is |
| 1111 | 1112 | ** called. This file control is the only mechanism to detect changes that |
| 1112 | 1113 | ** happen either internally or externally and that are associated with |
| 1113 | 1114 | ** a particular attached database. |
| 1115 | +** | |
| 1116 | +** <li>[[SQLITE_FCNTL_CKPT_START]] | |
| 1117 | +** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint | |
| 1118 | +** in wal mode before the client starts to copy pages from the wal | |
| 1119 | +** file to the database file. | |
| 1114 | 1120 | ** |
| 1115 | 1121 | ** <li>[[SQLITE_FCNTL_CKPT_DONE]] |
| 1116 | 1122 | ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint |
| 1117 | 1123 | ** in wal mode after the client has finished copying pages from the wal |
| 1118 | 1124 | ** file to the database file, but before the *-shm file is updated to |
| @@ -1153,10 +1159,12 @@ | ||
| 1153 | 1159 | #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 |
| 1154 | 1160 | #define SQLITE_FCNTL_LOCK_TIMEOUT 34 |
| 1155 | 1161 | #define SQLITE_FCNTL_DATA_VERSION 35 |
| 1156 | 1162 | #define SQLITE_FCNTL_SIZE_LIMIT 36 |
| 1157 | 1163 | #define SQLITE_FCNTL_CKPT_DONE 37 |
| 1164 | +#define SQLITE_FCNTL_RESERVE_BYTES 38 | |
| 1165 | +#define SQLITE_FCNTL_CKPT_START 39 | |
| 1158 | 1166 | |
| 1159 | 1167 | /* deprecated names */ |
| 1160 | 1168 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1161 | 1169 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1162 | 1170 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3531,12 +3539,23 @@ | ||
| 3531 | 3539 | ** |
| 3532 | 3540 | ** These are utility routines, useful to [VFS|custom VFS implementations], |
| 3533 | 3541 | ** that check if a database file was a URI that contained a specific query |
| 3534 | 3542 | ** parameter, and if so obtains the value of that query parameter. |
| 3535 | 3543 | ** |
| 3536 | -** If F is the database filename pointer passed into the xOpen() method of | |
| 3537 | -** a VFS implementation or it is the return value of [sqlite3_db_filename()] | |
| 3544 | +** The first parameter to these interfaces (hereafter referred to | |
| 3545 | +** as F) must be one of: | |
| 3546 | +** <ul> | |
| 3547 | +** <li> A database filename pointer created by the SQLite core and | |
| 3548 | +** passed into the xOpen() method of a VFS implemention, or | |
| 3549 | +** <li> A filename obtained from [sqlite3_db_filename()], or | |
| 3550 | +** <li> A new filename constructed using [sqlite3_create_filename()]. | |
| 3551 | +** </ul> | |
| 3552 | +** If the F parameter is not one of the above, then the behavior is | |
| 3553 | +** undefined and probably undesirable. Older versions of SQLite were | |
| 3554 | +** more tolerant of invalid F parameters than newer versions. | |
| 3555 | +** | |
| 3556 | +** If F is a suitable filename (as described in the previous paragraph) | |
| 3538 | 3557 | ** and if P is the name of the query parameter, then |
| 3539 | 3558 | ** sqlite3_uri_parameter(F,P) returns the value of the P |
| 3540 | 3559 | ** parameter if it exists or a NULL pointer if P does not appear as a |
| 3541 | 3560 | ** query parameter on F. If P is a query parameter of F and it |
| 3542 | 3561 | ** has no explicit value, then sqlite3_uri_parameter(F,P) returns |
| @@ -3615,10 +3634,29 @@ | ||
| 3615 | 3634 | */ |
| 3616 | 3635 | SQLITE_API const char *sqlite3_filename_database(const char*); |
| 3617 | 3636 | SQLITE_API const char *sqlite3_filename_journal(const char*); |
| 3618 | 3637 | SQLITE_API const char *sqlite3_filename_wal(const char*); |
| 3619 | 3638 | |
| 3639 | +/* | |
| 3640 | +** CAPI3REF: Database File Corresponding To A Journal | |
| 3641 | +** | |
| 3642 | +** ^If X is the name of a rollback or WAL-mode journal file that is | |
| 3643 | +** passed into the xOpen method of [sqlite3_vfs], then | |
| 3644 | +** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file] | |
| 3645 | +** object that represents the main database file. | |
| 3646 | +** | |
| 3647 | +** This routine is intended for use in custom [VFS] implementations | |
| 3648 | +** only. It is not a general-purpose interface. | |
| 3649 | +** The argument sqlite3_file_object(X) must be a filename pointer that | |
| 3650 | +** has been passed into [sqlite3_vfs].xOpen method where the | |
| 3651 | +** flags parameter to xOpen contains one of the bits | |
| 3652 | +** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use | |
| 3653 | +** of this routine results in undefined and probably undesirable | |
| 3654 | +** behavior. | |
| 3655 | +*/ | |
| 3656 | +SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); | |
| 3657 | + | |
| 3620 | 3658 | /* |
| 3621 | 3659 | ** CAPI3REF: Create and Destroy VFS Filenames |
| 3622 | 3660 | ** |
| 3623 | 3661 | ** These interfces are provided for use by [VFS shim] implementations and |
| 3624 | 3662 | ** are not useful outside of that context. |
| @@ -3649,11 +3687,11 @@ | ||
| 3649 | 3687 | ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may |
| 3650 | 3688 | ** be NULL pointers, though they can be empty strings. |
| 3651 | 3689 | ** |
| 3652 | 3690 | ** The sqlite3_free_filename(Y) routine releases a memory allocation |
| 3653 | 3691 | ** previously obtained from sqlite3_create_filename(). Invoking |
| 3654 | -** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op. | |
| 3692 | +** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op. | |
| 3655 | 3693 | ** |
| 3656 | 3694 | ** If the Y parameter to sqlite3_free_filename(Y) is anything other |
| 3657 | 3695 | ** than a NULL pointer or a pointer previously acquired from |
| 3658 | 3696 | ** sqlite3_create_filename(), then bad things such as heap |
| 3659 | 3697 | ** corruption or segfaults may occur. The value Y should be |
| @@ -4256,10 +4294,28 @@ | ||
| 4256 | 4294 | ** |
| 4257 | 4295 | ** ^The third argument is the value to bind to the parameter. |
| 4258 | 4296 | ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| 4259 | 4297 | ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter |
| 4260 | 4298 | ** is ignored and the end result is the same as sqlite3_bind_null(). |
| 4299 | +** ^If the third parameter to sqlite3_bind_text() is not NULL, then | |
| 4300 | +** it should be a pointer to well-formed UTF8 text. | |
| 4301 | +** ^If the third parameter to sqlite3_bind_text16() is not NULL, then | |
| 4302 | +** it should be a pointer to well-formed UTF16 text. | |
| 4303 | +** ^If the third parameter to sqlite3_bind_text64() is not NULL, then | |
| 4304 | +** it should be a pointer to a well-formed unicode string that is | |
| 4305 | +** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 | |
| 4306 | +** otherwise. | |
| 4307 | +** | |
| 4308 | +** [[byte-order determination rules]] ^The byte-order of | |
| 4309 | +** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) | |
| 4310 | +** found in first character, which is removed, or in the absence of a BOM | |
| 4311 | +** the byte order is the native byte order of the host | |
| 4312 | +** machine for sqlite3_bind_text16() or the byte order specified in | |
| 4313 | +** the 6th parameter for sqlite3_bind_text64().)^ | |
| 4314 | +** ^If UTF16 input text contains invalid unicode | |
| 4315 | +** characters, then SQLite might change those invalid characters | |
| 4316 | +** into the unicode replacement character: U+FFFD. | |
| 4261 | 4317 | ** |
| 4262 | 4318 | ** ^(In those routines that have a fourth argument, its value is the |
| 4263 | 4319 | ** number of bytes in the parameter. To be clear: the value is the |
| 4264 | 4320 | ** number of <u>bytes</u> in the value, not the number of characters.)^ |
| 4265 | 4321 | ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| @@ -4269,11 +4325,11 @@ | ||
| 4269 | 4325 | ** the behavior is undefined. |
| 4270 | 4326 | ** If a non-negative fourth parameter is provided to sqlite3_bind_text() |
| 4271 | 4327 | ** or sqlite3_bind_text16() or sqlite3_bind_text64() then |
| 4272 | 4328 | ** that parameter must be the byte offset |
| 4273 | 4329 | ** where the NUL terminator would occur assuming the string were NUL |
| 4274 | -** terminated. If any NUL characters occur at byte offsets less than | |
| 4330 | +** terminated. If any NUL characters occurs at byte offsets less than | |
| 4275 | 4331 | ** the value of the fourth parameter then the resulting string value will |
| 4276 | 4332 | ** contain embedded NULs. The result of expressions involving strings |
| 4277 | 4333 | ** with embedded NULs is undefined. |
| 4278 | 4334 | ** |
| 4279 | 4335 | ** ^The fifth argument to the BLOB and string binding interfaces |
| @@ -5594,12 +5650,13 @@ | ||
| 5594 | 5650 | ** cause the implemented SQL function to throw an exception. |
| 5595 | 5651 | ** ^SQLite uses the string pointed to by the |
| 5596 | 5652 | ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() |
| 5597 | 5653 | ** as the text of an error message. ^SQLite interprets the error |
| 5598 | 5654 | ** message string from sqlite3_result_error() as UTF-8. ^SQLite |
| 5599 | -** interprets the string from sqlite3_result_error16() as UTF-16 in native | |
| 5600 | -** byte order. ^If the third parameter to sqlite3_result_error() | |
| 5655 | +** interprets the string from sqlite3_result_error16() as UTF-16 using | |
| 5656 | +** the same [byte-order determination rules] as [sqlite3_bind_text16()]. | |
| 5657 | +** ^If the third parameter to sqlite3_result_error() | |
| 5601 | 5658 | ** or sqlite3_result_error16() is negative then SQLite takes as the error |
| 5602 | 5659 | ** message all text up through the first zero character. |
| 5603 | 5660 | ** ^If the third parameter to sqlite3_result_error() or |
| 5604 | 5661 | ** sqlite3_result_error16() is non-negative then SQLite takes that many |
| 5605 | 5662 | ** bytes (not characters) from the 2nd parameter as the error message. |
| @@ -5662,10 +5719,29 @@ | ||
| 5662 | 5719 | ** when it has finished using that result. |
| 5663 | 5720 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces |
| 5664 | 5721 | ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT |
| 5665 | 5722 | ** then SQLite makes a copy of the result into space obtained |
| 5666 | 5723 | ** from [sqlite3_malloc()] before it returns. |
| 5724 | +** | |
| 5725 | +** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and | |
| 5726 | +** sqlite3_result_text16be() routines, and for sqlite3_result_text64() | |
| 5727 | +** when the encoding is not UTF8, if the input UTF16 begins with a | |
| 5728 | +** byte-order mark (BOM, U+FEFF) then the BOM is removed from the | |
| 5729 | +** string and the rest of the string is interpreted according to the | |
| 5730 | +** byte-order specified by the BOM. ^The byte-order specified by | |
| 5731 | +** the BOM at the beginning of the text overrides the byte-order | |
| 5732 | +** specified by the interface procedure. ^So, for example, if | |
| 5733 | +** sqlite3_result_text16le() is invoked with text that begins | |
| 5734 | +** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the | |
| 5735 | +** first two bytes of input are skipped and the remaining input | |
| 5736 | +** is interpreted as UTF16BE text. | |
| 5737 | +** | |
| 5738 | +** ^For UTF16 input text to the sqlite3_result_text16(), | |
| 5739 | +** sqlite3_result_text16be(), sqlite3_result_text16le(), and | |
| 5740 | +** sqlite3_result_text64() routines, if the text contains invalid | |
| 5741 | +** UTF16 characters, the invalid characters might be converted | |
| 5742 | +** into the unicode replacement character, U+FFFD. | |
| 5667 | 5743 | ** |
| 5668 | 5744 | ** ^The sqlite3_result_value() interface sets the result of |
| 5669 | 5745 | ** the application-defined function to be a copy of the |
| 5670 | 5746 | ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The |
| 5671 | 5747 | ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] |
| @@ -7610,11 +7686,11 @@ | ||
| 7610 | 7686 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7611 | 7687 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7612 | 7688 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7613 | 7689 | #define SQLITE_TESTCTRL_ASSERT 12 |
| 7614 | 7690 | #define SQLITE_TESTCTRL_ALWAYS 13 |
| 7615 | -#define SQLITE_TESTCTRL_RESERVE 14 | |
| 7691 | +#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ | |
| 7616 | 7692 | #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 |
| 7617 | 7693 | #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ |
| 7618 | 7694 | #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ |
| 7619 | 7695 | #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 |
| 7620 | 7696 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 |
| 7621 | 7697 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.32.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3032000 |
| 128 | #define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -297,30 +297,26 @@ | |
| 297 | ** for the [sqlite3] object. |
| 298 | ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if |
| 299 | ** the [sqlite3] object is successfully destroyed and all associated |
| 300 | ** resources are deallocated. |
| 301 | ** |
| 302 | ** ^If the database connection is associated with unfinalized prepared |
| 303 | ** statements or unfinished sqlite3_backup objects then sqlite3_close() |
| 304 | ** will leave the database connection open and return [SQLITE_BUSY]. |
| 305 | ** ^If sqlite3_close_v2() is called with unfinalized prepared statements |
| 306 | ** and/or unfinished sqlite3_backups, then the database connection becomes |
| 307 | ** an unusable "zombie" which will automatically be deallocated when the |
| 308 | ** last prepared statement is finalized or the last sqlite3_backup is |
| 309 | ** finished. The sqlite3_close_v2() interface is intended for use with |
| 310 | ** host languages that are garbage collected, and where the order in which |
| 311 | ** destructors are called is arbitrary. |
| 312 | ** |
| 313 | ** Applications should [sqlite3_finalize | finalize] all [prepared statements], |
| 314 | ** [sqlite3_blob_close | close] all [BLOB handles], and |
| 315 | ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated |
| 316 | ** with the [sqlite3] object prior to attempting to close the object. ^If |
| 317 | ** sqlite3_close_v2() is called on a [database connection] that still has |
| 318 | ** outstanding [prepared statements], [BLOB handles], and/or |
| 319 | ** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation |
| 320 | ** of resources is deferred until all [prepared statements], [BLOB handles], |
| 321 | ** and [sqlite3_backup] objects are also destroyed. |
| 322 | ** |
| 323 | ** ^If an [sqlite3] object is destroyed while a transaction is open, |
| 324 | ** the transaction is automatically rolled back. |
| 325 | ** |
| 326 | ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] |
| @@ -505,22 +501,25 @@ | |
| 505 | #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) |
| 506 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 507 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 508 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 509 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 510 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 511 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 512 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 513 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 514 | #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) |
| 515 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| 516 | #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) |
| 517 | #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) |
| 518 | #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ |
| 519 | #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8)) |
| 520 | #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) |
| 521 | #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) |
| 522 | #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) |
| 523 | #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) |
| 524 | #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) |
| 525 | #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) |
| 526 | #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) |
| @@ -1085,14 +1084,16 @@ | |
| 1085 | ** so that all subsequent write operations are independent. |
| 1086 | ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without |
| 1087 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. |
| 1088 | ** |
| 1089 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] |
| 1090 | ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain |
| 1091 | ** a file lock using the xLock or xShmLock methods of the VFS to wait |
| 1092 | ** for up to M milliseconds before failing, where M is the single |
| 1093 | ** unsigned integer parameter. |
| 1094 | ** |
| 1095 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1096 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1097 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1098 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1109,10 +1110,15 @@ | |
| 1109 | ** a single attached database that occur due to other database connections, |
| 1110 | ** but omits changes implemented by the database connection on which it is |
| 1111 | ** called. This file control is the only mechanism to detect changes that |
| 1112 | ** happen either internally or externally and that are associated with |
| 1113 | ** a particular attached database. |
| 1114 | ** |
| 1115 | ** <li>[[SQLITE_FCNTL_CKPT_DONE]] |
| 1116 | ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint |
| 1117 | ** in wal mode after the client has finished copying pages from the wal |
| 1118 | ** file to the database file, but before the *-shm file is updated to |
| @@ -1153,10 +1159,12 @@ | |
| 1153 | #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 |
| 1154 | #define SQLITE_FCNTL_LOCK_TIMEOUT 34 |
| 1155 | #define SQLITE_FCNTL_DATA_VERSION 35 |
| 1156 | #define SQLITE_FCNTL_SIZE_LIMIT 36 |
| 1157 | #define SQLITE_FCNTL_CKPT_DONE 37 |
| 1158 | |
| 1159 | /* deprecated names */ |
| 1160 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1161 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1162 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3531,12 +3539,23 @@ | |
| 3531 | ** |
| 3532 | ** These are utility routines, useful to [VFS|custom VFS implementations], |
| 3533 | ** that check if a database file was a URI that contained a specific query |
| 3534 | ** parameter, and if so obtains the value of that query parameter. |
| 3535 | ** |
| 3536 | ** If F is the database filename pointer passed into the xOpen() method of |
| 3537 | ** a VFS implementation or it is the return value of [sqlite3_db_filename()] |
| 3538 | ** and if P is the name of the query parameter, then |
| 3539 | ** sqlite3_uri_parameter(F,P) returns the value of the P |
| 3540 | ** parameter if it exists or a NULL pointer if P does not appear as a |
| 3541 | ** query parameter on F. If P is a query parameter of F and it |
| 3542 | ** has no explicit value, then sqlite3_uri_parameter(F,P) returns |
| @@ -3615,10 +3634,29 @@ | |
| 3615 | */ |
| 3616 | SQLITE_API const char *sqlite3_filename_database(const char*); |
| 3617 | SQLITE_API const char *sqlite3_filename_journal(const char*); |
| 3618 | SQLITE_API const char *sqlite3_filename_wal(const char*); |
| 3619 | |
| 3620 | /* |
| 3621 | ** CAPI3REF: Create and Destroy VFS Filenames |
| 3622 | ** |
| 3623 | ** These interfces are provided for use by [VFS shim] implementations and |
| 3624 | ** are not useful outside of that context. |
| @@ -3649,11 +3687,11 @@ | |
| 3649 | ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may |
| 3650 | ** be NULL pointers, though they can be empty strings. |
| 3651 | ** |
| 3652 | ** The sqlite3_free_filename(Y) routine releases a memory allocation |
| 3653 | ** previously obtained from sqlite3_create_filename(). Invoking |
| 3654 | ** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op. |
| 3655 | ** |
| 3656 | ** If the Y parameter to sqlite3_free_filename(Y) is anything other |
| 3657 | ** than a NULL pointer or a pointer previously acquired from |
| 3658 | ** sqlite3_create_filename(), then bad things such as heap |
| 3659 | ** corruption or segfaults may occur. The value Y should be |
| @@ -4256,10 +4294,28 @@ | |
| 4256 | ** |
| 4257 | ** ^The third argument is the value to bind to the parameter. |
| 4258 | ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| 4259 | ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter |
| 4260 | ** is ignored and the end result is the same as sqlite3_bind_null(). |
| 4261 | ** |
| 4262 | ** ^(In those routines that have a fourth argument, its value is the |
| 4263 | ** number of bytes in the parameter. To be clear: the value is the |
| 4264 | ** number of <u>bytes</u> in the value, not the number of characters.)^ |
| 4265 | ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| @@ -4269,11 +4325,11 @@ | |
| 4269 | ** the behavior is undefined. |
| 4270 | ** If a non-negative fourth parameter is provided to sqlite3_bind_text() |
| 4271 | ** or sqlite3_bind_text16() or sqlite3_bind_text64() then |
| 4272 | ** that parameter must be the byte offset |
| 4273 | ** where the NUL terminator would occur assuming the string were NUL |
| 4274 | ** terminated. If any NUL characters occur at byte offsets less than |
| 4275 | ** the value of the fourth parameter then the resulting string value will |
| 4276 | ** contain embedded NULs. The result of expressions involving strings |
| 4277 | ** with embedded NULs is undefined. |
| 4278 | ** |
| 4279 | ** ^The fifth argument to the BLOB and string binding interfaces |
| @@ -5594,12 +5650,13 @@ | |
| 5594 | ** cause the implemented SQL function to throw an exception. |
| 5595 | ** ^SQLite uses the string pointed to by the |
| 5596 | ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() |
| 5597 | ** as the text of an error message. ^SQLite interprets the error |
| 5598 | ** message string from sqlite3_result_error() as UTF-8. ^SQLite |
| 5599 | ** interprets the string from sqlite3_result_error16() as UTF-16 in native |
| 5600 | ** byte order. ^If the third parameter to sqlite3_result_error() |
| 5601 | ** or sqlite3_result_error16() is negative then SQLite takes as the error |
| 5602 | ** message all text up through the first zero character. |
| 5603 | ** ^If the third parameter to sqlite3_result_error() or |
| 5604 | ** sqlite3_result_error16() is non-negative then SQLite takes that many |
| 5605 | ** bytes (not characters) from the 2nd parameter as the error message. |
| @@ -5662,10 +5719,29 @@ | |
| 5662 | ** when it has finished using that result. |
| 5663 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces |
| 5664 | ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT |
| 5665 | ** then SQLite makes a copy of the result into space obtained |
| 5666 | ** from [sqlite3_malloc()] before it returns. |
| 5667 | ** |
| 5668 | ** ^The sqlite3_result_value() interface sets the result of |
| 5669 | ** the application-defined function to be a copy of the |
| 5670 | ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The |
| 5671 | ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] |
| @@ -7610,11 +7686,11 @@ | |
| 7610 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7611 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7612 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7613 | #define SQLITE_TESTCTRL_ASSERT 12 |
| 7614 | #define SQLITE_TESTCTRL_ALWAYS 13 |
| 7615 | #define SQLITE_TESTCTRL_RESERVE 14 |
| 7616 | #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 |
| 7617 | #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ |
| 7618 | #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ |
| 7619 | #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 |
| 7620 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 |
| 7621 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.32.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3032000 |
| 128 | #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -297,30 +297,26 @@ | |
| 297 | ** for the [sqlite3] object. |
| 298 | ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if |
| 299 | ** the [sqlite3] object is successfully destroyed and all associated |
| 300 | ** resources are deallocated. |
| 301 | ** |
| 302 | ** Ideally, applications should [sqlite3_finalize | finalize] all |
| 303 | ** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and |
| 304 | ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated |
| 305 | ** with the [sqlite3] object prior to attempting to close the object. |
| 306 | ** ^If the database connection is associated with unfinalized prepared |
| 307 | ** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then |
| 308 | ** sqlite3_close() will leave the database connection open and return |
| 309 | ** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared |
| 310 | ** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups, |
| 311 | ** it returns [SQLITE_OK] regardless, but instead of deallocating the database |
| 312 | ** connection immediately, it marks the database connection as an unusable |
| 313 | ** "zombie" and makes arrangements to automatically deallocate the database |
| 314 | ** connection after all prepared statements are finalized, all BLOB handles |
| 315 | ** are closed, and all backups have finished. The sqlite3_close_v2() interface |
| 316 | ** is intended for use with host languages that are garbage collected, and |
| 317 | ** where the order in which destructors are called is arbitrary. |
| 318 | ** |
| 319 | ** ^If an [sqlite3] object is destroyed while a transaction is open, |
| 320 | ** the transaction is automatically rolled back. |
| 321 | ** |
| 322 | ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] |
| @@ -505,22 +501,25 @@ | |
| 501 | #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) |
| 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 507 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 508 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 509 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 510 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 511 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| 512 | #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) |
| 513 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| 514 | #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) |
| 515 | #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) |
| 516 | #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ |
| 517 | #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8)) |
| 518 | #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) |
| 519 | #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) |
| 520 | #define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8)) |
| 521 | #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) |
| 522 | #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) |
| 523 | #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) |
| 524 | #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) |
| 525 | #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) |
| @@ -1085,14 +1084,16 @@ | |
| 1084 | ** so that all subsequent write operations are independent. |
| 1085 | ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without |
| 1086 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. |
| 1087 | ** |
| 1088 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] |
| 1089 | ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS |
| 1090 | ** to block for up to M milliseconds before failing when attempting to |
| 1091 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 1092 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 1093 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 1094 | ** integer is overwritten with the previous value of M. |
| 1095 | ** |
| 1096 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1097 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1098 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1099 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1109,10 +1110,15 @@ | |
| 1110 | ** a single attached database that occur due to other database connections, |
| 1111 | ** but omits changes implemented by the database connection on which it is |
| 1112 | ** called. This file control is the only mechanism to detect changes that |
| 1113 | ** happen either internally or externally and that are associated with |
| 1114 | ** a particular attached database. |
| 1115 | ** |
| 1116 | ** <li>[[SQLITE_FCNTL_CKPT_START]] |
| 1117 | ** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint |
| 1118 | ** in wal mode before the client starts to copy pages from the wal |
| 1119 | ** file to the database file. |
| 1120 | ** |
| 1121 | ** <li>[[SQLITE_FCNTL_CKPT_DONE]] |
| 1122 | ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint |
| 1123 | ** in wal mode after the client has finished copying pages from the wal |
| 1124 | ** file to the database file, but before the *-shm file is updated to |
| @@ -1153,10 +1159,12 @@ | |
| 1159 | #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 |
| 1160 | #define SQLITE_FCNTL_LOCK_TIMEOUT 34 |
| 1161 | #define SQLITE_FCNTL_DATA_VERSION 35 |
| 1162 | #define SQLITE_FCNTL_SIZE_LIMIT 36 |
| 1163 | #define SQLITE_FCNTL_CKPT_DONE 37 |
| 1164 | #define SQLITE_FCNTL_RESERVE_BYTES 38 |
| 1165 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1166 | |
| 1167 | /* deprecated names */ |
| 1168 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1169 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1170 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3531,12 +3539,23 @@ | |
| 3539 | ** |
| 3540 | ** These are utility routines, useful to [VFS|custom VFS implementations], |
| 3541 | ** that check if a database file was a URI that contained a specific query |
| 3542 | ** parameter, and if so obtains the value of that query parameter. |
| 3543 | ** |
| 3544 | ** The first parameter to these interfaces (hereafter referred to |
| 3545 | ** as F) must be one of: |
| 3546 | ** <ul> |
| 3547 | ** <li> A database filename pointer created by the SQLite core and |
| 3548 | ** passed into the xOpen() method of a VFS implemention, or |
| 3549 | ** <li> A filename obtained from [sqlite3_db_filename()], or |
| 3550 | ** <li> A new filename constructed using [sqlite3_create_filename()]. |
| 3551 | ** </ul> |
| 3552 | ** If the F parameter is not one of the above, then the behavior is |
| 3553 | ** undefined and probably undesirable. Older versions of SQLite were |
| 3554 | ** more tolerant of invalid F parameters than newer versions. |
| 3555 | ** |
| 3556 | ** If F is a suitable filename (as described in the previous paragraph) |
| 3557 | ** and if P is the name of the query parameter, then |
| 3558 | ** sqlite3_uri_parameter(F,P) returns the value of the P |
| 3559 | ** parameter if it exists or a NULL pointer if P does not appear as a |
| 3560 | ** query parameter on F. If P is a query parameter of F and it |
| 3561 | ** has no explicit value, then sqlite3_uri_parameter(F,P) returns |
| @@ -3615,10 +3634,29 @@ | |
| 3634 | */ |
| 3635 | SQLITE_API const char *sqlite3_filename_database(const char*); |
| 3636 | SQLITE_API const char *sqlite3_filename_journal(const char*); |
| 3637 | SQLITE_API const char *sqlite3_filename_wal(const char*); |
| 3638 | |
| 3639 | /* |
| 3640 | ** CAPI3REF: Database File Corresponding To A Journal |
| 3641 | ** |
| 3642 | ** ^If X is the name of a rollback or WAL-mode journal file that is |
| 3643 | ** passed into the xOpen method of [sqlite3_vfs], then |
| 3644 | ** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file] |
| 3645 | ** object that represents the main database file. |
| 3646 | ** |
| 3647 | ** This routine is intended for use in custom [VFS] implementations |
| 3648 | ** only. It is not a general-purpose interface. |
| 3649 | ** The argument sqlite3_file_object(X) must be a filename pointer that |
| 3650 | ** has been passed into [sqlite3_vfs].xOpen method where the |
| 3651 | ** flags parameter to xOpen contains one of the bits |
| 3652 | ** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use |
| 3653 | ** of this routine results in undefined and probably undesirable |
| 3654 | ** behavior. |
| 3655 | */ |
| 3656 | SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); |
| 3657 | |
| 3658 | /* |
| 3659 | ** CAPI3REF: Create and Destroy VFS Filenames |
| 3660 | ** |
| 3661 | ** These interfces are provided for use by [VFS shim] implementations and |
| 3662 | ** are not useful outside of that context. |
| @@ -3649,11 +3687,11 @@ | |
| 3687 | ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may |
| 3688 | ** be NULL pointers, though they can be empty strings. |
| 3689 | ** |
| 3690 | ** The sqlite3_free_filename(Y) routine releases a memory allocation |
| 3691 | ** previously obtained from sqlite3_create_filename(). Invoking |
| 3692 | ** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op. |
| 3693 | ** |
| 3694 | ** If the Y parameter to sqlite3_free_filename(Y) is anything other |
| 3695 | ** than a NULL pointer or a pointer previously acquired from |
| 3696 | ** sqlite3_create_filename(), then bad things such as heap |
| 3697 | ** corruption or segfaults may occur. The value Y should be |
| @@ -4256,10 +4294,28 @@ | |
| 4294 | ** |
| 4295 | ** ^The third argument is the value to bind to the parameter. |
| 4296 | ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| 4297 | ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter |
| 4298 | ** is ignored and the end result is the same as sqlite3_bind_null(). |
| 4299 | ** ^If the third parameter to sqlite3_bind_text() is not NULL, then |
| 4300 | ** it should be a pointer to well-formed UTF8 text. |
| 4301 | ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then |
| 4302 | ** it should be a pointer to well-formed UTF16 text. |
| 4303 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then |
| 4304 | ** it should be a pointer to a well-formed unicode string that is |
| 4305 | ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 |
| 4306 | ** otherwise. |
| 4307 | ** |
| 4308 | ** [[byte-order determination rules]] ^The byte-order of |
| 4309 | ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) |
| 4310 | ** found in first character, which is removed, or in the absence of a BOM |
| 4311 | ** the byte order is the native byte order of the host |
| 4312 | ** machine for sqlite3_bind_text16() or the byte order specified in |
| 4313 | ** the 6th parameter for sqlite3_bind_text64().)^ |
| 4314 | ** ^If UTF16 input text contains invalid unicode |
| 4315 | ** characters, then SQLite might change those invalid characters |
| 4316 | ** into the unicode replacement character: U+FFFD. |
| 4317 | ** |
| 4318 | ** ^(In those routines that have a fourth argument, its value is the |
| 4319 | ** number of bytes in the parameter. To be clear: the value is the |
| 4320 | ** number of <u>bytes</u> in the value, not the number of characters.)^ |
| 4321 | ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() |
| @@ -4269,11 +4325,11 @@ | |
| 4325 | ** the behavior is undefined. |
| 4326 | ** If a non-negative fourth parameter is provided to sqlite3_bind_text() |
| 4327 | ** or sqlite3_bind_text16() or sqlite3_bind_text64() then |
| 4328 | ** that parameter must be the byte offset |
| 4329 | ** where the NUL terminator would occur assuming the string were NUL |
| 4330 | ** terminated. If any NUL characters occurs at byte offsets less than |
| 4331 | ** the value of the fourth parameter then the resulting string value will |
| 4332 | ** contain embedded NULs. The result of expressions involving strings |
| 4333 | ** with embedded NULs is undefined. |
| 4334 | ** |
| 4335 | ** ^The fifth argument to the BLOB and string binding interfaces |
| @@ -5594,12 +5650,13 @@ | |
| 5650 | ** cause the implemented SQL function to throw an exception. |
| 5651 | ** ^SQLite uses the string pointed to by the |
| 5652 | ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() |
| 5653 | ** as the text of an error message. ^SQLite interprets the error |
| 5654 | ** message string from sqlite3_result_error() as UTF-8. ^SQLite |
| 5655 | ** interprets the string from sqlite3_result_error16() as UTF-16 using |
| 5656 | ** the same [byte-order determination rules] as [sqlite3_bind_text16()]. |
| 5657 | ** ^If the third parameter to sqlite3_result_error() |
| 5658 | ** or sqlite3_result_error16() is negative then SQLite takes as the error |
| 5659 | ** message all text up through the first zero character. |
| 5660 | ** ^If the third parameter to sqlite3_result_error() or |
| 5661 | ** sqlite3_result_error16() is non-negative then SQLite takes that many |
| 5662 | ** bytes (not characters) from the 2nd parameter as the error message. |
| @@ -5662,10 +5719,29 @@ | |
| 5719 | ** when it has finished using that result. |
| 5720 | ** ^If the 4th parameter to the sqlite3_result_text* interfaces |
| 5721 | ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT |
| 5722 | ** then SQLite makes a copy of the result into space obtained |
| 5723 | ** from [sqlite3_malloc()] before it returns. |
| 5724 | ** |
| 5725 | ** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and |
| 5726 | ** sqlite3_result_text16be() routines, and for sqlite3_result_text64() |
| 5727 | ** when the encoding is not UTF8, if the input UTF16 begins with a |
| 5728 | ** byte-order mark (BOM, U+FEFF) then the BOM is removed from the |
| 5729 | ** string and the rest of the string is interpreted according to the |
| 5730 | ** byte-order specified by the BOM. ^The byte-order specified by |
| 5731 | ** the BOM at the beginning of the text overrides the byte-order |
| 5732 | ** specified by the interface procedure. ^So, for example, if |
| 5733 | ** sqlite3_result_text16le() is invoked with text that begins |
| 5734 | ** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the |
| 5735 | ** first two bytes of input are skipped and the remaining input |
| 5736 | ** is interpreted as UTF16BE text. |
| 5737 | ** |
| 5738 | ** ^For UTF16 input text to the sqlite3_result_text16(), |
| 5739 | ** sqlite3_result_text16be(), sqlite3_result_text16le(), and |
| 5740 | ** sqlite3_result_text64() routines, if the text contains invalid |
| 5741 | ** UTF16 characters, the invalid characters might be converted |
| 5742 | ** into the unicode replacement character, U+FFFD. |
| 5743 | ** |
| 5744 | ** ^The sqlite3_result_value() interface sets the result of |
| 5745 | ** the application-defined function to be a copy of the |
| 5746 | ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The |
| 5747 | ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] |
| @@ -7610,11 +7686,11 @@ | |
| 7686 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7687 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7688 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7689 | #define SQLITE_TESTCTRL_ASSERT 12 |
| 7690 | #define SQLITE_TESTCTRL_ALWAYS 13 |
| 7691 | #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ |
| 7692 | #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 |
| 7693 | #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ |
| 7694 | #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ |
| 7695 | #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 |
| 7696 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 |
| 7697 |
+2
-1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -707,11 +707,11 @@ | ||
| 707 | 707 | |
| 708 | 708 | /* |
| 709 | 709 | ** Generate code to load a single javascript file |
| 710 | 710 | */ |
| 711 | 711 | void style_load_one_js_file(const char *zFile){ |
| 712 | - @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script> | |
| 712 | + @ <script src='%R/builtin/%s(zFile)?id=%S(fossil_exe_id())'></script> | |
| 713 | 713 | } |
| 714 | 714 | |
| 715 | 715 | /* |
| 716 | 716 | ** All extra JS files to load. |
| 717 | 717 | */ |
| @@ -1259,10 +1259,11 @@ | ||
| 1259 | 1259 | @ anonymous-adds = %s(find_anon_capabilities(zCap))<br /> |
| 1260 | 1260 | } |
| 1261 | 1261 | @ g.zRepositoryName = %h(g.zRepositoryName)<br /> |
| 1262 | 1262 | @ load_average() = %f(load_average())<br /> |
| 1263 | 1263 | @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br /> |
| 1264 | + @ fossil_exe_id() = %h(fossil_exe_id())<br /> | |
| 1264 | 1265 | @ <hr /> |
| 1265 | 1266 | P("HTTP_USER_AGENT"); |
| 1266 | 1267 | cgi_print_all(showAll, 0); |
| 1267 | 1268 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| 1268 | 1269 | @ <hr /> |
| 1269 | 1270 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -707,11 +707,11 @@ | |
| 707 | |
| 708 | /* |
| 709 | ** Generate code to load a single javascript file |
| 710 | */ |
| 711 | void style_load_one_js_file(const char *zFile){ |
| 712 | @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script> |
| 713 | } |
| 714 | |
| 715 | /* |
| 716 | ** All extra JS files to load. |
| 717 | */ |
| @@ -1259,10 +1259,11 @@ | |
| 1259 | @ anonymous-adds = %s(find_anon_capabilities(zCap))<br /> |
| 1260 | } |
| 1261 | @ g.zRepositoryName = %h(g.zRepositoryName)<br /> |
| 1262 | @ load_average() = %f(load_average())<br /> |
| 1263 | @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br /> |
| 1264 | @ <hr /> |
| 1265 | P("HTTP_USER_AGENT"); |
| 1266 | cgi_print_all(showAll, 0); |
| 1267 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| 1268 | @ <hr /> |
| 1269 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -707,11 +707,11 @@ | |
| 707 | |
| 708 | /* |
| 709 | ** Generate code to load a single javascript file |
| 710 | */ |
| 711 | void style_load_one_js_file(const char *zFile){ |
| 712 | @ <script src='%R/builtin/%s(zFile)?id=%S(fossil_exe_id())'></script> |
| 713 | } |
| 714 | |
| 715 | /* |
| 716 | ** All extra JS files to load. |
| 717 | */ |
| @@ -1259,10 +1259,11 @@ | |
| 1259 | @ anonymous-adds = %s(find_anon_capabilities(zCap))<br /> |
| 1260 | } |
| 1261 | @ g.zRepositoryName = %h(g.zRepositoryName)<br /> |
| 1262 | @ load_average() = %f(load_average())<br /> |
| 1263 | @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br /> |
| 1264 | @ fossil_exe_id() = %h(fossil_exe_id())<br /> |
| 1265 | @ <hr /> |
| 1266 | P("HTTP_USER_AGENT"); |
| 1267 | cgi_print_all(showAll, 0); |
| 1268 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| 1269 | @ <hr /> |
| 1270 |
+1
-13
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -61,22 +61,10 @@ | ||
| 61 | 61 | g.url.flags |= URL_PROMPT_PW; |
| 62 | 62 | url_prompt_for_password(); |
| 63 | 63 | } |
| 64 | 64 | g.zHttpAuth = get_httpauth(); |
| 65 | 65 | url_remember(); |
| 66 | -#if 0 /* Disabled for now */ | |
| 67 | - if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ | |
| 68 | - /* When doing an automatic pull, also automatically pull shuns from | |
| 69 | - ** the server if pull_shuns is enabled. | |
| 70 | - ** | |
| 71 | - ** TODO: What happens if the shun list gets really big? | |
| 72 | - ** Maybe the shunning list should only be pulled on every 10th | |
| 73 | - ** autosync, or something? | |
| 74 | - */ | |
| 75 | - configSync = CONFIGSET_SHUN; | |
| 76 | - } | |
| 77 | -#endif | |
| 78 | 66 | if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE; |
| 79 | 67 | fossil_print("Autosync: %s\n", g.url.canonical); |
| 80 | 68 | url_enable_proxy("via proxy: "); |
| 81 | 69 | rc = client_sync(flags, configSync, 0, 0); |
| 82 | 70 | return rc; |
| @@ -163,11 +151,11 @@ | ||
| 163 | 151 | url_proxy_options(); |
| 164 | 152 | clone_ssh_find_options(); |
| 165 | 153 | if( !uvOnly ) db_find_and_open_repository(0, 0); |
| 166 | 154 | db_open_config(0, 1); |
| 167 | 155 | if( g.argc==2 ){ |
| 168 | - if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN; | |
| 156 | + if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN; | |
| 169 | 157 | }else if( g.argc==3 ){ |
| 170 | 158 | zUrl = g.argv[2]; |
| 171 | 159 | } |
| 172 | 160 | if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) |
| 173 | 161 | && db_get_boolean("uv-sync",0) |
| 174 | 162 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -61,22 +61,10 @@ | |
| 61 | g.url.flags |= URL_PROMPT_PW; |
| 62 | url_prompt_for_password(); |
| 63 | } |
| 64 | g.zHttpAuth = get_httpauth(); |
| 65 | url_remember(); |
| 66 | #if 0 /* Disabled for now */ |
| 67 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| 68 | /* When doing an automatic pull, also automatically pull shuns from |
| 69 | ** the server if pull_shuns is enabled. |
| 70 | ** |
| 71 | ** TODO: What happens if the shun list gets really big? |
| 72 | ** Maybe the shunning list should only be pulled on every 10th |
| 73 | ** autosync, or something? |
| 74 | */ |
| 75 | configSync = CONFIGSET_SHUN; |
| 76 | } |
| 77 | #endif |
| 78 | if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE; |
| 79 | fossil_print("Autosync: %s\n", g.url.canonical); |
| 80 | url_enable_proxy("via proxy: "); |
| 81 | rc = client_sync(flags, configSync, 0, 0); |
| 82 | return rc; |
| @@ -163,11 +151,11 @@ | |
| 163 | url_proxy_options(); |
| 164 | clone_ssh_find_options(); |
| 165 | if( !uvOnly ) db_find_and_open_repository(0, 0); |
| 166 | db_open_config(0, 1); |
| 167 | if( g.argc==2 ){ |
| 168 | if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN; |
| 169 | }else if( g.argc==3 ){ |
| 170 | zUrl = g.argv[2]; |
| 171 | } |
| 172 | if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) |
| 173 | && db_get_boolean("uv-sync",0) |
| 174 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -61,22 +61,10 @@ | |
| 61 | g.url.flags |= URL_PROMPT_PW; |
| 62 | url_prompt_for_password(); |
| 63 | } |
| 64 | g.zHttpAuth = get_httpauth(); |
| 65 | url_remember(); |
| 66 | if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE; |
| 67 | fossil_print("Autosync: %s\n", g.url.canonical); |
| 68 | url_enable_proxy("via proxy: "); |
| 69 | rc = client_sync(flags, configSync, 0, 0); |
| 70 | return rc; |
| @@ -163,11 +151,11 @@ | |
| 151 | url_proxy_options(); |
| 152 | clone_ssh_find_options(); |
| 153 | if( !uvOnly ) db_find_and_open_repository(0, 0); |
| 154 | db_open_config(0, 1); |
| 155 | if( g.argc==2 ){ |
| 156 | if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN; |
| 157 | }else if( g.argc==3 ){ |
| 158 | zUrl = g.argv[2]; |
| 159 | } |
| 160 | if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) |
| 161 | && db_get_boolean("uv-sync",0) |
| 162 |
+1
-1
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -220,11 +220,11 @@ | ||
| 220 | 220 | if( zCol ){ |
| 221 | 221 | db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d", |
| 222 | 222 | zCol, zValue, rid); |
| 223 | 223 | if( tagid==TAG_COMMENT ){ |
| 224 | 224 | char *zCopy = mprintf("%s", zValue); |
| 225 | - wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE); | |
| 225 | + backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1); | |
| 226 | 226 | free(zCopy); |
| 227 | 227 | } |
| 228 | 228 | } |
| 229 | 229 | if( tagid==TAG_DATE ){ |
| 230 | 230 | db_multi_exec("UPDATE event " |
| 231 | 231 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -220,11 +220,11 @@ | |
| 220 | if( zCol ){ |
| 221 | db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d", |
| 222 | zCol, zValue, rid); |
| 223 | if( tagid==TAG_COMMENT ){ |
| 224 | char *zCopy = mprintf("%s", zValue); |
| 225 | wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE); |
| 226 | free(zCopy); |
| 227 | } |
| 228 | } |
| 229 | if( tagid==TAG_DATE ){ |
| 230 | db_multi_exec("UPDATE event " |
| 231 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -220,11 +220,11 @@ | |
| 220 | if( zCol ){ |
| 221 | db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d", |
| 222 | zCol, zValue, rid); |
| 223 | if( tagid==TAG_COMMENT ){ |
| 224 | char *zCopy = mprintf("%s", zValue); |
| 225 | backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1); |
| 226 | free(zCopy); |
| 227 | } |
| 228 | } |
| 229 | if( tagid==TAG_DATE ){ |
| 230 | db_multi_exec("UPDATE event " |
| 231 |
+1
-1
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -443,11 +443,11 @@ | ||
| 443 | 443 | tar_begin(-1); |
| 444 | 444 | for(i=3; i<g.argc; i++){ |
| 445 | 445 | Blob file; |
| 446 | 446 | blob_zero(&file); |
| 447 | 447 | blob_read_from_file(&file, g.argv[i], eFType); |
| 448 | - tar_add_file(g.argv[i], &file, file_perm(0,0), file_mtime(0,0)); | |
| 448 | + tar_add_file(g.argv[i], &file, file_perm(0,eFType), file_mtime(0,eFType)); | |
| 449 | 449 | blob_reset(&file); |
| 450 | 450 | } |
| 451 | 451 | tar_finish(&zip); |
| 452 | 452 | blob_write_to_file(&zip, g.argv[2]); |
| 453 | 453 | } |
| 454 | 454 | |
| 455 | 455 | ADDED src/terminal.c |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | tar_begin(-1); |
| 444 | for(i=3; i<g.argc; i++){ |
| 445 | Blob file; |
| 446 | blob_zero(&file); |
| 447 | blob_read_from_file(&file, g.argv[i], eFType); |
| 448 | tar_add_file(g.argv[i], &file, file_perm(0,0), file_mtime(0,0)); |
| 449 | blob_reset(&file); |
| 450 | } |
| 451 | tar_finish(&zip); |
| 452 | blob_write_to_file(&zip, g.argv[2]); |
| 453 | } |
| 454 | |
| 455 | DDED src/terminal.c |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | tar_begin(-1); |
| 444 | for(i=3; i<g.argc; i++){ |
| 445 | Blob file; |
| 446 | blob_zero(&file); |
| 447 | blob_read_from_file(&file, g.argv[i], eFType); |
| 448 | tar_add_file(g.argv[i], &file, file_perm(0,eFType), file_mtime(0,eFType)); |
| 449 | blob_reset(&file); |
| 450 | } |
| 451 | tar_finish(&zip); |
| 452 | blob_write_to_file(&zip, g.argv[2]); |
| 453 | } |
| 454 | |
| 455 | DDED src/terminal.c |
+9
| --- a/src/terminal.c | ||
| +++ b/src/terminal.c | ||
| @@ -0,0 +1,9 @@ | ||
| 1 | +COMMAND: test-terminal-size | |
| 2 | +** | |
| 3 | +** Show the size of the terminal window from which the command is launched | |
| 4 | +** as two integers, the width in characters and the height in lines. | |
| 5 | +** | |
| 6 | +** If the size cannot be determined, two zeros are shown. | |
| 7 | +*/ | |
| 8 | +void test_terminal_size_cmd(void* Technically, this infosuch is supported; set; | |
| 9 | + fossil_print("%d %d\nchar *zNoC!=-1!=-1 |
| --- a/src/terminal.c | |
| +++ b/src/terminal.c | |
| @@ -0,0 +1,9 @@ | |
| --- a/src/terminal.c | |
| +++ b/src/terminal.c | |
| @@ -0,0 +1,9 @@ | |
| 1 | COMMAND: test-terminal-size |
| 2 | ** |
| 3 | ** Show the size of the terminal window from which the command is launched |
| 4 | ** as two integers, the width in characters and the height in lines. |
| 5 | ** |
| 6 | ** If the size cannot be determined, two zeros are shown. |
| 7 | */ |
| 8 | void test_terminal_size_cmd(void* Technically, this infosuch is supported; set; |
| 9 | fossil_print("%d %d\nchar *zNoC!=-1!=-1 |
+51
-5
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -41,11 +41,11 @@ | ||
| 41 | 41 | ** Add an appropriate tag to the output if "rid" is unpublished (private) |
| 42 | 42 | */ |
| 43 | 43 | #define UNPUB_TAG "<em>(unpublished)</em>" |
| 44 | 44 | void tag_private_status(int rid){ |
| 45 | 45 | if( content_is_private(rid) ){ |
| 46 | - cgi_printf("%s", UNPUB_TAG); | |
| 46 | + cgi_printf(" %s", UNPUB_TAG); | |
| 47 | 47 | } |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | 50 | /* |
| 51 | 51 | ** Generate a hyperlink to a version. |
| @@ -116,10 +116,11 @@ | ||
| 116 | 116 | #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */ |
| 117 | 117 | #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */ |
| 118 | 118 | #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */ |
| 119 | 119 | #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */ |
| 120 | 120 | #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */ |
| 121 | +#define TIMELINE_REFS 0x8000000 /* Output intended for References tab */ | |
| 121 | 122 | #endif |
| 122 | 123 | |
| 123 | 124 | /* |
| 124 | 125 | ** Hash a string and use the hash to determine a background color. |
| 125 | 126 | */ |
| @@ -560,12 +561,27 @@ | ||
| 560 | 561 | } |
| 561 | 562 | if( zType[0]!='c' ){ |
| 562 | 563 | /* Comments for anything other than a check-in are generated by |
| 563 | 564 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 564 | 565 | if( zType[0]=='w' ){ |
| 566 | + const char *zCom = blob_str(&comment); | |
| 567 | + char *zWiki; | |
| 565 | 568 | wiki_hyperlink_override(zUuid); |
| 566 | - wiki_convert(&comment, 0, WIKI_INLINE); | |
| 569 | + if( (tmFlags & TIMELINE_REFS)!=0 | |
| 570 | + && (zWiki = strstr(zCom,"wiki"))!=0 | |
| 571 | + ){ | |
| 572 | + /* The TIMELINE_REFS flag causes timeline comments of the | |
| 573 | + ** form "Changes to wiki..." or "Added wiki" to be changed | |
| 574 | + ** into just "Wiki..." */ | |
| 575 | + Blob rcom; | |
| 576 | + blob_init(&rcom, 0, 0); | |
| 577 | + blob_appendf(&rcom, "W%s", zWiki+1); | |
| 578 | + wiki_convert(&rcom, 0, WIKI_INLINE); | |
| 579 | + blob_reset(&rcom); | |
| 580 | + }else{ | |
| 581 | + wiki_convert(&comment, 0, WIKI_INLINE); | |
| 582 | + } | |
| 567 | 583 | wiki_hyperlink_override(0); |
| 568 | 584 | }else{ |
| 569 | 585 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 570 | 586 | } |
| 571 | 587 | }else{ |
| @@ -641,11 +657,16 @@ | ||
| 641 | 657 | || zType[0]=='n' || zType[0]=='f'){ |
| 642 | 658 | cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); |
| 643 | 659 | } |
| 644 | 660 | |
| 645 | 661 | if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){ |
| 646 | - char *zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate); | |
| 662 | + char *zLink; | |
| 663 | + if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){ | |
| 664 | + zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate); | |
| 665 | + }else{ | |
| 666 | + zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate); | |
| 667 | + } | |
| 647 | 668 | cgi_printf("user: %z%h</a>", href("%z",zLink), zDispUser); |
| 648 | 669 | }else{ |
| 649 | 670 | cgi_printf("user: %h", zDispUser); |
| 650 | 671 | } |
| 651 | 672 | |
| @@ -1291,11 +1312,11 @@ | ||
| 1291 | 1312 | ){ |
| 1292 | 1313 | if( zChng==0 || zChng[0]==0 ) return; |
| 1293 | 1314 | blob_append_sql(pSql," AND event.objid IN (" |
| 1294 | 1315 | "SELECT mlink.mid FROM mlink, filename" |
| 1295 | 1316 | " WHERE mlink.fnid=filename.fnid AND %s)", |
| 1296 | - glob_expr("filename.name", zChng)); | |
| 1317 | + glob_expr("filename.name", mprintf("\"%s\"", zChng))); | |
| 1297 | 1318 | } |
| 1298 | 1319 | static void addFileGlobDescription( |
| 1299 | 1320 | const char *zChng, /* The filename GLOB list */ |
| 1300 | 1321 | Blob *pDescription /* Result description */ |
| 1301 | 1322 | ){ |
| @@ -1721,10 +1742,11 @@ | ||
| 1721 | 1742 | || (bisectLocal && !g.perm.Setup) |
| 1722 | 1743 | ){ |
| 1723 | 1744 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1724 | 1745 | return; |
| 1725 | 1746 | } |
| 1747 | + etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA, 0); | |
| 1726 | 1748 | cookie_read_parameter("y","y"); |
| 1727 | 1749 | zType = P("y"); |
| 1728 | 1750 | if( zType==0 ){ |
| 1729 | 1751 | zType = g.perm.Read ? "ci" : "all"; |
| 1730 | 1752 | cgi_set_parameter("y", zType); |
| @@ -1856,11 +1878,25 @@ | ||
| 1856 | 1878 | " HAVING count(*)>1;\n" |
| 1857 | 1879 | "INSERT OR IGNORE INTO rnfork(rid)" |
| 1858 | 1880 | " SELECT cid FROM plink\n" |
| 1859 | 1881 | " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" |
| 1860 | 1882 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" |
| 1861 | - " AND pid IN rnfork;", | |
| 1883 | + " GROUP BY cid" | |
| 1884 | + " HAVING count(*)>1;\n", | |
| 1885 | + TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH | |
| 1886 | + ); | |
| 1887 | + db_multi_exec( | |
| 1888 | + "INSERT OR IGNORE INTO rnfork(rid)\n" | |
| 1889 | + " SELECT cid FROM plink\n" | |
| 1890 | + " WHERE pid IN rnfork" | |
| 1891 | + " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" | |
| 1892 | + " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" | |
| 1893 | + " UNION " | |
| 1894 | + " SELECT pid FROM plink\n" | |
| 1895 | + " WHERE cid IN rnfork" | |
| 1896 | + " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" | |
| 1897 | + " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n", | |
| 1862 | 1898 | TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH |
| 1863 | 1899 | ); |
| 1864 | 1900 | tmFlags |= TIMELINE_UNHIDE; |
| 1865 | 1901 | zType = "ci"; |
| 1866 | 1902 | disableY = 1; |
| @@ -2165,10 +2201,17 @@ | ||
| 2165 | 2201 | "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" |
| 2166 | 2202 | "INSERT OR IGNORE INTO selected_nodes" |
| 2167 | 2203 | " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" |
| 2168 | 2204 | " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ |
| 2169 | 2205 | ); |
| 2206 | + if( zMark ){ | |
| 2207 | + /* If the t=release option is used with m=UUID, then also | |
| 2208 | + ** include the UUID check-in in the display list */ | |
| 2209 | + int ridMark = name_to_rid(zMark); | |
| 2210 | + db_multi_exec( | |
| 2211 | + "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark); | |
| 2212 | + } | |
| 2170 | 2213 | if( !related ){ |
| 2171 | 2214 | blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); |
| 2172 | 2215 | }else{ |
| 2173 | 2216 | db_multi_exec( |
| 2174 | 2217 | "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" |
| @@ -2391,10 +2434,13 @@ | ||
| 2391 | 2434 | blob_appendf(&desc, " related to tags matching %h", zMatchDesc); |
| 2392 | 2435 | }else{ |
| 2393 | 2436 | blob_appendf(&desc, " with tags matching %h", zMatchDesc); |
| 2394 | 2437 | } |
| 2395 | 2438 | } |
| 2439 | + if( zMark ){ | |
| 2440 | + blob_appendf(&desc," plus check-in \"%h\"", zMark); | |
| 2441 | + } | |
| 2396 | 2442 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2397 | 2443 | } |
| 2398 | 2444 | addFileGlobDescription(zChng, &desc); |
| 2399 | 2445 | if( rAfter>0.0 ){ |
| 2400 | 2446 | if( rBefore>0.0 ){ |
| 2401 | 2447 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -41,11 +41,11 @@ | |
| 41 | ** Add an appropriate tag to the output if "rid" is unpublished (private) |
| 42 | */ |
| 43 | #define UNPUB_TAG "<em>(unpublished)</em>" |
| 44 | void tag_private_status(int rid){ |
| 45 | if( content_is_private(rid) ){ |
| 46 | cgi_printf("%s", UNPUB_TAG); |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | /* |
| 51 | ** Generate a hyperlink to a version. |
| @@ -116,10 +116,11 @@ | |
| 116 | #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */ |
| 117 | #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */ |
| 118 | #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */ |
| 119 | #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */ |
| 120 | #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */ |
| 121 | #endif |
| 122 | |
| 123 | /* |
| 124 | ** Hash a string and use the hash to determine a background color. |
| 125 | */ |
| @@ -560,12 +561,27 @@ | |
| 560 | } |
| 561 | if( zType[0]!='c' ){ |
| 562 | /* Comments for anything other than a check-in are generated by |
| 563 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 564 | if( zType[0]=='w' ){ |
| 565 | wiki_hyperlink_override(zUuid); |
| 566 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 567 | wiki_hyperlink_override(0); |
| 568 | }else{ |
| 569 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 570 | } |
| 571 | }else{ |
| @@ -641,11 +657,16 @@ | |
| 641 | || zType[0]=='n' || zType[0]=='f'){ |
| 642 | cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); |
| 643 | } |
| 644 | |
| 645 | if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){ |
| 646 | char *zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate); |
| 647 | cgi_printf("user: %z%h</a>", href("%z",zLink), zDispUser); |
| 648 | }else{ |
| 649 | cgi_printf("user: %h", zDispUser); |
| 650 | } |
| 651 | |
| @@ -1291,11 +1312,11 @@ | |
| 1291 | ){ |
| 1292 | if( zChng==0 || zChng[0]==0 ) return; |
| 1293 | blob_append_sql(pSql," AND event.objid IN (" |
| 1294 | "SELECT mlink.mid FROM mlink, filename" |
| 1295 | " WHERE mlink.fnid=filename.fnid AND %s)", |
| 1296 | glob_expr("filename.name", zChng)); |
| 1297 | } |
| 1298 | static void addFileGlobDescription( |
| 1299 | const char *zChng, /* The filename GLOB list */ |
| 1300 | Blob *pDescription /* Result description */ |
| 1301 | ){ |
| @@ -1721,10 +1742,11 @@ | |
| 1721 | || (bisectLocal && !g.perm.Setup) |
| 1722 | ){ |
| 1723 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1724 | return; |
| 1725 | } |
| 1726 | cookie_read_parameter("y","y"); |
| 1727 | zType = P("y"); |
| 1728 | if( zType==0 ){ |
| 1729 | zType = g.perm.Read ? "ci" : "all"; |
| 1730 | cgi_set_parameter("y", zType); |
| @@ -1856,11 +1878,25 @@ | |
| 1856 | " HAVING count(*)>1;\n" |
| 1857 | "INSERT OR IGNORE INTO rnfork(rid)" |
| 1858 | " SELECT cid FROM plink\n" |
| 1859 | " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" |
| 1860 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" |
| 1861 | " AND pid IN rnfork;", |
| 1862 | TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH |
| 1863 | ); |
| 1864 | tmFlags |= TIMELINE_UNHIDE; |
| 1865 | zType = "ci"; |
| 1866 | disableY = 1; |
| @@ -2165,10 +2201,17 @@ | |
| 2165 | "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" |
| 2166 | "INSERT OR IGNORE INTO selected_nodes" |
| 2167 | " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" |
| 2168 | " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ |
| 2169 | ); |
| 2170 | if( !related ){ |
| 2171 | blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); |
| 2172 | }else{ |
| 2173 | db_multi_exec( |
| 2174 | "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" |
| @@ -2391,10 +2434,13 @@ | |
| 2391 | blob_appendf(&desc, " related to tags matching %h", zMatchDesc); |
| 2392 | }else{ |
| 2393 | blob_appendf(&desc, " with tags matching %h", zMatchDesc); |
| 2394 | } |
| 2395 | } |
| 2396 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2397 | } |
| 2398 | addFileGlobDescription(zChng, &desc); |
| 2399 | if( rAfter>0.0 ){ |
| 2400 | if( rBefore>0.0 ){ |
| 2401 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -41,11 +41,11 @@ | |
| 41 | ** Add an appropriate tag to the output if "rid" is unpublished (private) |
| 42 | */ |
| 43 | #define UNPUB_TAG "<em>(unpublished)</em>" |
| 44 | void tag_private_status(int rid){ |
| 45 | if( content_is_private(rid) ){ |
| 46 | cgi_printf(" %s", UNPUB_TAG); |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | /* |
| 51 | ** Generate a hyperlink to a version. |
| @@ -116,10 +116,11 @@ | |
| 116 | #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */ |
| 117 | #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */ |
| 118 | #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */ |
| 119 | #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */ |
| 120 | #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */ |
| 121 | #define TIMELINE_REFS 0x8000000 /* Output intended for References tab */ |
| 122 | #endif |
| 123 | |
| 124 | /* |
| 125 | ** Hash a string and use the hash to determine a background color. |
| 126 | */ |
| @@ -560,12 +561,27 @@ | |
| 561 | } |
| 562 | if( zType[0]!='c' ){ |
| 563 | /* Comments for anything other than a check-in are generated by |
| 564 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 565 | if( zType[0]=='w' ){ |
| 566 | const char *zCom = blob_str(&comment); |
| 567 | char *zWiki; |
| 568 | wiki_hyperlink_override(zUuid); |
| 569 | if( (tmFlags & TIMELINE_REFS)!=0 |
| 570 | && (zWiki = strstr(zCom,"wiki"))!=0 |
| 571 | ){ |
| 572 | /* The TIMELINE_REFS flag causes timeline comments of the |
| 573 | ** form "Changes to wiki..." or "Added wiki" to be changed |
| 574 | ** into just "Wiki..." */ |
| 575 | Blob rcom; |
| 576 | blob_init(&rcom, 0, 0); |
| 577 | blob_appendf(&rcom, "W%s", zWiki+1); |
| 578 | wiki_convert(&rcom, 0, WIKI_INLINE); |
| 579 | blob_reset(&rcom); |
| 580 | }else{ |
| 581 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 582 | } |
| 583 | wiki_hyperlink_override(0); |
| 584 | }else{ |
| 585 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 586 | } |
| 587 | }else{ |
| @@ -641,11 +657,16 @@ | |
| 657 | || zType[0]=='n' || zType[0]=='f'){ |
| 658 | cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); |
| 659 | } |
| 660 | |
| 661 | if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){ |
| 662 | char *zLink; |
| 663 | if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){ |
| 664 | zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate); |
| 665 | }else{ |
| 666 | zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate); |
| 667 | } |
| 668 | cgi_printf("user: %z%h</a>", href("%z",zLink), zDispUser); |
| 669 | }else{ |
| 670 | cgi_printf("user: %h", zDispUser); |
| 671 | } |
| 672 | |
| @@ -1291,11 +1312,11 @@ | |
| 1312 | ){ |
| 1313 | if( zChng==0 || zChng[0]==0 ) return; |
| 1314 | blob_append_sql(pSql," AND event.objid IN (" |
| 1315 | "SELECT mlink.mid FROM mlink, filename" |
| 1316 | " WHERE mlink.fnid=filename.fnid AND %s)", |
| 1317 | glob_expr("filename.name", mprintf("\"%s\"", zChng))); |
| 1318 | } |
| 1319 | static void addFileGlobDescription( |
| 1320 | const char *zChng, /* The filename GLOB list */ |
| 1321 | Blob *pDescription /* Result description */ |
| 1322 | ){ |
| @@ -1721,10 +1742,11 @@ | |
| 1742 | || (bisectLocal && !g.perm.Setup) |
| 1743 | ){ |
| 1744 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1745 | return; |
| 1746 | } |
| 1747 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA, 0); |
| 1748 | cookie_read_parameter("y","y"); |
| 1749 | zType = P("y"); |
| 1750 | if( zType==0 ){ |
| 1751 | zType = g.perm.Read ? "ci" : "all"; |
| 1752 | cgi_set_parameter("y", zType); |
| @@ -1856,11 +1878,25 @@ | |
| 1878 | " HAVING count(*)>1;\n" |
| 1879 | "INSERT OR IGNORE INTO rnfork(rid)" |
| 1880 | " SELECT cid FROM plink\n" |
| 1881 | " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" |
| 1882 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" |
| 1883 | " GROUP BY cid" |
| 1884 | " HAVING count(*)>1;\n", |
| 1885 | TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH |
| 1886 | ); |
| 1887 | db_multi_exec( |
| 1888 | "INSERT OR IGNORE INTO rnfork(rid)\n" |
| 1889 | " SELECT cid FROM plink\n" |
| 1890 | " WHERE pid IN rnfork" |
| 1891 | " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" |
| 1892 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" |
| 1893 | " UNION " |
| 1894 | " SELECT pid FROM plink\n" |
| 1895 | " WHERE cid IN rnfork" |
| 1896 | " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" |
| 1897 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n", |
| 1898 | TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH |
| 1899 | ); |
| 1900 | tmFlags |= TIMELINE_UNHIDE; |
| 1901 | zType = "ci"; |
| 1902 | disableY = 1; |
| @@ -2165,10 +2201,17 @@ | |
| 2201 | "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" |
| 2202 | "INSERT OR IGNORE INTO selected_nodes" |
| 2203 | " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" |
| 2204 | " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ |
| 2205 | ); |
| 2206 | if( zMark ){ |
| 2207 | /* If the t=release option is used with m=UUID, then also |
| 2208 | ** include the UUID check-in in the display list */ |
| 2209 | int ridMark = name_to_rid(zMark); |
| 2210 | db_multi_exec( |
| 2211 | "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark); |
| 2212 | } |
| 2213 | if( !related ){ |
| 2214 | blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); |
| 2215 | }else{ |
| 2216 | db_multi_exec( |
| 2217 | "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" |
| @@ -2391,10 +2434,13 @@ | |
| 2434 | blob_appendf(&desc, " related to tags matching %h", zMatchDesc); |
| 2435 | }else{ |
| 2436 | blob_appendf(&desc, " with tags matching %h", zMatchDesc); |
| 2437 | } |
| 2438 | } |
| 2439 | if( zMark ){ |
| 2440 | blob_appendf(&desc," plus check-in \"%h\"", zMark); |
| 2441 | } |
| 2442 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2443 | } |
| 2444 | addFileGlobDescription(zChng, &desc); |
| 2445 | if( rAfter>0.0 ){ |
| 2446 | if( rBefore>0.0 ){ |
| 2447 |
+32
-6
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -194,10 +194,11 @@ | ||
| 194 | 194 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 195 | 195 | Blob sql1, sql2, sql3; |
| 196 | 196 | Stmt q; |
| 197 | 197 | int i, j; |
| 198 | 198 | char *aUsed; |
| 199 | + const char *zMimetype = 0; | |
| 199 | 200 | |
| 200 | 201 | if( tktid==0 ){ |
| 201 | 202 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 202 | 203 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 203 | 204 | tktid = db_last_insert_rowid(); |
| @@ -233,12 +234,22 @@ | ||
| 233 | 234 | zUsedByName++; |
| 234 | 235 | } |
| 235 | 236 | blob_append_sql(&sql2, ",\"%w\"", zUsedByName); |
| 236 | 237 | blob_append_sql(&sql3, ",%Q", p->aField[i].zValue); |
| 237 | 238 | } |
| 238 | - if( rid>0 ){ | |
| 239 | - wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0); | |
| 239 | + if( strcmp(zBaseName,"mimetype")==0 ){ | |
| 240 | + zMimetype = p->aField[i].zValue; | |
| 241 | + } | |
| 242 | + } | |
| 243 | + if( rid>0 ){ | |
| 244 | + for(i=0; i<p->nField; i++){ | |
| 245 | + const char *zName = p->aField[i].zName; | |
| 246 | + const char *zBaseName = zName[0]=='+' ? zName+1 : zName; | |
| 247 | + j = fieldId(zBaseName); | |
| 248 | + if( j<0 ) continue; | |
| 249 | + backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET, | |
| 250 | + p->rDate, i==0); | |
| 240 | 251 | } |
| 241 | 252 | } |
| 242 | 253 | blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid); |
| 243 | 254 | db_prepare(&q, "%s", blob_sql_text(&sql1)); |
| 244 | 255 | db_bind_double(&q, ":mtime", p->rDate); |
| @@ -845,10 +856,14 @@ | ||
| 845 | 856 | } |
| 846 | 857 | |
| 847 | 858 | /* |
| 848 | 859 | ** Draw a timeline for a ticket with tag.tagid given by the tagid |
| 849 | 860 | ** parameter. |
| 861 | +** | |
| 862 | +** If zType[0]=='c' then only show check-ins associated with the | |
| 863 | +** ticket. For any other value of zType, show all events associated | |
| 864 | +** with the ticket. | |
| 850 | 865 | */ |
| 851 | 866 | void tkt_draw_timeline(int tagid, const char *zType){ |
| 852 | 867 | Stmt q; |
| 853 | 868 | char *zFullUuid; |
| 854 | 869 | char *zSQL; |
| @@ -855,20 +870,26 @@ | ||
| 855 | 870 | zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", |
| 856 | 871 | tagid); |
| 857 | 872 | if( zType[0]=='c' ){ |
| 858 | 873 | zSQL = mprintf( |
| 859 | 874 | "%s AND event.objid IN " |
| 860 | - " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " | |
| 875 | + " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " | |
| 876 | + "AND srctype=0 " | |
| 861 | 877 | "AND '%s' GLOB (target||'*')) " |
| 862 | 878 | "ORDER BY mtime DESC", |
| 863 | 879 | timeline_query_for_www(), zFullUuid, zFullUuid |
| 864 | 880 | ); |
| 865 | 881 | }else{ |
| 866 | 882 | zSQL = mprintf( |
| 867 | 883 | "%s AND event.objid IN " |
| 868 | 884 | " (SELECT rid FROM tagxref WHERE tagid=%d" |
| 869 | - " UNION SELECT srcid FROM backlink" | |
| 885 | + " UNION" | |
| 886 | + " SELECT CASE srctype WHEN 2 THEN" | |
| 887 | + " (SELECT rid FROM tagxref WHERE tagid=backlink.srcid" | |
| 888 | + " ORDER BY mtime DESC LIMIT 1)" | |
| 889 | + " ELSE srcid END" | |
| 890 | + " FROM backlink" | |
| 870 | 891 | " WHERE target GLOB '%.4s*'" |
| 871 | 892 | " AND '%s' GLOB (target||'*')" |
| 872 | 893 | " UNION SELECT attachid FROM attachment" |
| 873 | 894 | " WHERE target=%Q) " |
| 874 | 895 | "ORDER BY mtime DESC", |
| @@ -875,21 +896,26 @@ | ||
| 875 | 896 | timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid |
| 876 | 897 | ); |
| 877 | 898 | } |
| 878 | 899 | db_prepare(&q, "%z", zSQL/*safe-for-%s*/); |
| 879 | 900 | www_print_timeline(&q, |
| 880 | - TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT, | |
| 901 | + TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT | | |
| 902 | + TIMELINE_REFS, | |
| 881 | 903 | 0, 0, 0, 0, 0, 0); |
| 882 | 904 | db_finalize(&q); |
| 883 | 905 | fossil_free(zFullUuid); |
| 884 | 906 | } |
| 885 | 907 | |
| 886 | 908 | /* |
| 887 | 909 | ** WEBPAGE: tkttimeline |
| 888 | -** URL: /tkttimeline?name=TICKETUUID&y=TYPE | |
| 910 | +** URL: /tkttimeline/TICKETUUID | |
| 889 | 911 | ** |
| 890 | 912 | ** Show the change history for a single ticket in timeline format. |
| 913 | +** | |
| 914 | +** Query parameters: | |
| 915 | +** | |
| 916 | +** y=ci Show only check-ins associated with the ticket | |
| 891 | 917 | */ |
| 892 | 918 | void tkttimeline_page(void){ |
| 893 | 919 | char *zTitle; |
| 894 | 920 | const char *zUuid; |
| 895 | 921 | int tagid; |
| 896 | 922 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -194,10 +194,11 @@ | |
| 194 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 195 | Blob sql1, sql2, sql3; |
| 196 | Stmt q; |
| 197 | int i, j; |
| 198 | char *aUsed; |
| 199 | |
| 200 | if( tktid==0 ){ |
| 201 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 202 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 203 | tktid = db_last_insert_rowid(); |
| @@ -233,12 +234,22 @@ | |
| 233 | zUsedByName++; |
| 234 | } |
| 235 | blob_append_sql(&sql2, ",\"%w\"", zUsedByName); |
| 236 | blob_append_sql(&sql3, ",%Q", p->aField[i].zValue); |
| 237 | } |
| 238 | if( rid>0 ){ |
| 239 | wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0); |
| 240 | } |
| 241 | } |
| 242 | blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid); |
| 243 | db_prepare(&q, "%s", blob_sql_text(&sql1)); |
| 244 | db_bind_double(&q, ":mtime", p->rDate); |
| @@ -845,10 +856,14 @@ | |
| 845 | } |
| 846 | |
| 847 | /* |
| 848 | ** Draw a timeline for a ticket with tag.tagid given by the tagid |
| 849 | ** parameter. |
| 850 | */ |
| 851 | void tkt_draw_timeline(int tagid, const char *zType){ |
| 852 | Stmt q; |
| 853 | char *zFullUuid; |
| 854 | char *zSQL; |
| @@ -855,20 +870,26 @@ | |
| 855 | zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", |
| 856 | tagid); |
| 857 | if( zType[0]=='c' ){ |
| 858 | zSQL = mprintf( |
| 859 | "%s AND event.objid IN " |
| 860 | " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " |
| 861 | "AND '%s' GLOB (target||'*')) " |
| 862 | "ORDER BY mtime DESC", |
| 863 | timeline_query_for_www(), zFullUuid, zFullUuid |
| 864 | ); |
| 865 | }else{ |
| 866 | zSQL = mprintf( |
| 867 | "%s AND event.objid IN " |
| 868 | " (SELECT rid FROM tagxref WHERE tagid=%d" |
| 869 | " UNION SELECT srcid FROM backlink" |
| 870 | " WHERE target GLOB '%.4s*'" |
| 871 | " AND '%s' GLOB (target||'*')" |
| 872 | " UNION SELECT attachid FROM attachment" |
| 873 | " WHERE target=%Q) " |
| 874 | "ORDER BY mtime DESC", |
| @@ -875,21 +896,26 @@ | |
| 875 | timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid |
| 876 | ); |
| 877 | } |
| 878 | db_prepare(&q, "%z", zSQL/*safe-for-%s*/); |
| 879 | www_print_timeline(&q, |
| 880 | TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT, |
| 881 | 0, 0, 0, 0, 0, 0); |
| 882 | db_finalize(&q); |
| 883 | fossil_free(zFullUuid); |
| 884 | } |
| 885 | |
| 886 | /* |
| 887 | ** WEBPAGE: tkttimeline |
| 888 | ** URL: /tkttimeline?name=TICKETUUID&y=TYPE |
| 889 | ** |
| 890 | ** Show the change history for a single ticket in timeline format. |
| 891 | */ |
| 892 | void tkttimeline_page(void){ |
| 893 | char *zTitle; |
| 894 | const char *zUuid; |
| 895 | int tagid; |
| 896 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -194,10 +194,11 @@ | |
| 194 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 195 | Blob sql1, sql2, sql3; |
| 196 | Stmt q; |
| 197 | int i, j; |
| 198 | char *aUsed; |
| 199 | const char *zMimetype = 0; |
| 200 | |
| 201 | if( tktid==0 ){ |
| 202 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 203 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 204 | tktid = db_last_insert_rowid(); |
| @@ -233,12 +234,22 @@ | |
| 234 | zUsedByName++; |
| 235 | } |
| 236 | blob_append_sql(&sql2, ",\"%w\"", zUsedByName); |
| 237 | blob_append_sql(&sql3, ",%Q", p->aField[i].zValue); |
| 238 | } |
| 239 | if( strcmp(zBaseName,"mimetype")==0 ){ |
| 240 | zMimetype = p->aField[i].zValue; |
| 241 | } |
| 242 | } |
| 243 | if( rid>0 ){ |
| 244 | for(i=0; i<p->nField; i++){ |
| 245 | const char *zName = p->aField[i].zName; |
| 246 | const char *zBaseName = zName[0]=='+' ? zName+1 : zName; |
| 247 | j = fieldId(zBaseName); |
| 248 | if( j<0 ) continue; |
| 249 | backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET, |
| 250 | p->rDate, i==0); |
| 251 | } |
| 252 | } |
| 253 | blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid); |
| 254 | db_prepare(&q, "%s", blob_sql_text(&sql1)); |
| 255 | db_bind_double(&q, ":mtime", p->rDate); |
| @@ -845,10 +856,14 @@ | |
| 856 | } |
| 857 | |
| 858 | /* |
| 859 | ** Draw a timeline for a ticket with tag.tagid given by the tagid |
| 860 | ** parameter. |
| 861 | ** |
| 862 | ** If zType[0]=='c' then only show check-ins associated with the |
| 863 | ** ticket. For any other value of zType, show all events associated |
| 864 | ** with the ticket. |
| 865 | */ |
| 866 | void tkt_draw_timeline(int tagid, const char *zType){ |
| 867 | Stmt q; |
| 868 | char *zFullUuid; |
| 869 | char *zSQL; |
| @@ -855,20 +870,26 @@ | |
| 870 | zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", |
| 871 | tagid); |
| 872 | if( zType[0]=='c' ){ |
| 873 | zSQL = mprintf( |
| 874 | "%s AND event.objid IN " |
| 875 | " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " |
| 876 | "AND srctype=0 " |
| 877 | "AND '%s' GLOB (target||'*')) " |
| 878 | "ORDER BY mtime DESC", |
| 879 | timeline_query_for_www(), zFullUuid, zFullUuid |
| 880 | ); |
| 881 | }else{ |
| 882 | zSQL = mprintf( |
| 883 | "%s AND event.objid IN " |
| 884 | " (SELECT rid FROM tagxref WHERE tagid=%d" |
| 885 | " UNION" |
| 886 | " SELECT CASE srctype WHEN 2 THEN" |
| 887 | " (SELECT rid FROM tagxref WHERE tagid=backlink.srcid" |
| 888 | " ORDER BY mtime DESC LIMIT 1)" |
| 889 | " ELSE srcid END" |
| 890 | " FROM backlink" |
| 891 | " WHERE target GLOB '%.4s*'" |
| 892 | " AND '%s' GLOB (target||'*')" |
| 893 | " UNION SELECT attachid FROM attachment" |
| 894 | " WHERE target=%Q) " |
| 895 | "ORDER BY mtime DESC", |
| @@ -875,21 +896,26 @@ | |
| 896 | timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid |
| 897 | ); |
| 898 | } |
| 899 | db_prepare(&q, "%z", zSQL/*safe-for-%s*/); |
| 900 | www_print_timeline(&q, |
| 901 | TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT | |
| 902 | TIMELINE_REFS, |
| 903 | 0, 0, 0, 0, 0, 0); |
| 904 | db_finalize(&q); |
| 905 | fossil_free(zFullUuid); |
| 906 | } |
| 907 | |
| 908 | /* |
| 909 | ** WEBPAGE: tkttimeline |
| 910 | ** URL: /tkttimeline/TICKETUUID |
| 911 | ** |
| 912 | ** Show the change history for a single ticket in timeline format. |
| 913 | ** |
| 914 | ** Query parameters: |
| 915 | ** |
| 916 | ** y=ci Show only check-ins associated with the ticket |
| 917 | */ |
| 918 | void tkttimeline_page(void){ |
| 919 | char *zTitle; |
| 920 | const char *zUuid; |
| 921 | int tagid; |
| 922 |
+51
-10
| --- src/unversioned.c | ||
| +++ src/unversioned.c | ||
| @@ -237,11 +237,14 @@ | ||
| 237 | 237 | ** edit FILE Bring up FILE in a text editor for modification. |
| 238 | 238 | ** |
| 239 | 239 | ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk |
| 240 | 240 | ** |
| 241 | 241 | ** list | ls Show all unversioned files held in the local |
| 242 | -** repository. | |
| 242 | +** repository. Options: | |
| 243 | +** | |
| 244 | +** --glob PATTERN Show only files that match | |
| 245 | +** --like PATTERN Show only files that match | |
| 243 | 246 | ** |
| 244 | 247 | ** revert ?URL? Restore the state of all unversioned files in the |
| 245 | 248 | ** local repository to match the remote repository |
| 246 | 249 | ** URL. |
| 247 | 250 | ** |
| @@ -250,11 +253,14 @@ | ||
| 250 | 253 | ** -n|--dryrun Show what would have happened |
| 251 | 254 | ** |
| 252 | 255 | ** remove|rm|delete FILE ... |
| 253 | 256 | ** Remove unversioned files from the local repository. |
| 254 | 257 | ** Changes are not pushed to other repositories until |
| 255 | -** the next sync. | |
| 258 | +** the next sync. Options: | |
| 259 | +** | |
| 260 | +** --glob PATTERN Remove files that match | |
| 261 | +** --like PATTERN Remove files that match | |
| 256 | 262 | ** |
| 257 | 263 | ** sync ?URL? Synchronize the state of all unversioned files with |
| 258 | 264 | ** the remote repository URL. The most recent version |
| 259 | 265 | ** of each file is propagated to all repositories and |
| 260 | 266 | ** all prior versions are permanently forgotten. |
| @@ -339,11 +345,13 @@ | ||
| 339 | 345 | |
| 340 | 346 | verify_all_options(); |
| 341 | 347 | if( g.argc!=4) usage("edit UVFILE"); |
| 342 | 348 | zUVFile = g.argv[3]; |
| 343 | 349 | zEditor = fossil_text_editor(); |
| 344 | - if( zEditor==0 ) fossil_fatal("no text editor - set the VISUAL env variable"); | |
| 350 | + if( zEditor==0 ){ | |
| 351 | + fossil_fatal("no text editor - set the VISUAL env variable"); | |
| 352 | + } | |
| 345 | 353 | zTFile = fossil_temp_filename(); |
| 346 | 354 | if( zTFile==0 ) fossil_fatal("cannot find a temporary filename"); |
| 347 | 355 | db_begin_transaction(); |
| 348 | 356 | content_rcvid_init("#!fossil unversioned edit"); |
| 349 | 357 | if( unversioned_content(zUVFile, &content) ){ |
| @@ -386,26 +394,40 @@ | ||
| 386 | 394 | fossil_print("%s\n", unversioned_content_hash(debugFlag)); |
| 387 | 395 | }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ |
| 388 | 396 | Stmt q; |
| 389 | 397 | int allFlag = find_option("all","a",0)!=0; |
| 390 | 398 | int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i'); |
| 399 | + char *zPattern = sqlite3_mprintf("true"); | |
| 400 | + const char *zGlob; | |
| 401 | + zGlob = find_option("glob",0,1); | |
| 402 | + if( zGlob ){ | |
| 403 | + sqlite3_free(zPattern); | |
| 404 | + zPattern = sqlite3_mprintf("(name GLOB %Q)", zGlob); | |
| 405 | + } | |
| 406 | + zGlob = find_option("like",0,1); | |
| 407 | + if( zGlob ){ | |
| 408 | + sqlite3_free(zPattern); | |
| 409 | + zPattern = sqlite3_mprintf("(name LIKE %Q)", zGlob); | |
| 410 | + } | |
| 391 | 411 | verify_all_options(); |
| 392 | 412 | if( !longFlag ){ |
| 393 | 413 | if( allFlag ){ |
| 394 | - db_prepare(&q, "SELECT name FROM unversioned ORDER BY name"); | |
| 414 | + db_prepare(&q, "SELECT name FROM unversioned WHERE %s ORDER BY name", | |
| 415 | + zPattern/*safe-for-%s*/); | |
| 395 | 416 | }else{ |
| 396 | - db_prepare(&q, "SELECT name FROM unversioned WHERE hash IS NOT NULL" | |
| 397 | - " ORDER BY name"); | |
| 417 | + db_prepare(&q, "SELECT name FROM unversioned" | |
| 418 | + " WHERE %s AND hash IS NOT NULL" | |
| 419 | + " ORDER BY name", zPattern/*safe-for-%s*/); | |
| 398 | 420 | } |
| 399 | 421 | while( db_step(&q)==SQLITE_ROW ){ |
| 400 | 422 | fossil_print("%s\n", db_column_text(&q,0)); |
| 401 | 423 | } |
| 402 | 424 | }else{ |
| 403 | 425 | db_prepare(&q, |
| 404 | 426 | "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name" |
| 405 | - " FROM unversioned" | |
| 406 | - " ORDER BY name;" | |
| 427 | + " FROM unversioned WHERE %s" | |
| 428 | + " ORDER BY name;", zPattern/*safe-for-%s*/ | |
| 407 | 429 | ); |
| 408 | 430 | while( db_step(&q)==SQLITE_ROW ){ |
| 409 | 431 | const char *zHash = db_column_text(&q, 0); |
| 410 | 432 | const char *zNoContent = ""; |
| 411 | 433 | if( zHash==0 ){ |
| @@ -423,20 +445,37 @@ | ||
| 423 | 445 | zNoContent |
| 424 | 446 | ); |
| 425 | 447 | } |
| 426 | 448 | } |
| 427 | 449 | db_finalize(&q); |
| 450 | + sqlite3_free(zPattern); | |
| 428 | 451 | }else if( memcmp(zCmd, "revert", nCmd)==0 ){ |
| 429 | - unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT); | |
| 452 | + unsigned syncFlags = | |
| 453 | + unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT); | |
| 430 | 454 | g.argv[1] = "sync"; |
| 431 | 455 | g.argv[2] = "--uv-noop"; |
| 432 | 456 | sync_unversioned(syncFlags); |
| 433 | 457 | }else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 |
| 434 | 458 | || memcmp(zCmd, "delete", nCmd)==0 ){ |
| 435 | 459 | int i; |
| 436 | - verify_all_options(); | |
| 460 | + const char *zGlob; | |
| 437 | 461 | db_begin_transaction(); |
| 462 | + while( (zGlob = find_option("glob",0,1))!=0 ){ | |
| 463 | + db_multi_exec( | |
| 464 | + "UPDATE unversioned" | |
| 465 | + " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q", | |
| 466 | + mtime, zGlob | |
| 467 | + ); | |
| 468 | + } | |
| 469 | + while( (zGlob = find_option("like",0,1))!=0 ){ | |
| 470 | + db_multi_exec( | |
| 471 | + "UPDATE unversioned" | |
| 472 | + " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name LIKE %Q", | |
| 473 | + mtime, zGlob | |
| 474 | + ); | |
| 475 | + } | |
| 476 | + verify_all_options(); | |
| 438 | 477 | for(i=3; i<g.argc; i++){ |
| 439 | 478 | db_multi_exec( |
| 440 | 479 | "UPDATE unversioned" |
| 441 | 480 | " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q", |
| 442 | 481 | mtime, g.argv[i] |
| @@ -485,10 +524,11 @@ | ||
| 485 | 524 | int showDel = 0; |
| 486 | 525 | char zSzName[100]; |
| 487 | 526 | |
| 488 | 527 | login_check_credentials(); |
| 489 | 528 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 529 | + etag_check(ETAG_DATA,0); | |
| 490 | 530 | style_header("Unversioned Files"); |
| 491 | 531 | if( !db_table_exists("repository","unversioned") ){ |
| 492 | 532 | @ No unversioned files on this server |
| 493 | 533 | style_footer(); |
| 494 | 534 | return; |
| @@ -597,10 +637,11 @@ | ||
| 597 | 637 | Blob json; |
| 598 | 638 | |
| 599 | 639 | login_check_credentials(); |
| 600 | 640 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 601 | 641 | cgi_set_content_type("text/json"); |
| 642 | + etag_check(ETAG_DATA,0); | |
| 602 | 643 | if( !db_table_exists("repository","unversioned") ){ |
| 603 | 644 | blob_init(&json, "[]", -1); |
| 604 | 645 | cgi_set_content(&json); |
| 605 | 646 | return; |
| 606 | 647 | } |
| 607 | 648 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -237,11 +237,14 @@ | |
| 237 | ** edit FILE Bring up FILE in a text editor for modification. |
| 238 | ** |
| 239 | ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk |
| 240 | ** |
| 241 | ** list | ls Show all unversioned files held in the local |
| 242 | ** repository. |
| 243 | ** |
| 244 | ** revert ?URL? Restore the state of all unversioned files in the |
| 245 | ** local repository to match the remote repository |
| 246 | ** URL. |
| 247 | ** |
| @@ -250,11 +253,14 @@ | |
| 250 | ** -n|--dryrun Show what would have happened |
| 251 | ** |
| 252 | ** remove|rm|delete FILE ... |
| 253 | ** Remove unversioned files from the local repository. |
| 254 | ** Changes are not pushed to other repositories until |
| 255 | ** the next sync. |
| 256 | ** |
| 257 | ** sync ?URL? Synchronize the state of all unversioned files with |
| 258 | ** the remote repository URL. The most recent version |
| 259 | ** of each file is propagated to all repositories and |
| 260 | ** all prior versions are permanently forgotten. |
| @@ -339,11 +345,13 @@ | |
| 339 | |
| 340 | verify_all_options(); |
| 341 | if( g.argc!=4) usage("edit UVFILE"); |
| 342 | zUVFile = g.argv[3]; |
| 343 | zEditor = fossil_text_editor(); |
| 344 | if( zEditor==0 ) fossil_fatal("no text editor - set the VISUAL env variable"); |
| 345 | zTFile = fossil_temp_filename(); |
| 346 | if( zTFile==0 ) fossil_fatal("cannot find a temporary filename"); |
| 347 | db_begin_transaction(); |
| 348 | content_rcvid_init("#!fossil unversioned edit"); |
| 349 | if( unversioned_content(zUVFile, &content) ){ |
| @@ -386,26 +394,40 @@ | |
| 386 | fossil_print("%s\n", unversioned_content_hash(debugFlag)); |
| 387 | }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ |
| 388 | Stmt q; |
| 389 | int allFlag = find_option("all","a",0)!=0; |
| 390 | int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i'); |
| 391 | verify_all_options(); |
| 392 | if( !longFlag ){ |
| 393 | if( allFlag ){ |
| 394 | db_prepare(&q, "SELECT name FROM unversioned ORDER BY name"); |
| 395 | }else{ |
| 396 | db_prepare(&q, "SELECT name FROM unversioned WHERE hash IS NOT NULL" |
| 397 | " ORDER BY name"); |
| 398 | } |
| 399 | while( db_step(&q)==SQLITE_ROW ){ |
| 400 | fossil_print("%s\n", db_column_text(&q,0)); |
| 401 | } |
| 402 | }else{ |
| 403 | db_prepare(&q, |
| 404 | "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name" |
| 405 | " FROM unversioned" |
| 406 | " ORDER BY name;" |
| 407 | ); |
| 408 | while( db_step(&q)==SQLITE_ROW ){ |
| 409 | const char *zHash = db_column_text(&q, 0); |
| 410 | const char *zNoContent = ""; |
| 411 | if( zHash==0 ){ |
| @@ -423,20 +445,37 @@ | |
| 423 | zNoContent |
| 424 | ); |
| 425 | } |
| 426 | } |
| 427 | db_finalize(&q); |
| 428 | }else if( memcmp(zCmd, "revert", nCmd)==0 ){ |
| 429 | unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT); |
| 430 | g.argv[1] = "sync"; |
| 431 | g.argv[2] = "--uv-noop"; |
| 432 | sync_unversioned(syncFlags); |
| 433 | }else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 |
| 434 | || memcmp(zCmd, "delete", nCmd)==0 ){ |
| 435 | int i; |
| 436 | verify_all_options(); |
| 437 | db_begin_transaction(); |
| 438 | for(i=3; i<g.argc; i++){ |
| 439 | db_multi_exec( |
| 440 | "UPDATE unversioned" |
| 441 | " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q", |
| 442 | mtime, g.argv[i] |
| @@ -485,10 +524,11 @@ | |
| 485 | int showDel = 0; |
| 486 | char zSzName[100]; |
| 487 | |
| 488 | login_check_credentials(); |
| 489 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 490 | style_header("Unversioned Files"); |
| 491 | if( !db_table_exists("repository","unversioned") ){ |
| 492 | @ No unversioned files on this server |
| 493 | style_footer(); |
| 494 | return; |
| @@ -597,10 +637,11 @@ | |
| 597 | Blob json; |
| 598 | |
| 599 | login_check_credentials(); |
| 600 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 601 | cgi_set_content_type("text/json"); |
| 602 | if( !db_table_exists("repository","unversioned") ){ |
| 603 | blob_init(&json, "[]", -1); |
| 604 | cgi_set_content(&json); |
| 605 | return; |
| 606 | } |
| 607 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -237,11 +237,14 @@ | |
| 237 | ** edit FILE Bring up FILE in a text editor for modification. |
| 238 | ** |
| 239 | ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk |
| 240 | ** |
| 241 | ** list | ls Show all unversioned files held in the local |
| 242 | ** repository. Options: |
| 243 | ** |
| 244 | ** --glob PATTERN Show only files that match |
| 245 | ** --like PATTERN Show only files that match |
| 246 | ** |
| 247 | ** revert ?URL? Restore the state of all unversioned files in the |
| 248 | ** local repository to match the remote repository |
| 249 | ** URL. |
| 250 | ** |
| @@ -250,11 +253,14 @@ | |
| 253 | ** -n|--dryrun Show what would have happened |
| 254 | ** |
| 255 | ** remove|rm|delete FILE ... |
| 256 | ** Remove unversioned files from the local repository. |
| 257 | ** Changes are not pushed to other repositories until |
| 258 | ** the next sync. Options: |
| 259 | ** |
| 260 | ** --glob PATTERN Remove files that match |
| 261 | ** --like PATTERN Remove files that match |
| 262 | ** |
| 263 | ** sync ?URL? Synchronize the state of all unversioned files with |
| 264 | ** the remote repository URL. The most recent version |
| 265 | ** of each file is propagated to all repositories and |
| 266 | ** all prior versions are permanently forgotten. |
| @@ -339,11 +345,13 @@ | |
| 345 | |
| 346 | verify_all_options(); |
| 347 | if( g.argc!=4) usage("edit UVFILE"); |
| 348 | zUVFile = g.argv[3]; |
| 349 | zEditor = fossil_text_editor(); |
| 350 | if( zEditor==0 ){ |
| 351 | fossil_fatal("no text editor - set the VISUAL env variable"); |
| 352 | } |
| 353 | zTFile = fossil_temp_filename(); |
| 354 | if( zTFile==0 ) fossil_fatal("cannot find a temporary filename"); |
| 355 | db_begin_transaction(); |
| 356 | content_rcvid_init("#!fossil unversioned edit"); |
| 357 | if( unversioned_content(zUVFile, &content) ){ |
| @@ -386,26 +394,40 @@ | |
| 394 | fossil_print("%s\n", unversioned_content_hash(debugFlag)); |
| 395 | }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ |
| 396 | Stmt q; |
| 397 | int allFlag = find_option("all","a",0)!=0; |
| 398 | int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i'); |
| 399 | char *zPattern = sqlite3_mprintf("true"); |
| 400 | const char *zGlob; |
| 401 | zGlob = find_option("glob",0,1); |
| 402 | if( zGlob ){ |
| 403 | sqlite3_free(zPattern); |
| 404 | zPattern = sqlite3_mprintf("(name GLOB %Q)", zGlob); |
| 405 | } |
| 406 | zGlob = find_option("like",0,1); |
| 407 | if( zGlob ){ |
| 408 | sqlite3_free(zPattern); |
| 409 | zPattern = sqlite3_mprintf("(name LIKE %Q)", zGlob); |
| 410 | } |
| 411 | verify_all_options(); |
| 412 | if( !longFlag ){ |
| 413 | if( allFlag ){ |
| 414 | db_prepare(&q, "SELECT name FROM unversioned WHERE %s ORDER BY name", |
| 415 | zPattern/*safe-for-%s*/); |
| 416 | }else{ |
| 417 | db_prepare(&q, "SELECT name FROM unversioned" |
| 418 | " WHERE %s AND hash IS NOT NULL" |
| 419 | " ORDER BY name", zPattern/*safe-for-%s*/); |
| 420 | } |
| 421 | while( db_step(&q)==SQLITE_ROW ){ |
| 422 | fossil_print("%s\n", db_column_text(&q,0)); |
| 423 | } |
| 424 | }else{ |
| 425 | db_prepare(&q, |
| 426 | "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name" |
| 427 | " FROM unversioned WHERE %s" |
| 428 | " ORDER BY name;", zPattern/*safe-for-%s*/ |
| 429 | ); |
| 430 | while( db_step(&q)==SQLITE_ROW ){ |
| 431 | const char *zHash = db_column_text(&q, 0); |
| 432 | const char *zNoContent = ""; |
| 433 | if( zHash==0 ){ |
| @@ -423,20 +445,37 @@ | |
| 445 | zNoContent |
| 446 | ); |
| 447 | } |
| 448 | } |
| 449 | db_finalize(&q); |
| 450 | sqlite3_free(zPattern); |
| 451 | }else if( memcmp(zCmd, "revert", nCmd)==0 ){ |
| 452 | unsigned syncFlags = |
| 453 | unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT); |
| 454 | g.argv[1] = "sync"; |
| 455 | g.argv[2] = "--uv-noop"; |
| 456 | sync_unversioned(syncFlags); |
| 457 | }else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 |
| 458 | || memcmp(zCmd, "delete", nCmd)==0 ){ |
| 459 | int i; |
| 460 | const char *zGlob; |
| 461 | db_begin_transaction(); |
| 462 | while( (zGlob = find_option("glob",0,1))!=0 ){ |
| 463 | db_multi_exec( |
| 464 | "UPDATE unversioned" |
| 465 | " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q", |
| 466 | mtime, zGlob |
| 467 | ); |
| 468 | } |
| 469 | while( (zGlob = find_option("like",0,1))!=0 ){ |
| 470 | db_multi_exec( |
| 471 | "UPDATE unversioned" |
| 472 | " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name LIKE %Q", |
| 473 | mtime, zGlob |
| 474 | ); |
| 475 | } |
| 476 | verify_all_options(); |
| 477 | for(i=3; i<g.argc; i++){ |
| 478 | db_multi_exec( |
| 479 | "UPDATE unversioned" |
| 480 | " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q", |
| 481 | mtime, g.argv[i] |
| @@ -485,10 +524,11 @@ | |
| 524 | int showDel = 0; |
| 525 | char zSzName[100]; |
| 526 | |
| 527 | login_check_credentials(); |
| 528 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 529 | etag_check(ETAG_DATA,0); |
| 530 | style_header("Unversioned Files"); |
| 531 | if( !db_table_exists("repository","unversioned") ){ |
| 532 | @ No unversioned files on this server |
| 533 | style_footer(); |
| 534 | return; |
| @@ -597,10 +637,11 @@ | |
| 637 | Blob json; |
| 638 | |
| 639 | login_check_credentials(); |
| 640 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 641 | cgi_set_content_type("text/json"); |
| 642 | etag_check(ETAG_DATA,0); |
| 643 | if( !db_table_exists("repository","unversioned") ){ |
| 644 | blob_init(&json, "[]", -1); |
| 645 | cgi_set_content(&json); |
| 646 | return; |
| 647 | } |
| 648 |
+18
-14
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -66,20 +66,10 @@ | ||
| 66 | 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | 67 | }; |
| 68 | 68 | #endif /* INTERFACE */ |
| 69 | 69 | |
| 70 | 70 | |
| 71 | -/* | |
| 72 | -** Convert a string to lower-case. | |
| 73 | -*/ | |
| 74 | -static void url_tolower(char *z){ | |
| 75 | - while( *z ){ | |
| 76 | - *z = fossil_tolower(*z); | |
| 77 | - z++; | |
| 78 | - } | |
| 79 | -} | |
| 80 | - | |
| 81 | 71 | /* |
| 82 | 72 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | 73 | ** as follows: |
| 84 | 74 | ** |
| 85 | 75 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | ||
| 177 | 167 | pUrlData->name++; |
| 178 | 168 | pUrlData->name[n-2] = 0; |
| 179 | 169 | } |
| 180 | 170 | zLogin = mprintf(""); |
| 181 | 171 | } |
| 182 | - url_tolower(pUrlData->name); | |
| 172 | + fossil_strtolwr(pUrlData->name); | |
| 183 | 173 | if( c==':' ){ |
| 184 | 174 | pUrlData->port = 0; |
| 185 | 175 | i++; |
| 186 | 176 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 187 | 177 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| @@ -370,18 +360,32 @@ | ||
| 370 | 360 | /* |
| 371 | 361 | ** Extract any proxy options from the command-line. |
| 372 | 362 | ** |
| 373 | 363 | ** --proxy URL|off |
| 374 | 364 | ** |
| 375 | -** This also happens to be a convenient function to use to look for | |
| 376 | -** the --nosync option that will temporarily disable the "autosync" | |
| 377 | -** feature. | |
| 365 | +** The original purpose of this routine is the above. But this | |
| 366 | +** also happens to be a convenient place to look for other | |
| 367 | +** network-related options: | |
| 368 | +** | |
| 369 | +** --nosync Temporarily disable "autosync" | |
| 370 | +** | |
| 371 | +** --ipv4 Disallow IPv6. Use only IPv4. | |
| 372 | +** | |
| 373 | +** --accept-any-cert Disable server SSL cert validation. Accept | |
| 374 | +** any SSL cert that the server provides. | |
| 375 | +** WARNING: this option opens you up to | |
| 376 | +** forged-DNS and man-in-the-middle attacks! | |
| 378 | 377 | */ |
| 379 | 378 | void url_proxy_options(void){ |
| 380 | 379 | zProxyOpt = find_option("proxy", 0, 1); |
| 381 | 380 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 382 | 381 | if( find_option("ipv4",0,0) ) g.fIPv4 = 1; |
| 382 | +#ifdef FOSSIL_ENABLE_SSL | |
| 383 | + if( find_option("accept-any-cert",0,0) ){ | |
| 384 | + ssl_disable_cert_verification(); | |
| 385 | + } | |
| 386 | +#endif /* FOSSIL_ENABLE_SSL */ | |
| 383 | 387 | } |
| 384 | 388 | |
| 385 | 389 | /* |
| 386 | 390 | ** If the "proxy" setting is defined, then change the URL settings |
| 387 | 391 | ** (initialized by a prior call to url_parse()) so that the HTTP |
| 388 | 392 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -66,20 +66,10 @@ | |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Convert a string to lower-case. |
| 73 | */ |
| 74 | static void url_tolower(char *z){ |
| 75 | while( *z ){ |
| 76 | *z = fossil_tolower(*z); |
| 77 | z++; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | ** as follows: |
| 84 | ** |
| 85 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | |
| 177 | pUrlData->name++; |
| 178 | pUrlData->name[n-2] = 0; |
| 179 | } |
| 180 | zLogin = mprintf(""); |
| 181 | } |
| 182 | url_tolower(pUrlData->name); |
| 183 | if( c==':' ){ |
| 184 | pUrlData->port = 0; |
| 185 | i++; |
| 186 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 187 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| @@ -370,18 +360,32 @@ | |
| 370 | /* |
| 371 | ** Extract any proxy options from the command-line. |
| 372 | ** |
| 373 | ** --proxy URL|off |
| 374 | ** |
| 375 | ** This also happens to be a convenient function to use to look for |
| 376 | ** the --nosync option that will temporarily disable the "autosync" |
| 377 | ** feature. |
| 378 | */ |
| 379 | void url_proxy_options(void){ |
| 380 | zProxyOpt = find_option("proxy", 0, 1); |
| 381 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 382 | if( find_option("ipv4",0,0) ) g.fIPv4 = 1; |
| 383 | } |
| 384 | |
| 385 | /* |
| 386 | ** If the "proxy" setting is defined, then change the URL settings |
| 387 | ** (initialized by a prior call to url_parse()) so that the HTTP |
| 388 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -66,20 +66,10 @@ | |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 73 | ** as follows: |
| 74 | ** |
| 75 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | |
| 167 | pUrlData->name++; |
| 168 | pUrlData->name[n-2] = 0; |
| 169 | } |
| 170 | zLogin = mprintf(""); |
| 171 | } |
| 172 | fossil_strtolwr(pUrlData->name); |
| 173 | if( c==':' ){ |
| 174 | pUrlData->port = 0; |
| 175 | i++; |
| 176 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 177 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| @@ -370,18 +360,32 @@ | |
| 360 | /* |
| 361 | ** Extract any proxy options from the command-line. |
| 362 | ** |
| 363 | ** --proxy URL|off |
| 364 | ** |
| 365 | ** The original purpose of this routine is the above. But this |
| 366 | ** also happens to be a convenient place to look for other |
| 367 | ** network-related options: |
| 368 | ** |
| 369 | ** --nosync Temporarily disable "autosync" |
| 370 | ** |
| 371 | ** --ipv4 Disallow IPv6. Use only IPv4. |
| 372 | ** |
| 373 | ** --accept-any-cert Disable server SSL cert validation. Accept |
| 374 | ** any SSL cert that the server provides. |
| 375 | ** WARNING: this option opens you up to |
| 376 | ** forged-DNS and man-in-the-middle attacks! |
| 377 | */ |
| 378 | void url_proxy_options(void){ |
| 379 | zProxyOpt = find_option("proxy", 0, 1); |
| 380 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 381 | if( find_option("ipv4",0,0) ) g.fIPv4 = 1; |
| 382 | #ifdef FOSSIL_ENABLE_SSL |
| 383 | if( find_option("accept-any-cert",0,0) ){ |
| 384 | ssl_disable_cert_verification(); |
| 385 | } |
| 386 | #endif /* FOSSIL_ENABLE_SSL */ |
| 387 | } |
| 388 | |
| 389 | /* |
| 390 | ** If the "proxy" setting is defined, then change the URL settings |
| 391 | ** (initialized by a prior call to url_parse()) so that the HTTP |
| 392 |
+15
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -140,10 +140,25 @@ | ||
| 140 | 140 | } |
| 141 | 141 | #else |
| 142 | 142 | fossil_free(p); |
| 143 | 143 | #endif |
| 144 | 144 | } |
| 145 | + | |
| 146 | +/* | |
| 147 | +** Translate every upper-case character in the input string into | |
| 148 | +** its equivalent lower-case. | |
| 149 | +*/ | |
| 150 | +char *fossil_strtolwr(char *zIn){ | |
| 151 | + char *zStart = zIn; | |
| 152 | + if( zIn ){ | |
| 153 | + while( *zIn ){ | |
| 154 | + *zIn = fossil_tolower(*zIn); | |
| 155 | + zIn++; | |
| 156 | + } | |
| 157 | + } | |
| 158 | + return zStart; | |
| 159 | +} | |
| 145 | 160 | |
| 146 | 161 | /* |
| 147 | 162 | ** This function implements a cross-platform "system()" interface. |
| 148 | 163 | */ |
| 149 | 164 | int fossil_system(const char *zOrigCmd){ |
| 150 | 165 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -140,10 +140,25 @@ | |
| 140 | } |
| 141 | #else |
| 142 | fossil_free(p); |
| 143 | #endif |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** This function implements a cross-platform "system()" interface. |
| 148 | */ |
| 149 | int fossil_system(const char *zOrigCmd){ |
| 150 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -140,10 +140,25 @@ | |
| 140 | } |
| 141 | #else |
| 142 | fossil_free(p); |
| 143 | #endif |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** Translate every upper-case character in the input string into |
| 148 | ** its equivalent lower-case. |
| 149 | */ |
| 150 | char *fossil_strtolwr(char *zIn){ |
| 151 | char *zStart = zIn; |
| 152 | if( zIn ){ |
| 153 | while( *zIn ){ |
| 154 | *zIn = fossil_tolower(*zIn); |
| 155 | zIn++; |
| 156 | } |
| 157 | } |
| 158 | return zStart; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** This function implements a cross-platform "system()" interface. |
| 163 | */ |
| 164 | int fossil_system(const char *zOrigCmd){ |
| 165 |
+46
-23
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -365,20 +365,22 @@ | ||
| 365 | 365 | search_screen(SRCH_WIKI, 0); |
| 366 | 366 | style_footer(); |
| 367 | 367 | } |
| 368 | 368 | |
| 369 | 369 | /* Return values from wiki_page_type() */ |
| 370 | -#define WIKITYPE_UNKNOWN (-1) | |
| 371 | -#define WIKITYPE_NORMAL 0 | |
| 372 | -#define WIKITYPE_BRANCH 1 | |
| 373 | -#define WIKITYPE_CHECKIN 2 | |
| 374 | -#define WIKITYPE_TAG 3 | |
| 370 | +#if INTERFACE | |
| 371 | +# define WIKITYPE_UNKNOWN (-1) | |
| 372 | +# define WIKITYPE_NORMAL 0 | |
| 373 | +# define WIKITYPE_BRANCH 1 | |
| 374 | +# define WIKITYPE_CHECKIN 2 | |
| 375 | +# define WIKITYPE_TAG 3 | |
| 376 | +#endif | |
| 375 | 377 | |
| 376 | 378 | /* |
| 377 | 379 | ** Figure out what type of wiki page we are dealing with. |
| 378 | 380 | */ |
| 379 | -static int wiki_page_type(const char *zPageName){ | |
| 381 | +int wiki_page_type(const char *zPageName){ | |
| 380 | 382 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 381 | 383 | return WIKITYPE_NORMAL; |
| 382 | 384 | }else |
| 383 | 385 | if( sqlite3_strglob("checkin/*", zPageName)==0 |
| 384 | 386 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| @@ -394,40 +396,58 @@ | ||
| 394 | 396 | return WIKITYPE_NORMAL; |
| 395 | 397 | } |
| 396 | 398 | |
| 397 | 399 | /* |
| 398 | 400 | ** Add an appropriate style_header() for either the /wiki or /wikiedit page |
| 399 | -** for zPageName. | |
| 401 | +** for zPageName. zExtra is an empty string for /wiki but has the text | |
| 402 | +** "Edit: " for /wikiedit. | |
| 403 | +** | |
| 404 | +** If the page is /wiki and the page is one of the special times (check-in, | |
| 405 | +** branch, or tag) and the "p" query parameter is omitted, then do a | |
| 406 | +** redirect to the display of the check-in, branch, or tag rather than | |
| 407 | +** continuing to the plain wiki display. | |
| 400 | 408 | */ |
| 401 | 409 | static int wiki_page_header( |
| 402 | - int eType, /* Page type. -1 for unknown */ | |
| 410 | + int eType, /* Page type. Might be WIKITYPE_UNKNOWN */ | |
| 403 | 411 | const char *zPageName, /* Name of the page */ |
| 404 | 412 | const char *zExtra /* Extra prefix text on the page header */ |
| 405 | 413 | ){ |
| 406 | - if( eType<0 ) eType = wiki_page_type(zPageName); | |
| 414 | + if( eType==WIKITYPE_UNKNOWN ) eType = wiki_page_type(zPageName); | |
| 407 | 415 | switch( eType ){ |
| 408 | 416 | case WIKITYPE_NORMAL: { |
| 409 | 417 | style_header("%s%s", zExtra, zPageName); |
| 410 | 418 | break; |
| 411 | 419 | } |
| 412 | 420 | case WIKITYPE_CHECKIN: { |
| 413 | 421 | zPageName += 8; |
| 414 | - style_header("Notes About Checkin %S", zPageName); | |
| 415 | - style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName); | |
| 416 | - style_submenu_element("Checkin Info","%R/info/%s", zPageName); | |
| 422 | + if( zExtra[0]==0 && !P("p") ){ | |
| 423 | + cgi_redirectf("%R/info/%s",zPageName); | |
| 424 | + }else{ | |
| 425 | + style_header("Notes About Checkin %S", zPageName); | |
| 426 | + style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName); | |
| 427 | + style_submenu_element("Checkin Info","%R/info/%s", zPageName); | |
| 428 | + } | |
| 417 | 429 | break; |
| 418 | 430 | } |
| 419 | 431 | case WIKITYPE_BRANCH: { |
| 420 | 432 | zPageName += 7; |
| 421 | - style_header("Notes About Branch %h", zPageName); | |
| 422 | - style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName); | |
| 433 | + if( zExtra[0]==0 && !P("p") ){ | |
| 434 | + cgi_redirectf("%R/timeline?r=%t", zPageName); | |
| 435 | + }else{ | |
| 436 | + style_header("Notes About Branch %h", zPageName); | |
| 437 | + style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName); | |
| 438 | + } | |
| 423 | 439 | break; |
| 424 | 440 | } |
| 425 | 441 | case WIKITYPE_TAG: { |
| 426 | 442 | zPageName += 4; |
| 427 | - style_header("Notes About Tag %h", zPageName); | |
| 428 | - style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); | |
| 443 | + if( zExtra[0]==0 && !P("p") ){ | |
| 444 | + cgi_redirectf("%R/timeline?t=%t",zPageName); | |
| 445 | + }else{ | |
| 446 | + style_header("Notes About Tag %h", zPageName); | |
| 447 | + style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); | |
| 448 | + } | |
| 429 | 449 | break; |
| 430 | 450 | } |
| 431 | 451 | } |
| 432 | 452 | return eType; |
| 433 | 453 | } |
| @@ -459,11 +479,14 @@ | ||
| 459 | 479 | ** |
| 460 | 480 | ** Query parameters: |
| 461 | 481 | ** |
| 462 | 482 | ** name=NAME Name of the wiki page to display. Required. |
| 463 | 483 | ** nsm Omit the submenu if present. (Mnemonic: No SubMenu) |
| 464 | -** | |
| 484 | +** p Always show just the wiki page. For special | |
| 485 | +** pages for check-ins, branches, or tags, there will | |
| 486 | +** be a redirect to the associated /info page unless | |
| 487 | +** this query parameter is present. | |
| 465 | 488 | */ |
| 466 | 489 | void wiki_page(void){ |
| 467 | 490 | char *zTag; |
| 468 | 491 | int rid = 0; |
| 469 | 492 | int isSandbox; |
| @@ -1724,17 +1747,17 @@ | ||
| 1724 | 1747 | } |
| 1725 | 1748 | |
| 1726 | 1749 | /* |
| 1727 | 1750 | ** Add an "Wiki" button in a submenu that links to the read-wiki page. |
| 1728 | 1751 | */ |
| 1729 | -static void wiki_submenu_to_read_wiki( | |
| 1752 | +static void wiki_submenu_to_edit_wiki( | |
| 1730 | 1753 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 1731 | 1754 | const char *zName, /* Name of the object */ |
| 1732 | 1755 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 1733 | 1756 | ){ |
| 1734 | 1757 | if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){ |
| 1735 | - style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName); | |
| 1758 | + style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); | |
| 1736 | 1759 | } |
| 1737 | 1760 | } |
| 1738 | 1761 | |
| 1739 | 1762 | /* |
| 1740 | 1763 | ** Check to see if there exists a wiki page with a name zPrefix/zName. |
| @@ -1770,24 +1793,24 @@ | ||
| 1770 | 1793 | Blob title = BLOB_INITIALIZER; |
| 1771 | 1794 | Blob markdown; |
| 1772 | 1795 | blob_init(&markdown, pWiki->zWiki, -1); |
| 1773 | 1796 | markdown_to_html(&markdown, &title, &tail); |
| 1774 | 1797 | if( blob_size(&title) ){ |
| 1775 | - @ <div class="section">%h(blob_str(&title))</div> | |
| 1798 | + @ <div class="section accordion">%h(blob_str(&title))</div> | |
| 1776 | 1799 | }else{ |
| 1777 | 1800 | wiki_section_label(zPrefix, zName, mFlags); |
| 1778 | 1801 | } |
| 1779 | - wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); | |
| 1802 | + wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); | |
| 1780 | 1803 | @ <div class="accordion_panel"> |
| 1781 | 1804 | convert_href_and_output(&tail); |
| 1782 | 1805 | @ </div> |
| 1783 | 1806 | blob_reset(&tail); |
| 1784 | 1807 | blob_reset(&title); |
| 1785 | 1808 | blob_reset(&markdown); |
| 1786 | 1809 | }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){ |
| 1787 | 1810 | wiki_section_label(zPrefix, zName, mFlags); |
| 1788 | - wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); | |
| 1811 | + wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); | |
| 1789 | 1812 | @ <div class="accordion_panel"><pre> |
| 1790 | 1813 | @ %h(pWiki->zWiki) |
| 1791 | 1814 | @ </pre></div> |
| 1792 | 1815 | }else{ |
| 1793 | 1816 | Blob tail = BLOB_INITIALIZER; |
| @@ -1800,11 +1823,11 @@ | ||
| 1800 | 1823 | pBody = &tail; |
| 1801 | 1824 | }else{ |
| 1802 | 1825 | wiki_section_label(zPrefix, zName, mFlags); |
| 1803 | 1826 | pBody = &wiki; |
| 1804 | 1827 | } |
| 1805 | - wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); | |
| 1828 | + wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); | |
| 1806 | 1829 | @ <div class="accordion_panel"><div class="wiki"> |
| 1807 | 1830 | wiki_convert(pBody, 0, WIKI_BUTTONS); |
| 1808 | 1831 | @ </div></div> |
| 1809 | 1832 | blob_reset(&tail); |
| 1810 | 1833 | blob_reset(&title); |
| 1811 | 1834 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -365,20 +365,22 @@ | |
| 365 | search_screen(SRCH_WIKI, 0); |
| 366 | style_footer(); |
| 367 | } |
| 368 | |
| 369 | /* Return values from wiki_page_type() */ |
| 370 | #define WIKITYPE_UNKNOWN (-1) |
| 371 | #define WIKITYPE_NORMAL 0 |
| 372 | #define WIKITYPE_BRANCH 1 |
| 373 | #define WIKITYPE_CHECKIN 2 |
| 374 | #define WIKITYPE_TAG 3 |
| 375 | |
| 376 | /* |
| 377 | ** Figure out what type of wiki page we are dealing with. |
| 378 | */ |
| 379 | static int wiki_page_type(const char *zPageName){ |
| 380 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 381 | return WIKITYPE_NORMAL; |
| 382 | }else |
| 383 | if( sqlite3_strglob("checkin/*", zPageName)==0 |
| 384 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| @@ -394,40 +396,58 @@ | |
| 394 | return WIKITYPE_NORMAL; |
| 395 | } |
| 396 | |
| 397 | /* |
| 398 | ** Add an appropriate style_header() for either the /wiki or /wikiedit page |
| 399 | ** for zPageName. |
| 400 | */ |
| 401 | static int wiki_page_header( |
| 402 | int eType, /* Page type. -1 for unknown */ |
| 403 | const char *zPageName, /* Name of the page */ |
| 404 | const char *zExtra /* Extra prefix text on the page header */ |
| 405 | ){ |
| 406 | if( eType<0 ) eType = wiki_page_type(zPageName); |
| 407 | switch( eType ){ |
| 408 | case WIKITYPE_NORMAL: { |
| 409 | style_header("%s%s", zExtra, zPageName); |
| 410 | break; |
| 411 | } |
| 412 | case WIKITYPE_CHECKIN: { |
| 413 | zPageName += 8; |
| 414 | style_header("Notes About Checkin %S", zPageName); |
| 415 | style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName); |
| 416 | style_submenu_element("Checkin Info","%R/info/%s", zPageName); |
| 417 | break; |
| 418 | } |
| 419 | case WIKITYPE_BRANCH: { |
| 420 | zPageName += 7; |
| 421 | style_header("Notes About Branch %h", zPageName); |
| 422 | style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName); |
| 423 | break; |
| 424 | } |
| 425 | case WIKITYPE_TAG: { |
| 426 | zPageName += 4; |
| 427 | style_header("Notes About Tag %h", zPageName); |
| 428 | style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); |
| 429 | break; |
| 430 | } |
| 431 | } |
| 432 | return eType; |
| 433 | } |
| @@ -459,11 +479,14 @@ | |
| 459 | ** |
| 460 | ** Query parameters: |
| 461 | ** |
| 462 | ** name=NAME Name of the wiki page to display. Required. |
| 463 | ** nsm Omit the submenu if present. (Mnemonic: No SubMenu) |
| 464 | ** |
| 465 | */ |
| 466 | void wiki_page(void){ |
| 467 | char *zTag; |
| 468 | int rid = 0; |
| 469 | int isSandbox; |
| @@ -1724,17 +1747,17 @@ | |
| 1724 | } |
| 1725 | |
| 1726 | /* |
| 1727 | ** Add an "Wiki" button in a submenu that links to the read-wiki page. |
| 1728 | */ |
| 1729 | static void wiki_submenu_to_read_wiki( |
| 1730 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 1731 | const char *zName, /* Name of the object */ |
| 1732 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 1733 | ){ |
| 1734 | if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){ |
| 1735 | style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName); |
| 1736 | } |
| 1737 | } |
| 1738 | |
| 1739 | /* |
| 1740 | ** Check to see if there exists a wiki page with a name zPrefix/zName. |
| @@ -1770,24 +1793,24 @@ | |
| 1770 | Blob title = BLOB_INITIALIZER; |
| 1771 | Blob markdown; |
| 1772 | blob_init(&markdown, pWiki->zWiki, -1); |
| 1773 | markdown_to_html(&markdown, &title, &tail); |
| 1774 | if( blob_size(&title) ){ |
| 1775 | @ <div class="section">%h(blob_str(&title))</div> |
| 1776 | }else{ |
| 1777 | wiki_section_label(zPrefix, zName, mFlags); |
| 1778 | } |
| 1779 | wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); |
| 1780 | @ <div class="accordion_panel"> |
| 1781 | convert_href_and_output(&tail); |
| 1782 | @ </div> |
| 1783 | blob_reset(&tail); |
| 1784 | blob_reset(&title); |
| 1785 | blob_reset(&markdown); |
| 1786 | }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){ |
| 1787 | wiki_section_label(zPrefix, zName, mFlags); |
| 1788 | wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); |
| 1789 | @ <div class="accordion_panel"><pre> |
| 1790 | @ %h(pWiki->zWiki) |
| 1791 | @ </pre></div> |
| 1792 | }else{ |
| 1793 | Blob tail = BLOB_INITIALIZER; |
| @@ -1800,11 +1823,11 @@ | |
| 1800 | pBody = &tail; |
| 1801 | }else{ |
| 1802 | wiki_section_label(zPrefix, zName, mFlags); |
| 1803 | pBody = &wiki; |
| 1804 | } |
| 1805 | wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); |
| 1806 | @ <div class="accordion_panel"><div class="wiki"> |
| 1807 | wiki_convert(pBody, 0, WIKI_BUTTONS); |
| 1808 | @ </div></div> |
| 1809 | blob_reset(&tail); |
| 1810 | blob_reset(&title); |
| 1811 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -365,20 +365,22 @@ | |
| 365 | search_screen(SRCH_WIKI, 0); |
| 366 | style_footer(); |
| 367 | } |
| 368 | |
| 369 | /* Return values from wiki_page_type() */ |
| 370 | #if INTERFACE |
| 371 | # define WIKITYPE_UNKNOWN (-1) |
| 372 | # define WIKITYPE_NORMAL 0 |
| 373 | # define WIKITYPE_BRANCH 1 |
| 374 | # define WIKITYPE_CHECKIN 2 |
| 375 | # define WIKITYPE_TAG 3 |
| 376 | #endif |
| 377 | |
| 378 | /* |
| 379 | ** Figure out what type of wiki page we are dealing with. |
| 380 | */ |
| 381 | int wiki_page_type(const char *zPageName){ |
| 382 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 383 | return WIKITYPE_NORMAL; |
| 384 | }else |
| 385 | if( sqlite3_strglob("checkin/*", zPageName)==0 |
| 386 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| @@ -394,40 +396,58 @@ | |
| 396 | return WIKITYPE_NORMAL; |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | ** Add an appropriate style_header() for either the /wiki or /wikiedit page |
| 401 | ** for zPageName. zExtra is an empty string for /wiki but has the text |
| 402 | ** "Edit: " for /wikiedit. |
| 403 | ** |
| 404 | ** If the page is /wiki and the page is one of the special times (check-in, |
| 405 | ** branch, or tag) and the "p" query parameter is omitted, then do a |
| 406 | ** redirect to the display of the check-in, branch, or tag rather than |
| 407 | ** continuing to the plain wiki display. |
| 408 | */ |
| 409 | static int wiki_page_header( |
| 410 | int eType, /* Page type. Might be WIKITYPE_UNKNOWN */ |
| 411 | const char *zPageName, /* Name of the page */ |
| 412 | const char *zExtra /* Extra prefix text on the page header */ |
| 413 | ){ |
| 414 | if( eType==WIKITYPE_UNKNOWN ) eType = wiki_page_type(zPageName); |
| 415 | switch( eType ){ |
| 416 | case WIKITYPE_NORMAL: { |
| 417 | style_header("%s%s", zExtra, zPageName); |
| 418 | break; |
| 419 | } |
| 420 | case WIKITYPE_CHECKIN: { |
| 421 | zPageName += 8; |
| 422 | if( zExtra[0]==0 && !P("p") ){ |
| 423 | cgi_redirectf("%R/info/%s",zPageName); |
| 424 | }else{ |
| 425 | style_header("Notes About Checkin %S", zPageName); |
| 426 | style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName); |
| 427 | style_submenu_element("Checkin Info","%R/info/%s", zPageName); |
| 428 | } |
| 429 | break; |
| 430 | } |
| 431 | case WIKITYPE_BRANCH: { |
| 432 | zPageName += 7; |
| 433 | if( zExtra[0]==0 && !P("p") ){ |
| 434 | cgi_redirectf("%R/timeline?r=%t", zPageName); |
| 435 | }else{ |
| 436 | style_header("Notes About Branch %h", zPageName); |
| 437 | style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName); |
| 438 | } |
| 439 | break; |
| 440 | } |
| 441 | case WIKITYPE_TAG: { |
| 442 | zPageName += 4; |
| 443 | if( zExtra[0]==0 && !P("p") ){ |
| 444 | cgi_redirectf("%R/timeline?t=%t",zPageName); |
| 445 | }else{ |
| 446 | style_header("Notes About Tag %h", zPageName); |
| 447 | style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); |
| 448 | } |
| 449 | break; |
| 450 | } |
| 451 | } |
| 452 | return eType; |
| 453 | } |
| @@ -459,11 +479,14 @@ | |
| 479 | ** |
| 480 | ** Query parameters: |
| 481 | ** |
| 482 | ** name=NAME Name of the wiki page to display. Required. |
| 483 | ** nsm Omit the submenu if present. (Mnemonic: No SubMenu) |
| 484 | ** p Always show just the wiki page. For special |
| 485 | ** pages for check-ins, branches, or tags, there will |
| 486 | ** be a redirect to the associated /info page unless |
| 487 | ** this query parameter is present. |
| 488 | */ |
| 489 | void wiki_page(void){ |
| 490 | char *zTag; |
| 491 | int rid = 0; |
| 492 | int isSandbox; |
| @@ -1724,17 +1747,17 @@ | |
| 1747 | } |
| 1748 | |
| 1749 | /* |
| 1750 | ** Add an "Wiki" button in a submenu that links to the read-wiki page. |
| 1751 | */ |
| 1752 | static void wiki_submenu_to_edit_wiki( |
| 1753 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 1754 | const char *zName, /* Name of the object */ |
| 1755 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 1756 | ){ |
| 1757 | if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){ |
| 1758 | style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); |
| 1759 | } |
| 1760 | } |
| 1761 | |
| 1762 | /* |
| 1763 | ** Check to see if there exists a wiki page with a name zPrefix/zName. |
| @@ -1770,24 +1793,24 @@ | |
| 1793 | Blob title = BLOB_INITIALIZER; |
| 1794 | Blob markdown; |
| 1795 | blob_init(&markdown, pWiki->zWiki, -1); |
| 1796 | markdown_to_html(&markdown, &title, &tail); |
| 1797 | if( blob_size(&title) ){ |
| 1798 | @ <div class="section accordion">%h(blob_str(&title))</div> |
| 1799 | }else{ |
| 1800 | wiki_section_label(zPrefix, zName, mFlags); |
| 1801 | } |
| 1802 | wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); |
| 1803 | @ <div class="accordion_panel"> |
| 1804 | convert_href_and_output(&tail); |
| 1805 | @ </div> |
| 1806 | blob_reset(&tail); |
| 1807 | blob_reset(&title); |
| 1808 | blob_reset(&markdown); |
| 1809 | }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){ |
| 1810 | wiki_section_label(zPrefix, zName, mFlags); |
| 1811 | wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); |
| 1812 | @ <div class="accordion_panel"><pre> |
| 1813 | @ %h(pWiki->zWiki) |
| 1814 | @ </pre></div> |
| 1815 | }else{ |
| 1816 | Blob tail = BLOB_INITIALIZER; |
| @@ -1800,11 +1823,11 @@ | |
| 1823 | pBody = &tail; |
| 1824 | }else{ |
| 1825 | wiki_section_label(zPrefix, zName, mFlags); |
| 1826 | pBody = &wiki; |
| 1827 | } |
| 1828 | wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); |
| 1829 | @ <div class="accordion_panel"><div class="wiki"> |
| 1830 | wiki_convert(pBody, 0, WIKI_BUTTONS); |
| 1831 | @ </div></div> |
| 1832 | blob_reset(&tail); |
| 1833 | blob_reset(&title); |
| 1834 |
+3
-21
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -1869,14 +1869,11 @@ | ||
| 1869 | 1869 | ** name. For each such hyperlink found, add an entry to the |
| 1870 | 1870 | ** backlink table. |
| 1871 | 1871 | */ |
| 1872 | 1872 | void wiki_extract_links( |
| 1873 | 1873 | char *z, /* The wiki text from which to extract links */ |
| 1874 | - int srcid, /* srcid field for new BACKLINK table entries */ | |
| 1875 | - int srctype, /* srctype field for new BACKLINK table entries */ | |
| 1876 | - double mtime, /* mtime field for new BACKLINK table entries */ | |
| 1877 | - int replaceFlag, /* True first delete prior BACKLINK entries */ | |
| 1874 | + Backlink *pBklnk, /* Backlink extraction context */ | |
| 1878 | 1875 | int flags /* wiki parsing flags */ |
| 1879 | 1876 | ){ |
| 1880 | 1877 | Renderer renderer; |
| 1881 | 1878 | int tokenType; |
| 1882 | 1879 | ParsedMarkup markup; |
| @@ -1892,14 +1889,10 @@ | ||
| 1892 | 1889 | if( wikiUsesHtml() ){ |
| 1893 | 1890 | renderer.state |= WIKI_HTMLONLY; |
| 1894 | 1891 | wikiHtmlOnly = 1; |
| 1895 | 1892 | } |
| 1896 | 1893 | inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0; |
| 1897 | - if( replaceFlag ){ | |
| 1898 | - db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d", | |
| 1899 | - srctype, srcid); | |
| 1900 | - } | |
| 1901 | 1894 | |
| 1902 | 1895 | while( z[0] ){ |
| 1903 | 1896 | if( wikiHtmlOnly ){ |
| 1904 | 1897 | n = nextRawToken(z, &renderer, &tokenType); |
| 1905 | 1898 | }else{ |
| @@ -1906,27 +1899,16 @@ | ||
| 1906 | 1899 | n = nextWikiToken(z, &renderer, &tokenType); |
| 1907 | 1900 | } |
| 1908 | 1901 | switch( tokenType ){ |
| 1909 | 1902 | case TOKEN_LINK: { |
| 1910 | 1903 | char *zTarget; |
| 1911 | - int i, c; | |
| 1912 | - char zLink[HNAME_MAX+4]; | |
| 1904 | + int i; | |
| 1913 | 1905 | |
| 1914 | 1906 | zTarget = &z[1]; |
| 1915 | 1907 | for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){} |
| 1916 | 1908 | while(i>1 && zTarget[i-1]==' '){ i--; } |
| 1917 | - c = zTarget[i]; | |
| 1918 | - zTarget[i] = 0; | |
| 1919 | - if( is_valid_hname(zTarget) ){ | |
| 1920 | - memcpy(zLink, zTarget, i+1); | |
| 1921 | - canonical16(zLink, i); | |
| 1922 | - db_multi_exec( | |
| 1923 | - "REPLACE INTO backlink(target,srctype,srcid,mtime)" | |
| 1924 | - "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime | |
| 1925 | - ); | |
| 1926 | - } | |
| 1927 | - zTarget[i] = c; | |
| 1909 | + backlink_create(pBklnk, zTarget, i); | |
| 1928 | 1910 | break; |
| 1929 | 1911 | } |
| 1930 | 1912 | case TOKEN_MARKUP: { |
| 1931 | 1913 | const char *zId; |
| 1932 | 1914 | int iDiv; |
| 1933 | 1915 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -1869,14 +1869,11 @@ | |
| 1869 | ** name. For each such hyperlink found, add an entry to the |
| 1870 | ** backlink table. |
| 1871 | */ |
| 1872 | void wiki_extract_links( |
| 1873 | char *z, /* The wiki text from which to extract links */ |
| 1874 | int srcid, /* srcid field for new BACKLINK table entries */ |
| 1875 | int srctype, /* srctype field for new BACKLINK table entries */ |
| 1876 | double mtime, /* mtime field for new BACKLINK table entries */ |
| 1877 | int replaceFlag, /* True first delete prior BACKLINK entries */ |
| 1878 | int flags /* wiki parsing flags */ |
| 1879 | ){ |
| 1880 | Renderer renderer; |
| 1881 | int tokenType; |
| 1882 | ParsedMarkup markup; |
| @@ -1892,14 +1889,10 @@ | |
| 1892 | if( wikiUsesHtml() ){ |
| 1893 | renderer.state |= WIKI_HTMLONLY; |
| 1894 | wikiHtmlOnly = 1; |
| 1895 | } |
| 1896 | inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0; |
| 1897 | if( replaceFlag ){ |
| 1898 | db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d", |
| 1899 | srctype, srcid); |
| 1900 | } |
| 1901 | |
| 1902 | while( z[0] ){ |
| 1903 | if( wikiHtmlOnly ){ |
| 1904 | n = nextRawToken(z, &renderer, &tokenType); |
| 1905 | }else{ |
| @@ -1906,27 +1899,16 @@ | |
| 1906 | n = nextWikiToken(z, &renderer, &tokenType); |
| 1907 | } |
| 1908 | switch( tokenType ){ |
| 1909 | case TOKEN_LINK: { |
| 1910 | char *zTarget; |
| 1911 | int i, c; |
| 1912 | char zLink[HNAME_MAX+4]; |
| 1913 | |
| 1914 | zTarget = &z[1]; |
| 1915 | for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){} |
| 1916 | while(i>1 && zTarget[i-1]==' '){ i--; } |
| 1917 | c = zTarget[i]; |
| 1918 | zTarget[i] = 0; |
| 1919 | if( is_valid_hname(zTarget) ){ |
| 1920 | memcpy(zLink, zTarget, i+1); |
| 1921 | canonical16(zLink, i); |
| 1922 | db_multi_exec( |
| 1923 | "REPLACE INTO backlink(target,srctype,srcid,mtime)" |
| 1924 | "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime |
| 1925 | ); |
| 1926 | } |
| 1927 | zTarget[i] = c; |
| 1928 | break; |
| 1929 | } |
| 1930 | case TOKEN_MARKUP: { |
| 1931 | const char *zId; |
| 1932 | int iDiv; |
| 1933 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -1869,14 +1869,11 @@ | |
| 1869 | ** name. For each such hyperlink found, add an entry to the |
| 1870 | ** backlink table. |
| 1871 | */ |
| 1872 | void wiki_extract_links( |
| 1873 | char *z, /* The wiki text from which to extract links */ |
| 1874 | Backlink *pBklnk, /* Backlink extraction context */ |
| 1875 | int flags /* wiki parsing flags */ |
| 1876 | ){ |
| 1877 | Renderer renderer; |
| 1878 | int tokenType; |
| 1879 | ParsedMarkup markup; |
| @@ -1892,14 +1889,10 @@ | |
| 1889 | if( wikiUsesHtml() ){ |
| 1890 | renderer.state |= WIKI_HTMLONLY; |
| 1891 | wikiHtmlOnly = 1; |
| 1892 | } |
| 1893 | inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0; |
| 1894 | |
| 1895 | while( z[0] ){ |
| 1896 | if( wikiHtmlOnly ){ |
| 1897 | n = nextRawToken(z, &renderer, &tokenType); |
| 1898 | }else{ |
| @@ -1906,27 +1899,16 @@ | |
| 1899 | n = nextWikiToken(z, &renderer, &tokenType); |
| 1900 | } |
| 1901 | switch( tokenType ){ |
| 1902 | case TOKEN_LINK: { |
| 1903 | char *zTarget; |
| 1904 | int i; |
| 1905 | |
| 1906 | zTarget = &z[1]; |
| 1907 | for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){} |
| 1908 | while(i>1 && zTarget[i-1]==' '){ i--; } |
| 1909 | backlink_create(pBklnk, zTarget, i); |
| 1910 | break; |
| 1911 | } |
| 1912 | case TOKEN_MARKUP: { |
| 1913 | const char *zId; |
| 1914 | int iDiv; |
| 1915 |
+153
-56
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -39,10 +39,11 @@ | ||
| 39 | 39 | Blob line; /* The current line of input */ |
| 40 | 40 | Blob aToken[6]; /* Tokenized version of line */ |
| 41 | 41 | Blob err; /* Error message text */ |
| 42 | 42 | int nToken; /* Number of tokens in line */ |
| 43 | 43 | int nIGotSent; /* Number of "igot" cards sent */ |
| 44 | + int nPrivIGot; /* Number of private "igot" cards */ | |
| 44 | 45 | int nGimmeSent; /* Number of gimme cards sent */ |
| 45 | 46 | int nFileSent; /* Number of files sent */ |
| 46 | 47 | int nDeltaSent; /* Number of deltas sent */ |
| 47 | 48 | int nFileRcvd; /* Number of files received */ |
| 48 | 49 | int nDeltaRcvd; /* Number of deltas received */ |
| @@ -49,11 +50,13 @@ | ||
| 49 | 50 | int nDanglingFile; /* Number of dangling deltas received */ |
| 50 | 51 | int mxSend; /* Stop sending "file" when pOut reaches this size */ |
| 51 | 52 | int resync; /* Send igot cards for all holdings */ |
| 52 | 53 | u8 syncPrivate; /* True to enable syncing private content */ |
| 53 | 54 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 54 | - u32 clientVersion; /* Version of the client software */ | |
| 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 */ | |
| 55 | 58 | time_t maxTime; /* Time when this transfer should be finished */ |
| 56 | 59 | }; |
| 57 | 60 | |
| 58 | 61 | |
| 59 | 62 | /* |
| @@ -524,20 +527,32 @@ | ||
| 524 | 527 | static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){ |
| 525 | 528 | Blob content, uuid; |
| 526 | 529 | int size = 0; |
| 527 | 530 | int isPriv = content_is_private(rid); |
| 528 | 531 | |
| 529 | - if( pXfer->syncPrivate==0 && isPriv ) return; | |
| 532 | + if( isPriv && pXfer->syncPrivate==0 ){ | |
| 533 | + if( pXfer->remoteDate>=20200413 && pUuid && blob_size(pUuid)>0 ){ | |
| 534 | + /* If the artifact is private and we are not doing a private sync, | |
| 535 | + ** at least tell the other side that the artifact exists and is | |
| 536 | + ** known to be private. But only do this for newer clients since | |
| 537 | + ** older ones will throw an error if they get a private igot card | |
| 538 | + ** and private syncing is disallowed */ | |
| 539 | + blob_appendf(pXfer->pOut, "igot %b 1\n", pUuid); | |
| 540 | + pXfer->nIGotSent++; | |
| 541 | + pXfer->nPrivIGot++; | |
| 542 | + } | |
| 543 | + return; | |
| 544 | + } | |
| 530 | 545 | if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){ |
| 531 | 546 | return; |
| 532 | 547 | } |
| 533 | 548 | blob_zero(&uuid); |
| 534 | 549 | db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid); |
| 535 | 550 | if( blob_size(&uuid)==0 ){ |
| 536 | 551 | return; |
| 537 | 552 | } |
| 538 | - if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->clientVersion<20000 ){ | |
| 553 | + if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->remoteVersion<20000 ){ | |
| 539 | 554 | xfer_cannot_send_sha3_error(pXfer); |
| 540 | 555 | return; |
| 541 | 556 | } |
| 542 | 557 | if( pUuid ){ |
| 543 | 558 | if( blob_compare(pUuid, &uuid)!=0 ){ |
| @@ -626,11 +641,11 @@ | ||
| 626 | 641 | szC = db_column_bytes(&q1, 2); |
| 627 | 642 | zContent = db_column_raw(&q1, 2); |
| 628 | 643 | srcIsPrivate = db_column_int(&q1, 3); |
| 629 | 644 | zDelta = db_column_text(&q1, 4); |
| 630 | 645 | if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); |
| 631 | - if( pXfer->clientVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){ | |
| 646 | + if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){ | |
| 632 | 647 | xfer_cannot_send_sha3_error(pXfer); |
| 633 | 648 | db_reset(&q1); |
| 634 | 649 | return; |
| 635 | 650 | } |
| 636 | 651 | blob_appendf(pXfer->pOut, "cfile %s ", zUuid); |
| @@ -690,11 +705,11 @@ | ||
| 690 | 705 | ); |
| 691 | 706 | } |
| 692 | 707 | if( db_step(&q1)==SQLITE_ROW ){ |
| 693 | 708 | sqlite3_int64 mtime = db_column_int64(&q1, 0); |
| 694 | 709 | const char *zHash = db_column_text(&q1, 1); |
| 695 | - if( pXfer->clientVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){ | |
| 710 | + if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){ | |
| 696 | 711 | xfer_cannot_send_sha3_error(pXfer); |
| 697 | 712 | db_reset(&q1); |
| 698 | 713 | return; |
| 699 | 714 | } |
| 700 | 715 | if( blob_size(pXfer->pOut)>=pXfer->mxSend ){ |
| @@ -956,30 +971,48 @@ | ||
| 956 | 971 | } |
| 957 | 972 | |
| 958 | 973 | /* |
| 959 | 974 | ** Send an igot message for every entry in unclustered table. |
| 960 | 975 | ** Return the number of cards sent. |
| 976 | +** | |
| 977 | +** Except: | |
| 978 | +** * Do not send igot cards for shunned artifacts | |
| 979 | +** * Do not send igot cards for phantoms | |
| 980 | +** * Do not send igot cards for private artifacts | |
| 981 | +** * Do not send igot cards for any artifact that is in the | |
| 982 | +** ONREMOTE table, if that table exists. | |
| 983 | +** | |
| 984 | +** If the pXfer->resync flag is set, that means we are doing a "--verily" | |
| 985 | +** sync and all artifacts that don't meet the restrictions above should | |
| 986 | +** be sent. | |
| 961 | 987 | */ |
| 962 | 988 | static int send_unclustered(Xfer *pXfer){ |
| 963 | 989 | Stmt q; |
| 964 | 990 | int cnt = 0; |
| 991 | + const char *zExtra; | |
| 992 | + if( db_table_exists("temp","onremote") ){ | |
| 993 | + zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)"; | |
| 994 | + }else{ | |
| 995 | + zExtra = ""; | |
| 996 | + } | |
| 965 | 997 | if( pXfer->resync ){ |
| 966 | 998 | db_prepare(&q, |
| 967 | 999 | "SELECT uuid, rid FROM blob" |
| 968 | 1000 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 969 | 1001 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 970 | - " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" | |
| 1002 | + " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s" | |
| 971 | 1003 | " AND blob.rid<=%d" |
| 972 | 1004 | " ORDER BY blob.rid DESC", |
| 973 | - pXfer->resync | |
| 1005 | + zExtra /*safe-for-%s*/, pXfer->resync | |
| 974 | 1006 | ); |
| 975 | 1007 | }else{ |
| 976 | 1008 | db_prepare(&q, |
| 977 | 1009 | "SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/" |
| 978 | 1010 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 979 | 1011 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 980 | - " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" | |
| 1012 | + " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s", | |
| 1013 | + zExtra /*safe-for-%s*/ | |
| 981 | 1014 | ); |
| 982 | 1015 | } |
| 983 | 1016 | while( db_step(&q)==SQLITE_ROW ){ |
| 984 | 1017 | blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); |
| 985 | 1018 | cnt++; |
| @@ -1191,11 +1224,11 @@ | ||
| 1191 | 1224 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 1192 | 1225 | |
| 1193 | 1226 | /* file HASH SIZE \n CONTENT |
| 1194 | 1227 | ** file HASH DELTASRC SIZE \n CONTENT |
| 1195 | 1228 | ** |
| 1196 | - ** Accept a file from the client. | |
| 1229 | + ** Server accepts a file from the client. | |
| 1197 | 1230 | */ |
| 1198 | 1231 | if( blob_eq(&xfer.aToken[0], "file") ){ |
| 1199 | 1232 | if( !isPush ){ |
| 1200 | 1233 | cgi_reset_content(); |
| 1201 | 1234 | @ error not\sauthorized\sto\swrite |
| @@ -1212,11 +1245,11 @@ | ||
| 1212 | 1245 | }else |
| 1213 | 1246 | |
| 1214 | 1247 | /* cfile HASH USIZE CSIZE \n CONTENT |
| 1215 | 1248 | ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT |
| 1216 | 1249 | ** |
| 1217 | - ** Accept a file from the client. | |
| 1250 | + ** Server accepts a compressed file from the client. | |
| 1218 | 1251 | */ |
| 1219 | 1252 | if( blob_eq(&xfer.aToken[0], "cfile") ){ |
| 1220 | 1253 | if( !isPush ){ |
| 1221 | 1254 | cgi_reset_content(); |
| 1222 | 1255 | @ error not\sauthorized\sto\swrite |
| @@ -1232,11 +1265,11 @@ | ||
| 1232 | 1265 | } |
| 1233 | 1266 | }else |
| 1234 | 1267 | |
| 1235 | 1268 | /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT |
| 1236 | 1269 | ** |
| 1237 | - ** Accept an unversioned file from the client. | |
| 1270 | + ** Server accepts an unversioned file from the client. | |
| 1238 | 1271 | */ |
| 1239 | 1272 | if( blob_eq(&xfer.aToken[0], "uvfile") ){ |
| 1240 | 1273 | xfer_accept_unversioned_file(&xfer, g.perm.WrUnver); |
| 1241 | 1274 | if( blob_size(&xfer.err) ){ |
| 1242 | 1275 | cgi_reset_content(); |
| @@ -1246,11 +1279,11 @@ | ||
| 1246 | 1279 | } |
| 1247 | 1280 | }else |
| 1248 | 1281 | |
| 1249 | 1282 | /* gimme HASH |
| 1250 | 1283 | ** |
| 1251 | - ** Client is requesting a file. Send it. | |
| 1284 | + ** Client is requesting a file from the server. Send it. | |
| 1252 | 1285 | */ |
| 1253 | 1286 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 1254 | 1287 | && xfer.nToken==2 |
| 1255 | 1288 | && blob_is_hname(&xfer.aToken[1]) |
| 1256 | 1289 | ){ |
| @@ -1263,11 +1296,11 @@ | ||
| 1263 | 1296 | } |
| 1264 | 1297 | }else |
| 1265 | 1298 | |
| 1266 | 1299 | /* uvgimme NAME |
| 1267 | 1300 | ** |
| 1268 | - ** Client is requesting an unversioned file. Send it. | |
| 1301 | + ** Client is requesting an unversioned file from the server. Send it. | |
| 1269 | 1302 | */ |
| 1270 | 1303 | if( blob_eq(&xfer.aToken[0], "uvgimme") |
| 1271 | 1304 | && xfer.nToken==2 |
| 1272 | 1305 | && blob_is_filename(&xfer.aToken[1]) |
| 1273 | 1306 | ){ |
| @@ -1275,23 +1308,44 @@ | ||
| 1275 | 1308 | }else |
| 1276 | 1309 | |
| 1277 | 1310 | /* igot HASH ?ISPRIVATE? |
| 1278 | 1311 | ** |
| 1279 | 1312 | ** Client announces that it has a particular file. If the ISPRIVATE |
| 1280 | - ** argument exists and is non-zero, then the file is a private file. | |
| 1313 | + ** argument exists and is "1", then the file is a private file. | |
| 1281 | 1314 | */ |
| 1282 | 1315 | if( xfer.nToken>=2 |
| 1283 | 1316 | && blob_eq(&xfer.aToken[0], "igot") |
| 1284 | 1317 | && blob_is_hname(&xfer.aToken[1]) |
| 1285 | 1318 | ){ |
| 1286 | 1319 | if( isPush ){ |
| 1320 | + int rid = 0; | |
| 1321 | + int isPriv = 0; | |
| 1287 | 1322 | if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){ |
| 1288 | - rid_from_uuid(&xfer.aToken[1], 1, 0); | |
| 1323 | + /* Client says the artifact is public */ | |
| 1324 | + rid = rid_from_uuid(&xfer.aToken[1], 1, 0); | |
| 1289 | 1325 | }else if( g.perm.Private ){ |
| 1290 | - rid_from_uuid(&xfer.aToken[1], 1, 1); | |
| 1326 | + /* Client says the artifact is private and the client has | |
| 1327 | + ** permission to push private content. Create a new phantom | |
| 1328 | + ** artifact that is marked private. */ | |
| 1329 | + rid = rid_from_uuid(&xfer.aToken[1], 1, 1); | |
| 1330 | + isPriv = 1; | |
| 1291 | 1331 | }else{ |
| 1292 | - server_private_xfer_not_authorized(); | |
| 1332 | + /* Client says the artifact is private and the client is unable | |
| 1333 | + ** or unwilling to send us the artifact. If we already hold the | |
| 1334 | + ** artifact here on the server as a phantom, make sure that | |
| 1335 | + ** phantom is marked as private so that we don't keep asking about | |
| 1336 | + ** it in subsequent sync requests. */ | |
| 1337 | + rid = rid_from_uuid(&xfer.aToken[1], 0, 1); | |
| 1338 | + isPriv = 1; | |
| 1339 | + } | |
| 1340 | + if( rid ){ | |
| 1341 | + remote_has(rid); | |
| 1342 | + if( isPriv ){ | |
| 1343 | + content_make_private(rid); | |
| 1344 | + }else{ | |
| 1345 | + content_make_public(rid); | |
| 1346 | + } | |
| 1293 | 1347 | } |
| 1294 | 1348 | } |
| 1295 | 1349 | }else |
| 1296 | 1350 | |
| 1297 | 1351 | |
| @@ -1388,11 +1442,12 @@ | ||
| 1388 | 1442 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 1389 | 1443 | }else |
| 1390 | 1444 | |
| 1391 | 1445 | /* login USER NONCE SIGNATURE |
| 1392 | 1446 | ** |
| 1393 | - ** Check for a valid login. This has to happen before anything else. | |
| 1447 | + ** The client has sent login credentials to the server. | |
| 1448 | + ** Validate the login. This has to happen before anything else. | |
| 1394 | 1449 | ** The client can send multiple logins. Permissions are cumulative. |
| 1395 | 1450 | */ |
| 1396 | 1451 | if( blob_eq(&xfer.aToken[0], "login") |
| 1397 | 1452 | && xfer.nToken==4 |
| 1398 | 1453 | ){ |
| @@ -1410,11 +1465,11 @@ | ||
| 1410 | 1465 | } |
| 1411 | 1466 | }else |
| 1412 | 1467 | |
| 1413 | 1468 | /* reqconfig NAME |
| 1414 | 1469 | ** |
| 1415 | - ** Request a configuration value | |
| 1470 | + ** Client is requesting a configuration value from the server | |
| 1416 | 1471 | */ |
| 1417 | 1472 | if( blob_eq(&xfer.aToken[0], "reqconfig") |
| 1418 | 1473 | && xfer.nToken==2 |
| 1419 | 1474 | ){ |
| 1420 | 1475 | if( g.perm.Read ){ |
| @@ -1429,12 +1484,12 @@ | ||
| 1429 | 1484 | } |
| 1430 | 1485 | }else |
| 1431 | 1486 | |
| 1432 | 1487 | /* config NAME SIZE \n CONTENT |
| 1433 | 1488 | ** |
| 1434 | - ** Receive a configuration value from the client. This is only | |
| 1435 | - ** permitted for high-privilege users. | |
| 1489 | + ** Client has sent a configuration value to the server. | |
| 1490 | + ** This is only permitted for high-privilege users. | |
| 1436 | 1491 | */ |
| 1437 | 1492 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 1438 | 1493 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 1439 | 1494 | const char *zName = blob_str(&xfer.aToken[1]); |
| 1440 | 1495 | Blob content; |
| @@ -1474,11 +1529,11 @@ | ||
| 1474 | 1529 | }else |
| 1475 | 1530 | |
| 1476 | 1531 | |
| 1477 | 1532 | /* private |
| 1478 | 1533 | ** |
| 1479 | - ** This card indicates that the next "file" or "cfile" will contain | |
| 1534 | + ** The client card indicates that the next "file" or "cfile" will contain | |
| 1480 | 1535 | ** private content. |
| 1481 | 1536 | */ |
| 1482 | 1537 | if( blob_eq(&xfer.aToken[0], "private") ){ |
| 1483 | 1538 | if( !g.perm.Private ){ |
| 1484 | 1539 | server_private_xfer_not_authorized(); |
| @@ -1495,10 +1550,12 @@ | ||
| 1495 | 1550 | ** ignored. |
| 1496 | 1551 | */ |
| 1497 | 1552 | if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ |
| 1498 | 1553 | |
| 1499 | 1554 | /* pragma send-private |
| 1555 | + ** | |
| 1556 | + ** The client is requesting private artifacts. | |
| 1500 | 1557 | ** |
| 1501 | 1558 | ** If the user has the "x" privilege (which must be set explicitly - |
| 1502 | 1559 | ** it is not automatic with "a" or "s") then this pragma causes |
| 1503 | 1560 | ** private information to be pulled in addition to public records. |
| 1504 | 1561 | */ |
| @@ -1511,22 +1568,32 @@ | ||
| 1511 | 1568 | } |
| 1512 | 1569 | } |
| 1513 | 1570 | |
| 1514 | 1571 | /* pragma send-catalog |
| 1515 | 1572 | ** |
| 1516 | - ** Send igot cards for all known artifacts. | |
| 1573 | + ** The client wants to see igot cards for all known artifacts. | |
| 1574 | + ** This is used as part of "sync --verily" to help ensure that | |
| 1575 | + ** no artifacts have been missed on prior syncs. | |
| 1517 | 1576 | */ |
| 1518 | 1577 | if( blob_eq(&xfer.aToken[1], "send-catalog") ){ |
| 1519 | 1578 | xfer.resync = 0x7fffffff; |
| 1520 | 1579 | } |
| 1521 | 1580 | |
| 1522 | - /* pragma client-version VERSION | |
| 1581 | + /* pragma client-version VERSION ?DATE? ?TIME? | |
| 1523 | 1582 | ** |
| 1524 | - ** Let the server know what version of Fossil is running on the client. | |
| 1583 | + ** The client announces to the server what version of Fossil it | |
| 1584 | + ** is running. The DATE and TIME are a pure numeric ISO8601 time | |
| 1585 | + ** for the specific check-in of the client. | |
| 1525 | 1586 | */ |
| 1526 | 1587 | if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){ |
| 1527 | - xfer.clientVersion = atoi(blob_str(&xfer.aToken[2])); | |
| 1588 | + xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2])); | |
| 1589 | + if( xfer.nToken>=5 ){ | |
| 1590 | + xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); | |
| 1591 | + xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); | |
| 1592 | + @ pragma server-version %d(RELEASE_VERSION_NUMBER) \ | |
| 1593 | + @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME) | |
| 1594 | + } | |
| 1528 | 1595 | } |
| 1529 | 1596 | |
| 1530 | 1597 | /* pragma uv-hash HASH |
| 1531 | 1598 | ** |
| 1532 | 1599 | ** The client wants to make sure that unversioned files are all synced. |
| @@ -1550,11 +1617,11 @@ | ||
| 1550 | 1617 | |
| 1551 | 1618 | /* pragma ci-lock CHECKIN-HASH CLIENT-ID |
| 1552 | 1619 | ** |
| 1553 | 1620 | ** The client wants to make non-branch commit against the check-in |
| 1554 | 1621 | ** identified by CHECKIN-HASH. The server will remember this and |
| 1555 | - ** subsequent ci-lock request from different clients will generate | |
| 1622 | + ** subsequent ci-lock requests from different clients will generate | |
| 1556 | 1623 | ** a ci-lock-fail pragma in the reply. |
| 1557 | 1624 | */ |
| 1558 | 1625 | if( blob_eq(&xfer.aToken[1], "ci-lock") |
| 1559 | 1626 | && xfer.nToken==4 |
| 1560 | 1627 | && blob_is_hname(&xfer.aToken[2]) |
| @@ -1808,11 +1875,11 @@ | ||
| 1808 | 1875 | memset(&xfer, 0, sizeof(xfer)); |
| 1809 | 1876 | xfer.pIn = &recv; |
| 1810 | 1877 | xfer.pOut = &send; |
| 1811 | 1878 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1812 | 1879 | xfer.maxTime = -1; |
| 1813 | - xfer.clientVersion = RELEASE_VERSION_NUMBER; | |
| 1880 | + xfer.remoteVersion = RELEASE_VERSION_NUMBER; | |
| 1814 | 1881 | if( syncFlags & SYNC_PRIVATE ){ |
| 1815 | 1882 | g.perm.Private = 1; |
| 1816 | 1883 | xfer.syncPrivate = 1; |
| 1817 | 1884 | } |
| 1818 | 1885 | |
| @@ -1856,13 +1923,16 @@ | ||
| 1856 | 1923 | " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;" |
| 1857 | 1924 | ); |
| 1858 | 1925 | } |
| 1859 | 1926 | |
| 1860 | 1927 | /* |
| 1861 | - ** Always begin with a clone, pull, or push message | |
| 1928 | + ** The request from the client always begin with a clone, pull, | |
| 1929 | + ** or push message. | |
| 1862 | 1930 | */ |
| 1863 | - blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER); | |
| 1931 | + blob_appendf(&send, "pragma client-version %d %d %d\n", | |
| 1932 | + RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE, | |
| 1933 | + MANIFEST_NUMERIC_TIME); | |
| 1864 | 1934 | if( syncFlags & SYNC_CLONE ){ |
| 1865 | 1935 | blob_appendf(&send, "clone 3 %d\n", cloneSeqno); |
| 1866 | 1936 | syncFlags &= ~(SYNC_PUSH|SYNC_PULL); |
| 1867 | 1937 | nCardSent++; |
| 1868 | 1938 | /* TBD: Request all transferable configuration values */ |
| @@ -1898,20 +1968,19 @@ | ||
| 1898 | 1968 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 1899 | 1969 | ); |
| 1900 | 1970 | manifest_crosslink_begin(); |
| 1901 | 1971 | |
| 1902 | 1972 | |
| 1903 | - /* Send back the most recently received cookie. Let the server | |
| 1904 | - ** figure out if this is a cookie that it cares about. | |
| 1973 | + /* Client sends the most recently received cookie back to the server. | |
| 1974 | + ** Let the server figure out if this is a cookie that it cares about. | |
| 1905 | 1975 | */ |
| 1906 | 1976 | zCookie = db_get("cookie", 0); |
| 1907 | 1977 | if( zCookie ){ |
| 1908 | 1978 | blob_appendf(&send, "cookie %s\n", zCookie); |
| 1909 | 1979 | } |
| 1910 | 1980 | |
| 1911 | - /* Generate gimme cards for phantoms and leaf cards | |
| 1912 | - ** for all leaves. | |
| 1981 | + /* Client sends gimme cards for phantoms | |
| 1913 | 1982 | */ |
| 1914 | 1983 | if( (syncFlags & SYNC_PULL)!=0 |
| 1915 | 1984 | || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) |
| 1916 | 1985 | ){ |
| 1917 | 1986 | request_phantoms(&xfer, mxPhantomReq); |
| @@ -1920,11 +1989,11 @@ | ||
| 1920 | 1989 | send_unsent(&xfer); |
| 1921 | 1990 | nCardSent += send_unclustered(&xfer); |
| 1922 | 1991 | if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); |
| 1923 | 1992 | } |
| 1924 | 1993 | |
| 1925 | - /* Send configuration parameter requests. On a clone, delay sending | |
| 1994 | + /* Client sends configuration parameter requests. On a clone, delay sending | |
| 1926 | 1995 | ** this until the second cycle since the login card might fail on |
| 1927 | 1996 | ** the first cycle. |
| 1928 | 1997 | */ |
| 1929 | 1998 | if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ |
| 1930 | 1999 | const char *zName; |
| @@ -1937,13 +2006,13 @@ | ||
| 1937 | 2006 | } |
| 1938 | 2007 | origConfigRcvMask = configRcvMask; |
| 1939 | 2008 | configRcvMask = 0; |
| 1940 | 2009 | } |
| 1941 | 2010 | |
| 1942 | - /* Send a request to sync unversioned files. On a clone, delay sending | |
| 1943 | - ** this until the second cycle since the login card might fail on | |
| 1944 | - ** the first cycle. | |
| 2011 | + /* Client sends a request to sync unversioned files. | |
| 2012 | + ** On a clone, delay sending this until the second cycle since | |
| 2013 | + ** the login card might fail on the first cycle. | |
| 1945 | 2014 | */ |
| 1946 | 2015 | if( (syncFlags & SYNC_UNVERSIONED)!=0 |
| 1947 | 2016 | && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) |
| 1948 | 2017 | && !uvHashSent |
| 1949 | 2018 | ){ |
| @@ -1950,11 +2019,12 @@ | ||
| 1950 | 2019 | blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0)); |
| 1951 | 2020 | nCardSent++; |
| 1952 | 2021 | uvHashSent = 1; |
| 1953 | 2022 | } |
| 1954 | 2023 | |
| 1955 | - /* Send configuration parameters being pushed */ | |
| 2024 | + /* On a "fossil config push", the client send configuration parameters | |
| 2025 | + ** being pushed up to the server */ | |
| 1956 | 2026 | if( configSendMask ){ |
| 1957 | 2027 | if( zOpType==0 ) zOpType = "Push"; |
| 1958 | 2028 | nCardSent += configure_send_group(xfer.pOut, configSendMask, 0); |
| 1959 | 2029 | configSendMask = 0; |
| 1960 | 2030 | } |
| @@ -2010,11 +2080,11 @@ | ||
| 2010 | 2080 | zCkinLock = 0; |
| 2011 | 2081 | }else if( zClientId ){ |
| 2012 | 2082 | blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); |
| 2013 | 2083 | } |
| 2014 | 2084 | |
| 2015 | - /* Append randomness to the end of the message. This makes all | |
| 2085 | + /* Append randomness to the end of the uplink message. This makes all | |
| 2016 | 2086 | ** messages unique so that that the login-card nonce will always |
| 2017 | 2087 | ** be unique. |
| 2018 | 2088 | */ |
| 2019 | 2089 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 2020 | 2090 | blob_appendf(&send, "# %s\n", zRandomness); |
| @@ -2052,14 +2122,17 @@ | ||
| 2052 | 2122 | nCardRcvd = 0; |
| 2053 | 2123 | xfer.nFileSent = 0; |
| 2054 | 2124 | xfer.nDeltaSent = 0; |
| 2055 | 2125 | xfer.nGimmeSent = 0; |
| 2056 | 2126 | xfer.nIGotSent = 0; |
| 2127 | + xfer.nPrivIGot = 0; | |
| 2057 | 2128 | |
| 2058 | 2129 | lastPctDone = -1; |
| 2059 | 2130 | blob_reset(&send); |
| 2060 | - blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER); | |
| 2131 | + blob_appendf(&send, "pragma client-version %d %d %d\n", | |
| 2132 | + RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE, | |
| 2133 | + MANIFEST_NUMERIC_TIME); | |
| 2061 | 2134 | rArrivalTime = db_double(0.0, "SELECT julianday('now')"); |
| 2062 | 2135 | |
| 2063 | 2136 | /* Send the send-private pragma if we are trying to sync private data */ |
| 2064 | 2137 | if( syncFlags & SYNC_PRIVATE ){ |
| 2065 | 2138 | blob_append(&send, "pragma send-private\n", -1); |
| @@ -2112,30 +2185,30 @@ | ||
| 2112 | 2185 | } |
| 2113 | 2186 | |
| 2114 | 2187 | /* file HASH SIZE \n CONTENT |
| 2115 | 2188 | ** file HASH DELTASRC SIZE \n CONTENT |
| 2116 | 2189 | ** |
| 2117 | - ** Receive a file transmitted from the server. | |
| 2190 | + ** Client receives a file transmitted from the server. | |
| 2118 | 2191 | */ |
| 2119 | 2192 | if( blob_eq(&xfer.aToken[0],"file") ){ |
| 2120 | 2193 | xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0); |
| 2121 | 2194 | nArtifactRcvd++; |
| 2122 | 2195 | }else |
| 2123 | 2196 | |
| 2124 | 2197 | /* cfile HASH USIZE CSIZE \n CONTENT |
| 2125 | 2198 | ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT |
| 2126 | 2199 | ** |
| 2127 | - ** Receive a compressed file transmitted from the server. | |
| 2200 | + ** Client receives a compressed file transmitted from the server. | |
| 2128 | 2201 | */ |
| 2129 | 2202 | if( blob_eq(&xfer.aToken[0],"cfile") ){ |
| 2130 | 2203 | xfer_accept_compressed_file(&xfer, 0, 0); |
| 2131 | 2204 | nArtifactRcvd++; |
| 2132 | 2205 | }else |
| 2133 | 2206 | |
| 2134 | 2207 | /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT |
| 2135 | 2208 | ** |
| 2136 | - ** Accept an unversioned file from the server. | |
| 2209 | + ** Client accepts an unversioned file from the server. | |
| 2137 | 2210 | */ |
| 2138 | 2211 | if( blob_eq(&xfer.aToken[0], "uvfile") ){ |
| 2139 | 2212 | xfer_accept_unversioned_file(&xfer, 1); |
| 2140 | 2213 | nArtifactRcvd++; |
| 2141 | 2214 | nUvFileRcvd++; |
| @@ -2145,13 +2218,14 @@ | ||
| 2145 | 2218 | } |
| 2146 | 2219 | }else |
| 2147 | 2220 | |
| 2148 | 2221 | /* gimme HASH |
| 2149 | 2222 | ** |
| 2150 | - ** Server is requesting a file. If the file is a manifest, assume | |
| 2151 | - ** that the server will also want to know all of the content files | |
| 2152 | - ** associated with the manifest and send those too. | |
| 2223 | + ** Client receives an artifact request from the server. | |
| 2224 | + ** If the file is a manifest, assume that the server will also want | |
| 2225 | + ** to know all of the content artifacts associated with the manifest | |
| 2226 | + ** and send those too. | |
| 2153 | 2227 | */ |
| 2154 | 2228 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 2155 | 2229 | && xfer.nToken==2 |
| 2156 | 2230 | && blob_is_hname(&xfer.aToken[1]) |
| 2157 | 2231 | ){ |
| @@ -2179,11 +2253,15 @@ | ||
| 2179 | 2253 | ){ |
| 2180 | 2254 | int rid; |
| 2181 | 2255 | int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1"); |
| 2182 | 2256 | rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 2183 | 2257 | if( rid>0 ){ |
| 2184 | - if( !isPriv ) content_make_public(rid); | |
| 2258 | + if( isPriv ){ | |
| 2259 | + content_make_private(rid); | |
| 2260 | + }else{ | |
| 2261 | + content_make_public(rid); | |
| 2262 | + } | |
| 2185 | 2263 | }else if( isPriv && !g.perm.Private ){ |
| 2186 | 2264 | /* ignore private files */ |
| 2187 | 2265 | }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){ |
| 2188 | 2266 | rid = content_new(blob_str(&xfer.aToken[1]), isPriv); |
| 2189 | 2267 | if( rid ) newPhantom = 1; |
| @@ -2264,11 +2342,11 @@ | ||
| 2264 | 2342 | }else |
| 2265 | 2343 | |
| 2266 | 2344 | /* push SERVERCODE PRODUCTCODE |
| 2267 | 2345 | ** |
| 2268 | 2346 | ** Should only happen in response to a clone. This message tells |
| 2269 | - ** the client what product to use for the new database. | |
| 2347 | + ** the client what product code to use for the new database. | |
| 2270 | 2348 | */ |
| 2271 | 2349 | if( blob_eq(&xfer.aToken[0],"push") |
| 2272 | 2350 | && xfer.nToken==3 |
| 2273 | 2351 | && (syncFlags & SYNC_CLONE)!=0 |
| 2274 | 2352 | && blob_is_hname(&xfer.aToken[2]) |
| @@ -2281,11 +2359,11 @@ | ||
| 2281 | 2359 | nCardSent++; |
| 2282 | 2360 | }else |
| 2283 | 2361 | |
| 2284 | 2362 | /* config NAME SIZE \n CONTENT |
| 2285 | 2363 | ** |
| 2286 | - ** Receive a configuration value from the server. | |
| 2364 | + ** Client receive a configuration value from the server. | |
| 2287 | 2365 | ** |
| 2288 | 2366 | ** The received configuration setting is silently ignored if it was |
| 2289 | 2367 | ** not requested by a prior "reqconfig" sent from client to server. |
| 2290 | 2368 | */ |
| 2291 | 2369 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| @@ -2303,11 +2381,11 @@ | ||
| 2303 | 2381 | }else |
| 2304 | 2382 | |
| 2305 | 2383 | |
| 2306 | 2384 | /* cookie TEXT |
| 2307 | 2385 | ** |
| 2308 | - ** The server might include a cookie in its reply. The client | |
| 2386 | + ** The client reserves a cookie from the server. The client | |
| 2309 | 2387 | ** should remember this cookie and send it back to the server |
| 2310 | 2388 | ** in its next query. |
| 2311 | 2389 | ** |
| 2312 | 2390 | ** Each cookie received overwrites the prior cookie from the |
| 2313 | 2391 | ** same server. |
| @@ -2317,12 +2395,12 @@ | ||
| 2317 | 2395 | }else |
| 2318 | 2396 | |
| 2319 | 2397 | |
| 2320 | 2398 | /* private |
| 2321 | 2399 | ** |
| 2322 | - ** This card indicates that the next "file" or "cfile" will contain | |
| 2323 | - ** private content. | |
| 2400 | + ** The server tells the client that the next "file" or "cfile" will | |
| 2401 | + ** contain private content. | |
| 2324 | 2402 | */ |
| 2325 | 2403 | if( blob_eq(&xfer.aToken[0], "private") ){ |
| 2326 | 2404 | xfer.nextIsPrivate = 1; |
| 2327 | 2405 | }else |
| 2328 | 2406 | |
| @@ -2338,11 +2416,12 @@ | ||
| 2338 | 2416 | blob_is_int(&xfer.aToken[1], &cloneSeqno); |
| 2339 | 2417 | }else |
| 2340 | 2418 | |
| 2341 | 2419 | /* message MESSAGE |
| 2342 | 2420 | ** |
| 2343 | - ** Print a message. Similar to "error" but does not stop processing. | |
| 2421 | + ** A message is received from the server. Print it. | |
| 2422 | + ** Similar to "error" but does not stop processing. | |
| 2344 | 2423 | ** |
| 2345 | 2424 | ** If the "login failed" message is seen, clear the sync password prior |
| 2346 | 2425 | ** to the next cycle. |
| 2347 | 2426 | */ |
| 2348 | 2427 | if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){ |
| @@ -2364,11 +2443,27 @@ | ||
| 2364 | 2443 | ** The server can send pragmas to try to convey meta-information to |
| 2365 | 2444 | ** the client. These are informational only. Unknown pragmas are |
| 2366 | 2445 | ** silently ignored. |
| 2367 | 2446 | */ |
| 2368 | 2447 | if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ |
| 2369 | - /* If the server is unwill to accept new unversioned content (because | |
| 2448 | + /* pragma server-version VERSION ?DATE? ?TIME? | |
| 2449 | + ** | |
| 2450 | + ** The servger announces to the server what version of Fossil it | |
| 2451 | + ** is running. The DATE and TIME are a pure numeric ISO8601 time | |
| 2452 | + ** for the specific check-in of the client. | |
| 2453 | + */ | |
| 2454 | + if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){ | |
| 2455 | + xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2])); | |
| 2456 | + if( xfer.nToken>=5 ){ | |
| 2457 | + xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); | |
| 2458 | + xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); | |
| 2459 | + } | |
| 2460 | + } | |
| 2461 | + | |
| 2462 | + /* pragma uv-pull-only | |
| 2463 | + ** | |
| 2464 | + ** If the server is unwill to accept new unversioned content (because | |
| 2370 | 2465 | ** this client lacks the necessary permissions) then it sends a |
| 2371 | 2466 | ** "uv-pull-only" pragma so that the client will know not to waste |
| 2372 | 2467 | ** bandwidth trying to upload unversioned content. If the server |
| 2373 | 2468 | ** does accept new unversioned content, it sends "uv-push-ok". |
| 2374 | 2469 | */ |
| @@ -2407,11 +2502,12 @@ | ||
| 2407 | 2502 | } |
| 2408 | 2503 | }else |
| 2409 | 2504 | |
| 2410 | 2505 | /* error MESSAGE |
| 2411 | 2506 | ** |
| 2412 | - ** Report an error and abandon the sync session. | |
| 2507 | + ** The server is reporting an error. The client will abandon | |
| 2508 | + ** the sync session. | |
| 2413 | 2509 | ** |
| 2414 | 2510 | ** Except, when cloning we will sometimes get an error on the |
| 2415 | 2511 | ** first message exchange because the project-code is unknown |
| 2416 | 2512 | ** and so the login card on the request was invalid. The project-code |
| 2417 | 2513 | ** is returned in the reply before the error card, so second and |
| @@ -2491,10 +2587,11 @@ | ||
| 2491 | 2587 | ** another round |
| 2492 | 2588 | */ |
| 2493 | 2589 | if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){ |
| 2494 | 2590 | go = 1; |
| 2495 | 2591 | } |
| 2592 | + if( xfer.nPrivIGot>0 && nCycle==1 ) go = 1; | |
| 2496 | 2593 | |
| 2497 | 2594 | /* If this is a clone, the go at least two rounds */ |
| 2498 | 2595 | if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1; |
| 2499 | 2596 | |
| 2500 | 2597 | /* Stop the cycle if the server sends a "clone_seqno 0" card and |
| 2501 | 2598 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -39,10 +39,11 @@ | |
| 39 | Blob line; /* The current line of input */ |
| 40 | Blob aToken[6]; /* Tokenized version of line */ |
| 41 | Blob err; /* Error message text */ |
| 42 | int nToken; /* Number of tokens in line */ |
| 43 | int nIGotSent; /* Number of "igot" cards sent */ |
| 44 | int nGimmeSent; /* Number of gimme cards sent */ |
| 45 | int nFileSent; /* Number of files sent */ |
| 46 | int nDeltaSent; /* Number of deltas sent */ |
| 47 | int nFileRcvd; /* Number of files received */ |
| 48 | int nDeltaRcvd; /* Number of deltas received */ |
| @@ -49,11 +50,13 @@ | |
| 49 | int nDanglingFile; /* Number of dangling deltas received */ |
| 50 | int mxSend; /* Stop sending "file" when pOut reaches this size */ |
| 51 | int resync; /* Send igot cards for all holdings */ |
| 52 | u8 syncPrivate; /* True to enable syncing private content */ |
| 53 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 54 | u32 clientVersion; /* Version of the client software */ |
| 55 | time_t maxTime; /* Time when this transfer should be finished */ |
| 56 | }; |
| 57 | |
| 58 | |
| 59 | /* |
| @@ -524,20 +527,32 @@ | |
| 524 | static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){ |
| 525 | Blob content, uuid; |
| 526 | int size = 0; |
| 527 | int isPriv = content_is_private(rid); |
| 528 | |
| 529 | if( pXfer->syncPrivate==0 && isPriv ) return; |
| 530 | if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){ |
| 531 | return; |
| 532 | } |
| 533 | blob_zero(&uuid); |
| 534 | db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid); |
| 535 | if( blob_size(&uuid)==0 ){ |
| 536 | return; |
| 537 | } |
| 538 | if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->clientVersion<20000 ){ |
| 539 | xfer_cannot_send_sha3_error(pXfer); |
| 540 | return; |
| 541 | } |
| 542 | if( pUuid ){ |
| 543 | if( blob_compare(pUuid, &uuid)!=0 ){ |
| @@ -626,11 +641,11 @@ | |
| 626 | szC = db_column_bytes(&q1, 2); |
| 627 | zContent = db_column_raw(&q1, 2); |
| 628 | srcIsPrivate = db_column_int(&q1, 3); |
| 629 | zDelta = db_column_text(&q1, 4); |
| 630 | if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); |
| 631 | if( pXfer->clientVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){ |
| 632 | xfer_cannot_send_sha3_error(pXfer); |
| 633 | db_reset(&q1); |
| 634 | return; |
| 635 | } |
| 636 | blob_appendf(pXfer->pOut, "cfile %s ", zUuid); |
| @@ -690,11 +705,11 @@ | |
| 690 | ); |
| 691 | } |
| 692 | if( db_step(&q1)==SQLITE_ROW ){ |
| 693 | sqlite3_int64 mtime = db_column_int64(&q1, 0); |
| 694 | const char *zHash = db_column_text(&q1, 1); |
| 695 | if( pXfer->clientVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){ |
| 696 | xfer_cannot_send_sha3_error(pXfer); |
| 697 | db_reset(&q1); |
| 698 | return; |
| 699 | } |
| 700 | if( blob_size(pXfer->pOut)>=pXfer->mxSend ){ |
| @@ -956,30 +971,48 @@ | |
| 956 | } |
| 957 | |
| 958 | /* |
| 959 | ** Send an igot message for every entry in unclustered table. |
| 960 | ** Return the number of cards sent. |
| 961 | */ |
| 962 | static int send_unclustered(Xfer *pXfer){ |
| 963 | Stmt q; |
| 964 | int cnt = 0; |
| 965 | if( pXfer->resync ){ |
| 966 | db_prepare(&q, |
| 967 | "SELECT uuid, rid FROM blob" |
| 968 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 969 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 970 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" |
| 971 | " AND blob.rid<=%d" |
| 972 | " ORDER BY blob.rid DESC", |
| 973 | pXfer->resync |
| 974 | ); |
| 975 | }else{ |
| 976 | db_prepare(&q, |
| 977 | "SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/" |
| 978 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 979 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 980 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" |
| 981 | ); |
| 982 | } |
| 983 | while( db_step(&q)==SQLITE_ROW ){ |
| 984 | blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); |
| 985 | cnt++; |
| @@ -1191,11 +1224,11 @@ | |
| 1191 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 1192 | |
| 1193 | /* file HASH SIZE \n CONTENT |
| 1194 | ** file HASH DELTASRC SIZE \n CONTENT |
| 1195 | ** |
| 1196 | ** Accept a file from the client. |
| 1197 | */ |
| 1198 | if( blob_eq(&xfer.aToken[0], "file") ){ |
| 1199 | if( !isPush ){ |
| 1200 | cgi_reset_content(); |
| 1201 | @ error not\sauthorized\sto\swrite |
| @@ -1212,11 +1245,11 @@ | |
| 1212 | }else |
| 1213 | |
| 1214 | /* cfile HASH USIZE CSIZE \n CONTENT |
| 1215 | ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT |
| 1216 | ** |
| 1217 | ** Accept a file from the client. |
| 1218 | */ |
| 1219 | if( blob_eq(&xfer.aToken[0], "cfile") ){ |
| 1220 | if( !isPush ){ |
| 1221 | cgi_reset_content(); |
| 1222 | @ error not\sauthorized\sto\swrite |
| @@ -1232,11 +1265,11 @@ | |
| 1232 | } |
| 1233 | }else |
| 1234 | |
| 1235 | /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT |
| 1236 | ** |
| 1237 | ** Accept an unversioned file from the client. |
| 1238 | */ |
| 1239 | if( blob_eq(&xfer.aToken[0], "uvfile") ){ |
| 1240 | xfer_accept_unversioned_file(&xfer, g.perm.WrUnver); |
| 1241 | if( blob_size(&xfer.err) ){ |
| 1242 | cgi_reset_content(); |
| @@ -1246,11 +1279,11 @@ | |
| 1246 | } |
| 1247 | }else |
| 1248 | |
| 1249 | /* gimme HASH |
| 1250 | ** |
| 1251 | ** Client is requesting a file. Send it. |
| 1252 | */ |
| 1253 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 1254 | && xfer.nToken==2 |
| 1255 | && blob_is_hname(&xfer.aToken[1]) |
| 1256 | ){ |
| @@ -1263,11 +1296,11 @@ | |
| 1263 | } |
| 1264 | }else |
| 1265 | |
| 1266 | /* uvgimme NAME |
| 1267 | ** |
| 1268 | ** Client is requesting an unversioned file. Send it. |
| 1269 | */ |
| 1270 | if( blob_eq(&xfer.aToken[0], "uvgimme") |
| 1271 | && xfer.nToken==2 |
| 1272 | && blob_is_filename(&xfer.aToken[1]) |
| 1273 | ){ |
| @@ -1275,23 +1308,44 @@ | |
| 1275 | }else |
| 1276 | |
| 1277 | /* igot HASH ?ISPRIVATE? |
| 1278 | ** |
| 1279 | ** Client announces that it has a particular file. If the ISPRIVATE |
| 1280 | ** argument exists and is non-zero, then the file is a private file. |
| 1281 | */ |
| 1282 | if( xfer.nToken>=2 |
| 1283 | && blob_eq(&xfer.aToken[0], "igot") |
| 1284 | && blob_is_hname(&xfer.aToken[1]) |
| 1285 | ){ |
| 1286 | if( isPush ){ |
| 1287 | if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){ |
| 1288 | rid_from_uuid(&xfer.aToken[1], 1, 0); |
| 1289 | }else if( g.perm.Private ){ |
| 1290 | rid_from_uuid(&xfer.aToken[1], 1, 1); |
| 1291 | }else{ |
| 1292 | server_private_xfer_not_authorized(); |
| 1293 | } |
| 1294 | } |
| 1295 | }else |
| 1296 | |
| 1297 | |
| @@ -1388,11 +1442,12 @@ | |
| 1388 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 1389 | }else |
| 1390 | |
| 1391 | /* login USER NONCE SIGNATURE |
| 1392 | ** |
| 1393 | ** Check for a valid login. This has to happen before anything else. |
| 1394 | ** The client can send multiple logins. Permissions are cumulative. |
| 1395 | */ |
| 1396 | if( blob_eq(&xfer.aToken[0], "login") |
| 1397 | && xfer.nToken==4 |
| 1398 | ){ |
| @@ -1410,11 +1465,11 @@ | |
| 1410 | } |
| 1411 | }else |
| 1412 | |
| 1413 | /* reqconfig NAME |
| 1414 | ** |
| 1415 | ** Request a configuration value |
| 1416 | */ |
| 1417 | if( blob_eq(&xfer.aToken[0], "reqconfig") |
| 1418 | && xfer.nToken==2 |
| 1419 | ){ |
| 1420 | if( g.perm.Read ){ |
| @@ -1429,12 +1484,12 @@ | |
| 1429 | } |
| 1430 | }else |
| 1431 | |
| 1432 | /* config NAME SIZE \n CONTENT |
| 1433 | ** |
| 1434 | ** Receive a configuration value from the client. This is only |
| 1435 | ** permitted for high-privilege users. |
| 1436 | */ |
| 1437 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 1438 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 1439 | const char *zName = blob_str(&xfer.aToken[1]); |
| 1440 | Blob content; |
| @@ -1474,11 +1529,11 @@ | |
| 1474 | }else |
| 1475 | |
| 1476 | |
| 1477 | /* private |
| 1478 | ** |
| 1479 | ** This card indicates that the next "file" or "cfile" will contain |
| 1480 | ** private content. |
| 1481 | */ |
| 1482 | if( blob_eq(&xfer.aToken[0], "private") ){ |
| 1483 | if( !g.perm.Private ){ |
| 1484 | server_private_xfer_not_authorized(); |
| @@ -1495,10 +1550,12 @@ | |
| 1495 | ** ignored. |
| 1496 | */ |
| 1497 | if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ |
| 1498 | |
| 1499 | /* pragma send-private |
| 1500 | ** |
| 1501 | ** If the user has the "x" privilege (which must be set explicitly - |
| 1502 | ** it is not automatic with "a" or "s") then this pragma causes |
| 1503 | ** private information to be pulled in addition to public records. |
| 1504 | */ |
| @@ -1511,22 +1568,32 @@ | |
| 1511 | } |
| 1512 | } |
| 1513 | |
| 1514 | /* pragma send-catalog |
| 1515 | ** |
| 1516 | ** Send igot cards for all known artifacts. |
| 1517 | */ |
| 1518 | if( blob_eq(&xfer.aToken[1], "send-catalog") ){ |
| 1519 | xfer.resync = 0x7fffffff; |
| 1520 | } |
| 1521 | |
| 1522 | /* pragma client-version VERSION |
| 1523 | ** |
| 1524 | ** Let the server know what version of Fossil is running on the client. |
| 1525 | */ |
| 1526 | if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){ |
| 1527 | xfer.clientVersion = atoi(blob_str(&xfer.aToken[2])); |
| 1528 | } |
| 1529 | |
| 1530 | /* pragma uv-hash HASH |
| 1531 | ** |
| 1532 | ** The client wants to make sure that unversioned files are all synced. |
| @@ -1550,11 +1617,11 @@ | |
| 1550 | |
| 1551 | /* pragma ci-lock CHECKIN-HASH CLIENT-ID |
| 1552 | ** |
| 1553 | ** The client wants to make non-branch commit against the check-in |
| 1554 | ** identified by CHECKIN-HASH. The server will remember this and |
| 1555 | ** subsequent ci-lock request from different clients will generate |
| 1556 | ** a ci-lock-fail pragma in the reply. |
| 1557 | */ |
| 1558 | if( blob_eq(&xfer.aToken[1], "ci-lock") |
| 1559 | && xfer.nToken==4 |
| 1560 | && blob_is_hname(&xfer.aToken[2]) |
| @@ -1808,11 +1875,11 @@ | |
| 1808 | memset(&xfer, 0, sizeof(xfer)); |
| 1809 | xfer.pIn = &recv; |
| 1810 | xfer.pOut = &send; |
| 1811 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1812 | xfer.maxTime = -1; |
| 1813 | xfer.clientVersion = RELEASE_VERSION_NUMBER; |
| 1814 | if( syncFlags & SYNC_PRIVATE ){ |
| 1815 | g.perm.Private = 1; |
| 1816 | xfer.syncPrivate = 1; |
| 1817 | } |
| 1818 | |
| @@ -1856,13 +1923,16 @@ | |
| 1856 | " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;" |
| 1857 | ); |
| 1858 | } |
| 1859 | |
| 1860 | /* |
| 1861 | ** Always begin with a clone, pull, or push message |
| 1862 | */ |
| 1863 | blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER); |
| 1864 | if( syncFlags & SYNC_CLONE ){ |
| 1865 | blob_appendf(&send, "clone 3 %d\n", cloneSeqno); |
| 1866 | syncFlags &= ~(SYNC_PUSH|SYNC_PULL); |
| 1867 | nCardSent++; |
| 1868 | /* TBD: Request all transferable configuration values */ |
| @@ -1898,20 +1968,19 @@ | |
| 1898 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 1899 | ); |
| 1900 | manifest_crosslink_begin(); |
| 1901 | |
| 1902 | |
| 1903 | /* Send back the most recently received cookie. Let the server |
| 1904 | ** figure out if this is a cookie that it cares about. |
| 1905 | */ |
| 1906 | zCookie = db_get("cookie", 0); |
| 1907 | if( zCookie ){ |
| 1908 | blob_appendf(&send, "cookie %s\n", zCookie); |
| 1909 | } |
| 1910 | |
| 1911 | /* Generate gimme cards for phantoms and leaf cards |
| 1912 | ** for all leaves. |
| 1913 | */ |
| 1914 | if( (syncFlags & SYNC_PULL)!=0 |
| 1915 | || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) |
| 1916 | ){ |
| 1917 | request_phantoms(&xfer, mxPhantomReq); |
| @@ -1920,11 +1989,11 @@ | |
| 1920 | send_unsent(&xfer); |
| 1921 | nCardSent += send_unclustered(&xfer); |
| 1922 | if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); |
| 1923 | } |
| 1924 | |
| 1925 | /* Send configuration parameter requests. On a clone, delay sending |
| 1926 | ** this until the second cycle since the login card might fail on |
| 1927 | ** the first cycle. |
| 1928 | */ |
| 1929 | if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ |
| 1930 | const char *zName; |
| @@ -1937,13 +2006,13 @@ | |
| 1937 | } |
| 1938 | origConfigRcvMask = configRcvMask; |
| 1939 | configRcvMask = 0; |
| 1940 | } |
| 1941 | |
| 1942 | /* Send a request to sync unversioned files. On a clone, delay sending |
| 1943 | ** this until the second cycle since the login card might fail on |
| 1944 | ** the first cycle. |
| 1945 | */ |
| 1946 | if( (syncFlags & SYNC_UNVERSIONED)!=0 |
| 1947 | && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) |
| 1948 | && !uvHashSent |
| 1949 | ){ |
| @@ -1950,11 +2019,12 @@ | |
| 1950 | blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0)); |
| 1951 | nCardSent++; |
| 1952 | uvHashSent = 1; |
| 1953 | } |
| 1954 | |
| 1955 | /* Send configuration parameters being pushed */ |
| 1956 | if( configSendMask ){ |
| 1957 | if( zOpType==0 ) zOpType = "Push"; |
| 1958 | nCardSent += configure_send_group(xfer.pOut, configSendMask, 0); |
| 1959 | configSendMask = 0; |
| 1960 | } |
| @@ -2010,11 +2080,11 @@ | |
| 2010 | zCkinLock = 0; |
| 2011 | }else if( zClientId ){ |
| 2012 | blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); |
| 2013 | } |
| 2014 | |
| 2015 | /* Append randomness to the end of the message. This makes all |
| 2016 | ** messages unique so that that the login-card nonce will always |
| 2017 | ** be unique. |
| 2018 | */ |
| 2019 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 2020 | blob_appendf(&send, "# %s\n", zRandomness); |
| @@ -2052,14 +2122,17 @@ | |
| 2052 | nCardRcvd = 0; |
| 2053 | xfer.nFileSent = 0; |
| 2054 | xfer.nDeltaSent = 0; |
| 2055 | xfer.nGimmeSent = 0; |
| 2056 | xfer.nIGotSent = 0; |
| 2057 | |
| 2058 | lastPctDone = -1; |
| 2059 | blob_reset(&send); |
| 2060 | blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER); |
| 2061 | rArrivalTime = db_double(0.0, "SELECT julianday('now')"); |
| 2062 | |
| 2063 | /* Send the send-private pragma if we are trying to sync private data */ |
| 2064 | if( syncFlags & SYNC_PRIVATE ){ |
| 2065 | blob_append(&send, "pragma send-private\n", -1); |
| @@ -2112,30 +2185,30 @@ | |
| 2112 | } |
| 2113 | |
| 2114 | /* file HASH SIZE \n CONTENT |
| 2115 | ** file HASH DELTASRC SIZE \n CONTENT |
| 2116 | ** |
| 2117 | ** Receive a file transmitted from the server. |
| 2118 | */ |
| 2119 | if( blob_eq(&xfer.aToken[0],"file") ){ |
| 2120 | xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0); |
| 2121 | nArtifactRcvd++; |
| 2122 | }else |
| 2123 | |
| 2124 | /* cfile HASH USIZE CSIZE \n CONTENT |
| 2125 | ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT |
| 2126 | ** |
| 2127 | ** Receive a compressed file transmitted from the server. |
| 2128 | */ |
| 2129 | if( blob_eq(&xfer.aToken[0],"cfile") ){ |
| 2130 | xfer_accept_compressed_file(&xfer, 0, 0); |
| 2131 | nArtifactRcvd++; |
| 2132 | }else |
| 2133 | |
| 2134 | /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT |
| 2135 | ** |
| 2136 | ** Accept an unversioned file from the server. |
| 2137 | */ |
| 2138 | if( blob_eq(&xfer.aToken[0], "uvfile") ){ |
| 2139 | xfer_accept_unversioned_file(&xfer, 1); |
| 2140 | nArtifactRcvd++; |
| 2141 | nUvFileRcvd++; |
| @@ -2145,13 +2218,14 @@ | |
| 2145 | } |
| 2146 | }else |
| 2147 | |
| 2148 | /* gimme HASH |
| 2149 | ** |
| 2150 | ** Server is requesting a file. If the file is a manifest, assume |
| 2151 | ** that the server will also want to know all of the content files |
| 2152 | ** associated with the manifest and send those too. |
| 2153 | */ |
| 2154 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 2155 | && xfer.nToken==2 |
| 2156 | && blob_is_hname(&xfer.aToken[1]) |
| 2157 | ){ |
| @@ -2179,11 +2253,15 @@ | |
| 2179 | ){ |
| 2180 | int rid; |
| 2181 | int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1"); |
| 2182 | rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 2183 | if( rid>0 ){ |
| 2184 | if( !isPriv ) content_make_public(rid); |
| 2185 | }else if( isPriv && !g.perm.Private ){ |
| 2186 | /* ignore private files */ |
| 2187 | }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){ |
| 2188 | rid = content_new(blob_str(&xfer.aToken[1]), isPriv); |
| 2189 | if( rid ) newPhantom = 1; |
| @@ -2264,11 +2342,11 @@ | |
| 2264 | }else |
| 2265 | |
| 2266 | /* push SERVERCODE PRODUCTCODE |
| 2267 | ** |
| 2268 | ** Should only happen in response to a clone. This message tells |
| 2269 | ** the client what product to use for the new database. |
| 2270 | */ |
| 2271 | if( blob_eq(&xfer.aToken[0],"push") |
| 2272 | && xfer.nToken==3 |
| 2273 | && (syncFlags & SYNC_CLONE)!=0 |
| 2274 | && blob_is_hname(&xfer.aToken[2]) |
| @@ -2281,11 +2359,11 @@ | |
| 2281 | nCardSent++; |
| 2282 | }else |
| 2283 | |
| 2284 | /* config NAME SIZE \n CONTENT |
| 2285 | ** |
| 2286 | ** Receive a configuration value from the server. |
| 2287 | ** |
| 2288 | ** The received configuration setting is silently ignored if it was |
| 2289 | ** not requested by a prior "reqconfig" sent from client to server. |
| 2290 | */ |
| 2291 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| @@ -2303,11 +2381,11 @@ | |
| 2303 | }else |
| 2304 | |
| 2305 | |
| 2306 | /* cookie TEXT |
| 2307 | ** |
| 2308 | ** The server might include a cookie in its reply. The client |
| 2309 | ** should remember this cookie and send it back to the server |
| 2310 | ** in its next query. |
| 2311 | ** |
| 2312 | ** Each cookie received overwrites the prior cookie from the |
| 2313 | ** same server. |
| @@ -2317,12 +2395,12 @@ | |
| 2317 | }else |
| 2318 | |
| 2319 | |
| 2320 | /* private |
| 2321 | ** |
| 2322 | ** This card indicates that the next "file" or "cfile" will contain |
| 2323 | ** private content. |
| 2324 | */ |
| 2325 | if( blob_eq(&xfer.aToken[0], "private") ){ |
| 2326 | xfer.nextIsPrivate = 1; |
| 2327 | }else |
| 2328 | |
| @@ -2338,11 +2416,12 @@ | |
| 2338 | blob_is_int(&xfer.aToken[1], &cloneSeqno); |
| 2339 | }else |
| 2340 | |
| 2341 | /* message MESSAGE |
| 2342 | ** |
| 2343 | ** Print a message. Similar to "error" but does not stop processing. |
| 2344 | ** |
| 2345 | ** If the "login failed" message is seen, clear the sync password prior |
| 2346 | ** to the next cycle. |
| 2347 | */ |
| 2348 | if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){ |
| @@ -2364,11 +2443,27 @@ | |
| 2364 | ** The server can send pragmas to try to convey meta-information to |
| 2365 | ** the client. These are informational only. Unknown pragmas are |
| 2366 | ** silently ignored. |
| 2367 | */ |
| 2368 | if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ |
| 2369 | /* If the server is unwill to accept new unversioned content (because |
| 2370 | ** this client lacks the necessary permissions) then it sends a |
| 2371 | ** "uv-pull-only" pragma so that the client will know not to waste |
| 2372 | ** bandwidth trying to upload unversioned content. If the server |
| 2373 | ** does accept new unversioned content, it sends "uv-push-ok". |
| 2374 | */ |
| @@ -2407,11 +2502,12 @@ | |
| 2407 | } |
| 2408 | }else |
| 2409 | |
| 2410 | /* error MESSAGE |
| 2411 | ** |
| 2412 | ** Report an error and abandon the sync session. |
| 2413 | ** |
| 2414 | ** Except, when cloning we will sometimes get an error on the |
| 2415 | ** first message exchange because the project-code is unknown |
| 2416 | ** and so the login card on the request was invalid. The project-code |
| 2417 | ** is returned in the reply before the error card, so second and |
| @@ -2491,10 +2587,11 @@ | |
| 2491 | ** another round |
| 2492 | */ |
| 2493 | if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){ |
| 2494 | go = 1; |
| 2495 | } |
| 2496 | |
| 2497 | /* If this is a clone, the go at least two rounds */ |
| 2498 | if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1; |
| 2499 | |
| 2500 | /* Stop the cycle if the server sends a "clone_seqno 0" card and |
| 2501 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -39,10 +39,11 @@ | |
| 39 | Blob line; /* The current line of input */ |
| 40 | Blob aToken[6]; /* Tokenized version of line */ |
| 41 | Blob err; /* Error message text */ |
| 42 | int nToken; /* Number of tokens in line */ |
| 43 | int nIGotSent; /* Number of "igot" cards sent */ |
| 44 | int nPrivIGot; /* Number of private "igot" cards */ |
| 45 | int nGimmeSent; /* Number of gimme cards sent */ |
| 46 | int nFileSent; /* Number of files sent */ |
| 47 | int nDeltaSent; /* Number of deltas sent */ |
| 48 | int nFileRcvd; /* Number of files received */ |
| 49 | int nDeltaRcvd; /* Number of deltas received */ |
| @@ -49,11 +50,13 @@ | |
| 50 | int nDanglingFile; /* Number of dangling deltas received */ |
| 51 | int mxSend; /* Stop sending "file" when pOut reaches this size */ |
| 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 | /* |
| @@ -524,20 +527,32 @@ | |
| 527 | static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){ |
| 528 | Blob content, uuid; |
| 529 | int size = 0; |
| 530 | int isPriv = content_is_private(rid); |
| 531 | |
| 532 | if( isPriv && pXfer->syncPrivate==0 ){ |
| 533 | if( pXfer->remoteDate>=20200413 && pUuid && blob_size(pUuid)>0 ){ |
| 534 | /* If the artifact is private and we are not doing a private sync, |
| 535 | ** at least tell the other side that the artifact exists and is |
| 536 | ** known to be private. But only do this for newer clients since |
| 537 | ** older ones will throw an error if they get a private igot card |
| 538 | ** and private syncing is disallowed */ |
| 539 | blob_appendf(pXfer->pOut, "igot %b 1\n", pUuid); |
| 540 | pXfer->nIGotSent++; |
| 541 | pXfer->nPrivIGot++; |
| 542 | } |
| 543 | return; |
| 544 | } |
| 545 | if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){ |
| 546 | return; |
| 547 | } |
| 548 | blob_zero(&uuid); |
| 549 | db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid); |
| 550 | if( blob_size(&uuid)==0 ){ |
| 551 | return; |
| 552 | } |
| 553 | if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->remoteVersion<20000 ){ |
| 554 | xfer_cannot_send_sha3_error(pXfer); |
| 555 | return; |
| 556 | } |
| 557 | if( pUuid ){ |
| 558 | if( blob_compare(pUuid, &uuid)!=0 ){ |
| @@ -626,11 +641,11 @@ | |
| 641 | szC = db_column_bytes(&q1, 2); |
| 642 | zContent = db_column_raw(&q1, 2); |
| 643 | srcIsPrivate = db_column_int(&q1, 3); |
| 644 | zDelta = db_column_text(&q1, 4); |
| 645 | if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); |
| 646 | if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){ |
| 647 | xfer_cannot_send_sha3_error(pXfer); |
| 648 | db_reset(&q1); |
| 649 | return; |
| 650 | } |
| 651 | blob_appendf(pXfer->pOut, "cfile %s ", zUuid); |
| @@ -690,11 +705,11 @@ | |
| 705 | ); |
| 706 | } |
| 707 | if( db_step(&q1)==SQLITE_ROW ){ |
| 708 | sqlite3_int64 mtime = db_column_int64(&q1, 0); |
| 709 | const char *zHash = db_column_text(&q1, 1); |
| 710 | if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){ |
| 711 | xfer_cannot_send_sha3_error(pXfer); |
| 712 | db_reset(&q1); |
| 713 | return; |
| 714 | } |
| 715 | if( blob_size(pXfer->pOut)>=pXfer->mxSend ){ |
| @@ -956,30 +971,48 @@ | |
| 971 | } |
| 972 | |
| 973 | /* |
| 974 | ** Send an igot message for every entry in unclustered table. |
| 975 | ** Return the number of cards sent. |
| 976 | ** |
| 977 | ** Except: |
| 978 | ** * Do not send igot cards for shunned artifacts |
| 979 | ** * Do not send igot cards for phantoms |
| 980 | ** * Do not send igot cards for private artifacts |
| 981 | ** * Do not send igot cards for any artifact that is in the |
| 982 | ** ONREMOTE table, if that table exists. |
| 983 | ** |
| 984 | ** If the pXfer->resync flag is set, that means we are doing a "--verily" |
| 985 | ** sync and all artifacts that don't meet the restrictions above should |
| 986 | ** be sent. |
| 987 | */ |
| 988 | static int send_unclustered(Xfer *pXfer){ |
| 989 | Stmt q; |
| 990 | int cnt = 0; |
| 991 | const char *zExtra; |
| 992 | if( db_table_exists("temp","onremote") ){ |
| 993 | zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)"; |
| 994 | }else{ |
| 995 | zExtra = ""; |
| 996 | } |
| 997 | if( pXfer->resync ){ |
| 998 | db_prepare(&q, |
| 999 | "SELECT uuid, rid FROM blob" |
| 1000 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 1001 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 1002 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s" |
| 1003 | " AND blob.rid<=%d" |
| 1004 | " ORDER BY blob.rid DESC", |
| 1005 | zExtra /*safe-for-%s*/, pXfer->resync |
| 1006 | ); |
| 1007 | }else{ |
| 1008 | db_prepare(&q, |
| 1009 | "SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/" |
| 1010 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 1011 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 1012 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s", |
| 1013 | zExtra /*safe-for-%s*/ |
| 1014 | ); |
| 1015 | } |
| 1016 | while( db_step(&q)==SQLITE_ROW ){ |
| 1017 | blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); |
| 1018 | cnt++; |
| @@ -1191,11 +1224,11 @@ | |
| 1224 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 1225 | |
| 1226 | /* file HASH SIZE \n CONTENT |
| 1227 | ** file HASH DELTASRC SIZE \n CONTENT |
| 1228 | ** |
| 1229 | ** Server accepts a file from the client. |
| 1230 | */ |
| 1231 | if( blob_eq(&xfer.aToken[0], "file") ){ |
| 1232 | if( !isPush ){ |
| 1233 | cgi_reset_content(); |
| 1234 | @ error not\sauthorized\sto\swrite |
| @@ -1212,11 +1245,11 @@ | |
| 1245 | }else |
| 1246 | |
| 1247 | /* cfile HASH USIZE CSIZE \n CONTENT |
| 1248 | ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT |
| 1249 | ** |
| 1250 | ** Server accepts a compressed file from the client. |
| 1251 | */ |
| 1252 | if( blob_eq(&xfer.aToken[0], "cfile") ){ |
| 1253 | if( !isPush ){ |
| 1254 | cgi_reset_content(); |
| 1255 | @ error not\sauthorized\sto\swrite |
| @@ -1232,11 +1265,11 @@ | |
| 1265 | } |
| 1266 | }else |
| 1267 | |
| 1268 | /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT |
| 1269 | ** |
| 1270 | ** Server accepts an unversioned file from the client. |
| 1271 | */ |
| 1272 | if( blob_eq(&xfer.aToken[0], "uvfile") ){ |
| 1273 | xfer_accept_unversioned_file(&xfer, g.perm.WrUnver); |
| 1274 | if( blob_size(&xfer.err) ){ |
| 1275 | cgi_reset_content(); |
| @@ -1246,11 +1279,11 @@ | |
| 1279 | } |
| 1280 | }else |
| 1281 | |
| 1282 | /* gimme HASH |
| 1283 | ** |
| 1284 | ** Client is requesting a file from the server. Send it. |
| 1285 | */ |
| 1286 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 1287 | && xfer.nToken==2 |
| 1288 | && blob_is_hname(&xfer.aToken[1]) |
| 1289 | ){ |
| @@ -1263,11 +1296,11 @@ | |
| 1296 | } |
| 1297 | }else |
| 1298 | |
| 1299 | /* uvgimme NAME |
| 1300 | ** |
| 1301 | ** Client is requesting an unversioned file from the server. Send it. |
| 1302 | */ |
| 1303 | if( blob_eq(&xfer.aToken[0], "uvgimme") |
| 1304 | && xfer.nToken==2 |
| 1305 | && blob_is_filename(&xfer.aToken[1]) |
| 1306 | ){ |
| @@ -1275,23 +1308,44 @@ | |
| 1308 | }else |
| 1309 | |
| 1310 | /* igot HASH ?ISPRIVATE? |
| 1311 | ** |
| 1312 | ** Client announces that it has a particular file. If the ISPRIVATE |
| 1313 | ** argument exists and is "1", then the file is a private file. |
| 1314 | */ |
| 1315 | if( xfer.nToken>=2 |
| 1316 | && blob_eq(&xfer.aToken[0], "igot") |
| 1317 | && blob_is_hname(&xfer.aToken[1]) |
| 1318 | ){ |
| 1319 | if( isPush ){ |
| 1320 | int rid = 0; |
| 1321 | int isPriv = 0; |
| 1322 | if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){ |
| 1323 | /* Client says the artifact is public */ |
| 1324 | rid = rid_from_uuid(&xfer.aToken[1], 1, 0); |
| 1325 | }else if( g.perm.Private ){ |
| 1326 | /* Client says the artifact is private and the client has |
| 1327 | ** permission to push private content. Create a new phantom |
| 1328 | ** artifact that is marked private. */ |
| 1329 | rid = rid_from_uuid(&xfer.aToken[1], 1, 1); |
| 1330 | isPriv = 1; |
| 1331 | }else{ |
| 1332 | /* Client says the artifact is private and the client is unable |
| 1333 | ** or unwilling to send us the artifact. If we already hold the |
| 1334 | ** artifact here on the server as a phantom, make sure that |
| 1335 | ** phantom is marked as private so that we don't keep asking about |
| 1336 | ** it in subsequent sync requests. */ |
| 1337 | rid = rid_from_uuid(&xfer.aToken[1], 0, 1); |
| 1338 | isPriv = 1; |
| 1339 | } |
| 1340 | if( rid ){ |
| 1341 | remote_has(rid); |
| 1342 | if( isPriv ){ |
| 1343 | content_make_private(rid); |
| 1344 | }else{ |
| 1345 | content_make_public(rid); |
| 1346 | } |
| 1347 | } |
| 1348 | } |
| 1349 | }else |
| 1350 | |
| 1351 | |
| @@ -1388,11 +1442,12 @@ | |
| 1442 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 1443 | }else |
| 1444 | |
| 1445 | /* login USER NONCE SIGNATURE |
| 1446 | ** |
| 1447 | ** The client has sent login credentials to the server. |
| 1448 | ** Validate the login. This has to happen before anything else. |
| 1449 | ** The client can send multiple logins. Permissions are cumulative. |
| 1450 | */ |
| 1451 | if( blob_eq(&xfer.aToken[0], "login") |
| 1452 | && xfer.nToken==4 |
| 1453 | ){ |
| @@ -1410,11 +1465,11 @@ | |
| 1465 | } |
| 1466 | }else |
| 1467 | |
| 1468 | /* reqconfig NAME |
| 1469 | ** |
| 1470 | ** Client is requesting a configuration value from the server |
| 1471 | */ |
| 1472 | if( blob_eq(&xfer.aToken[0], "reqconfig") |
| 1473 | && xfer.nToken==2 |
| 1474 | ){ |
| 1475 | if( g.perm.Read ){ |
| @@ -1429,12 +1484,12 @@ | |
| 1484 | } |
| 1485 | }else |
| 1486 | |
| 1487 | /* config NAME SIZE \n CONTENT |
| 1488 | ** |
| 1489 | ** Client has sent a configuration value to the server. |
| 1490 | ** This is only permitted for high-privilege users. |
| 1491 | */ |
| 1492 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| 1493 | && blob_is_int(&xfer.aToken[2], &size) ){ |
| 1494 | const char *zName = blob_str(&xfer.aToken[1]); |
| 1495 | Blob content; |
| @@ -1474,11 +1529,11 @@ | |
| 1529 | }else |
| 1530 | |
| 1531 | |
| 1532 | /* private |
| 1533 | ** |
| 1534 | ** The client card indicates that the next "file" or "cfile" will contain |
| 1535 | ** private content. |
| 1536 | */ |
| 1537 | if( blob_eq(&xfer.aToken[0], "private") ){ |
| 1538 | if( !g.perm.Private ){ |
| 1539 | server_private_xfer_not_authorized(); |
| @@ -1495,10 +1550,12 @@ | |
| 1550 | ** ignored. |
| 1551 | */ |
| 1552 | if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ |
| 1553 | |
| 1554 | /* pragma send-private |
| 1555 | ** |
| 1556 | ** The client is requesting private artifacts. |
| 1557 | ** |
| 1558 | ** If the user has the "x" privilege (which must be set explicitly - |
| 1559 | ** it is not automatic with "a" or "s") then this pragma causes |
| 1560 | ** private information to be pulled in addition to public records. |
| 1561 | */ |
| @@ -1511,22 +1568,32 @@ | |
| 1568 | } |
| 1569 | } |
| 1570 | |
| 1571 | /* pragma send-catalog |
| 1572 | ** |
| 1573 | ** The client wants to see igot cards for all known artifacts. |
| 1574 | ** This is used as part of "sync --verily" to help ensure that |
| 1575 | ** no artifacts have been missed on prior syncs. |
| 1576 | */ |
| 1577 | if( blob_eq(&xfer.aToken[1], "send-catalog") ){ |
| 1578 | xfer.resync = 0x7fffffff; |
| 1579 | } |
| 1580 | |
| 1581 | /* pragma client-version VERSION ?DATE? ?TIME? |
| 1582 | ** |
| 1583 | ** The client announces to the server what version of Fossil it |
| 1584 | ** is running. The DATE and TIME are a pure numeric ISO8601 time |
| 1585 | ** for the specific check-in of the client. |
| 1586 | */ |
| 1587 | if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){ |
| 1588 | xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2])); |
| 1589 | if( xfer.nToken>=5 ){ |
| 1590 | xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); |
| 1591 | xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); |
| 1592 | @ pragma server-version %d(RELEASE_VERSION_NUMBER) \ |
| 1593 | @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME) |
| 1594 | } |
| 1595 | } |
| 1596 | |
| 1597 | /* pragma uv-hash HASH |
| 1598 | ** |
| 1599 | ** The client wants to make sure that unversioned files are all synced. |
| @@ -1550,11 +1617,11 @@ | |
| 1617 | |
| 1618 | /* pragma ci-lock CHECKIN-HASH CLIENT-ID |
| 1619 | ** |
| 1620 | ** The client wants to make non-branch commit against the check-in |
| 1621 | ** identified by CHECKIN-HASH. The server will remember this and |
| 1622 | ** subsequent ci-lock requests from different clients will generate |
| 1623 | ** a ci-lock-fail pragma in the reply. |
| 1624 | */ |
| 1625 | if( blob_eq(&xfer.aToken[1], "ci-lock") |
| 1626 | && xfer.nToken==4 |
| 1627 | && blob_is_hname(&xfer.aToken[2]) |
| @@ -1808,11 +1875,11 @@ | |
| 1875 | memset(&xfer, 0, sizeof(xfer)); |
| 1876 | xfer.pIn = &recv; |
| 1877 | xfer.pOut = &send; |
| 1878 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1879 | xfer.maxTime = -1; |
| 1880 | xfer.remoteVersion = RELEASE_VERSION_NUMBER; |
| 1881 | if( syncFlags & SYNC_PRIVATE ){ |
| 1882 | g.perm.Private = 1; |
| 1883 | xfer.syncPrivate = 1; |
| 1884 | } |
| 1885 | |
| @@ -1856,13 +1923,16 @@ | |
| 1923 | " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;" |
| 1924 | ); |
| 1925 | } |
| 1926 | |
| 1927 | /* |
| 1928 | ** The request from the client always begin with a clone, pull, |
| 1929 | ** or push message. |
| 1930 | */ |
| 1931 | blob_appendf(&send, "pragma client-version %d %d %d\n", |
| 1932 | RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE, |
| 1933 | MANIFEST_NUMERIC_TIME); |
| 1934 | if( syncFlags & SYNC_CLONE ){ |
| 1935 | blob_appendf(&send, "clone 3 %d\n", cloneSeqno); |
| 1936 | syncFlags &= ~(SYNC_PUSH|SYNC_PULL); |
| 1937 | nCardSent++; |
| 1938 | /* TBD: Request all transferable configuration values */ |
| @@ -1898,20 +1968,19 @@ | |
| 1968 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 1969 | ); |
| 1970 | manifest_crosslink_begin(); |
| 1971 | |
| 1972 | |
| 1973 | /* Client sends the most recently received cookie back to the server. |
| 1974 | ** Let the server figure out if this is a cookie that it cares about. |
| 1975 | */ |
| 1976 | zCookie = db_get("cookie", 0); |
| 1977 | if( zCookie ){ |
| 1978 | blob_appendf(&send, "cookie %s\n", zCookie); |
| 1979 | } |
| 1980 | |
| 1981 | /* Client sends gimme cards for phantoms |
| 1982 | */ |
| 1983 | if( (syncFlags & SYNC_PULL)!=0 |
| 1984 | || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) |
| 1985 | ){ |
| 1986 | request_phantoms(&xfer, mxPhantomReq); |
| @@ -1920,11 +1989,11 @@ | |
| 1989 | send_unsent(&xfer); |
| 1990 | nCardSent += send_unclustered(&xfer); |
| 1991 | if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); |
| 1992 | } |
| 1993 | |
| 1994 | /* Client sends configuration parameter requests. On a clone, delay sending |
| 1995 | ** this until the second cycle since the login card might fail on |
| 1996 | ** the first cycle. |
| 1997 | */ |
| 1998 | if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ |
| 1999 | const char *zName; |
| @@ -1937,13 +2006,13 @@ | |
| 2006 | } |
| 2007 | origConfigRcvMask = configRcvMask; |
| 2008 | configRcvMask = 0; |
| 2009 | } |
| 2010 | |
| 2011 | /* Client sends a request to sync unversioned files. |
| 2012 | ** On a clone, delay sending this until the second cycle since |
| 2013 | ** the login card might fail on the first cycle. |
| 2014 | */ |
| 2015 | if( (syncFlags & SYNC_UNVERSIONED)!=0 |
| 2016 | && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) |
| 2017 | && !uvHashSent |
| 2018 | ){ |
| @@ -1950,11 +2019,12 @@ | |
| 2019 | blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0)); |
| 2020 | nCardSent++; |
| 2021 | uvHashSent = 1; |
| 2022 | } |
| 2023 | |
| 2024 | /* On a "fossil config push", the client send configuration parameters |
| 2025 | ** being pushed up to the server */ |
| 2026 | if( configSendMask ){ |
| 2027 | if( zOpType==0 ) zOpType = "Push"; |
| 2028 | nCardSent += configure_send_group(xfer.pOut, configSendMask, 0); |
| 2029 | configSendMask = 0; |
| 2030 | } |
| @@ -2010,11 +2080,11 @@ | |
| 2080 | zCkinLock = 0; |
| 2081 | }else if( zClientId ){ |
| 2082 | blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); |
| 2083 | } |
| 2084 | |
| 2085 | /* Append randomness to the end of the uplink message. This makes all |
| 2086 | ** messages unique so that that the login-card nonce will always |
| 2087 | ** be unique. |
| 2088 | */ |
| 2089 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 2090 | blob_appendf(&send, "# %s\n", zRandomness); |
| @@ -2052,14 +2122,17 @@ | |
| 2122 | nCardRcvd = 0; |
| 2123 | xfer.nFileSent = 0; |
| 2124 | xfer.nDeltaSent = 0; |
| 2125 | xfer.nGimmeSent = 0; |
| 2126 | xfer.nIGotSent = 0; |
| 2127 | xfer.nPrivIGot = 0; |
| 2128 | |
| 2129 | lastPctDone = -1; |
| 2130 | blob_reset(&send); |
| 2131 | blob_appendf(&send, "pragma client-version %d %d %d\n", |
| 2132 | RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE, |
| 2133 | MANIFEST_NUMERIC_TIME); |
| 2134 | rArrivalTime = db_double(0.0, "SELECT julianday('now')"); |
| 2135 | |
| 2136 | /* Send the send-private pragma if we are trying to sync private data */ |
| 2137 | if( syncFlags & SYNC_PRIVATE ){ |
| 2138 | blob_append(&send, "pragma send-private\n", -1); |
| @@ -2112,30 +2185,30 @@ | |
| 2185 | } |
| 2186 | |
| 2187 | /* file HASH SIZE \n CONTENT |
| 2188 | ** file HASH DELTASRC SIZE \n CONTENT |
| 2189 | ** |
| 2190 | ** Client receives a file transmitted from the server. |
| 2191 | */ |
| 2192 | if( blob_eq(&xfer.aToken[0],"file") ){ |
| 2193 | xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0); |
| 2194 | nArtifactRcvd++; |
| 2195 | }else |
| 2196 | |
| 2197 | /* cfile HASH USIZE CSIZE \n CONTENT |
| 2198 | ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT |
| 2199 | ** |
| 2200 | ** Client receives a compressed file transmitted from the server. |
| 2201 | */ |
| 2202 | if( blob_eq(&xfer.aToken[0],"cfile") ){ |
| 2203 | xfer_accept_compressed_file(&xfer, 0, 0); |
| 2204 | nArtifactRcvd++; |
| 2205 | }else |
| 2206 | |
| 2207 | /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT |
| 2208 | ** |
| 2209 | ** Client accepts an unversioned file from the server. |
| 2210 | */ |
| 2211 | if( blob_eq(&xfer.aToken[0], "uvfile") ){ |
| 2212 | xfer_accept_unversioned_file(&xfer, 1); |
| 2213 | nArtifactRcvd++; |
| 2214 | nUvFileRcvd++; |
| @@ -2145,13 +2218,14 @@ | |
| 2218 | } |
| 2219 | }else |
| 2220 | |
| 2221 | /* gimme HASH |
| 2222 | ** |
| 2223 | ** Client receives an artifact request from the server. |
| 2224 | ** If the file is a manifest, assume that the server will also want |
| 2225 | ** to know all of the content artifacts associated with the manifest |
| 2226 | ** and send those too. |
| 2227 | */ |
| 2228 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 2229 | && xfer.nToken==2 |
| 2230 | && blob_is_hname(&xfer.aToken[1]) |
| 2231 | ){ |
| @@ -2179,11 +2253,15 @@ | |
| 2253 | ){ |
| 2254 | int rid; |
| 2255 | int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1"); |
| 2256 | rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 2257 | if( rid>0 ){ |
| 2258 | if( isPriv ){ |
| 2259 | content_make_private(rid); |
| 2260 | }else{ |
| 2261 | content_make_public(rid); |
| 2262 | } |
| 2263 | }else if( isPriv && !g.perm.Private ){ |
| 2264 | /* ignore private files */ |
| 2265 | }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){ |
| 2266 | rid = content_new(blob_str(&xfer.aToken[1]), isPriv); |
| 2267 | if( rid ) newPhantom = 1; |
| @@ -2264,11 +2342,11 @@ | |
| 2342 | }else |
| 2343 | |
| 2344 | /* push SERVERCODE PRODUCTCODE |
| 2345 | ** |
| 2346 | ** Should only happen in response to a clone. This message tells |
| 2347 | ** the client what product code to use for the new database. |
| 2348 | */ |
| 2349 | if( blob_eq(&xfer.aToken[0],"push") |
| 2350 | && xfer.nToken==3 |
| 2351 | && (syncFlags & SYNC_CLONE)!=0 |
| 2352 | && blob_is_hname(&xfer.aToken[2]) |
| @@ -2281,11 +2359,11 @@ | |
| 2359 | nCardSent++; |
| 2360 | }else |
| 2361 | |
| 2362 | /* config NAME SIZE \n CONTENT |
| 2363 | ** |
| 2364 | ** Client receive a configuration value from the server. |
| 2365 | ** |
| 2366 | ** The received configuration setting is silently ignored if it was |
| 2367 | ** not requested by a prior "reqconfig" sent from client to server. |
| 2368 | */ |
| 2369 | if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 |
| @@ -2303,11 +2381,11 @@ | |
| 2381 | }else |
| 2382 | |
| 2383 | |
| 2384 | /* cookie TEXT |
| 2385 | ** |
| 2386 | ** The client reserves a cookie from the server. The client |
| 2387 | ** should remember this cookie and send it back to the server |
| 2388 | ** in its next query. |
| 2389 | ** |
| 2390 | ** Each cookie received overwrites the prior cookie from the |
| 2391 | ** same server. |
| @@ -2317,12 +2395,12 @@ | |
| 2395 | }else |
| 2396 | |
| 2397 | |
| 2398 | /* private |
| 2399 | ** |
| 2400 | ** The server tells the client that the next "file" or "cfile" will |
| 2401 | ** contain private content. |
| 2402 | */ |
| 2403 | if( blob_eq(&xfer.aToken[0], "private") ){ |
| 2404 | xfer.nextIsPrivate = 1; |
| 2405 | }else |
| 2406 | |
| @@ -2338,11 +2416,12 @@ | |
| 2416 | blob_is_int(&xfer.aToken[1], &cloneSeqno); |
| 2417 | }else |
| 2418 | |
| 2419 | /* message MESSAGE |
| 2420 | ** |
| 2421 | ** A message is received from the server. Print it. |
| 2422 | ** Similar to "error" but does not stop processing. |
| 2423 | ** |
| 2424 | ** If the "login failed" message is seen, clear the sync password prior |
| 2425 | ** to the next cycle. |
| 2426 | */ |
| 2427 | if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){ |
| @@ -2364,11 +2443,27 @@ | |
| 2443 | ** The server can send pragmas to try to convey meta-information to |
| 2444 | ** the client. These are informational only. Unknown pragmas are |
| 2445 | ** silently ignored. |
| 2446 | */ |
| 2447 | if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ |
| 2448 | /* pragma server-version VERSION ?DATE? ?TIME? |
| 2449 | ** |
| 2450 | ** The servger announces to the server what version of Fossil it |
| 2451 | ** is running. The DATE and TIME are a pure numeric ISO8601 time |
| 2452 | ** for the specific check-in of the client. |
| 2453 | */ |
| 2454 | if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){ |
| 2455 | xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2])); |
| 2456 | if( xfer.nToken>=5 ){ |
| 2457 | xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); |
| 2458 | xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); |
| 2459 | } |
| 2460 | } |
| 2461 | |
| 2462 | /* pragma uv-pull-only |
| 2463 | ** |
| 2464 | ** If the server is unwill to accept new unversioned content (because |
| 2465 | ** this client lacks the necessary permissions) then it sends a |
| 2466 | ** "uv-pull-only" pragma so that the client will know not to waste |
| 2467 | ** bandwidth trying to upload unversioned content. If the server |
| 2468 | ** does accept new unversioned content, it sends "uv-push-ok". |
| 2469 | */ |
| @@ -2407,11 +2502,12 @@ | |
| 2502 | } |
| 2503 | }else |
| 2504 | |
| 2505 | /* error MESSAGE |
| 2506 | ** |
| 2507 | ** The server is reporting an error. The client will abandon |
| 2508 | ** the sync session. |
| 2509 | ** |
| 2510 | ** Except, when cloning we will sometimes get an error on the |
| 2511 | ** first message exchange because the project-code is unknown |
| 2512 | ** and so the login card on the request was invalid. The project-code |
| 2513 | ** is returned in the reply before the error card, so second and |
| @@ -2491,10 +2587,11 @@ | |
| 2587 | ** another round |
| 2588 | */ |
| 2589 | if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){ |
| 2590 | go = 1; |
| 2591 | } |
| 2592 | if( xfer.nPrivIGot>0 && nCycle==1 ) go = 1; |
| 2593 | |
| 2594 | /* If this is a clone, the go at least two rounds */ |
| 2595 | if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1; |
| 2596 | |
| 2597 | /* Stop the cycle if the server sends a "clone_seqno 0" card and |
| 2598 |
+1
-1
| --- src/zip.c | ||
| +++ src/zip.c | ||
| @@ -592,11 +592,11 @@ | ||
| 592 | 592 | } |
| 593 | 593 | zip_open(); |
| 594 | 594 | for(i=3; i<g.argc; i++){ |
| 595 | 595 | blob_zero(&file); |
| 596 | 596 | blob_read_from_file(&file, g.argv[i], eFType); |
| 597 | - zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,0)); | |
| 597 | + zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType)); | |
| 598 | 598 | blob_reset(&file); |
| 599 | 599 | } |
| 600 | 600 | zip_close(&sArchive); |
| 601 | 601 | blob_write_to_file(&zip, g.argv[2]); |
| 602 | 602 | } |
| 603 | 603 | |
| 604 | 604 | ADDED test/subdir with spaces/filename with spaces.txt |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -592,11 +592,11 @@ | |
| 592 | } |
| 593 | zip_open(); |
| 594 | for(i=3; i<g.argc; i++){ |
| 595 | blob_zero(&file); |
| 596 | blob_read_from_file(&file, g.argv[i], eFType); |
| 597 | zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,0)); |
| 598 | blob_reset(&file); |
| 599 | } |
| 600 | zip_close(&sArchive); |
| 601 | blob_write_to_file(&zip, g.argv[2]); |
| 602 | } |
| 603 | |
| 604 | DDED test/subdir with spaces/filename with spaces.txt |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -592,11 +592,11 @@ | |
| 592 | } |
| 593 | zip_open(); |
| 594 | for(i=3; i<g.argc; i++){ |
| 595 | blob_zero(&file); |
| 596 | blob_read_from_file(&file, g.argv[i], eFType); |
| 597 | zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType)); |
| 598 | blob_reset(&file); |
| 599 | } |
| 600 | zip_close(&sArchive); |
| 601 | blob_write_to_file(&zip, g.argv[2]); |
| 602 | } |
| 603 | |
| 604 | DDED test/subdir with spaces/filename with spaces.txt |
| --- a/test/subdir with spaces/filename with spaces.txt | ||
| +++ b/test/subdir with spaces/filename with spaces.txt | ||
| @@ -0,0 +1,2 @@ | ||
| 1 | +This file has a name that contains spaces. It is used to help verify | |
| 2 | +that fossil can handle filenames that contain spaces. |
| --- a/test/subdir with spaces/filename with spaces.txt | |
| +++ b/test/subdir with spaces/filename with spaces.txt | |
| @@ -0,0 +1,2 @@ | |
| --- a/test/subdir with spaces/filename with spaces.txt | |
| +++ b/test/subdir with spaces/filename with spaces.txt | |
| @@ -0,0 +1,2 @@ | |
| 1 | This file has a name that contains spaces. It is used to help verify |
| 2 | that fossil can handle filenames that contain spaces. |
+1
-1
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -391,11 +391,11 @@ | ||
| 391 | 391 | if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \ |
| 392 | 392 | $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} { |
| 393 | 393 | return |
| 394 | 394 | } |
| 395 | 395 | catch {exec $::fossilexe info} res |
| 396 | - if {![regexp {use --repository} $res]} { | |
| 396 | + if {[regexp {local-root:} $res]} { | |
| 397 | 397 | set projectName <unknown> |
| 398 | 398 | set localRoot <unknown> |
| 399 | 399 | regexp -line -- {^project-name: (.*)$} $res dummy projectName |
| 400 | 400 | set projectName [string trim $projectName] |
| 401 | 401 | regexp -line -- {^local-root: (.*)$} $res dummy localRoot |
| 402 | 402 | |
| 403 | 403 | ADDED tools/fossil-diff-log |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -391,11 +391,11 @@ | |
| 391 | if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \ |
| 392 | $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} { |
| 393 | return |
| 394 | } |
| 395 | catch {exec $::fossilexe info} res |
| 396 | if {![regexp {use --repository} $res]} { |
| 397 | set projectName <unknown> |
| 398 | set localRoot <unknown> |
| 399 | regexp -line -- {^project-name: (.*)$} $res dummy projectName |
| 400 | set projectName [string trim $projectName] |
| 401 | regexp -line -- {^local-root: (.*)$} $res dummy localRoot |
| 402 | |
| 403 | DDED tools/fossil-diff-log |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -391,11 +391,11 @@ | |
| 391 | if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \ |
| 392 | $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} { |
| 393 | return |
| 394 | } |
| 395 | catch {exec $::fossilexe info} res |
| 396 | if {[regexp {local-root:} $res]} { |
| 397 | set projectName <unknown> |
| 398 | set localRoot <unknown> |
| 399 | regexp -line -- {^project-name: (.*)$} $res dummy projectName |
| 400 | set projectName [string trim $projectName] |
| 401 | regexp -line -- {^local-root: (.*)$} $res dummy localRoot |
| 402 | |
| 403 | DDED tools/fossil-diff-log |
No diff available
No diff available
No diff available
+72
| --- a/tools/fossil-diff-log | ||
| +++ b/tools/fossil-diff-log | ||
| @@ -0,0 +1,72 @@ | ||
| 1 | +#!/usr/bin/env perl | |
| 2 | +# Fossil emulation of the "git log --patch / -p" feature: emit a stream | |
| 3 | +# of diffs from one version to the next for each file named on the | |
| 4 | +# command line. | |
| 5 | +# | |
| 6 | +# LIMITATIONS: It does not assume "all files" if you give no args, and | |
| 7 | +# it cannot take a directory to mean "all files under this parent". | |
| 8 | +# | |
| 9 | +# PREREQUISITES: This script needs several CPAN modules to run properly. | |
| 10 | +# There are multiple methods to install them: | |
| 11 | +# | |
| 12 | +# sudo dnf install perl-File-Which perl-IO-Interactive | |
| 13 | +# sudo apt install libfile-which-perl libio-interactive-perl | |
| 14 | +# sudo cpanm File::Which IO::Interactive | |
| 15 | +# ...etc... | |
| 16 | + | |
| 17 | +use strict; | |
| 18 | +use warnings; | |
| 19 | + | |
| 20 | +use Carp; | |
| 21 | +use File::Which; | |
| 22 | +use IO::Interactive qw(is_interactive); | |
| 23 | + | |
| 24 | +die "usage: $0 <files...>\n\n" unless @ARGV; | |
| 25 | + | |
| 26 | +my $out; | |
| 27 | +if (is_interactive()) { | |
| 28 | + my $pager = $ENV{PAGER} || which('less') || which('more'); | |
| 29 | + open $out, '|-', $pager or croak "Cannot pipe to $pager: $!"; | |
| 30 | +} | |
| 31 | +else { | |
| 32 | + $out = *STDOUT; | |
| 33 | +} | |
| 34 | + | |
| 35 | +open my $bcmd, '-|', 'fossil branch current' | |
| 36 | + or die "Cannot get branch: $!\n"; | |
| 37 | +my $cbranch = <$bcmd>; | |
| 38 | +chomp $cbranch; | |
| 39 | +close $bcmd; | |
| 40 | + | |
| 41 | +for my $file (@ARGV) { | |
| 42 | + my $lastckid; | |
| 43 | + open my $finfo, '-|', "fossil finfo --brief --limit 0 '$file'" | |
| 44 | + or die "Failed to get file info: $!\n"; | |
| 45 | + my @filines = <$finfo>; | |
| 46 | + close $finfo; | |
| 47 | + | |
| 48 | + for my $line (@filines) { | |
| 49 | + my ($currckid, $date, $user, $branch, @cwords) = split ' ', $line; | |
| 50 | + next unless $branch eq $cbranch; | |
| 51 | + if (defined $lastckid and defined $branch) { | |
| 52 | + my $comment = join ' ', @cwords; | |
| 53 | + open my $diff, '-|', 'fossil', 'diff', $file, | |
| 54 | + '--from', $currckid, | |
| 55 | + '--to', $lastckid, | |
| 56 | + or die "Failed to diff $currckid -> $lastckid: $!\n"; | |
| 57 | + my @dl = <$diff>; | |
| 58 | + close $diff; | |
| 59 | + my $patch = join '', @dl; | |
| 60 | + | |
| 61 | + print $out <<"OUT" | |
| 62 | +Checkin ID $currckid to $branch by $user on $date | |
| 63 | +Comment: $comment | |
| 64 | + | |
| 65 | +$patch | |
| 66 | + | |
| 67 | +OUT | |
| 68 | + } | |
| 69 | + | |
| 70 | + $lastckid = $currckid; | |
| 71 | + } | |
| 72 | +} |
| --- a/tools/fossil-diff-log | |
| +++ b/tools/fossil-diff-log | |
| @@ -0,0 +1,72 @@ | |
| --- a/tools/fossil-diff-log | |
| +++ b/tools/fossil-diff-log | |
| @@ -0,0 +1,72 @@ | |
| 1 | #!/usr/bin/env perl |
| 2 | # Fossil emulation of the "git log --patch / -p" feature: emit a stream |
| 3 | # of diffs from one version to the next for each file named on the |
| 4 | # command line. |
| 5 | # |
| 6 | # LIMITATIONS: It does not assume "all files" if you give no args, and |
| 7 | # it cannot take a directory to mean "all files under this parent". |
| 8 | # |
| 9 | # PREREQUISITES: This script needs several CPAN modules to run properly. |
| 10 | # There are multiple methods to install them: |
| 11 | # |
| 12 | # sudo dnf install perl-File-Which perl-IO-Interactive |
| 13 | # sudo apt install libfile-which-perl libio-interactive-perl |
| 14 | # sudo cpanm File::Which IO::Interactive |
| 15 | # ...etc... |
| 16 | |
| 17 | use strict; |
| 18 | use warnings; |
| 19 | |
| 20 | use Carp; |
| 21 | use File::Which; |
| 22 | use IO::Interactive qw(is_interactive); |
| 23 | |
| 24 | die "usage: $0 <files...>\n\n" unless @ARGV; |
| 25 | |
| 26 | my $out; |
| 27 | if (is_interactive()) { |
| 28 | my $pager = $ENV{PAGER} || which('less') || which('more'); |
| 29 | open $out, '|-', $pager or croak "Cannot pipe to $pager: $!"; |
| 30 | } |
| 31 | else { |
| 32 | $out = *STDOUT; |
| 33 | } |
| 34 | |
| 35 | open my $bcmd, '-|', 'fossil branch current' |
| 36 | or die "Cannot get branch: $!\n"; |
| 37 | my $cbranch = <$bcmd>; |
| 38 | chomp $cbranch; |
| 39 | close $bcmd; |
| 40 | |
| 41 | for my $file (@ARGV) { |
| 42 | my $lastckid; |
| 43 | open my $finfo, '-|', "fossil finfo --brief --limit 0 '$file'" |
| 44 | or die "Failed to get file info: $!\n"; |
| 45 | my @filines = <$finfo>; |
| 46 | close $finfo; |
| 47 | |
| 48 | for my $line (@filines) { |
| 49 | my ($currckid, $date, $user, $branch, @cwords) = split ' ', $line; |
| 50 | next unless $branch eq $cbranch; |
| 51 | if (defined $lastckid and defined $branch) { |
| 52 | my $comment = join ' ', @cwords; |
| 53 | open my $diff, '-|', 'fossil', 'diff', $file, |
| 54 | '--from', $currckid, |
| 55 | '--to', $lastckid, |
| 56 | or die "Failed to diff $currckid -> $lastckid: $!\n"; |
| 57 | my @dl = <$diff>; |
| 58 | close $diff; |
| 59 | my $patch = join '', @dl; |
| 60 | |
| 61 | print $out <<"OUT" |
| 62 | Checkin ID $currckid to $branch by $user on $date |
| 63 | Comment: $comment |
| 64 | |
| 65 | $patch |
| 66 | |
| 67 | OUT |
| 68 | } |
| 69 | |
| 70 | $lastckid = $currckid; |
| 71 | } |
| 72 | } |
No diff available
No diff available
+16
-4
| --- win/Makefile.dmc | ||
| +++ win/Makefile.dmc | ||
| @@ -28,13 +28,13 @@ | ||
| 28 | 28 | |
| 29 | 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | 30 | |
| 31 | 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | 32 | |
| 33 | -SRC = add_.c alerts_.c allrepo_.c attach_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c | |
| 33 | +SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c | |
| 34 | 34 | |
| 35 | -OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 35 | +OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 36 | 36 | |
| 37 | 37 | |
| 38 | 38 | RC=$(DMDIR)\bin\rcc |
| 39 | 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | 40 | |
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | |
| 50 | 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | 52 | |
| 53 | 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | - +echo add alerts allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 54 | + +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 55 | 55 | +echo fossil >> $@ |
| 56 | 56 | +echo fossil >> $@ |
| 57 | 57 | +echo $(LIBS) >> $@ |
| 58 | 58 | +echo. >> $@ |
| 59 | 59 | +echo fossil >> $@ |
| @@ -152,10 +152,16 @@ | ||
| 152 | 152 | $(OBJDIR)\attach$O : attach_.c attach.h |
| 153 | 153 | $(TCC) -o$@ -c attach_.c |
| 154 | 154 | |
| 155 | 155 | attach_.c : $(SRCDIR)\attach.c |
| 156 | 156 | +translate$E $** > $@ |
| 157 | + | |
| 158 | +$(OBJDIR)\backlink$O : backlink_.c backlink.h | |
| 159 | + $(TCC) -o$@ -c backlink_.c | |
| 160 | + | |
| 161 | +backlink_.c : $(SRCDIR)\backlink.c | |
| 162 | + +translate$E $** > $@ | |
| 157 | 163 | |
| 158 | 164 | $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h |
| 159 | 165 | $(TCC) -o$@ -c backoffice_.c |
| 160 | 166 | |
| 161 | 167 | backoffice_.c : $(SRCDIR)\backoffice.c |
| @@ -824,10 +830,16 @@ | ||
| 824 | 830 | $(OBJDIR)\tar$O : tar_.c tar.h |
| 825 | 831 | $(TCC) -o$@ -c tar_.c |
| 826 | 832 | |
| 827 | 833 | tar_.c : $(SRCDIR)\tar.c |
| 828 | 834 | +translate$E $** > $@ |
| 835 | + | |
| 836 | +$(OBJDIR)\terminal$O : terminal_.c terminal.h | |
| 837 | + $(TCC) -o$@ -c terminal_.c | |
| 838 | + | |
| 839 | +terminal_.c : $(SRCDIR)\terminal.c | |
| 840 | + +translate$E $** > $@ | |
| 829 | 841 | |
| 830 | 842 | $(OBJDIR)\th_main$O : th_main_.c th_main.h |
| 831 | 843 | $(TCC) -o$@ -c th_main_.c |
| 832 | 844 | |
| 833 | 845 | th_main_.c : $(SRCDIR)\th_main.c |
| @@ -964,7 +976,7 @@ | ||
| 964 | 976 | |
| 965 | 977 | zip_.c : $(SRCDIR)\zip.c |
| 966 | 978 | +translate$E $** > $@ |
| 967 | 979 | |
| 968 | 980 | headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h |
| 969 | - +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 981 | + +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 970 | 982 | @copy /Y nul: headers |
| 971 | 983 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | |
| 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c alerts_.c allrepo_.c attach_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add alerts allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -152,10 +152,16 @@ | |
| 152 | $(OBJDIR)\attach$O : attach_.c attach.h |
| 153 | $(TCC) -o$@ -c attach_.c |
| 154 | |
| 155 | attach_.c : $(SRCDIR)\attach.c |
| 156 | +translate$E $** > $@ |
| 157 | |
| 158 | $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h |
| 159 | $(TCC) -o$@ -c backoffice_.c |
| 160 | |
| 161 | backoffice_.c : $(SRCDIR)\backoffice.c |
| @@ -824,10 +830,16 @@ | |
| 824 | $(OBJDIR)\tar$O : tar_.c tar.h |
| 825 | $(TCC) -o$@ -c tar_.c |
| 826 | |
| 827 | tar_.c : $(SRCDIR)\tar.c |
| 828 | +translate$E $** > $@ |
| 829 | |
| 830 | $(OBJDIR)\th_main$O : th_main_.c th_main.h |
| 831 | $(TCC) -o$@ -c th_main_.c |
| 832 | |
| 833 | th_main_.c : $(SRCDIR)\th_main.c |
| @@ -964,7 +976,7 @@ | |
| 964 | |
| 965 | zip_.c : $(SRCDIR)\zip.c |
| 966 | +translate$E $** > $@ |
| 967 | |
| 968 | headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h |
| 969 | +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 970 | @copy /Y nul: headers |
| 971 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | |
| 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -152,10 +152,16 @@ | |
| 152 | $(OBJDIR)\attach$O : attach_.c attach.h |
| 153 | $(TCC) -o$@ -c attach_.c |
| 154 | |
| 155 | attach_.c : $(SRCDIR)\attach.c |
| 156 | +translate$E $** > $@ |
| 157 | |
| 158 | $(OBJDIR)\backlink$O : backlink_.c backlink.h |
| 159 | $(TCC) -o$@ -c backlink_.c |
| 160 | |
| 161 | backlink_.c : $(SRCDIR)\backlink.c |
| 162 | +translate$E $** > $@ |
| 163 | |
| 164 | $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h |
| 165 | $(TCC) -o$@ -c backoffice_.c |
| 166 | |
| 167 | backoffice_.c : $(SRCDIR)\backoffice.c |
| @@ -824,10 +830,16 @@ | |
| 830 | $(OBJDIR)\tar$O : tar_.c tar.h |
| 831 | $(TCC) -o$@ -c tar_.c |
| 832 | |
| 833 | tar_.c : $(SRCDIR)\tar.c |
| 834 | +translate$E $** > $@ |
| 835 | |
| 836 | $(OBJDIR)\terminal$O : terminal_.c terminal.h |
| 837 | $(TCC) -o$@ -c terminal_.c |
| 838 | |
| 839 | terminal_.c : $(SRCDIR)\terminal.c |
| 840 | +translate$E $** > $@ |
| 841 | |
| 842 | $(OBJDIR)\th_main$O : th_main_.c th_main.h |
| 843 | $(TCC) -o$@ -c th_main_.c |
| 844 | |
| 845 | th_main_.c : $(SRCDIR)\th_main.c |
| @@ -964,7 +976,7 @@ | |
| 976 | |
| 977 | zip_.c : $(SRCDIR)\zip.c |
| 978 | +translate$E $** > $@ |
| 979 | |
| 980 | headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h |
| 981 | +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 982 | @copy /Y nul: headers |
| 983 |
+25
-1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | #### The directories where the OpenSSL include and library files are located. |
| 175 | 175 | # The recommended usage here is to use the Sysinternals junction tool |
| 176 | 176 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 177 | 177 | # Fossil source code directory and the target OpenSSL source directory. |
| 178 | 178 | # |
| 179 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f | |
| 179 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g | |
| 180 | 180 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 181 | 181 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 182 | 182 | |
| 183 | 183 | #### Either the directory where the Tcl library is installed or the Tcl |
| 184 | 184 | # source code directory resides (depending on the value of the macro |
| @@ -440,10 +440,11 @@ | ||
| 440 | 440 | SRC = \ |
| 441 | 441 | $(SRCDIR)/add.c \ |
| 442 | 442 | $(SRCDIR)/alerts.c \ |
| 443 | 443 | $(SRCDIR)/allrepo.c \ |
| 444 | 444 | $(SRCDIR)/attach.c \ |
| 445 | + $(SRCDIR)/backlink.c \ | |
| 445 | 446 | $(SRCDIR)/backoffice.c \ |
| 446 | 447 | $(SRCDIR)/bag.c \ |
| 447 | 448 | $(SRCDIR)/bisect.c \ |
| 448 | 449 | $(SRCDIR)/blob.c \ |
| 449 | 450 | $(SRCDIR)/branch.c \ |
| @@ -552,10 +553,11 @@ | ||
| 552 | 553 | $(SRCDIR)/statrep.c \ |
| 553 | 554 | $(SRCDIR)/style.c \ |
| 554 | 555 | $(SRCDIR)/sync.c \ |
| 555 | 556 | $(SRCDIR)/tag.c \ |
| 556 | 557 | $(SRCDIR)/tar.c \ |
| 558 | + $(SRCDIR)/terminal.c \ | |
| 557 | 559 | $(SRCDIR)/th_main.c \ |
| 558 | 560 | $(SRCDIR)/timeline.c \ |
| 559 | 561 | $(SRCDIR)/tkt.c \ |
| 560 | 562 | $(SRCDIR)/tktsetup.c \ |
| 561 | 563 | $(SRCDIR)/undo.c \ |
| @@ -672,10 +674,11 @@ | ||
| 672 | 674 | TRANS_SRC = \ |
| 673 | 675 | $(OBJDIR)/add_.c \ |
| 674 | 676 | $(OBJDIR)/alerts_.c \ |
| 675 | 677 | $(OBJDIR)/allrepo_.c \ |
| 676 | 678 | $(OBJDIR)/attach_.c \ |
| 679 | + $(OBJDIR)/backlink_.c \ | |
| 677 | 680 | $(OBJDIR)/backoffice_.c \ |
| 678 | 681 | $(OBJDIR)/bag_.c \ |
| 679 | 682 | $(OBJDIR)/bisect_.c \ |
| 680 | 683 | $(OBJDIR)/blob_.c \ |
| 681 | 684 | $(OBJDIR)/branch_.c \ |
| @@ -784,10 +787,11 @@ | ||
| 784 | 787 | $(OBJDIR)/statrep_.c \ |
| 785 | 788 | $(OBJDIR)/style_.c \ |
| 786 | 789 | $(OBJDIR)/sync_.c \ |
| 787 | 790 | $(OBJDIR)/tag_.c \ |
| 788 | 791 | $(OBJDIR)/tar_.c \ |
| 792 | + $(OBJDIR)/terminal_.c \ | |
| 789 | 793 | $(OBJDIR)/th_main_.c \ |
| 790 | 794 | $(OBJDIR)/timeline_.c \ |
| 791 | 795 | $(OBJDIR)/tkt_.c \ |
| 792 | 796 | $(OBJDIR)/tktsetup_.c \ |
| 793 | 797 | $(OBJDIR)/undo_.c \ |
| @@ -813,10 +817,11 @@ | ||
| 813 | 817 | OBJ = \ |
| 814 | 818 | $(OBJDIR)/add.o \ |
| 815 | 819 | $(OBJDIR)/alerts.o \ |
| 816 | 820 | $(OBJDIR)/allrepo.o \ |
| 817 | 821 | $(OBJDIR)/attach.o \ |
| 822 | + $(OBJDIR)/backlink.o \ | |
| 818 | 823 | $(OBJDIR)/backoffice.o \ |
| 819 | 824 | $(OBJDIR)/bag.o \ |
| 820 | 825 | $(OBJDIR)/bisect.o \ |
| 821 | 826 | $(OBJDIR)/blob.o \ |
| 822 | 827 | $(OBJDIR)/branch.o \ |
| @@ -925,10 +930,11 @@ | ||
| 925 | 930 | $(OBJDIR)/statrep.o \ |
| 926 | 931 | $(OBJDIR)/style.o \ |
| 927 | 932 | $(OBJDIR)/sync.o \ |
| 928 | 933 | $(OBJDIR)/tag.o \ |
| 929 | 934 | $(OBJDIR)/tar.o \ |
| 935 | + $(OBJDIR)/terminal.o \ | |
| 930 | 936 | $(OBJDIR)/th_main.o \ |
| 931 | 937 | $(OBJDIR)/timeline.o \ |
| 932 | 938 | $(OBJDIR)/tkt.o \ |
| 933 | 939 | $(OBJDIR)/tktsetup.o \ |
| 934 | 940 | $(OBJDIR)/undo.o \ |
| @@ -1174,10 +1180,11 @@ | ||
| 1174 | 1180 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 1175 | 1181 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 1176 | 1182 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 1177 | 1183 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 1178 | 1184 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 1185 | + $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ | |
| 1179 | 1186 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 1180 | 1187 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 1181 | 1188 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 1182 | 1189 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 1183 | 1190 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -1286,10 +1293,11 @@ | ||
| 1286 | 1293 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1287 | 1294 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1288 | 1295 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1289 | 1296 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1290 | 1297 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1298 | + $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ | |
| 1291 | 1299 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 1292 | 1300 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 1293 | 1301 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 1294 | 1302 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 1295 | 1303 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -1349,10 +1357,18 @@ | ||
| 1349 | 1357 | |
| 1350 | 1358 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 1351 | 1359 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 1352 | 1360 | |
| 1353 | 1361 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 1362 | + | |
| 1363 | +$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE) | |
| 1364 | + $(TRANSLATE) $(SRCDIR)/backlink.c >$@ | |
| 1365 | + | |
| 1366 | +$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h | |
| 1367 | + $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c | |
| 1368 | + | |
| 1369 | +$(OBJDIR)/backlink.h: $(OBJDIR)/headers | |
| 1354 | 1370 | |
| 1355 | 1371 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) |
| 1356 | 1372 | $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ |
| 1357 | 1373 | |
| 1358 | 1374 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -2245,10 +2261,18 @@ | ||
| 2245 | 2261 | |
| 2246 | 2262 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 2247 | 2263 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 2248 | 2264 | |
| 2249 | 2265 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 2266 | + | |
| 2267 | +$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) | |
| 2268 | + $(TRANSLATE) $(SRCDIR)/terminal.c >$@ | |
| 2269 | + | |
| 2270 | +$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h | |
| 2271 | + $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c | |
| 2272 | + | |
| 2273 | +$(OBJDIR)/terminal.h: $(OBJDIR)/headers | |
| 2250 | 2274 | |
| 2251 | 2275 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) |
| 2252 | 2276 | $(TRANSLATE) $(SRCDIR)/th_main.c >$@ |
| 2253 | 2277 | |
| 2254 | 2278 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 2255 | 2279 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -174,11 +174,11 @@ | |
| 174 | #### The directories where the OpenSSL include and library files are located. |
| 175 | # The recommended usage here is to use the Sysinternals junction tool |
| 176 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 177 | # Fossil source code directory and the target OpenSSL source directory. |
| 178 | # |
| 179 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f |
| 180 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 181 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 182 | |
| 183 | #### Either the directory where the Tcl library is installed or the Tcl |
| 184 | # source code directory resides (depending on the value of the macro |
| @@ -440,10 +440,11 @@ | |
| 440 | SRC = \ |
| 441 | $(SRCDIR)/add.c \ |
| 442 | $(SRCDIR)/alerts.c \ |
| 443 | $(SRCDIR)/allrepo.c \ |
| 444 | $(SRCDIR)/attach.c \ |
| 445 | $(SRCDIR)/backoffice.c \ |
| 446 | $(SRCDIR)/bag.c \ |
| 447 | $(SRCDIR)/bisect.c \ |
| 448 | $(SRCDIR)/blob.c \ |
| 449 | $(SRCDIR)/branch.c \ |
| @@ -552,10 +553,11 @@ | |
| 552 | $(SRCDIR)/statrep.c \ |
| 553 | $(SRCDIR)/style.c \ |
| 554 | $(SRCDIR)/sync.c \ |
| 555 | $(SRCDIR)/tag.c \ |
| 556 | $(SRCDIR)/tar.c \ |
| 557 | $(SRCDIR)/th_main.c \ |
| 558 | $(SRCDIR)/timeline.c \ |
| 559 | $(SRCDIR)/tkt.c \ |
| 560 | $(SRCDIR)/tktsetup.c \ |
| 561 | $(SRCDIR)/undo.c \ |
| @@ -672,10 +674,11 @@ | |
| 672 | TRANS_SRC = \ |
| 673 | $(OBJDIR)/add_.c \ |
| 674 | $(OBJDIR)/alerts_.c \ |
| 675 | $(OBJDIR)/allrepo_.c \ |
| 676 | $(OBJDIR)/attach_.c \ |
| 677 | $(OBJDIR)/backoffice_.c \ |
| 678 | $(OBJDIR)/bag_.c \ |
| 679 | $(OBJDIR)/bisect_.c \ |
| 680 | $(OBJDIR)/blob_.c \ |
| 681 | $(OBJDIR)/branch_.c \ |
| @@ -784,10 +787,11 @@ | |
| 784 | $(OBJDIR)/statrep_.c \ |
| 785 | $(OBJDIR)/style_.c \ |
| 786 | $(OBJDIR)/sync_.c \ |
| 787 | $(OBJDIR)/tag_.c \ |
| 788 | $(OBJDIR)/tar_.c \ |
| 789 | $(OBJDIR)/th_main_.c \ |
| 790 | $(OBJDIR)/timeline_.c \ |
| 791 | $(OBJDIR)/tkt_.c \ |
| 792 | $(OBJDIR)/tktsetup_.c \ |
| 793 | $(OBJDIR)/undo_.c \ |
| @@ -813,10 +817,11 @@ | |
| 813 | OBJ = \ |
| 814 | $(OBJDIR)/add.o \ |
| 815 | $(OBJDIR)/alerts.o \ |
| 816 | $(OBJDIR)/allrepo.o \ |
| 817 | $(OBJDIR)/attach.o \ |
| 818 | $(OBJDIR)/backoffice.o \ |
| 819 | $(OBJDIR)/bag.o \ |
| 820 | $(OBJDIR)/bisect.o \ |
| 821 | $(OBJDIR)/blob.o \ |
| 822 | $(OBJDIR)/branch.o \ |
| @@ -925,10 +930,11 @@ | |
| 925 | $(OBJDIR)/statrep.o \ |
| 926 | $(OBJDIR)/style.o \ |
| 927 | $(OBJDIR)/sync.o \ |
| 928 | $(OBJDIR)/tag.o \ |
| 929 | $(OBJDIR)/tar.o \ |
| 930 | $(OBJDIR)/th_main.o \ |
| 931 | $(OBJDIR)/timeline.o \ |
| 932 | $(OBJDIR)/tkt.o \ |
| 933 | $(OBJDIR)/tktsetup.o \ |
| 934 | $(OBJDIR)/undo.o \ |
| @@ -1174,10 +1180,11 @@ | |
| 1174 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 1175 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 1176 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 1177 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 1178 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 1179 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 1180 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 1181 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 1182 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 1183 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -1286,10 +1293,11 @@ | |
| 1286 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1287 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1288 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1289 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1290 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1291 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 1292 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 1293 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 1294 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 1295 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -1349,10 +1357,18 @@ | |
| 1349 | |
| 1350 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 1351 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 1352 | |
| 1353 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 1354 | |
| 1355 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) |
| 1356 | $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ |
| 1357 | |
| 1358 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -2245,10 +2261,18 @@ | |
| 2245 | |
| 2246 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 2247 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 2248 | |
| 2249 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 2250 | |
| 2251 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) |
| 2252 | $(TRANSLATE) $(SRCDIR)/th_main.c >$@ |
| 2253 | |
| 2254 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 2255 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -174,11 +174,11 @@ | |
| 174 | #### The directories where the OpenSSL include and library files are located. |
| 175 | # The recommended usage here is to use the Sysinternals junction tool |
| 176 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 177 | # Fossil source code directory and the target OpenSSL source directory. |
| 178 | # |
| 179 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g |
| 180 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 181 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 182 | |
| 183 | #### Either the directory where the Tcl library is installed or the Tcl |
| 184 | # source code directory resides (depending on the value of the macro |
| @@ -440,10 +440,11 @@ | |
| 440 | SRC = \ |
| 441 | $(SRCDIR)/add.c \ |
| 442 | $(SRCDIR)/alerts.c \ |
| 443 | $(SRCDIR)/allrepo.c \ |
| 444 | $(SRCDIR)/attach.c \ |
| 445 | $(SRCDIR)/backlink.c \ |
| 446 | $(SRCDIR)/backoffice.c \ |
| 447 | $(SRCDIR)/bag.c \ |
| 448 | $(SRCDIR)/bisect.c \ |
| 449 | $(SRCDIR)/blob.c \ |
| 450 | $(SRCDIR)/branch.c \ |
| @@ -552,10 +553,11 @@ | |
| 553 | $(SRCDIR)/statrep.c \ |
| 554 | $(SRCDIR)/style.c \ |
| 555 | $(SRCDIR)/sync.c \ |
| 556 | $(SRCDIR)/tag.c \ |
| 557 | $(SRCDIR)/tar.c \ |
| 558 | $(SRCDIR)/terminal.c \ |
| 559 | $(SRCDIR)/th_main.c \ |
| 560 | $(SRCDIR)/timeline.c \ |
| 561 | $(SRCDIR)/tkt.c \ |
| 562 | $(SRCDIR)/tktsetup.c \ |
| 563 | $(SRCDIR)/undo.c \ |
| @@ -672,10 +674,11 @@ | |
| 674 | TRANS_SRC = \ |
| 675 | $(OBJDIR)/add_.c \ |
| 676 | $(OBJDIR)/alerts_.c \ |
| 677 | $(OBJDIR)/allrepo_.c \ |
| 678 | $(OBJDIR)/attach_.c \ |
| 679 | $(OBJDIR)/backlink_.c \ |
| 680 | $(OBJDIR)/backoffice_.c \ |
| 681 | $(OBJDIR)/bag_.c \ |
| 682 | $(OBJDIR)/bisect_.c \ |
| 683 | $(OBJDIR)/blob_.c \ |
| 684 | $(OBJDIR)/branch_.c \ |
| @@ -784,10 +787,11 @@ | |
| 787 | $(OBJDIR)/statrep_.c \ |
| 788 | $(OBJDIR)/style_.c \ |
| 789 | $(OBJDIR)/sync_.c \ |
| 790 | $(OBJDIR)/tag_.c \ |
| 791 | $(OBJDIR)/tar_.c \ |
| 792 | $(OBJDIR)/terminal_.c \ |
| 793 | $(OBJDIR)/th_main_.c \ |
| 794 | $(OBJDIR)/timeline_.c \ |
| 795 | $(OBJDIR)/tkt_.c \ |
| 796 | $(OBJDIR)/tktsetup_.c \ |
| 797 | $(OBJDIR)/undo_.c \ |
| @@ -813,10 +817,11 @@ | |
| 817 | OBJ = \ |
| 818 | $(OBJDIR)/add.o \ |
| 819 | $(OBJDIR)/alerts.o \ |
| 820 | $(OBJDIR)/allrepo.o \ |
| 821 | $(OBJDIR)/attach.o \ |
| 822 | $(OBJDIR)/backlink.o \ |
| 823 | $(OBJDIR)/backoffice.o \ |
| 824 | $(OBJDIR)/bag.o \ |
| 825 | $(OBJDIR)/bisect.o \ |
| 826 | $(OBJDIR)/blob.o \ |
| 827 | $(OBJDIR)/branch.o \ |
| @@ -925,10 +930,11 @@ | |
| 930 | $(OBJDIR)/statrep.o \ |
| 931 | $(OBJDIR)/style.o \ |
| 932 | $(OBJDIR)/sync.o \ |
| 933 | $(OBJDIR)/tag.o \ |
| 934 | $(OBJDIR)/tar.o \ |
| 935 | $(OBJDIR)/terminal.o \ |
| 936 | $(OBJDIR)/th_main.o \ |
| 937 | $(OBJDIR)/timeline.o \ |
| 938 | $(OBJDIR)/tkt.o \ |
| 939 | $(OBJDIR)/tktsetup.o \ |
| 940 | $(OBJDIR)/undo.o \ |
| @@ -1174,10 +1180,11 @@ | |
| 1180 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 1181 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 1182 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 1183 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 1184 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 1185 | $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ |
| 1186 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 1187 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 1188 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 1189 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 1190 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -1286,10 +1293,11 @@ | |
| 1293 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1294 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1295 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1296 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1297 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1298 | $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ |
| 1299 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 1300 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 1301 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 1302 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 1303 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -1349,10 +1357,18 @@ | |
| 1357 | |
| 1358 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 1359 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 1360 | |
| 1361 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 1362 | |
| 1363 | $(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE) |
| 1364 | $(TRANSLATE) $(SRCDIR)/backlink.c >$@ |
| 1365 | |
| 1366 | $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h |
| 1367 | $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c |
| 1368 | |
| 1369 | $(OBJDIR)/backlink.h: $(OBJDIR)/headers |
| 1370 | |
| 1371 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) |
| 1372 | $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ |
| 1373 | |
| 1374 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -2245,10 +2261,18 @@ | |
| 2261 | |
| 2262 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 2263 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 2264 | |
| 2265 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 2266 | |
| 2267 | $(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) |
| 2268 | $(TRANSLATE) $(SRCDIR)/terminal.c >$@ |
| 2269 | |
| 2270 | $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h |
| 2271 | $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c |
| 2272 | |
| 2273 | $(OBJDIR)/terminal.h: $(OBJDIR)/headers |
| 2274 | |
| 2275 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) |
| 2276 | $(TRANSLATE) $(SRCDIR)/th_main.c >$@ |
| 2277 | |
| 2278 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 2279 |
+26
-1
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | #### The directories where the OpenSSL include and library files are located. |
| 175 | 175 | # The recommended usage here is to use the Sysinternals junction tool |
| 176 | 176 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 177 | 177 | # Fossil source code directory and the target OpenSSL source directory. |
| 178 | 178 | # |
| 179 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f | |
| 179 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g | |
| 180 | 180 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 181 | 181 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 182 | 182 | |
| 183 | 183 | #### Either the directory where the Tcl library is installed or the Tcl |
| 184 | 184 | # source code directory resides (depending on the value of the macro |
| @@ -440,10 +440,11 @@ | ||
| 440 | 440 | SRC = \ |
| 441 | 441 | $(SRCDIR)/add.c \ |
| 442 | 442 | $(SRCDIR)/alerts.c \ |
| 443 | 443 | $(SRCDIR)/allrepo.c \ |
| 444 | 444 | $(SRCDIR)/attach.c \ |
| 445 | + $(SRCDIR)/backlink.c \ | |
| 445 | 446 | $(SRCDIR)/backoffice.c \ |
| 446 | 447 | $(SRCDIR)/bag.c \ |
| 447 | 448 | $(SRCDIR)/bisect.c \ |
| 448 | 449 | $(SRCDIR)/blob.c \ |
| 449 | 450 | $(SRCDIR)/branch.c \ |
| @@ -552,10 +553,11 @@ | ||
| 552 | 553 | $(SRCDIR)/statrep.c \ |
| 553 | 554 | $(SRCDIR)/style.c \ |
| 554 | 555 | $(SRCDIR)/sync.c \ |
| 555 | 556 | $(SRCDIR)/tag.c \ |
| 556 | 557 | $(SRCDIR)/tar.c \ |
| 558 | + $(SRCDIR)/terminal.c \ | |
| 557 | 559 | $(SRCDIR)/th_main.c \ |
| 558 | 560 | $(SRCDIR)/timeline.c \ |
| 559 | 561 | $(SRCDIR)/tkt.c \ |
| 560 | 562 | $(SRCDIR)/tktsetup.c \ |
| 561 | 563 | $(SRCDIR)/undo.c \ |
| @@ -633,10 +635,11 @@ | ||
| 633 | 635 | $(SRCDIR)/../skins/rounded1/header.txt \ |
| 634 | 636 | $(SRCDIR)/../skins/xekri/css.txt \ |
| 635 | 637 | $(SRCDIR)/../skins/xekri/details.txt \ |
| 636 | 638 | $(SRCDIR)/../skins/xekri/footer.txt \ |
| 637 | 639 | $(SRCDIR)/../skins/xekri/header.txt \ |
| 640 | + $(SRCDIR)/accordion.js \ | |
| 638 | 641 | $(SRCDIR)/ci_edit.js \ |
| 639 | 642 | $(SRCDIR)/copybtn.js \ |
| 640 | 643 | $(SRCDIR)/diff.tcl \ |
| 641 | 644 | $(SRCDIR)/forum.js \ |
| 642 | 645 | $(SRCDIR)/graph.js \ |
| @@ -671,10 +674,11 @@ | ||
| 671 | 674 | TRANS_SRC = \ |
| 672 | 675 | $(OBJDIR)/add_.c \ |
| 673 | 676 | $(OBJDIR)/alerts_.c \ |
| 674 | 677 | $(OBJDIR)/allrepo_.c \ |
| 675 | 678 | $(OBJDIR)/attach_.c \ |
| 679 | + $(OBJDIR)/backlink_.c \ | |
| 676 | 680 | $(OBJDIR)/backoffice_.c \ |
| 677 | 681 | $(OBJDIR)/bag_.c \ |
| 678 | 682 | $(OBJDIR)/bisect_.c \ |
| 679 | 683 | $(OBJDIR)/blob_.c \ |
| 680 | 684 | $(OBJDIR)/branch_.c \ |
| @@ -783,10 +787,11 @@ | ||
| 783 | 787 | $(OBJDIR)/statrep_.c \ |
| 784 | 788 | $(OBJDIR)/style_.c \ |
| 785 | 789 | $(OBJDIR)/sync_.c \ |
| 786 | 790 | $(OBJDIR)/tag_.c \ |
| 787 | 791 | $(OBJDIR)/tar_.c \ |
| 792 | + $(OBJDIR)/terminal_.c \ | |
| 788 | 793 | $(OBJDIR)/th_main_.c \ |
| 789 | 794 | $(OBJDIR)/timeline_.c \ |
| 790 | 795 | $(OBJDIR)/tkt_.c \ |
| 791 | 796 | $(OBJDIR)/tktsetup_.c \ |
| 792 | 797 | $(OBJDIR)/undo_.c \ |
| @@ -812,10 +817,11 @@ | ||
| 812 | 817 | OBJ = \ |
| 813 | 818 | $(OBJDIR)/add.o \ |
| 814 | 819 | $(OBJDIR)/alerts.o \ |
| 815 | 820 | $(OBJDIR)/allrepo.o \ |
| 816 | 821 | $(OBJDIR)/attach.o \ |
| 822 | + $(OBJDIR)/backlink.o \ | |
| 817 | 823 | $(OBJDIR)/backoffice.o \ |
| 818 | 824 | $(OBJDIR)/bag.o \ |
| 819 | 825 | $(OBJDIR)/bisect.o \ |
| 820 | 826 | $(OBJDIR)/blob.o \ |
| 821 | 827 | $(OBJDIR)/branch.o \ |
| @@ -924,10 +930,11 @@ | ||
| 924 | 930 | $(OBJDIR)/statrep.o \ |
| 925 | 931 | $(OBJDIR)/style.o \ |
| 926 | 932 | $(OBJDIR)/sync.o \ |
| 927 | 933 | $(OBJDIR)/tag.o \ |
| 928 | 934 | $(OBJDIR)/tar.o \ |
| 935 | + $(OBJDIR)/terminal.o \ | |
| 929 | 936 | $(OBJDIR)/th_main.o \ |
| 930 | 937 | $(OBJDIR)/timeline.o \ |
| 931 | 938 | $(OBJDIR)/tkt.o \ |
| 932 | 939 | $(OBJDIR)/tktsetup.o \ |
| 933 | 940 | $(OBJDIR)/undo.o \ |
| @@ -1173,10 +1180,11 @@ | ||
| 1173 | 1180 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 1174 | 1181 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 1175 | 1182 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 1176 | 1183 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 1177 | 1184 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 1185 | + $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ | |
| 1178 | 1186 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 1179 | 1187 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 1180 | 1188 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 1181 | 1189 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 1182 | 1190 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -1285,10 +1293,11 @@ | ||
| 1285 | 1293 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1286 | 1294 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1287 | 1295 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1288 | 1296 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1289 | 1297 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1298 | + $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ | |
| 1290 | 1299 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 1291 | 1300 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 1292 | 1301 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 1293 | 1302 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 1294 | 1303 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -1348,10 +1357,18 @@ | ||
| 1348 | 1357 | |
| 1349 | 1358 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 1350 | 1359 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 1351 | 1360 | |
| 1352 | 1361 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 1362 | + | |
| 1363 | +$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE) | |
| 1364 | + $(TRANSLATE) $(SRCDIR)/backlink.c >$@ | |
| 1365 | + | |
| 1366 | +$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h | |
| 1367 | + $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c | |
| 1368 | + | |
| 1369 | +$(OBJDIR)/backlink.h: $(OBJDIR)/headers | |
| 1353 | 1370 | |
| 1354 | 1371 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) |
| 1355 | 1372 | $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ |
| 1356 | 1373 | |
| 1357 | 1374 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -2244,10 +2261,18 @@ | ||
| 2244 | 2261 | |
| 2245 | 2262 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 2246 | 2263 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 2247 | 2264 | |
| 2248 | 2265 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 2266 | + | |
| 2267 | +$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) | |
| 2268 | + $(TRANSLATE) $(SRCDIR)/terminal.c >$@ | |
| 2269 | + | |
| 2270 | +$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h | |
| 2271 | + $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c | |
| 2272 | + | |
| 2273 | +$(OBJDIR)/terminal.h: $(OBJDIR)/headers | |
| 2249 | 2274 | |
| 2250 | 2275 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) |
| 2251 | 2276 | $(TRANSLATE) $(SRCDIR)/th_main.c >$@ |
| 2252 | 2277 | |
| 2253 | 2278 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 2254 | 2279 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -174,11 +174,11 @@ | |
| 174 | #### The directories where the OpenSSL include and library files are located. |
| 175 | # The recommended usage here is to use the Sysinternals junction tool |
| 176 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 177 | # Fossil source code directory and the target OpenSSL source directory. |
| 178 | # |
| 179 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f |
| 180 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 181 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 182 | |
| 183 | #### Either the directory where the Tcl library is installed or the Tcl |
| 184 | # source code directory resides (depending on the value of the macro |
| @@ -440,10 +440,11 @@ | |
| 440 | SRC = \ |
| 441 | $(SRCDIR)/add.c \ |
| 442 | $(SRCDIR)/alerts.c \ |
| 443 | $(SRCDIR)/allrepo.c \ |
| 444 | $(SRCDIR)/attach.c \ |
| 445 | $(SRCDIR)/backoffice.c \ |
| 446 | $(SRCDIR)/bag.c \ |
| 447 | $(SRCDIR)/bisect.c \ |
| 448 | $(SRCDIR)/blob.c \ |
| 449 | $(SRCDIR)/branch.c \ |
| @@ -552,10 +553,11 @@ | |
| 552 | $(SRCDIR)/statrep.c \ |
| 553 | $(SRCDIR)/style.c \ |
| 554 | $(SRCDIR)/sync.c \ |
| 555 | $(SRCDIR)/tag.c \ |
| 556 | $(SRCDIR)/tar.c \ |
| 557 | $(SRCDIR)/th_main.c \ |
| 558 | $(SRCDIR)/timeline.c \ |
| 559 | $(SRCDIR)/tkt.c \ |
| 560 | $(SRCDIR)/tktsetup.c \ |
| 561 | $(SRCDIR)/undo.c \ |
| @@ -633,10 +635,11 @@ | |
| 633 | $(SRCDIR)/../skins/rounded1/header.txt \ |
| 634 | $(SRCDIR)/../skins/xekri/css.txt \ |
| 635 | $(SRCDIR)/../skins/xekri/details.txt \ |
| 636 | $(SRCDIR)/../skins/xekri/footer.txt \ |
| 637 | $(SRCDIR)/../skins/xekri/header.txt \ |
| 638 | $(SRCDIR)/ci_edit.js \ |
| 639 | $(SRCDIR)/copybtn.js \ |
| 640 | $(SRCDIR)/diff.tcl \ |
| 641 | $(SRCDIR)/forum.js \ |
| 642 | $(SRCDIR)/graph.js \ |
| @@ -671,10 +674,11 @@ | |
| 671 | TRANS_SRC = \ |
| 672 | $(OBJDIR)/add_.c \ |
| 673 | $(OBJDIR)/alerts_.c \ |
| 674 | $(OBJDIR)/allrepo_.c \ |
| 675 | $(OBJDIR)/attach_.c \ |
| 676 | $(OBJDIR)/backoffice_.c \ |
| 677 | $(OBJDIR)/bag_.c \ |
| 678 | $(OBJDIR)/bisect_.c \ |
| 679 | $(OBJDIR)/blob_.c \ |
| 680 | $(OBJDIR)/branch_.c \ |
| @@ -783,10 +787,11 @@ | |
| 783 | $(OBJDIR)/statrep_.c \ |
| 784 | $(OBJDIR)/style_.c \ |
| 785 | $(OBJDIR)/sync_.c \ |
| 786 | $(OBJDIR)/tag_.c \ |
| 787 | $(OBJDIR)/tar_.c \ |
| 788 | $(OBJDIR)/th_main_.c \ |
| 789 | $(OBJDIR)/timeline_.c \ |
| 790 | $(OBJDIR)/tkt_.c \ |
| 791 | $(OBJDIR)/tktsetup_.c \ |
| 792 | $(OBJDIR)/undo_.c \ |
| @@ -812,10 +817,11 @@ | |
| 812 | OBJ = \ |
| 813 | $(OBJDIR)/add.o \ |
| 814 | $(OBJDIR)/alerts.o \ |
| 815 | $(OBJDIR)/allrepo.o \ |
| 816 | $(OBJDIR)/attach.o \ |
| 817 | $(OBJDIR)/backoffice.o \ |
| 818 | $(OBJDIR)/bag.o \ |
| 819 | $(OBJDIR)/bisect.o \ |
| 820 | $(OBJDIR)/blob.o \ |
| 821 | $(OBJDIR)/branch.o \ |
| @@ -924,10 +930,11 @@ | |
| 924 | $(OBJDIR)/statrep.o \ |
| 925 | $(OBJDIR)/style.o \ |
| 926 | $(OBJDIR)/sync.o \ |
| 927 | $(OBJDIR)/tag.o \ |
| 928 | $(OBJDIR)/tar.o \ |
| 929 | $(OBJDIR)/th_main.o \ |
| 930 | $(OBJDIR)/timeline.o \ |
| 931 | $(OBJDIR)/tkt.o \ |
| 932 | $(OBJDIR)/tktsetup.o \ |
| 933 | $(OBJDIR)/undo.o \ |
| @@ -1173,10 +1180,11 @@ | |
| 1173 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 1174 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 1175 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 1176 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 1177 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 1178 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 1179 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 1180 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 1181 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 1182 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -1285,10 +1293,11 @@ | |
| 1285 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1286 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1287 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1288 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1289 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1290 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 1291 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 1292 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 1293 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 1294 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -1348,10 +1357,18 @@ | |
| 1348 | |
| 1349 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 1350 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 1351 | |
| 1352 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 1353 | |
| 1354 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) |
| 1355 | $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ |
| 1356 | |
| 1357 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -2244,10 +2261,18 @@ | |
| 2244 | |
| 2245 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 2246 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 2247 | |
| 2248 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 2249 | |
| 2250 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) |
| 2251 | $(TRANSLATE) $(SRCDIR)/th_main.c >$@ |
| 2252 | |
| 2253 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 2254 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -174,11 +174,11 @@ | |
| 174 | #### The directories where the OpenSSL include and library files are located. |
| 175 | # The recommended usage here is to use the Sysinternals junction tool |
| 176 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 177 | # Fossil source code directory and the target OpenSSL source directory. |
| 178 | # |
| 179 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g |
| 180 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 181 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 182 | |
| 183 | #### Either the directory where the Tcl library is installed or the Tcl |
| 184 | # source code directory resides (depending on the value of the macro |
| @@ -440,10 +440,11 @@ | |
| 440 | SRC = \ |
| 441 | $(SRCDIR)/add.c \ |
| 442 | $(SRCDIR)/alerts.c \ |
| 443 | $(SRCDIR)/allrepo.c \ |
| 444 | $(SRCDIR)/attach.c \ |
| 445 | $(SRCDIR)/backlink.c \ |
| 446 | $(SRCDIR)/backoffice.c \ |
| 447 | $(SRCDIR)/bag.c \ |
| 448 | $(SRCDIR)/bisect.c \ |
| 449 | $(SRCDIR)/blob.c \ |
| 450 | $(SRCDIR)/branch.c \ |
| @@ -552,10 +553,11 @@ | |
| 553 | $(SRCDIR)/statrep.c \ |
| 554 | $(SRCDIR)/style.c \ |
| 555 | $(SRCDIR)/sync.c \ |
| 556 | $(SRCDIR)/tag.c \ |
| 557 | $(SRCDIR)/tar.c \ |
| 558 | $(SRCDIR)/terminal.c \ |
| 559 | $(SRCDIR)/th_main.c \ |
| 560 | $(SRCDIR)/timeline.c \ |
| 561 | $(SRCDIR)/tkt.c \ |
| 562 | $(SRCDIR)/tktsetup.c \ |
| 563 | $(SRCDIR)/undo.c \ |
| @@ -633,10 +635,11 @@ | |
| 635 | $(SRCDIR)/../skins/rounded1/header.txt \ |
| 636 | $(SRCDIR)/../skins/xekri/css.txt \ |
| 637 | $(SRCDIR)/../skins/xekri/details.txt \ |
| 638 | $(SRCDIR)/../skins/xekri/footer.txt \ |
| 639 | $(SRCDIR)/../skins/xekri/header.txt \ |
| 640 | $(SRCDIR)/accordion.js \ |
| 641 | $(SRCDIR)/ci_edit.js \ |
| 642 | $(SRCDIR)/copybtn.js \ |
| 643 | $(SRCDIR)/diff.tcl \ |
| 644 | $(SRCDIR)/forum.js \ |
| 645 | $(SRCDIR)/graph.js \ |
| @@ -671,10 +674,11 @@ | |
| 674 | TRANS_SRC = \ |
| 675 | $(OBJDIR)/add_.c \ |
| 676 | $(OBJDIR)/alerts_.c \ |
| 677 | $(OBJDIR)/allrepo_.c \ |
| 678 | $(OBJDIR)/attach_.c \ |
| 679 | $(OBJDIR)/backlink_.c \ |
| 680 | $(OBJDIR)/backoffice_.c \ |
| 681 | $(OBJDIR)/bag_.c \ |
| 682 | $(OBJDIR)/bisect_.c \ |
| 683 | $(OBJDIR)/blob_.c \ |
| 684 | $(OBJDIR)/branch_.c \ |
| @@ -783,10 +787,11 @@ | |
| 787 | $(OBJDIR)/statrep_.c \ |
| 788 | $(OBJDIR)/style_.c \ |
| 789 | $(OBJDIR)/sync_.c \ |
| 790 | $(OBJDIR)/tag_.c \ |
| 791 | $(OBJDIR)/tar_.c \ |
| 792 | $(OBJDIR)/terminal_.c \ |
| 793 | $(OBJDIR)/th_main_.c \ |
| 794 | $(OBJDIR)/timeline_.c \ |
| 795 | $(OBJDIR)/tkt_.c \ |
| 796 | $(OBJDIR)/tktsetup_.c \ |
| 797 | $(OBJDIR)/undo_.c \ |
| @@ -812,10 +817,11 @@ | |
| 817 | OBJ = \ |
| 818 | $(OBJDIR)/add.o \ |
| 819 | $(OBJDIR)/alerts.o \ |
| 820 | $(OBJDIR)/allrepo.o \ |
| 821 | $(OBJDIR)/attach.o \ |
| 822 | $(OBJDIR)/backlink.o \ |
| 823 | $(OBJDIR)/backoffice.o \ |
| 824 | $(OBJDIR)/bag.o \ |
| 825 | $(OBJDIR)/bisect.o \ |
| 826 | $(OBJDIR)/blob.o \ |
| 827 | $(OBJDIR)/branch.o \ |
| @@ -924,10 +930,11 @@ | |
| 930 | $(OBJDIR)/statrep.o \ |
| 931 | $(OBJDIR)/style.o \ |
| 932 | $(OBJDIR)/sync.o \ |
| 933 | $(OBJDIR)/tag.o \ |
| 934 | $(OBJDIR)/tar.o \ |
| 935 | $(OBJDIR)/terminal.o \ |
| 936 | $(OBJDIR)/th_main.o \ |
| 937 | $(OBJDIR)/timeline.o \ |
| 938 | $(OBJDIR)/tkt.o \ |
| 939 | $(OBJDIR)/tktsetup.o \ |
| 940 | $(OBJDIR)/undo.o \ |
| @@ -1173,10 +1180,11 @@ | |
| 1180 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 1181 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 1182 | $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ |
| 1183 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 1184 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| 1185 | $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ |
| 1186 | $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ |
| 1187 | $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ |
| 1188 | $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ |
| 1189 | $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ |
| 1190 | $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ |
| @@ -1285,10 +1293,11 @@ | |
| 1293 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1294 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1295 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1296 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1297 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1298 | $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ |
| 1299 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 1300 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 1301 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 1302 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 1303 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| @@ -1348,10 +1357,18 @@ | |
| 1357 | |
| 1358 | $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h |
| 1359 | $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c |
| 1360 | |
| 1361 | $(OBJDIR)/attach.h: $(OBJDIR)/headers |
| 1362 | |
| 1363 | $(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE) |
| 1364 | $(TRANSLATE) $(SRCDIR)/backlink.c >$@ |
| 1365 | |
| 1366 | $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h |
| 1367 | $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c |
| 1368 | |
| 1369 | $(OBJDIR)/backlink.h: $(OBJDIR)/headers |
| 1370 | |
| 1371 | $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) |
| 1372 | $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ |
| 1373 | |
| 1374 | $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h |
| @@ -2244,10 +2261,18 @@ | |
| 2261 | |
| 2262 | $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h |
| 2263 | $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c |
| 2264 | |
| 2265 | $(OBJDIR)/tar.h: $(OBJDIR)/headers |
| 2266 | |
| 2267 | $(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) |
| 2268 | $(TRANSLATE) $(SRCDIR)/terminal.c >$@ |
| 2269 | |
| 2270 | $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h |
| 2271 | $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c |
| 2272 | |
| 2273 | $(OBJDIR)/terminal.h: $(OBJDIR)/headers |
| 2274 | |
| 2275 | $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) |
| 2276 | $(TRANSLATE) $(SRCDIR)/th_main.c >$@ |
| 2277 | |
| 2278 | $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h |
| 2279 |
+21
-1
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -98,11 +98,11 @@ | ||
| 98 | 98 | !ifndef USE_SEE |
| 99 | 99 | USE_SEE = 0 |
| 100 | 100 | !endif |
| 101 | 101 | |
| 102 | 102 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 103 | -SSLDIR = $(B)\compat\openssl-1.1.1f | |
| 103 | +SSLDIR = $(B)\compat\openssl-1.1.1g | |
| 104 | 104 | SSLINCDIR = $(SSLDIR)\include |
| 105 | 105 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 106 | 106 | SSLLIBDIR = $(SSLDIR) |
| 107 | 107 | !else |
| 108 | 108 | SSLLIBDIR = $(SSLDIR) |
| @@ -348,10 +348,11 @@ | ||
| 348 | 348 | |
| 349 | 349 | SRC = add_.c \ |
| 350 | 350 | alerts_.c \ |
| 351 | 351 | allrepo_.c \ |
| 352 | 352 | attach_.c \ |
| 353 | + backlink_.c \ | |
| 353 | 354 | backoffice_.c \ |
| 354 | 355 | bag_.c \ |
| 355 | 356 | bisect_.c \ |
| 356 | 357 | blob_.c \ |
| 357 | 358 | branch_.c \ |
| @@ -460,10 +461,11 @@ | ||
| 460 | 461 | statrep_.c \ |
| 461 | 462 | style_.c \ |
| 462 | 463 | sync_.c \ |
| 463 | 464 | tag_.c \ |
| 464 | 465 | tar_.c \ |
| 466 | + terminal_.c \ | |
| 465 | 467 | th_main_.c \ |
| 466 | 468 | timeline_.c \ |
| 467 | 469 | tkt_.c \ |
| 468 | 470 | tktsetup_.c \ |
| 469 | 471 | undo_.c \ |
| @@ -578,10 +580,11 @@ | ||
| 578 | 580 | |
| 579 | 581 | OBJ = $(OX)\add$O \ |
| 580 | 582 | $(OX)\alerts$O \ |
| 581 | 583 | $(OX)\allrepo$O \ |
| 582 | 584 | $(OX)\attach$O \ |
| 585 | + $(OX)\backlink$O \ | |
| 583 | 586 | $(OX)\backoffice$O \ |
| 584 | 587 | $(OX)\bag$O \ |
| 585 | 588 | $(OX)\bisect$O \ |
| 586 | 589 | $(OX)\blob$O \ |
| 587 | 590 | $(OX)\branch$O \ |
| @@ -693,10 +696,11 @@ | ||
| 693 | 696 | $(OX)\statrep$O \ |
| 694 | 697 | $(OX)\style$O \ |
| 695 | 698 | $(OX)\sync$O \ |
| 696 | 699 | $(OX)\tag$O \ |
| 697 | 700 | $(OX)\tar$O \ |
| 701 | + $(OX)\terminal$O \ | |
| 698 | 702 | $(OX)\th$O \ |
| 699 | 703 | $(OX)\th_lang$O \ |
| 700 | 704 | $(OX)\th_main$O \ |
| 701 | 705 | $(OX)\th_tcl$O \ |
| 702 | 706 | $(OX)\timeline$O \ |
| @@ -781,10 +785,11 @@ | ||
| 781 | 785 | $(OX)\linkopts: $B\win\Makefile.msc |
| 782 | 786 | echo $(OX)\add.obj > $@ |
| 783 | 787 | echo $(OX)\alerts.obj >> $@ |
| 784 | 788 | echo $(OX)\allrepo.obj >> $@ |
| 785 | 789 | echo $(OX)\attach.obj >> $@ |
| 790 | + echo $(OX)\backlink.obj >> $@ | |
| 786 | 791 | echo $(OX)\backoffice.obj >> $@ |
| 787 | 792 | echo $(OX)\bag.obj >> $@ |
| 788 | 793 | echo $(OX)\bisect.obj >> $@ |
| 789 | 794 | echo $(OX)\blob.obj >> $@ |
| 790 | 795 | echo $(OX)\branch.obj >> $@ |
| @@ -896,10 +901,11 @@ | ||
| 896 | 901 | echo $(OX)\statrep.obj >> $@ |
| 897 | 902 | echo $(OX)\style.obj >> $@ |
| 898 | 903 | echo $(OX)\sync.obj >> $@ |
| 899 | 904 | echo $(OX)\tag.obj >> $@ |
| 900 | 905 | echo $(OX)\tar.obj >> $@ |
| 906 | + echo $(OX)\terminal.obj >> $@ | |
| 901 | 907 | echo $(OX)\th.obj >> $@ |
| 902 | 908 | echo $(OX)\th_lang.obj >> $@ |
| 903 | 909 | echo $(OX)\th_main.obj >> $@ |
| 904 | 910 | echo $(OX)\th_tcl.obj >> $@ |
| 905 | 911 | echo $(OX)\timeline.obj >> $@ |
| @@ -1063,10 +1069,16 @@ | ||
| 1063 | 1069 | $(OX)\attach$O : attach_.c attach.h |
| 1064 | 1070 | $(TCC) /Fo$@ -c attach_.c |
| 1065 | 1071 | |
| 1066 | 1072 | attach_.c : $(SRCDIR)\attach.c |
| 1067 | 1073 | translate$E $** > $@ |
| 1074 | + | |
| 1075 | +$(OX)\backlink$O : backlink_.c backlink.h | |
| 1076 | + $(TCC) /Fo$@ -c backlink_.c | |
| 1077 | + | |
| 1078 | +backlink_.c : $(SRCDIR)\backlink.c | |
| 1079 | + translate$E $** > $@ | |
| 1068 | 1080 | |
| 1069 | 1081 | $(OX)\backoffice$O : backoffice_.c backoffice.h |
| 1070 | 1082 | $(TCC) /Fo$@ -c backoffice_.c |
| 1071 | 1083 | |
| 1072 | 1084 | backoffice_.c : $(SRCDIR)\backoffice.c |
| @@ -1735,10 +1747,16 @@ | ||
| 1735 | 1747 | $(OX)\tar$O : tar_.c tar.h |
| 1736 | 1748 | $(TCC) /Fo$@ -c tar_.c |
| 1737 | 1749 | |
| 1738 | 1750 | tar_.c : $(SRCDIR)\tar.c |
| 1739 | 1751 | translate$E $** > $@ |
| 1752 | + | |
| 1753 | +$(OX)\terminal$O : terminal_.c terminal.h | |
| 1754 | + $(TCC) /Fo$@ -c terminal_.c | |
| 1755 | + | |
| 1756 | +terminal_.c : $(SRCDIR)\terminal.c | |
| 1757 | + translate$E $** > $@ | |
| 1740 | 1758 | |
| 1741 | 1759 | $(OX)\th_main$O : th_main_.c th_main.h |
| 1742 | 1760 | $(TCC) /Fo$@ -c th_main_.c |
| 1743 | 1761 | |
| 1744 | 1762 | th_main_.c : $(SRCDIR)\th_main.c |
| @@ -1882,10 +1900,11 @@ | ||
| 1882 | 1900 | headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h |
| 1883 | 1901 | makeheaders$E add_.c:add.h \ |
| 1884 | 1902 | alerts_.c:alerts.h \ |
| 1885 | 1903 | allrepo_.c:allrepo.h \ |
| 1886 | 1904 | attach_.c:attach.h \ |
| 1905 | + backlink_.c:backlink.h \ | |
| 1887 | 1906 | backoffice_.c:backoffice.h \ |
| 1888 | 1907 | bag_.c:bag.h \ |
| 1889 | 1908 | bisect_.c:bisect.h \ |
| 1890 | 1909 | blob_.c:blob.h \ |
| 1891 | 1910 | branch_.c:branch.h \ |
| @@ -1994,10 +2013,11 @@ | ||
| 1994 | 2013 | statrep_.c:statrep.h \ |
| 1995 | 2014 | style_.c:style.h \ |
| 1996 | 2015 | sync_.c:sync.h \ |
| 1997 | 2016 | tag_.c:tag.h \ |
| 1998 | 2017 | tar_.c:tar.h \ |
| 2018 | + terminal_.c:terminal.h \ | |
| 1999 | 2019 | th_main_.c:th_main.h \ |
| 2000 | 2020 | timeline_.c:timeline.h \ |
| 2001 | 2021 | tkt_.c:tkt.h \ |
| 2002 | 2022 | tktsetup_.c:tktsetup.h \ |
| 2003 | 2023 | undo_.c:undo.h \ |
| 2004 | 2024 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -98,11 +98,11 @@ | |
| 98 | !ifndef USE_SEE |
| 99 | USE_SEE = 0 |
| 100 | !endif |
| 101 | |
| 102 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 103 | SSLDIR = $(B)\compat\openssl-1.1.1f |
| 104 | SSLINCDIR = $(SSLDIR)\include |
| 105 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 106 | SSLLIBDIR = $(SSLDIR) |
| 107 | !else |
| 108 | SSLLIBDIR = $(SSLDIR) |
| @@ -348,10 +348,11 @@ | |
| 348 | |
| 349 | SRC = add_.c \ |
| 350 | alerts_.c \ |
| 351 | allrepo_.c \ |
| 352 | attach_.c \ |
| 353 | backoffice_.c \ |
| 354 | bag_.c \ |
| 355 | bisect_.c \ |
| 356 | blob_.c \ |
| 357 | branch_.c \ |
| @@ -460,10 +461,11 @@ | |
| 460 | statrep_.c \ |
| 461 | style_.c \ |
| 462 | sync_.c \ |
| 463 | tag_.c \ |
| 464 | tar_.c \ |
| 465 | th_main_.c \ |
| 466 | timeline_.c \ |
| 467 | tkt_.c \ |
| 468 | tktsetup_.c \ |
| 469 | undo_.c \ |
| @@ -578,10 +580,11 @@ | |
| 578 | |
| 579 | OBJ = $(OX)\add$O \ |
| 580 | $(OX)\alerts$O \ |
| 581 | $(OX)\allrepo$O \ |
| 582 | $(OX)\attach$O \ |
| 583 | $(OX)\backoffice$O \ |
| 584 | $(OX)\bag$O \ |
| 585 | $(OX)\bisect$O \ |
| 586 | $(OX)\blob$O \ |
| 587 | $(OX)\branch$O \ |
| @@ -693,10 +696,11 @@ | |
| 693 | $(OX)\statrep$O \ |
| 694 | $(OX)\style$O \ |
| 695 | $(OX)\sync$O \ |
| 696 | $(OX)\tag$O \ |
| 697 | $(OX)\tar$O \ |
| 698 | $(OX)\th$O \ |
| 699 | $(OX)\th_lang$O \ |
| 700 | $(OX)\th_main$O \ |
| 701 | $(OX)\th_tcl$O \ |
| 702 | $(OX)\timeline$O \ |
| @@ -781,10 +785,11 @@ | |
| 781 | $(OX)\linkopts: $B\win\Makefile.msc |
| 782 | echo $(OX)\add.obj > $@ |
| 783 | echo $(OX)\alerts.obj >> $@ |
| 784 | echo $(OX)\allrepo.obj >> $@ |
| 785 | echo $(OX)\attach.obj >> $@ |
| 786 | echo $(OX)\backoffice.obj >> $@ |
| 787 | echo $(OX)\bag.obj >> $@ |
| 788 | echo $(OX)\bisect.obj >> $@ |
| 789 | echo $(OX)\blob.obj >> $@ |
| 790 | echo $(OX)\branch.obj >> $@ |
| @@ -896,10 +901,11 @@ | |
| 896 | echo $(OX)\statrep.obj >> $@ |
| 897 | echo $(OX)\style.obj >> $@ |
| 898 | echo $(OX)\sync.obj >> $@ |
| 899 | echo $(OX)\tag.obj >> $@ |
| 900 | echo $(OX)\tar.obj >> $@ |
| 901 | echo $(OX)\th.obj >> $@ |
| 902 | echo $(OX)\th_lang.obj >> $@ |
| 903 | echo $(OX)\th_main.obj >> $@ |
| 904 | echo $(OX)\th_tcl.obj >> $@ |
| 905 | echo $(OX)\timeline.obj >> $@ |
| @@ -1063,10 +1069,16 @@ | |
| 1063 | $(OX)\attach$O : attach_.c attach.h |
| 1064 | $(TCC) /Fo$@ -c attach_.c |
| 1065 | |
| 1066 | attach_.c : $(SRCDIR)\attach.c |
| 1067 | translate$E $** > $@ |
| 1068 | |
| 1069 | $(OX)\backoffice$O : backoffice_.c backoffice.h |
| 1070 | $(TCC) /Fo$@ -c backoffice_.c |
| 1071 | |
| 1072 | backoffice_.c : $(SRCDIR)\backoffice.c |
| @@ -1735,10 +1747,16 @@ | |
| 1735 | $(OX)\tar$O : tar_.c tar.h |
| 1736 | $(TCC) /Fo$@ -c tar_.c |
| 1737 | |
| 1738 | tar_.c : $(SRCDIR)\tar.c |
| 1739 | translate$E $** > $@ |
| 1740 | |
| 1741 | $(OX)\th_main$O : th_main_.c th_main.h |
| 1742 | $(TCC) /Fo$@ -c th_main_.c |
| 1743 | |
| 1744 | th_main_.c : $(SRCDIR)\th_main.c |
| @@ -1882,10 +1900,11 @@ | |
| 1882 | headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h |
| 1883 | makeheaders$E add_.c:add.h \ |
| 1884 | alerts_.c:alerts.h \ |
| 1885 | allrepo_.c:allrepo.h \ |
| 1886 | attach_.c:attach.h \ |
| 1887 | backoffice_.c:backoffice.h \ |
| 1888 | bag_.c:bag.h \ |
| 1889 | bisect_.c:bisect.h \ |
| 1890 | blob_.c:blob.h \ |
| 1891 | branch_.c:branch.h \ |
| @@ -1994,10 +2013,11 @@ | |
| 1994 | statrep_.c:statrep.h \ |
| 1995 | style_.c:style.h \ |
| 1996 | sync_.c:sync.h \ |
| 1997 | tag_.c:tag.h \ |
| 1998 | tar_.c:tar.h \ |
| 1999 | th_main_.c:th_main.h \ |
| 2000 | timeline_.c:timeline.h \ |
| 2001 | tkt_.c:tkt.h \ |
| 2002 | tktsetup_.c:tktsetup.h \ |
| 2003 | undo_.c:undo.h \ |
| 2004 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -98,11 +98,11 @@ | |
| 98 | !ifndef USE_SEE |
| 99 | USE_SEE = 0 |
| 100 | !endif |
| 101 | |
| 102 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 103 | SSLDIR = $(B)\compat\openssl-1.1.1g |
| 104 | SSLINCDIR = $(SSLDIR)\include |
| 105 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 106 | SSLLIBDIR = $(SSLDIR) |
| 107 | !else |
| 108 | SSLLIBDIR = $(SSLDIR) |
| @@ -348,10 +348,11 @@ | |
| 348 | |
| 349 | SRC = add_.c \ |
| 350 | alerts_.c \ |
| 351 | allrepo_.c \ |
| 352 | attach_.c \ |
| 353 | backlink_.c \ |
| 354 | backoffice_.c \ |
| 355 | bag_.c \ |
| 356 | bisect_.c \ |
| 357 | blob_.c \ |
| 358 | branch_.c \ |
| @@ -460,10 +461,11 @@ | |
| 461 | statrep_.c \ |
| 462 | style_.c \ |
| 463 | sync_.c \ |
| 464 | tag_.c \ |
| 465 | tar_.c \ |
| 466 | terminal_.c \ |
| 467 | th_main_.c \ |
| 468 | timeline_.c \ |
| 469 | tkt_.c \ |
| 470 | tktsetup_.c \ |
| 471 | undo_.c \ |
| @@ -578,10 +580,11 @@ | |
| 580 | |
| 581 | OBJ = $(OX)\add$O \ |
| 582 | $(OX)\alerts$O \ |
| 583 | $(OX)\allrepo$O \ |
| 584 | $(OX)\attach$O \ |
| 585 | $(OX)\backlink$O \ |
| 586 | $(OX)\backoffice$O \ |
| 587 | $(OX)\bag$O \ |
| 588 | $(OX)\bisect$O \ |
| 589 | $(OX)\blob$O \ |
| 590 | $(OX)\branch$O \ |
| @@ -693,10 +696,11 @@ | |
| 696 | $(OX)\statrep$O \ |
| 697 | $(OX)\style$O \ |
| 698 | $(OX)\sync$O \ |
| 699 | $(OX)\tag$O \ |
| 700 | $(OX)\tar$O \ |
| 701 | $(OX)\terminal$O \ |
| 702 | $(OX)\th$O \ |
| 703 | $(OX)\th_lang$O \ |
| 704 | $(OX)\th_main$O \ |
| 705 | $(OX)\th_tcl$O \ |
| 706 | $(OX)\timeline$O \ |
| @@ -781,10 +785,11 @@ | |
| 785 | $(OX)\linkopts: $B\win\Makefile.msc |
| 786 | echo $(OX)\add.obj > $@ |
| 787 | echo $(OX)\alerts.obj >> $@ |
| 788 | echo $(OX)\allrepo.obj >> $@ |
| 789 | echo $(OX)\attach.obj >> $@ |
| 790 | echo $(OX)\backlink.obj >> $@ |
| 791 | echo $(OX)\backoffice.obj >> $@ |
| 792 | echo $(OX)\bag.obj >> $@ |
| 793 | echo $(OX)\bisect.obj >> $@ |
| 794 | echo $(OX)\blob.obj >> $@ |
| 795 | echo $(OX)\branch.obj >> $@ |
| @@ -896,10 +901,11 @@ | |
| 901 | echo $(OX)\statrep.obj >> $@ |
| 902 | echo $(OX)\style.obj >> $@ |
| 903 | echo $(OX)\sync.obj >> $@ |
| 904 | echo $(OX)\tag.obj >> $@ |
| 905 | echo $(OX)\tar.obj >> $@ |
| 906 | echo $(OX)\terminal.obj >> $@ |
| 907 | echo $(OX)\th.obj >> $@ |
| 908 | echo $(OX)\th_lang.obj >> $@ |
| 909 | echo $(OX)\th_main.obj >> $@ |
| 910 | echo $(OX)\th_tcl.obj >> $@ |
| 911 | echo $(OX)\timeline.obj >> $@ |
| @@ -1063,10 +1069,16 @@ | |
| 1069 | $(OX)\attach$O : attach_.c attach.h |
| 1070 | $(TCC) /Fo$@ -c attach_.c |
| 1071 | |
| 1072 | attach_.c : $(SRCDIR)\attach.c |
| 1073 | translate$E $** > $@ |
| 1074 | |
| 1075 | $(OX)\backlink$O : backlink_.c backlink.h |
| 1076 | $(TCC) /Fo$@ -c backlink_.c |
| 1077 | |
| 1078 | backlink_.c : $(SRCDIR)\backlink.c |
| 1079 | translate$E $** > $@ |
| 1080 | |
| 1081 | $(OX)\backoffice$O : backoffice_.c backoffice.h |
| 1082 | $(TCC) /Fo$@ -c backoffice_.c |
| 1083 | |
| 1084 | backoffice_.c : $(SRCDIR)\backoffice.c |
| @@ -1735,10 +1747,16 @@ | |
| 1747 | $(OX)\tar$O : tar_.c tar.h |
| 1748 | $(TCC) /Fo$@ -c tar_.c |
| 1749 | |
| 1750 | tar_.c : $(SRCDIR)\tar.c |
| 1751 | translate$E $** > $@ |
| 1752 | |
| 1753 | $(OX)\terminal$O : terminal_.c terminal.h |
| 1754 | $(TCC) /Fo$@ -c terminal_.c |
| 1755 | |
| 1756 | terminal_.c : $(SRCDIR)\terminal.c |
| 1757 | translate$E $** > $@ |
| 1758 | |
| 1759 | $(OX)\th_main$O : th_main_.c th_main.h |
| 1760 | $(TCC) /Fo$@ -c th_main_.c |
| 1761 | |
| 1762 | th_main_.c : $(SRCDIR)\th_main.c |
| @@ -1882,10 +1900,11 @@ | |
| 1900 | headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h |
| 1901 | makeheaders$E add_.c:add.h \ |
| 1902 | alerts_.c:alerts.h \ |
| 1903 | allrepo_.c:allrepo.h \ |
| 1904 | attach_.c:attach.h \ |
| 1905 | backlink_.c:backlink.h \ |
| 1906 | backoffice_.c:backoffice.h \ |
| 1907 | bag_.c:bag.h \ |
| 1908 | bisect_.c:bisect.h \ |
| 1909 | blob_.c:blob.h \ |
| 1910 | branch_.c:branch.h \ |
| @@ -1994,10 +2013,11 @@ | |
| 2013 | statrep_.c:statrep.h \ |
| 2014 | style_.c:style.h \ |
| 2015 | sync_.c:sync.h \ |
| 2016 | tag_.c:tag.h \ |
| 2017 | tar_.c:tar.h \ |
| 2018 | terminal_.c:terminal.h \ |
| 2019 | th_main_.c:th_main.h \ |
| 2020 | timeline_.c:timeline.h \ |
| 2021 | tkt_.c:tkt.h \ |
| 2022 | tktsetup_.c:tktsetup.h \ |
| 2023 | undo_.c:undo.h \ |
| 2024 |
+1
-1
| --- www/antibot.wiki | ||
| +++ www/antibot.wiki | ||
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | <title>Defense Against Spiders</title> |
| 2 | 2 | |
| 3 | 3 | The website presented by a Fossil server has many hyperlinks. |
| 4 | 4 | Even a modest project can have millions of pages in its |
| 5 | 5 | tree, and many of those pages (for example diffs and annotations |
| 6 | -and ZIP archive of older check-ins) can be expensive to compute. | |
| 6 | +and ZIP archives of older check-ins) can be expensive to compute. | |
| 7 | 7 | If a spider or bot tries to walk a website implemented by |
| 8 | 8 | Fossil, it can present a crippling bandwidth and CPU load. |
| 9 | 9 | |
| 10 | 10 | The website presented by a Fossil server is intended to be used |
| 11 | 11 | interactively by humans, not walked by spiders. This article |
| 12 | 12 |
| --- www/antibot.wiki | |
| +++ www/antibot.wiki | |
| @@ -1,11 +1,11 @@ | |
| 1 | <title>Defense Against Spiders</title> |
| 2 | |
| 3 | The website presented by a Fossil server has many hyperlinks. |
| 4 | Even a modest project can have millions of pages in its |
| 5 | tree, and many of those pages (for example diffs and annotations |
| 6 | and ZIP archive of older check-ins) can be expensive to compute. |
| 7 | If a spider or bot tries to walk a website implemented by |
| 8 | Fossil, it can present a crippling bandwidth and CPU load. |
| 9 | |
| 10 | The website presented by a Fossil server is intended to be used |
| 11 | interactively by humans, not walked by spiders. This article |
| 12 |
| --- www/antibot.wiki | |
| +++ www/antibot.wiki | |
| @@ -1,11 +1,11 @@ | |
| 1 | <title>Defense Against Spiders</title> |
| 2 | |
| 3 | The website presented by a Fossil server has many hyperlinks. |
| 4 | Even a modest project can have millions of pages in its |
| 5 | tree, and many of those pages (for example diffs and annotations |
| 6 | and ZIP archives of older check-ins) can be expensive to compute. |
| 7 | If a spider or bot tries to walk a website implemented by |
| 8 | Fossil, it can present a crippling bandwidth and CPU load. |
| 9 | |
| 10 | The website presented by a Fossil server is intended to be used |
| 11 | interactively by humans, not walked by spiders. This article |
| 12 |
+1
-1
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -161,11 +161,11 @@ | ||
| 161 | 161 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 162 | 162 | first <a href="https://www.openssl.org/source/">download the official |
| 163 | 163 | source code for OpenSSL</a> and extract it to an appropriately named |
| 164 | 164 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 165 | 165 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 166 | -"<b>compat/openssl-1.1.1f</b>"), then make sure that some recent | |
| 166 | +"<b>compat/openssl-1.1.1g</b>"), then make sure that some recent | |
| 167 | 167 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 168 | 168 | and finally run one of the following commands: |
| 169 | 169 | <blockquote><pre> |
| 170 | 170 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 171 | 171 | </pre></blockquote> |
| 172 | 172 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -161,11 +161,11 @@ | |
| 161 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 162 | first <a href="https://www.openssl.org/source/">download the official |
| 163 | source code for OpenSSL</a> and extract it to an appropriately named |
| 164 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 165 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 166 | "<b>compat/openssl-1.1.1f</b>"), then make sure that some recent |
| 167 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 168 | and finally run one of the following commands: |
| 169 | <blockquote><pre> |
| 170 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 171 | </pre></blockquote> |
| 172 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -161,11 +161,11 @@ | |
| 161 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 162 | first <a href="https://www.openssl.org/source/">download the official |
| 163 | source code for OpenSSL</a> and extract it to an appropriately named |
| 164 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 165 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 166 | "<b>compat/openssl-1.1.1g</b>"), then make sure that some recent |
| 167 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 168 | and finally run one of the following commands: |
| 169 | <blockquote><pre> |
| 170 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 171 | </pre></blockquote> |
| 172 |
+43
-4
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,16 +1,41 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <a name='v2_11'></a> |
| 4 | 4 | <h2>Changes for Version 2.11 (pending)</h2> |
| 5 | 5 | |
| 6 | - * Support Markdown in the default ticket configuration. | |
| 6 | + * Support [/md_rules|Markdown] in the default ticket configuration. | |
| 7 | + * Timestamp strings in [./checkin_names.wiki|object names] | |
| 8 | + can now omit punctation. So, for example, "202004181942" and | |
| 9 | + "2020-04-18 19:42" mean the same thing. | |
| 10 | + * Enhance backlink processing so that it works with Markdown-formatted | |
| 11 | + tickets and so that it works for wiki pages. | |
| 12 | + Ticket [a3572c6a5b47cd5a]. | |
| 13 | + <ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to | |
| 14 | + take full advantage of this fix. Fossil will continue | |
| 15 | + to work without the rebuild, but the new backlinks will be missing.</ul> | |
| 16 | + * The algorithm for finding the | |
| 17 | + [./tech_overview.wiki#configloc|location of the configuration database] | |
| 18 | + is enhanced to be XDG-compliant. | |
| 19 | + * Add a hide/show feature to | |
| 20 | + [./wikitheory.wiki#assocwiki|associated wiki] display on | |
| 21 | + check-in and branch information pages. | |
| 22 | + * Enhance the "[/help?cmd=info|fossil info]" command so that it | |
| 23 | + works with no arguments even if not within an open check-out. | |
| 24 | + * Many improvements to the forum and especially email notification | |
| 25 | + of forum posts, in response to community feedback after switching | |
| 26 | + SQLite support from a mailing list over to the forum. | |
| 27 | + * Minimum length of a self-registered user ID increased from 3 to 6 | |
| 28 | + characters. | |
| 29 | + * When the "vfx" query parameter is used on the | |
| 30 | + "[/help?cmd=/timeline|/timeline]" page, it causes the complete | |
| 31 | + text of forum posts to be displayed. | |
| 7 | 32 | * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful. |
| 8 | 33 | * Expose the [/help?cmd=redirect-to-https|redirect-to-https] |
| 9 | 34 | setting to the [/help?cmd=settings|settings] command. |
| 10 | 35 | * Improve support for CGI on IIS web servers. |
| 11 | - * The [/help?cmd=/ext|/ext page] can now render index files, | |
| 36 | + * The [./serverext.wiki|/ext page] can now render index files, | |
| 12 | 37 | in the same way as the embedded docs. |
| 13 | 38 | * Most commands now support the Unix-conventional "<tt>--</tt>" |
| 14 | 39 | flag to treat all following arguments as filenames |
| 15 | 40 | instead of flags. |
| 16 | 41 | * Added the [/help?cmd=mimetypes|mimetypes config setting] |
| @@ -18,18 +43,24 @@ | ||
| 18 | 43 | * Add an option on the /Admin/Timeline setup page to set a default |
| 19 | 44 | timeline style other than "Modern". |
| 20 | 45 | * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs |
| 21 | 46 | of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated |
| 22 | 47 | into the check-in hash for the document currently being viewed. |
| 48 | + * Added the [/help?cmd=/phantoms|/phantoms] webpage that shows all | |
| 49 | + phantom artifacts. | |
| 50 | + * Enhancements to phantom processing to try to reduce | |
| 51 | + bandwidth-using chatter about phantoms on the sync protocol. | |
| 23 | 52 | * Security: Fossil now assumes that the schema of every |
| 24 | 53 | database it opens has been tampered with by an adversary and takes |
| 25 | 54 | extra precautions to ensure that such tampering is harmless. |
| 26 | 55 | * Security: Fossil now puts the Content-Security-Policy in the |
| 27 | 56 | HTTP reply header, in addition to also leaving it in the |
| 28 | - HTML <head> section, so that it is always available, if | |
| 57 | + HTML <head> section, so that it is always available, even | |
| 29 | 58 | if a custom skin overrides the HTML <head> and omits |
| 30 | 59 | the CSP in the process. |
| 60 | + * Output of the [/help?cmd=diff|fossil diff -y] command automatically | |
| 61 | + adjusts according to the terminal width. | |
| 31 | 62 | * The Content-Security-Policy is now set using the |
| 32 | 63 | [/help?cmd=default-csp|default-csp setting]. |
| 33 | 64 | * Merge conflicts caused via the [/help?cmd=merge|merge] and |
| 34 | 65 | [/help?cmd=update|update] commands no longer leave temporary |
| 35 | 66 | files behind unless the new <tt>--keep-merge-files</tt> flag |
| @@ -36,21 +67,29 @@ | ||
| 36 | 67 | is used. |
| 37 | 68 | * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible |
| 38 | 69 | to all users if the new "artifact_stats_enable" setting is turned |
| 39 | 70 | on. There is a new checkbox under the /Admin/Access menu to turn |
| 40 | 71 | that capability on and off. |
| 72 | + * Add the [/help?cmd=tls-config|fossil tls-config] command for viewing | |
| 73 | + the TLS configuration and the list of SSL Cert exceptions. | |
| 41 | 74 | * Captchas all include a button to read the captcha using an audio |
| 42 | 75 | file, so that they can be completed by the visually impaired. |
| 76 | + * Stop using the IP address as part of the login cookie. | |
| 77 | + * Bug fix: fix the SSL cert validation logic so that if an exception | |
| 78 | + is allowed for particular site, the exception expires as soon as the | |
| 79 | + cert changes values. | |
| 80 | + * Bug fix: the FTS search into for forum posts is now kept up-to-date | |
| 81 | + correctly. | |
| 43 | 82 | * Bug fix: the "fossil git export" command is now working on Windows |
| 44 | 83 | * Bug fix: display Technote items on the timeline correctly |
| 45 | 84 | * Bug fix: fix the capability summary matrix of the Security Audit |
| 46 | 85 | page so that it does not add "anonymous" capabilities to the |
| 47 | 86 | "nobody" user. |
| 48 | 87 | * Update internal Unicode character tables, used in regular expression |
| 49 | 88 | handling, from version 12.1 to 13. |
| 50 | 89 | * Many documentation enhancements. |
| 51 | - * Several minor enhancements to existing features. | |
| 90 | + * Many minor enhancements to existing features. | |
| 52 | 91 | |
| 53 | 92 | <a name='v2_10'></a> |
| 54 | 93 | <h2>Changes for Version 2.10 (2019-10-04)</h2> |
| 55 | 94 | |
| 56 | 95 | * Added support for [./serverext.wiki|CGI-based Server Extensions]. |
| 57 | 96 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,16 +1,41 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_11'></a> |
| 4 | <h2>Changes for Version 2.11 (pending)</h2> |
| 5 | |
| 6 | * Support Markdown in the default ticket configuration. |
| 7 | * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful. |
| 8 | * Expose the [/help?cmd=redirect-to-https|redirect-to-https] |
| 9 | setting to the [/help?cmd=settings|settings] command. |
| 10 | * Improve support for CGI on IIS web servers. |
| 11 | * The [/help?cmd=/ext|/ext page] can now render index files, |
| 12 | in the same way as the embedded docs. |
| 13 | * Most commands now support the Unix-conventional "<tt>--</tt>" |
| 14 | flag to treat all following arguments as filenames |
| 15 | instead of flags. |
| 16 | * Added the [/help?cmd=mimetypes|mimetypes config setting] |
| @@ -18,18 +43,24 @@ | |
| 18 | * Add an option on the /Admin/Timeline setup page to set a default |
| 19 | timeline style other than "Modern". |
| 20 | * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs |
| 21 | of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated |
| 22 | into the check-in hash for the document currently being viewed. |
| 23 | * Security: Fossil now assumes that the schema of every |
| 24 | database it opens has been tampered with by an adversary and takes |
| 25 | extra precautions to ensure that such tampering is harmless. |
| 26 | * Security: Fossil now puts the Content-Security-Policy in the |
| 27 | HTTP reply header, in addition to also leaving it in the |
| 28 | HTML <head> section, so that it is always available, if |
| 29 | if a custom skin overrides the HTML <head> and omits |
| 30 | the CSP in the process. |
| 31 | * The Content-Security-Policy is now set using the |
| 32 | [/help?cmd=default-csp|default-csp setting]. |
| 33 | * Merge conflicts caused via the [/help?cmd=merge|merge] and |
| 34 | [/help?cmd=update|update] commands no longer leave temporary |
| 35 | files behind unless the new <tt>--keep-merge-files</tt> flag |
| @@ -36,21 +67,29 @@ | |
| 36 | is used. |
| 37 | * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible |
| 38 | to all users if the new "artifact_stats_enable" setting is turned |
| 39 | on. There is a new checkbox under the /Admin/Access menu to turn |
| 40 | that capability on and off. |
| 41 | * Captchas all include a button to read the captcha using an audio |
| 42 | file, so that they can be completed by the visually impaired. |
| 43 | * Bug fix: the "fossil git export" command is now working on Windows |
| 44 | * Bug fix: display Technote items on the timeline correctly |
| 45 | * Bug fix: fix the capability summary matrix of the Security Audit |
| 46 | page so that it does not add "anonymous" capabilities to the |
| 47 | "nobody" user. |
| 48 | * Update internal Unicode character tables, used in regular expression |
| 49 | handling, from version 12.1 to 13. |
| 50 | * Many documentation enhancements. |
| 51 | * Several minor enhancements to existing features. |
| 52 | |
| 53 | <a name='v2_10'></a> |
| 54 | <h2>Changes for Version 2.10 (2019-10-04)</h2> |
| 55 | |
| 56 | * Added support for [./serverext.wiki|CGI-based Server Extensions]. |
| 57 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,16 +1,41 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_11'></a> |
| 4 | <h2>Changes for Version 2.11 (pending)</h2> |
| 5 | |
| 6 | * Support [/md_rules|Markdown] in the default ticket configuration. |
| 7 | * Timestamp strings in [./checkin_names.wiki|object names] |
| 8 | can now omit punctation. So, for example, "202004181942" and |
| 9 | "2020-04-18 19:42" mean the same thing. |
| 10 | * Enhance backlink processing so that it works with Markdown-formatted |
| 11 | tickets and so that it works for wiki pages. |
| 12 | Ticket [a3572c6a5b47cd5a]. |
| 13 | <ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to |
| 14 | take full advantage of this fix. Fossil will continue |
| 15 | to work without the rebuild, but the new backlinks will be missing.</ul> |
| 16 | * The algorithm for finding the |
| 17 | [./tech_overview.wiki#configloc|location of the configuration database] |
| 18 | is enhanced to be XDG-compliant. |
| 19 | * Add a hide/show feature to |
| 20 | [./wikitheory.wiki#assocwiki|associated wiki] display on |
| 21 | check-in and branch information pages. |
| 22 | * Enhance the "[/help?cmd=info|fossil info]" command so that it |
| 23 | works with no arguments even if not within an open check-out. |
| 24 | * Many improvements to the forum and especially email notification |
| 25 | of forum posts, in response to community feedback after switching |
| 26 | SQLite support from a mailing list over to the forum. |
| 27 | * Minimum length of a self-registered user ID increased from 3 to 6 |
| 28 | characters. |
| 29 | * When the "vfx" query parameter is used on the |
| 30 | "[/help?cmd=/timeline|/timeline]" page, it causes the complete |
| 31 | text of forum posts to be displayed. |
| 32 | * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful. |
| 33 | * Expose the [/help?cmd=redirect-to-https|redirect-to-https] |
| 34 | setting to the [/help?cmd=settings|settings] command. |
| 35 | * Improve support for CGI on IIS web servers. |
| 36 | * The [./serverext.wiki|/ext page] can now render index files, |
| 37 | in the same way as the embedded docs. |
| 38 | * Most commands now support the Unix-conventional "<tt>--</tt>" |
| 39 | flag to treat all following arguments as filenames |
| 40 | instead of flags. |
| 41 | * Added the [/help?cmd=mimetypes|mimetypes config setting] |
| @@ -18,18 +43,24 @@ | |
| 43 | * Add an option on the /Admin/Timeline setup page to set a default |
| 44 | timeline style other than "Modern". |
| 45 | * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs |
| 46 | of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated |
| 47 | into the check-in hash for the document currently being viewed. |
| 48 | * Added the [/help?cmd=/phantoms|/phantoms] webpage that shows all |
| 49 | phantom artifacts. |
| 50 | * Enhancements to phantom processing to try to reduce |
| 51 | bandwidth-using chatter about phantoms on the sync protocol. |
| 52 | * Security: Fossil now assumes that the schema of every |
| 53 | database it opens has been tampered with by an adversary and takes |
| 54 | extra precautions to ensure that such tampering is harmless. |
| 55 | * Security: Fossil now puts the Content-Security-Policy in the |
| 56 | HTTP reply header, in addition to also leaving it in the |
| 57 | HTML <head> section, so that it is always available, even |
| 58 | if a custom skin overrides the HTML <head> and omits |
| 59 | the CSP in the process. |
| 60 | * Output of the [/help?cmd=diff|fossil diff -y] command automatically |
| 61 | adjusts according to the terminal width. |
| 62 | * The Content-Security-Policy is now set using the |
| 63 | [/help?cmd=default-csp|default-csp setting]. |
| 64 | * Merge conflicts caused via the [/help?cmd=merge|merge] and |
| 65 | [/help?cmd=update|update] commands no longer leave temporary |
| 66 | files behind unless the new <tt>--keep-merge-files</tt> flag |
| @@ -36,21 +67,29 @@ | |
| 67 | is used. |
| 68 | * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible |
| 69 | to all users if the new "artifact_stats_enable" setting is turned |
| 70 | on. There is a new checkbox under the /Admin/Access menu to turn |
| 71 | that capability on and off. |
| 72 | * Add the [/help?cmd=tls-config|fossil tls-config] command for viewing |
| 73 | the TLS configuration and the list of SSL Cert exceptions. |
| 74 | * Captchas all include a button to read the captcha using an audio |
| 75 | file, so that they can be completed by the visually impaired. |
| 76 | * Stop using the IP address as part of the login cookie. |
| 77 | * Bug fix: fix the SSL cert validation logic so that if an exception |
| 78 | is allowed for particular site, the exception expires as soon as the |
| 79 | cert changes values. |
| 80 | * Bug fix: the FTS search into for forum posts is now kept up-to-date |
| 81 | correctly. |
| 82 | * Bug fix: the "fossil git export" command is now working on Windows |
| 83 | * Bug fix: display Technote items on the timeline correctly |
| 84 | * Bug fix: fix the capability summary matrix of the Security Audit |
| 85 | page so that it does not add "anonymous" capabilities to the |
| 86 | "nobody" user. |
| 87 | * Update internal Unicode character tables, used in regular expression |
| 88 | handling, from version 12.1 to 13. |
| 89 | * Many documentation enhancements. |
| 90 | * Many minor enhancements to existing features. |
| 91 | |
| 92 | <a name='v2_10'></a> |
| 93 | <h2>Changes for Version 2.10 (2019-10-04)</h2> |
| 94 | |
| 95 | * Added support for [./serverext.wiki|CGI-based Server Extensions]. |
| 96 |
+1
-2
| --- www/css-tricks.md | ||
| +++ www/css-tricks.md | ||
| @@ -48,11 +48,11 @@ | ||
| 48 | 48 | ## Is it Really `!important`? |
| 49 | 49 | |
| 50 | 50 | By and large, CSS's `!important` qualifier is not needed when |
| 51 | 51 | customzing Fossil's CSS. On occasion, however, particular styles may |
| 52 | 52 | be set directly on DOM elements when Fossil generates its HTML, and |
| 53 | -such cases require the user of `!important` to override them. | |
| 53 | +such cases require the use of `!important` to override them. | |
| 54 | 54 | |
| 55 | 55 | |
| 56 | 56 | <!-- ============================================================ --> |
| 57 | 57 | # Main UI CSS |
| 58 | 58 | |
| @@ -103,6 +103,5 @@ | ||
| 103 | 103 | div.forumPostBody { |
| 104 | 104 | max-height: 25em; /* change to the preferred maximum effective height */ |
| 105 | 105 | overflow: auto; /* tells the browser to add scrollbars as needed */ |
| 106 | 106 | } |
| 107 | 107 | ``` |
| 108 | - | |
| 109 | 108 |
| --- www/css-tricks.md | |
| +++ www/css-tricks.md | |
| @@ -48,11 +48,11 @@ | |
| 48 | ## Is it Really `!important`? |
| 49 | |
| 50 | By and large, CSS's `!important` qualifier is not needed when |
| 51 | customzing Fossil's CSS. On occasion, however, particular styles may |
| 52 | be set directly on DOM elements when Fossil generates its HTML, and |
| 53 | such cases require the user of `!important` to override them. |
| 54 | |
| 55 | |
| 56 | <!-- ============================================================ --> |
| 57 | # Main UI CSS |
| 58 | |
| @@ -103,6 +103,5 @@ | |
| 103 | div.forumPostBody { |
| 104 | max-height: 25em; /* change to the preferred maximum effective height */ |
| 105 | overflow: auto; /* tells the browser to add scrollbars as needed */ |
| 106 | } |
| 107 | ``` |
| 108 | |
| 109 |
| --- www/css-tricks.md | |
| +++ www/css-tricks.md | |
| @@ -48,11 +48,11 @@ | |
| 48 | ## Is it Really `!important`? |
| 49 | |
| 50 | By and large, CSS's `!important` qualifier is not needed when |
| 51 | customzing Fossil's CSS. On occasion, however, particular styles may |
| 52 | be set directly on DOM elements when Fossil generates its HTML, and |
| 53 | such cases require the use of `!important` to override them. |
| 54 | |
| 55 | |
| 56 | <!-- ============================================================ --> |
| 57 | # Main UI CSS |
| 58 | |
| @@ -103,6 +103,5 @@ | |
| 103 | div.forumPostBody { |
| 104 | max-height: 25em; /* change to the preferred maximum effective height */ |
| 105 | overflow: auto; /* tells the browser to add scrollbars as needed */ |
| 106 | } |
| 107 | ``` |
| 108 |
+32
-44
| --- www/env-opts.md | ||
| +++ www/env-opts.md | ||
| @@ -112,25 +112,14 @@ | ||
| 112 | 112 | |
| 113 | 113 | |
| 114 | 114 | Environment Variables |
| 115 | 115 | --------------------- |
| 116 | 116 | |
| 117 | -On most platforms, the location of the user’s account-wide `.fossil` | |
| 118 | -file is either `FOSSIL_HOME` or `HOME`, in that order. This ordering | |
| 119 | -lets you put this file somewhere other than at the top of your user’s | |
| 120 | -home directory by defining `FOSSIL_HOME` to mask the always-defined | |
| 121 | -`HOME`. | |
| 122 | - | |
| 123 | -For native Windows builds and for Cygwin builds, the file is called | |
| 124 | -`_fossil` instead to avoid problems with old programs that assume file | |
| 125 | -names cannot begin with a dot, as was true in old versions of Windows | |
| 126 | -and in MS-DOS. (Newer Microsoft OSes and file systems don’t have a | |
| 127 | -problem with such files, but still we take the safe path in case you’re | |
| 128 | -on a system with software that can’t cope.) We start our search with | |
| 129 | -`FOSSIL_HOME` again, but instead of falling back to `HOME`, we instead | |
| 130 | -try `USERPROFILE`, then `LOCALAPPDATA`, then `APPDATA`, and finally we | |
| 131 | -concatenate `HOMEDRIVE` + `HOMEPATH`. | |
| 117 | +The location of the user's account-wide [configuration database][configdb] | |
| 118 | +depends on the operating system and on the existance of various | |
| 119 | +environment variables and/or files. See the discussion of the | |
| 120 | +[configuration database location algorithm][configloc] for details. | |
| 132 | 121 | |
| 133 | 122 | `EDITOR`: Name the editor to use for check-in and stash comments. |
| 134 | 123 | Overridden by the local or global `editor` setting or the `VISUAL` |
| 135 | 124 | environment variable. |
| 136 | 125 | |
| @@ -149,16 +138,13 @@ | ||
| 149 | 138 | local interactive user via the command line). This can be useful for |
| 150 | 139 | local (or remote) testing of the moderation subsystem and its impact |
| 151 | 140 | on the contents and status of wiki pages. |
| 152 | 141 | |
| 153 | 142 | |
| 154 | -`FOSSIL_HOME`: Location of the `~/.fossil` file. The first environment | |
| 155 | -variable found in the environment from the list `FOSSIL_HOME`, | |
| 156 | -`LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and | |
| 157 | -`HOMEPATH` (Windows, used together), and `HOME` is used as the | |
| 158 | -location of the `~/.fossil` file. | |
| 159 | - | |
| 143 | +`FOSSIL_HOME`: Location of [configuration database][configdb]. | |
| 144 | +See the [configuration database location][configloc] description | |
| 145 | +for additional information. | |
| 160 | 146 | |
| 161 | 147 | `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for |
| 162 | 148 | SEE as text to be hashed into the actual encryption key. This has no |
| 163 | 149 | effect if Fossil was not compiled with SEE support enabled. |
| 164 | 150 | |
| @@ -195,15 +181,12 @@ | ||
| 195 | 181 | |
| 196 | 182 | `GATEWAY_INTERFACE`: If present and the `--nocgi` option is not, assume |
| 197 | 183 | fossil is invoked from a web server as a CGI command, and act |
| 198 | 184 | accordingly. |
| 199 | 185 | |
| 200 | -`HOME`: Location of the `~/.fossil` file. The first environment | |
| 201 | -variable found in the environment from the list `FOSSIL_HOME`, | |
| 202 | -`LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and | |
| 203 | -`HOMEPATH` (Windows, used together), and `HOME` is used as the | |
| 204 | -location of the `~/.fossil` file. | |
| 186 | +`HOME`: Potential location of the [configuration database][configdb]. | |
| 187 | +See the [configuration database location][configloc] description for details. | |
| 205 | 188 | |
| 206 | 189 | `HOMEDRIVE`, `HOMEPATH`: (Windows) Location of the `~/.fossil` file. |
| 207 | 190 | The first environment variable found in the environment from the list |
| 208 | 191 | `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), |
| 209 | 192 | `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is |
| @@ -402,28 +385,30 @@ | ||
| 402 | 385 | first found environment variable from the list `FOSSIL_USER`, `USER`, |
| 403 | 386 | `LOGNAME`, and `USERNAME`, is the user name. As a final fallback, if |
| 404 | 387 | none of those are set, then the default user name is "root". |
| 405 | 388 | |
| 406 | 389 | |
| 407 | -### Home Directory | |
| 408 | - | |
| 409 | -Fossil keeps some information interesting to each user in the user's | |
| 410 | -home directory. This includes the global settings and the list of | |
| 411 | -repositories and checkouts used by `fossil all`. | |
| 412 | - | |
| 413 | -The user's home directory is specified by the first environment | |
| 414 | -variable found in the environment from the list `FOSSIL_HOME`, | |
| 415 | -`LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and | |
| 416 | -`HOMEPATH` (Windows, used together), and `HOME`. | |
| 417 | - | |
| 418 | -SQLite has its own notion of the user's home directory, which is only | |
| 419 | -exposed if the interactive SQL shell is run with the "fossil | |
| 420 | -sqlite3" command. Being a separate library, SQLite uses many of the | |
| 421 | -same variables to find the home directory, but uses them in a | |
| 422 | -different order, and does not use the `FOSSIL_HOME` variable at all. | |
| 423 | - | |
| 424 | - | |
| 390 | +### Configuration Database Location | |
| 391 | + | |
| 392 | +Fossil keeps some information pertinent to each user in the user's | |
| 393 | +[configuration database file][configdb]. | |
| 394 | +The configuration database file includes the global settings | |
| 395 | +and the list of repositories and checkouts used by `fossil all`. | |
| 396 | + | |
| 397 | +The location of the configuration database file depends on the | |
| 398 | +operating system and on the existance of various environment | |
| 399 | +variables and/or files. In brief, the configuration database is | |
| 400 | +usually: | |
| 401 | + | |
| 402 | + * Traditional unix → "`$HOME/.fossil`" | |
| 403 | + * Windows → "`%LOCALAPPDATA%/_fossil`" | |
| 404 | + * [XDG-unix][xdg] → "`$HOME/.config/fossil.db`" | |
| 405 | + | |
| 406 | +[xdg]: https://www.freedesktop.org/wiki/ | |
| 407 | + | |
| 408 | +See the [configuration database location | |
| 409 | +algorithm][configloc] discussion for full information. | |
| 425 | 410 | |
| 426 | 411 | ### SQLite VFS to use |
| 427 | 412 | |
| 428 | 413 | See [the SQLite documentation](http://www.sqlite.org/vfs.html) for an |
| 429 | 414 | explanation of what a VFS actually is and what it does. |
| @@ -491,5 +476,8 @@ | ||
| 491 | 476 | On Apple platforms, it assumes that `open` is the command to open an |
| 492 | 477 | URL in the user's configured default browser. |
| 493 | 478 | |
| 494 | 479 | On Windows platforms, it assumes that `start` is the command to open |
| 495 | 480 | an URL in the user's configured default browser. |
| 481 | + | |
| 482 | +[configdb]: ./tech_overview.wiki#configdb | |
| 483 | +[configloc]: ./tech_overview.wiki#configloc | |
| 496 | 484 |
| --- www/env-opts.md | |
| +++ www/env-opts.md | |
| @@ -112,25 +112,14 @@ | |
| 112 | |
| 113 | |
| 114 | Environment Variables |
| 115 | --------------------- |
| 116 | |
| 117 | On most platforms, the location of the user’s account-wide `.fossil` |
| 118 | file is either `FOSSIL_HOME` or `HOME`, in that order. This ordering |
| 119 | lets you put this file somewhere other than at the top of your user’s |
| 120 | home directory by defining `FOSSIL_HOME` to mask the always-defined |
| 121 | `HOME`. |
| 122 | |
| 123 | For native Windows builds and for Cygwin builds, the file is called |
| 124 | `_fossil` instead to avoid problems with old programs that assume file |
| 125 | names cannot begin with a dot, as was true in old versions of Windows |
| 126 | and in MS-DOS. (Newer Microsoft OSes and file systems don’t have a |
| 127 | problem with such files, but still we take the safe path in case you’re |
| 128 | on a system with software that can’t cope.) We start our search with |
| 129 | `FOSSIL_HOME` again, but instead of falling back to `HOME`, we instead |
| 130 | try `USERPROFILE`, then `LOCALAPPDATA`, then `APPDATA`, and finally we |
| 131 | concatenate `HOMEDRIVE` + `HOMEPATH`. |
| 132 | |
| 133 | `EDITOR`: Name the editor to use for check-in and stash comments. |
| 134 | Overridden by the local or global `editor` setting or the `VISUAL` |
| 135 | environment variable. |
| 136 | |
| @@ -149,16 +138,13 @@ | |
| 149 | local interactive user via the command line). This can be useful for |
| 150 | local (or remote) testing of the moderation subsystem and its impact |
| 151 | on the contents and status of wiki pages. |
| 152 | |
| 153 | |
| 154 | `FOSSIL_HOME`: Location of the `~/.fossil` file. The first environment |
| 155 | variable found in the environment from the list `FOSSIL_HOME`, |
| 156 | `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and |
| 157 | `HOMEPATH` (Windows, used together), and `HOME` is used as the |
| 158 | location of the `~/.fossil` file. |
| 159 | |
| 160 | |
| 161 | `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for |
| 162 | SEE as text to be hashed into the actual encryption key. This has no |
| 163 | effect if Fossil was not compiled with SEE support enabled. |
| 164 | |
| @@ -195,15 +181,12 @@ | |
| 195 | |
| 196 | `GATEWAY_INTERFACE`: If present and the `--nocgi` option is not, assume |
| 197 | fossil is invoked from a web server as a CGI command, and act |
| 198 | accordingly. |
| 199 | |
| 200 | `HOME`: Location of the `~/.fossil` file. The first environment |
| 201 | variable found in the environment from the list `FOSSIL_HOME`, |
| 202 | `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and |
| 203 | `HOMEPATH` (Windows, used together), and `HOME` is used as the |
| 204 | location of the `~/.fossil` file. |
| 205 | |
| 206 | `HOMEDRIVE`, `HOMEPATH`: (Windows) Location of the `~/.fossil` file. |
| 207 | The first environment variable found in the environment from the list |
| 208 | `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), |
| 209 | `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is |
| @@ -402,28 +385,30 @@ | |
| 402 | first found environment variable from the list `FOSSIL_USER`, `USER`, |
| 403 | `LOGNAME`, and `USERNAME`, is the user name. As a final fallback, if |
| 404 | none of those are set, then the default user name is "root". |
| 405 | |
| 406 | |
| 407 | ### Home Directory |
| 408 | |
| 409 | Fossil keeps some information interesting to each user in the user's |
| 410 | home directory. This includes the global settings and the list of |
| 411 | repositories and checkouts used by `fossil all`. |
| 412 | |
| 413 | The user's home directory is specified by the first environment |
| 414 | variable found in the environment from the list `FOSSIL_HOME`, |
| 415 | `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and |
| 416 | `HOMEPATH` (Windows, used together), and `HOME`. |
| 417 | |
| 418 | SQLite has its own notion of the user's home directory, which is only |
| 419 | exposed if the interactive SQL shell is run with the "fossil |
| 420 | sqlite3" command. Being a separate library, SQLite uses many of the |
| 421 | same variables to find the home directory, but uses them in a |
| 422 | different order, and does not use the `FOSSIL_HOME` variable at all. |
| 423 | |
| 424 | |
| 425 | |
| 426 | ### SQLite VFS to use |
| 427 | |
| 428 | See [the SQLite documentation](http://www.sqlite.org/vfs.html) for an |
| 429 | explanation of what a VFS actually is and what it does. |
| @@ -491,5 +476,8 @@ | |
| 491 | On Apple platforms, it assumes that `open` is the command to open an |
| 492 | URL in the user's configured default browser. |
| 493 | |
| 494 | On Windows platforms, it assumes that `start` is the command to open |
| 495 | an URL in the user's configured default browser. |
| 496 |
| --- www/env-opts.md | |
| +++ www/env-opts.md | |
| @@ -112,25 +112,14 @@ | |
| 112 | |
| 113 | |
| 114 | Environment Variables |
| 115 | --------------------- |
| 116 | |
| 117 | The location of the user's account-wide [configuration database][configdb] |
| 118 | depends on the operating system and on the existance of various |
| 119 | environment variables and/or files. See the discussion of the |
| 120 | [configuration database location algorithm][configloc] for details. |
| 121 | |
| 122 | `EDITOR`: Name the editor to use for check-in and stash comments. |
| 123 | Overridden by the local or global `editor` setting or the `VISUAL` |
| 124 | environment variable. |
| 125 | |
| @@ -149,16 +138,13 @@ | |
| 138 | local interactive user via the command line). This can be useful for |
| 139 | local (or remote) testing of the moderation subsystem and its impact |
| 140 | on the contents and status of wiki pages. |
| 141 | |
| 142 | |
| 143 | `FOSSIL_HOME`: Location of [configuration database][configdb]. |
| 144 | See the [configuration database location][configloc] description |
| 145 | for additional information. |
| 146 | |
| 147 | `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for |
| 148 | SEE as text to be hashed into the actual encryption key. This has no |
| 149 | effect if Fossil was not compiled with SEE support enabled. |
| 150 | |
| @@ -195,15 +181,12 @@ | |
| 181 | |
| 182 | `GATEWAY_INTERFACE`: If present and the `--nocgi` option is not, assume |
| 183 | fossil is invoked from a web server as a CGI command, and act |
| 184 | accordingly. |
| 185 | |
| 186 | `HOME`: Potential location of the [configuration database][configdb]. |
| 187 | See the [configuration database location][configloc] description for details. |
| 188 | |
| 189 | `HOMEDRIVE`, `HOMEPATH`: (Windows) Location of the `~/.fossil` file. |
| 190 | The first environment variable found in the environment from the list |
| 191 | `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), |
| 192 | `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is |
| @@ -402,28 +385,30 @@ | |
| 385 | first found environment variable from the list `FOSSIL_USER`, `USER`, |
| 386 | `LOGNAME`, and `USERNAME`, is the user name. As a final fallback, if |
| 387 | none of those are set, then the default user name is "root". |
| 388 | |
| 389 | |
| 390 | ### Configuration Database Location |
| 391 | |
| 392 | Fossil keeps some information pertinent to each user in the user's |
| 393 | [configuration database file][configdb]. |
| 394 | The configuration database file includes the global settings |
| 395 | and the list of repositories and checkouts used by `fossil all`. |
| 396 | |
| 397 | The location of the configuration database file depends on the |
| 398 | operating system and on the existance of various environment |
| 399 | variables and/or files. In brief, the configuration database is |
| 400 | usually: |
| 401 | |
| 402 | * Traditional unix → "`$HOME/.fossil`" |
| 403 | * Windows → "`%LOCALAPPDATA%/_fossil`" |
| 404 | * [XDG-unix][xdg] → "`$HOME/.config/fossil.db`" |
| 405 | |
| 406 | [xdg]: https://www.freedesktop.org/wiki/ |
| 407 | |
| 408 | See the [configuration database location |
| 409 | algorithm][configloc] discussion for full information. |
| 410 | |
| 411 | ### SQLite VFS to use |
| 412 | |
| 413 | See [the SQLite documentation](http://www.sqlite.org/vfs.html) for an |
| 414 | explanation of what a VFS actually is and what it does. |
| @@ -491,5 +476,8 @@ | |
| 476 | On Apple platforms, it assumes that `open` is the command to open an |
| 477 | URL in the user's configured default browser. |
| 478 | |
| 479 | On Windows platforms, it assumes that `start` is the command to open |
| 480 | an URL in the user's configured default browser. |
| 481 | |
| 482 | [configdb]: ./tech_overview.wiki#configdb |
| 483 | [configloc]: ./tech_overview.wiki#configloc |
| 484 |
+3
-1
| --- www/faq.wiki | ||
| +++ www/faq.wiki | ||
| @@ -1,9 +1,11 @@ | ||
| 1 | 1 | <title>Fossil FAQ</title> |
| 2 | 2 | <h1 align="center">Frequently Asked Questions</h1> |
| 3 | 3 | |
| 4 | -<p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. | |
| 4 | +<p>Note: | |
| 5 | +This page is old and has not been kept up-to-date. See the | |
| 6 | +[/finfo?name=www/faq.wiki|change history of this page].</p> | |
| 5 | 7 | |
| 6 | 8 | <ol> |
| 7 | 9 | <li><a href="#q1">What GUIs are available for fossil?</a></li> |
| 8 | 10 | <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> |
| 9 | 11 | <li><a href="#q3">How do I create a new branch?</a></li> |
| 10 | 12 |
| --- www/faq.wiki | |
| +++ www/faq.wiki | |
| @@ -1,9 +1,11 @@ | |
| 1 | <title>Fossil FAQ</title> |
| 2 | <h1 align="center">Frequently Asked Questions</h1> |
| 3 | |
| 4 | <p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. |
| 5 | |
| 6 | <ol> |
| 7 | <li><a href="#q1">What GUIs are available for fossil?</a></li> |
| 8 | <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> |
| 9 | <li><a href="#q3">How do I create a new branch?</a></li> |
| 10 |
| --- www/faq.wiki | |
| +++ www/faq.wiki | |
| @@ -1,9 +1,11 @@ | |
| 1 | <title>Fossil FAQ</title> |
| 2 | <h1 align="center">Frequently Asked Questions</h1> |
| 3 | |
| 4 | <p>Note: |
| 5 | This page is old and has not been kept up-to-date. See the |
| 6 | [/finfo?name=www/faq.wiki|change history of this page].</p> |
| 7 | |
| 8 | <ol> |
| 9 | <li><a href="#q1">What GUIs are available for fossil?</a></li> |
| 10 | <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> |
| 11 | <li><a href="#q3">How do I create a new branch?</a></li> |
| 12 |
+8
-8
| --- www/fossil-v-git.wiki | ||
| +++ www/fossil-v-git.wiki | ||
| @@ -114,14 +114,14 @@ | ||
| 114 | 114 | the design. One way to describe Fossil is that it is |
| 115 | 115 | "[https://github.com/ | GitHub]-in-a-box." |
| 116 | 116 | |
| 117 | 117 | Fossil can do operations over all local repo clones and check-out |
| 118 | 118 | directories with a single command. For example, Fossil lets you say |
| 119 | -<tt>fossil all pull</tt> on a laptop prior to taking it off the network | |
| 119 | +<tt>fossil all sync</tt> on a laptop prior to taking it off the network | |
| 120 | 120 | hosting those repos. You can sync up to all of the private repos on your |
| 121 | 121 | company network plus those public Internet-hosted repos you use. Whether |
| 122 | -going out for a working lunch or on a transoceanic an airplane trip, one | |
| 122 | +going out for a working lunch or on a transoceanic airplane trip, one | |
| 123 | 123 | command gets you in sync. This works with several other Fossil |
| 124 | 124 | sub-commands, such as <tt>fossil all changes</tt> to get a list of files |
| 125 | 125 | that you forgot to commit prior to the end of your working day, across |
| 126 | 126 | all repos. |
| 127 | 127 | |
| @@ -258,11 +258,11 @@ | ||
| 258 | 258 | are looking at some historical check-in then you cannot ask "What came |
| 259 | 259 | next?" or "What are the children of this check-in?" |
| 260 | 260 | |
| 261 | 261 | Fossil, on the other hand, parses essential information about check-ins |
| 262 | 262 | (parents, children, committers, comments, files changed, etc.) into a |
| 263 | -relational database that can be easily queried using concise SQL | |
| 263 | +relational database that can easily be queried using concise SQL | |
| 264 | 264 | statements to find both ancestors and descendants of a check-in. This is |
| 265 | 265 | the hybrid data model mentioned above: Fossil manages your check-in and |
| 266 | 266 | other data in a NoSQL block chain structured data store, but that's backed |
| 267 | 267 | by a set of relational lookup tables for quick indexing into that |
| 268 | 268 | artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The |
| @@ -759,13 +759,13 @@ | ||
| 759 | 759 | |
| 760 | 760 | Fossil cannot sensibly work that way because of its default-enabled |
| 761 | 761 | autosync feature. Instead of jumping straight to the commit step, Fossil |
| 762 | 762 | applies the proposed merge to the local working directory only, |
| 763 | 763 | requiring a separate check-in step before the change is committed to the |
| 764 | -repository blockchain. This gives you a chance to test the change, | |
| 765 | -whether manually, or by running your software's automatic tests, or | |
| 766 | -both. | |
| 764 | +repository blockchain. This gives you a chance to test the change first, | |
| 765 | +either manually or by running your software's automatic tests. (Ideally, | |
| 766 | +both!) | |
| 767 | 767 | |
| 768 | 768 | Another difference is that because Fossil requires an explicit commit |
| 769 | 769 | for a merge, it makes you give an explicit commit <i>message</i> for |
| 770 | 770 | each merge, whereas Git writes that commit message itself by default |
| 771 | 771 | unless you give the optional <tt>--edit</tt> flag to override it. |
| @@ -805,11 +805,11 @@ | ||
| 805 | 805 | brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is |
| 806 | 806 | currently 41 lines long, to which you want to add the 600 lines of |
| 807 | 807 | [./branching.wiki | the branching document]. The equivalent |
| 808 | 808 | documentation in Git is the aggregation of the man pages for the above |
| 809 | 809 | three commands, which is over 1000 lines, much of it mutually redundant. |
| 810 | -(e.g. the <tt>--edit</tt> and <tt>--no-commit</tt> options get | |
| 810 | +(e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get | |
| 811 | 811 | described three times, each time differently.) Fossil's |
| 812 | 812 | documentation is not only more concise, it gives a nice split of brief |
| 813 | 813 | online help and full online documentation. |
| 814 | 814 | |
| 815 | 815 | |
| @@ -855,11 +855,11 @@ | ||
| 855 | 855 | weakening the case for continuing to use SHA-1. |
| 856 | 856 | |
| 857 | 857 | The practical impact of attacks like SHAttered and SHAmbles on the |
| 858 | 858 | Git and Fossil blockchains isn't clear, but you want to have your repositories |
| 859 | 859 | moved over to a stronger hash algorithm before someone figures out how |
| 860 | -to make use of the weaknesses in the old one. Fossil had this covered | |
| 860 | +to make use of the weaknesses in the old one. Fossil has had this covered | |
| 861 | 861 | for years now, so that the solution is now almost universally deployed. |
| 862 | 862 | |
| 863 | 863 | <hr/> |
| 864 | 864 | |
| 865 | 865 | <h3>Asides and Digressions</h3> |
| 866 | 866 | |
| 867 | 867 | ADDED www/gitusers.md |
| 868 | 868 | ADDED www/history.md |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -114,14 +114,14 @@ | |
| 114 | the design. One way to describe Fossil is that it is |
| 115 | "[https://github.com/ | GitHub]-in-a-box." |
| 116 | |
| 117 | Fossil can do operations over all local repo clones and check-out |
| 118 | directories with a single command. For example, Fossil lets you say |
| 119 | <tt>fossil all pull</tt> on a laptop prior to taking it off the network |
| 120 | hosting those repos. You can sync up to all of the private repos on your |
| 121 | company network plus those public Internet-hosted repos you use. Whether |
| 122 | going out for a working lunch or on a transoceanic an airplane trip, one |
| 123 | command gets you in sync. This works with several other Fossil |
| 124 | sub-commands, such as <tt>fossil all changes</tt> to get a list of files |
| 125 | that you forgot to commit prior to the end of your working day, across |
| 126 | all repos. |
| 127 | |
| @@ -258,11 +258,11 @@ | |
| 258 | are looking at some historical check-in then you cannot ask "What came |
| 259 | next?" or "What are the children of this check-in?" |
| 260 | |
| 261 | Fossil, on the other hand, parses essential information about check-ins |
| 262 | (parents, children, committers, comments, files changed, etc.) into a |
| 263 | relational database that can be easily queried using concise SQL |
| 264 | statements to find both ancestors and descendants of a check-in. This is |
| 265 | the hybrid data model mentioned above: Fossil manages your check-in and |
| 266 | other data in a NoSQL block chain structured data store, but that's backed |
| 267 | by a set of relational lookup tables for quick indexing into that |
| 268 | artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The |
| @@ -759,13 +759,13 @@ | |
| 759 | |
| 760 | Fossil cannot sensibly work that way because of its default-enabled |
| 761 | autosync feature. Instead of jumping straight to the commit step, Fossil |
| 762 | applies the proposed merge to the local working directory only, |
| 763 | requiring a separate check-in step before the change is committed to the |
| 764 | repository blockchain. This gives you a chance to test the change, |
| 765 | whether manually, or by running your software's automatic tests, or |
| 766 | both. |
| 767 | |
| 768 | Another difference is that because Fossil requires an explicit commit |
| 769 | for a merge, it makes you give an explicit commit <i>message</i> for |
| 770 | each merge, whereas Git writes that commit message itself by default |
| 771 | unless you give the optional <tt>--edit</tt> flag to override it. |
| @@ -805,11 +805,11 @@ | |
| 805 | brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is |
| 806 | currently 41 lines long, to which you want to add the 600 lines of |
| 807 | [./branching.wiki | the branching document]. The equivalent |
| 808 | documentation in Git is the aggregation of the man pages for the above |
| 809 | three commands, which is over 1000 lines, much of it mutually redundant. |
| 810 | (e.g. the <tt>--edit</tt> and <tt>--no-commit</tt> options get |
| 811 | described three times, each time differently.) Fossil's |
| 812 | documentation is not only more concise, it gives a nice split of brief |
| 813 | online help and full online documentation. |
| 814 | |
| 815 | |
| @@ -855,11 +855,11 @@ | |
| 855 | weakening the case for continuing to use SHA-1. |
| 856 | |
| 857 | The practical impact of attacks like SHAttered and SHAmbles on the |
| 858 | Git and Fossil blockchains isn't clear, but you want to have your repositories |
| 859 | moved over to a stronger hash algorithm before someone figures out how |
| 860 | to make use of the weaknesses in the old one. Fossil had this covered |
| 861 | for years now, so that the solution is now almost universally deployed. |
| 862 | |
| 863 | <hr/> |
| 864 | |
| 865 | <h3>Asides and Digressions</h3> |
| 866 | |
| 867 | DDED www/gitusers.md |
| 868 | DDED www/history.md |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -114,14 +114,14 @@ | |
| 114 | the design. One way to describe Fossil is that it is |
| 115 | "[https://github.com/ | GitHub]-in-a-box." |
| 116 | |
| 117 | Fossil can do operations over all local repo clones and check-out |
| 118 | directories with a single command. For example, Fossil lets you say |
| 119 | <tt>fossil all sync</tt> on a laptop prior to taking it off the network |
| 120 | hosting those repos. You can sync up to all of the private repos on your |
| 121 | company network plus those public Internet-hosted repos you use. Whether |
| 122 | going out for a working lunch or on a transoceanic airplane trip, one |
| 123 | command gets you in sync. This works with several other Fossil |
| 124 | sub-commands, such as <tt>fossil all changes</tt> to get a list of files |
| 125 | that you forgot to commit prior to the end of your working day, across |
| 126 | all repos. |
| 127 | |
| @@ -258,11 +258,11 @@ | |
| 258 | are looking at some historical check-in then you cannot ask "What came |
| 259 | next?" or "What are the children of this check-in?" |
| 260 | |
| 261 | Fossil, on the other hand, parses essential information about check-ins |
| 262 | (parents, children, committers, comments, files changed, etc.) into a |
| 263 | relational database that can easily be queried using concise SQL |
| 264 | statements to find both ancestors and descendants of a check-in. This is |
| 265 | the hybrid data model mentioned above: Fossil manages your check-in and |
| 266 | other data in a NoSQL block chain structured data store, but that's backed |
| 267 | by a set of relational lookup tables for quick indexing into that |
| 268 | artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The |
| @@ -759,13 +759,13 @@ | |
| 759 | |
| 760 | Fossil cannot sensibly work that way because of its default-enabled |
| 761 | autosync feature. Instead of jumping straight to the commit step, Fossil |
| 762 | applies the proposed merge to the local working directory only, |
| 763 | requiring a separate check-in step before the change is committed to the |
| 764 | repository blockchain. This gives you a chance to test the change first, |
| 765 | either manually or by running your software's automatic tests. (Ideally, |
| 766 | both!) |
| 767 | |
| 768 | Another difference is that because Fossil requires an explicit commit |
| 769 | for a merge, it makes you give an explicit commit <i>message</i> for |
| 770 | each merge, whereas Git writes that commit message itself by default |
| 771 | unless you give the optional <tt>--edit</tt> flag to override it. |
| @@ -805,11 +805,11 @@ | |
| 805 | brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is |
| 806 | currently 41 lines long, to which you want to add the 600 lines of |
| 807 | [./branching.wiki | the branching document]. The equivalent |
| 808 | documentation in Git is the aggregation of the man pages for the above |
| 809 | three commands, which is over 1000 lines, much of it mutually redundant. |
| 810 | (e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get |
| 811 | described three times, each time differently.) Fossil's |
| 812 | documentation is not only more concise, it gives a nice split of brief |
| 813 | online help and full online documentation. |
| 814 | |
| 815 | |
| @@ -855,11 +855,11 @@ | |
| 855 | weakening the case for continuing to use SHA-1. |
| 856 | |
| 857 | The practical impact of attacks like SHAttered and SHAmbles on the |
| 858 | Git and Fossil blockchains isn't clear, but you want to have your repositories |
| 859 | moved over to a stronger hash algorithm before someone figures out how |
| 860 | to make use of the weaknesses in the old one. Fossil has had this covered |
| 861 | for years now, so that the solution is now almost universally deployed. |
| 862 | |
| 863 | <hr/> |
| 864 | |
| 865 | <h3>Asides and Digressions</h3> |
| 866 | |
| 867 | DDED www/gitusers.md |
| 868 | DDED www/history.md |
+143
| --- a/www/gitusers.md | ||
| +++ b/www/gitusers.md | ||
| @@ -0,0 +1,143 @@ | ||
| 1 | +. | |
| 2 | + | |
| 3 | + | |
| 4 | +## Help Improve This Document | |
| 5 | + | |
| 6 | + experience, and you are new to Fossil | |
| 7 | +and are struggling with some concepts, please ask for help on the | |
| 8 | +[Fossil Forum][1]. The people who write this document are intimately | |
| 9 | +familiar with Fossil and less It is difficult for | |
| 10 | + initimately familiar | |
| 11 | +with Git ## Help Improve This Document | |
| 12 | + | |
| 13 | + experience, and you are new to Fossil | |
| 14 | +and are struggling with some concepts, please ask for help on the | |
| 15 | +[Fossil Forum][1]. The people who write this document are intimately | |
| 16 | +familiar with Fossil and less It is difficult for | |
| 17 | + initimately familiar | |
| 18 | +with Git and less familiar with Fossil. AskingForum | |
| 19 | +will help us tSpecific suggestions on how to improve this document are also welcomed, | |
| 20 | +of course. | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | +## <a | |
| 25 | +----in | |
| 26 | +which is stored. A(See for more Fossil terms of art that may | |
| 27 | +be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as | |
| 28 | +Git d. | |
| 29 | + | |
| 30 | + | |
| 31 | +## Help Improve This Document | |
| 32 | + | |
| 33 | + experience, and you are new to Fossil | |
| 34 | +and are struggling with some concepts, please ask for help on the | |
| 35 | +[Fossil Forum][1]. The people who write this document are intimately | |
| 36 | +familiar with Fossil and less It is difficult for | |
| 37 | + initimately familiar | |
| 38 | +with Git and less familiar with Fossil. AskingForum | |
| 39 | +will help us tSpecific suggestions on how to improve this document are also welcomed, | |
| 40 | +of course. | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | +## <a | |
| 45 | +----in | |
| 46 | +which is stored. A(See for more Fossil terms of art that may | |
| 47 | +be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as | |
| 48 | +Gfter-The-Fact | |
| 49 | + | |
| 50 | +Fossil per“open” command][open], which hwhen you commit using | |
| 51 | +the "`--branch` _BRANCH-NAME_" command-line option. he | |
| 52 | +[Fossil Forum][1]. The pecommium][1]. The people who wrIt is not necessary to create branches ahead of time, as in Git, though | |
| 53 | +that is allowedme concepts, please branch new`prove This Document | |
| 54 | + | |
| 55 | + experience, and you are new to Fossil | |
| 56 | +and are struggling with some concepts, please ask for help on the | |
| 57 | +[Fossil Forum][1]. The people who wrfic suggestions on how to ik2p@40C,L:, | |
| 58 | +so what you want is3d@43a,G6@47E,l@4NQ,2:isUF@4O9,2b@4rP,3X@4u0,6q:Unlike Git, Fossil’s “clone and open� call here that isn’t | |
| 59 | +needed in the Git case. This is an indirect reflection of Fossil’s | |
| 60 | +[multiple working directories](#mwd) design philosophy: its | |
| 61 | +[`open` command][open] requires that you either issue it in an empty | |
| 62 | +directory or one containing a prior closed check-out. In exchange for | |
| 63 | +this extra command, we getS@5Bc,1s: | |
| 64 | +[superior handling][shwmd] of multiple workingy can reside | |
| 65 | +h fromS@4xk,3X: | |
| 66 | +comma hierarchy with the check-out as with Git, but it | |
| 67 | +is more common to putopen� call herein a separate directory. | |
| 68 | + | |
| 69 | +[2]: | |
| 70 | +Fossil repositories are a single file, rather than being a directory | |
| 71 | +hierarsitory file | |
| 72 | +rn to parenwillI, but acum[1]. but it is best to usence, and you are new to Fossil | |
| 73 | +and are struggling all branches, all wiki, all tickets,Fossil -i,3m@3uC,3:—S@3xx,1:,1W@3yS,P:maybe one part-day a week2p@40C,L:ne and open� - everything. | |
| 74 | +�s | |
| 75 | +[multiple working directories](#mwd) design philosophy: its | |
| 76 | +[`open` command][open] requires that you either issue it in an empty | |
| 77 | +directory or one containing a prior closed check-out. In exchange for | |
| 78 | +this extra command, we getS@5Bc,1s: | |
| 79 | +[superior handling][shwmd] of multiple working directories. To get the | |
| 80 | +full power of this feature, you’d switch fromS@4xk,3X: | |
| 81 | +command form to the separate clone-and-open form shown in | |
| 82 | +[the quick start guide][qs], which adds one more command. | |
| 83 | + | |
| 84 | +We can’t spin the longer final command as a trade-off giving us extra | |
| 85 | +power, though: the simple fact is, 5f@50N,1:KU@56j## T[2]: | |
| 86 | +Fossil repositornge for | |
| 87 | +this extra command, we getS@5Bc,1s: | |
| 88 | +[superior handling][shwmd] of multiple workingy can reside | |
| 89 | +h fromS@4xk,3X: | |
| 90 | +comma hierarchy with the check-out as with Git, but it | |
| 91 | +is more common to putopen� call herein a separate directory. | |
| 92 | + | |
| 93 | +[2]: | |
| 94 | +Fossil repositories are a single file, rather than being a directory | |
| 95 | +hierarchy as with the "` parenwillI, but acum[1]. . | |
| 96 | +This is a [, all wiki, all tickets,Fo open� call here that isn�the [Rebase Considered Harmndirect reflection of Fossilflection of Fossil’s | |
| 97 | +[m�s | |
| 98 | +[multiple working directories](#mwd) design philosophy: its | |
| 99 | +[`open` command][open] requires that you either issue it in an empty | |
| 100 | +directory or one containing a prior closed check-out. In exchange for | |
| 101 | +this extra command, we getS@5Bc,1s: | |
| 102 | +[superior handling][shwmd] of multiple working directories. To get the | |
| 103 | +full power of this feature, you’d switch fromS@4xk,3X: | |
| 104 | +command form to the separate clone-and-open form show | |
| 105 | + | |
| 106 | +## Only One "origin" At A Time | |
| 107 | + | |
| 108 | +A extra command, we only keeps track of one "origin" server at a time. | |
| 109 | +If you specify a new "origin" it forgets the previous one. Use the | |
| 110 | +"`fossil remote`" command to see or change the "origin". | |
| 111 | + | |
| 112 | +Fossil uses a very different sync protocol than Git, so it is, not "`masterisn't as | |
| 113 | +important for Fossil to keep track of multiple origins as it is with | |
| 114 | +Git. So only having a single origin has never been a big enough problem | |
| 115 | +in Fossil thGit. | |
| 116 | + | |
| 117 | +These naming conventions are so embedded in each system, that the | |
| 118 | +"trunk" branch name is automatically translated to "master" when | |
| 119 | +a [Fossil rerget to give the `--baactually | |
| 120 | +showsadding more DAG display, whereas | |
| 121 | +there is no provision for recording cherry-picks in the Git file | |
| 122 | +format, so you have to talk about the cherry-pick in the commit | |
| 123 | +comment if you want to remember it. | |
| 124 | + | |
| 125 | +## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default) | |
| 126 | + | |
| 127 | +By default, | |
| 128 | +the "`fossil mv`" and "`fossil rm`" commands wal repo, so | |
| 129 | +doi is no provision for recording cherry-picks in the Git file | |
| 130 | +format, so you have to talk about the cherry-pick in the commit | |
| 131 | +comment if you want to remember it. | |
| 132 | + | |
| 133 | +## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default) | |
| 134 | + | |
| 135 | +By default, | |
| 136 | +the "`fossil mv`" and "`fossil rm`" commands wal repo, so | |
| 137 | +doing this , but do not actually | |
| 138 | +dn’t use `--set-upstream/-u` hercause we didn’t use `--set-upstream/-u` here, we have to name the | |
| 139 | +“worwork” origin explicitly in these commands. (This also shows Git’s | |
| 140 | +unwillingness to sync branch names, covered elsewhere in this document.) check-out. | |
| 141 | +If you runis also shows Git’s | |
| 142 | +uit makes a notation in your per-user "~/.fossil" settings file so that | |
| 143 | +the "--hard" behavio |
| --- a/www/gitusers.md | |
| +++ b/www/gitusers.md | |
| @@ -0,0 +1,143 @@ | |
| --- a/www/gitusers.md | |
| +++ b/www/gitusers.md | |
| @@ -0,0 +1,143 @@ | |
| 1 | . |
| 2 | |
| 3 | |
| 4 | ## Help Improve This Document |
| 5 | |
| 6 | experience, and you are new to Fossil |
| 7 | and are struggling with some concepts, please ask for help on the |
| 8 | [Fossil Forum][1]. The people who write this document are intimately |
| 9 | familiar with Fossil and less It is difficult for |
| 10 | initimately familiar |
| 11 | with Git ## Help Improve This Document |
| 12 | |
| 13 | experience, and you are new to Fossil |
| 14 | and are struggling with some concepts, please ask for help on the |
| 15 | [Fossil Forum][1]. The people who write this document are intimately |
| 16 | familiar with Fossil and less It is difficult for |
| 17 | initimately familiar |
| 18 | with Git and less familiar with Fossil. AskingForum |
| 19 | will help us tSpecific suggestions on how to improve this document are also welcomed, |
| 20 | of course. |
| 21 | |
| 22 | |
| 23 | |
| 24 | ## <a |
| 25 | ----in |
| 26 | which is stored. A(See for more Fossil terms of art that may |
| 27 | be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as |
| 28 | Git d. |
| 29 | |
| 30 | |
| 31 | ## Help Improve This Document |
| 32 | |
| 33 | experience, and you are new to Fossil |
| 34 | and are struggling with some concepts, please ask for help on the |
| 35 | [Fossil Forum][1]. The people who write this document are intimately |
| 36 | familiar with Fossil and less It is difficult for |
| 37 | initimately familiar |
| 38 | with Git and less familiar with Fossil. AskingForum |
| 39 | will help us tSpecific suggestions on how to improve this document are also welcomed, |
| 40 | of course. |
| 41 | |
| 42 | |
| 43 | |
| 44 | ## <a |
| 45 | ----in |
| 46 | which is stored. A(See for more Fossil terms of art that may |
| 47 | be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as |
| 48 | Gfter-The-Fact |
| 49 | |
| 50 | Fossil per“open” command][open], which hwhen you commit using |
| 51 | the "`--branch` _BRANCH-NAME_" command-line option. he |
| 52 | [Fossil Forum][1]. The pecommium][1]. The people who wrIt is not necessary to create branches ahead of time, as in Git, though |
| 53 | that is allowedme concepts, please branch new`prove This Document |
| 54 | |
| 55 | experience, and you are new to Fossil |
| 56 | and are struggling with some concepts, please ask for help on the |
| 57 | [Fossil Forum][1]. The people who wrfic suggestions on how to ik2p@40C,L:, |
| 58 | so what you want is3d@43a,G6@47E,l@4NQ,2:isUF@4O9,2b@4rP,3X@4u0,6q:Unlike Git, Fossil’s “clone and open� call here that isn’t |
| 59 | needed in the Git case. This is an indirect reflection of Fossil’s |
| 60 | [multiple working directories](#mwd) design philosophy: its |
| 61 | [`open` command][open] requires that you either issue it in an empty |
| 62 | directory or one containing a prior closed check-out. In exchange for |
| 63 | this extra command, we getS@5Bc,1s: |
| 64 | [superior handling][shwmd] of multiple workingy can reside |
| 65 | h fromS@4xk,3X: |
| 66 | comma hierarchy with the check-out as with Git, but it |
| 67 | is more common to putopen� call herein a separate directory. |
| 68 | |
| 69 | [2]: |
| 70 | Fossil repositories are a single file, rather than being a directory |
| 71 | hierarsitory file |
| 72 | rn to parenwillI, but acum[1]. but it is best to usence, and you are new to Fossil |
| 73 | and are struggling all branches, all wiki, all tickets,Fossil -i,3m@3uC,3:—S@3xx,1:,1W@3yS,P:maybe one part-day a week2p@40C,L:ne and open� - everything. |
| 74 | �s |
| 75 | [multiple working directories](#mwd) design philosophy: its |
| 76 | [`open` command][open] requires that you either issue it in an empty |
| 77 | directory or one containing a prior closed check-out. In exchange for |
| 78 | this extra command, we getS@5Bc,1s: |
| 79 | [superior handling][shwmd] of multiple working directories. To get the |
| 80 | full power of this feature, you’d switch fromS@4xk,3X: |
| 81 | command form to the separate clone-and-open form shown in |
| 82 | [the quick start guide][qs], which adds one more command. |
| 83 | |
| 84 | We can’t spin the longer final command as a trade-off giving us extra |
| 85 | power, though: the simple fact is, 5f@50N,1:KU@56j## T[2]: |
| 86 | Fossil repositornge for |
| 87 | this extra command, we getS@5Bc,1s: |
| 88 | [superior handling][shwmd] of multiple workingy can reside |
| 89 | h fromS@4xk,3X: |
| 90 | comma hierarchy with the check-out as with Git, but it |
| 91 | is more common to putopen� call herein a separate directory. |
| 92 | |
| 93 | [2]: |
| 94 | Fossil repositories are a single file, rather than being a directory |
| 95 | hierarchy as with the "` parenwillI, but acum[1]. . |
| 96 | This is a [, all wiki, all tickets,Fo open� call here that isn�the [Rebase Considered Harmndirect reflection of Fossilflection of Fossil’s |
| 97 | [m�s |
| 98 | [multiple working directories](#mwd) design philosophy: its |
| 99 | [`open` command][open] requires that you either issue it in an empty |
| 100 | directory or one containing a prior closed check-out. In exchange for |
| 101 | this extra command, we getS@5Bc,1s: |
| 102 | [superior handling][shwmd] of multiple working directories. To get the |
| 103 | full power of this feature, you’d switch fromS@4xk,3X: |
| 104 | command form to the separate clone-and-open form show |
| 105 | |
| 106 | ## Only One "origin" At A Time |
| 107 | |
| 108 | A extra command, we only keeps track of one "origin" server at a time. |
| 109 | If you specify a new "origin" it forgets the previous one. Use the |
| 110 | "`fossil remote`" command to see or change the "origin". |
| 111 | |
| 112 | Fossil uses a very different sync protocol than Git, so it is, not "`masterisn't as |
| 113 | important for Fossil to keep track of multiple origins as it is with |
| 114 | Git. So only having a single origin has never been a big enough problem |
| 115 | in Fossil thGit. |
| 116 | |
| 117 | These naming conventions are so embedded in each system, that the |
| 118 | "trunk" branch name is automatically translated to "master" when |
| 119 | a [Fossil rerget to give the `--baactually |
| 120 | showsadding more DAG display, whereas |
| 121 | there is no provision for recording cherry-picks in the Git file |
| 122 | format, so you have to talk about the cherry-pick in the commit |
| 123 | comment if you want to remember it. |
| 124 | |
| 125 | ## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default) |
| 126 | |
| 127 | By default, |
| 128 | the "`fossil mv`" and "`fossil rm`" commands wal repo, so |
| 129 | doi is no provision for recording cherry-picks in the Git file |
| 130 | format, so you have to talk about the cherry-pick in the commit |
| 131 | comment if you want to remember it. |
| 132 | |
| 133 | ## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default) |
| 134 | |
| 135 | By default, |
| 136 | the "`fossil mv`" and "`fossil rm`" commands wal repo, so |
| 137 | doing this , but do not actually |
| 138 | dn’t use `--set-upstream/-u` hercause we didn’t use `--set-upstream/-u` here, we have to name the |
| 139 | “worwork” origin explicitly in these commands. (This also shows Git’s |
| 140 | unwillingness to sync branch names, covered elsewhere in this document.) check-out. |
| 141 | If you runis also shows Git’s |
| 142 | uit makes a notation in your per-user "~/.fossil" settings file so that |
| 143 | the "--hard" behavio |
+72
| --- a/www/history.md | ||
| +++ b/www/history.md | ||
| @@ -0,0 +1,72 @@ | ||
| 1 | +# The History And Purpose Of Fossil | |
| 2 | + | |
| 3 | +Fossil is a [distributed version control system (DVCS)][100] written | |
| 4 | +beginning in [2007][105] by the [architect of SQLite][110] for the | |
| 5 | +purpose of managing the [SQLite project][115]. | |
| 6 | + | |
| 7 | +[100]: https://en.wikipedia.org/wiki/Distributed_version_control | |
| 8 | +[105: /timeline?a=1970-01-01&n1=10 | |
| 9 | +[110]: https://sqlite.org/crew.html | |
| 10 | +[115]: https://sqlite.org/ | |
| 11 | + | |
| 12 | +Though Fossil was originally written specifically to support SQLite, | |
| 13 | +it is now also used by countless other projects. The SQLite architect (drh) | |
| 14 | +is still the top committer to Fossil, but there are also | |
| 15 | +[many other contributors][120]. | |
| 16 | + | |
| 17 | +[120]: /reports?type=ci&view=byuser | |
| 18 | + | |
| 19 | +## Histor | |
| 20 | +The SQLite project started out using [CVS][300], as CVS was the most | |
| 21 | +commonly used version control system in thant era (circa 2000). CVS | |
| 22 | +was an amazing version control system for its day in that it allowed | |
| 23 | +multiple developers to be editing the same file at the same time. | |
| 24 | + | |
| 25 | +[300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System | |
| 26 | + | |
| 27 | +Though innovative and much loved in its time, CVS was not without problems. | |
| 28 | +Among those was a lack of visibility into the project history and the | |
| 29 | +lack of itry tPurcpose Of Fossil | |
| 30 | + | |
| 31 | +Fossil is a [distributed version control system (DVCS)][100] written | |
| 32 | +beginning in [2007][105] by the [architect of SQLite][110] for the | |
| 33 | +purpose of managin | |
| 34 | + | |
| 35 | +[100]: https://en.wikipedia.org/wiki/Distributed_version_control | |
| 36 | +[105]: /timeline?a=1970-01-01&n1=10 | |
| 37 | +[110]: https://sqlite.org/crew.html | |
| 38 | +[115]: https://sqlite.org/ | |
| 39 | + | |
| 40 | +Though Fossil was originally written specifically to support SQLite, | |
| 41 | +it is now also used by countless other projects. The SQLite architect (drh) | |
| 42 | +is still the top committer to Fossil, but there are also | |
| 43 | +[many other contributors][120]. | |
| 44 | + | |
| 45 | +[120]: /reports?type=ci&view=byuser | |
| 46 | + | |
| 47 | +## Histor | |
| 48 | +The SQLite project started out using [CVS][300], as CVS was the most | |
| 49 | +commonly used version control system in thant era (circa 2000). CVS | |
| 50 | +was an amazing version control system for its day in that it allowed | |
| 51 | +multiple developers to be editing the same file at the same time. | |
| 52 | + | |
| 53 | +[300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System | |
| 54 | + | |
| 55 | +T=10 | |
| 56 | +[3320030101&n into the project history and the | |
| 57 | +lack of integrated bug tracking. To address these deone was the first VCS to make use of | |
| 58 | +SHA1 to identify artifacts. Monotone stored its content in a SQLite | |
| 59 | +database, which is what brought it to the ssil | |
| 60 | + | |
| 61 | +Fossil is a [distributed version control system (DVCS)][100] written | |
| 62 | +beginning in [2007][105] by the [architect of SQLite][110] for the | |
| 63 | +purpose of managing the [SQLite project][115]. | |
| 64 | + | |
| 65 | +[100]: https://en.wikipedia.org/wiki/Distributed_version_control | |
| 66 | +[105]: /timeline?a=1970-01-01&n1=10 | |
| 67 | +[110]: https://sqlite.org/crew.html | |
| 68 | +[115]: https://sqlite.org/ | |
| 69 | + | |
| 70 | +Though Fossil was originally written specifically to support SQLite, | |
| 71 | +it is now also used by countless other projects. The SQLite architect (drh) | |
| 72 | +is still the top co=12&y=ci |
| --- a/www/history.md | |
| +++ b/www/history.md | |
| @@ -0,0 +1,72 @@ | |
| --- a/www/history.md | |
| +++ b/www/history.md | |
| @@ -0,0 +1,72 @@ | |
| 1 | # The History And Purpose Of Fossil |
| 2 | |
| 3 | Fossil is a [distributed version control system (DVCS)][100] written |
| 4 | beginning in [2007][105] by the [architect of SQLite][110] for the |
| 5 | purpose of managing the [SQLite project][115]. |
| 6 | |
| 7 | [100]: https://en.wikipedia.org/wiki/Distributed_version_control |
| 8 | [105: /timeline?a=1970-01-01&n1=10 |
| 9 | [110]: https://sqlite.org/crew.html |
| 10 | [115]: https://sqlite.org/ |
| 11 | |
| 12 | Though Fossil was originally written specifically to support SQLite, |
| 13 | it is now also used by countless other projects. The SQLite architect (drh) |
| 14 | is still the top committer to Fossil, but there are also |
| 15 | [many other contributors][120]. |
| 16 | |
| 17 | [120]: /reports?type=ci&view=byuser |
| 18 | |
| 19 | ## Histor |
| 20 | The SQLite project started out using [CVS][300], as CVS was the most |
| 21 | commonly used version control system in thant era (circa 2000). CVS |
| 22 | was an amazing version control system for its day in that it allowed |
| 23 | multiple developers to be editing the same file at the same time. |
| 24 | |
| 25 | [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System |
| 26 | |
| 27 | Though innovative and much loved in its time, CVS was not without problems. |
| 28 | Among those was a lack of visibility into the project history and the |
| 29 | lack of itry tPurcpose Of Fossil |
| 30 | |
| 31 | Fossil is a [distributed version control system (DVCS)][100] written |
| 32 | beginning in [2007][105] by the [architect of SQLite][110] for the |
| 33 | purpose of managin |
| 34 | |
| 35 | [100]: https://en.wikipedia.org/wiki/Distributed_version_control |
| 36 | [105]: /timeline?a=1970-01-01&n1=10 |
| 37 | [110]: https://sqlite.org/crew.html |
| 38 | [115]: https://sqlite.org/ |
| 39 | |
| 40 | Though Fossil was originally written specifically to support SQLite, |
| 41 | it is now also used by countless other projects. The SQLite architect (drh) |
| 42 | is still the top committer to Fossil, but there are also |
| 43 | [many other contributors][120]. |
| 44 | |
| 45 | [120]: /reports?type=ci&view=byuser |
| 46 | |
| 47 | ## Histor |
| 48 | The SQLite project started out using [CVS][300], as CVS was the most |
| 49 | commonly used version control system in thant era (circa 2000). CVS |
| 50 | was an amazing version control system for its day in that it allowed |
| 51 | multiple developers to be editing the same file at the same time. |
| 52 | |
| 53 | [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System |
| 54 | |
| 55 | T=10 |
| 56 | [3320030101&n into the project history and the |
| 57 | lack of integrated bug tracking. To address these deone was the first VCS to make use of |
| 58 | SHA1 to identify artifacts. Monotone stored its content in a SQLite |
| 59 | database, which is what brought it to the ssil |
| 60 | |
| 61 | Fossil is a [distributed version control system (DVCS)][100] written |
| 62 | beginning in [2007][105] by the [architect of SQLite][110] for the |
| 63 | purpose of managing the [SQLite project][115]. |
| 64 | |
| 65 | [100]: https://en.wikipedia.org/wiki/Distributed_version_control |
| 66 | [105]: /timeline?a=1970-01-01&n1=10 |
| 67 | [110]: https://sqlite.org/crew.html |
| 68 | [115]: https://sqlite.org/ |
| 69 | |
| 70 | Though Fossil was originally written specifically to support SQLite, |
| 71 | it is now also used by countless other projects. The SQLite architect (drh) |
| 72 | is still the top co=12&y=ci |
+21
-72
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -5,18 +5,18 @@ | ||
| 5 | 5 | <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'> |
| 6 | 6 | <ul> |
| 7 | 7 | <li> [/uv/download.html | Download] |
| 8 | 8 | <li> [./quickstart.wiki | Quick Start] |
| 9 | 9 | <li> [./build.wiki | Install] |
| 10 | -<li> [../COPYRIGHT-BSD2.txt | License] | |
| 11 | -<li> [./faq.wiki | FAQ] | |
| 10 | +<li> [https://fossil-scm.org/forum | Support/Forum ] | |
| 11 | +<li> [./hints.wiki | Tips & Hints] | |
| 12 | 12 | <li> [./changes.wiki | Change Log] |
| 13 | +<li> [../COPYRIGHT-BSD2.txt | License] | |
| 14 | +<li> [./userlinks.wiki | User inks] | |
| 13 | 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 14 | 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 15 | -<li> [./hints.wiki | Tip & Hints] | |
| 16 | 17 | <li> [./permutedindex.html | Documentation Index] |
| 17 | -<li> [https://fossil-scm.org/forum | Forum ] | |
| 18 | 18 | </ul> |
| 19 | 19 | <img src="fossil3.gif" align="center"> |
| 20 | 20 | </div> |
| 21 | 21 | |
| 22 | 22 | <p>Fossil is a simple, high-reliability, distributed software configuration |
| @@ -83,73 +83,22 @@ | ||
| 83 | 83 | the repository are consistent prior to each commit. |
| 84 | 84 | |
| 85 | 85 | 8. <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 86 | 86 | |
| 87 | 87 | <hr> |
| 88 | -<h3>Links For Fossil Users:</h3> | |
| 89 | - | |
| 90 | - * [./permutedindex.html | Documentation index] with [/search?c=d | full text search]. | |
| 91 | - * [./reviews.wiki | Testimonials] from satisfied Fossil users and | |
| 92 | - [./quotes.wiki | Quotes] about Fossil and other DVCSes. | |
| 93 | - * [./faq.wiki | Frequently Asked Questions] | |
| 94 | - * The [./concepts.wiki | concepts] behind Fossil. | |
| 95 | - [./whyusefossil.wiki#definitions | Another viewpoint]. | |
| 96 | - * [./quickstart.wiki | Quick Start] guide to using Fossil. | |
| 97 | - * [./qandc.wiki | Questions & Criticisms] directed at Fossil. | |
| 98 | - * [./build.wiki | Compiling and Installing] | |
| 99 | - * Fossil supports [./embeddeddoc.wiki | embedded documentation] | |
| 100 | - that is versioned along with project source code. | |
| 101 | - * Fossil uses an [./fileformat.wiki | enduring file format] that is | |
| 102 | - designed to be readable, searchable, and extensible by people | |
| 103 | - not yet born. | |
| 104 | - * A tutorial on [./branching.wiki | branching], what it means and how | |
| 105 | - to do it using Fossil. | |
| 106 | - * The [./selfcheck.wiki | automatic self-check] mechanism | |
| 107 | - helps insure project integrity. | |
| 108 | - * Fossil contains a [./wikitheory.wiki | built-in wiki]. | |
| 109 | - * An [./event.wiki | Event] is a special kind of wiki page associated | |
| 110 | - with a point in time rather than a name. | |
| 111 | - * [./settings.wiki | Settings] control the behaviour of Fossil. | |
| 112 | - * [./ssl.wiki | Use SSL] to encrypt communication with the server. | |
| 113 | - * There is a | |
| 114 | - [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] | |
| 115 | - (with publicly readable | |
| 116 | - [http://www.mail-archive.com/[email protected] | archives]) | |
| 117 | - available for discussing Fossil issues. | |
| 118 | - * [./stats.wiki | Performance statistics] taken from real-world projects | |
| 119 | - hosted on Fossil. | |
| 120 | - * How to [./shunning.wiki | delete content] from a Fossil repository. | |
| 121 | - * How Fossil does [./password.wiki | password management]. | |
| 122 | - * On-line [/help | help]. | |
| 123 | - * Documentation on the | |
| 124 | - [http://www.sqliteconcepts.org/THManual.pdf | TH1 scripting language], | |
| 125 | - used to customize [./custom_ticket.wiki | ticketing], and several other | |
| 126 | - subsystems, including [./customskin.md | theming]. | |
| 127 | - * List of [./th1.md | TH1 commands provided by Fossil itself] that expose | |
| 128 | - its key functionality to TH1 scripts. | |
| 129 | - * List of [./th1-hooks.md | TH1 hooks exposed by Fossil] that enable | |
| 130 | - customization of commands and web pages. | |
| 131 | - * A free hosting server for Fossil repositories is available at | |
| 132 | - [http://chiselapp.com/]. | |
| 133 | - * How to [./server/ | set up a server] for your repository. | |
| 134 | - * Customizing the [./custom_ticket.wiki | ticket system]. | |
| 135 | - * Methods to [./checkin_names.wiki | identify a specific check-in]. | |
| 136 | - * [./inout.wiki | Import and export] from and to Git. | |
| 137 | - * [./fossil-v-git.wiki | Fossil versus Git]. | |
| 138 | - * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] | |
| 139 | - (contributed by Gilles Ganault on 2013-01-08). | |
| 140 | - * [./antibot.wiki | How Fossil defends against abuse by spiders and bots]. | |
| 141 | - | |
| 142 | -<h3>Links For Fossil Developers:</h3> | |
| 143 | - | |
| 144 | - * [./contribute.wiki | Contributing] code or documentation to the | |
| 145 | - Fossil project. | |
| 146 | - * [./theory1.wiki | Thoughts On The Design Of Fossil]. | |
| 147 | - * [./pop.wiki | Principles Of Operation] | |
| 148 | - * [./tech_overview.wiki | A Technical Overview Of Fossil]. | |
| 149 | - * The [./fileformat.wiki | file format] used by every content | |
| 150 | - file stored in the repository. | |
| 151 | - * The [./delta_format.wiki | format of deltas] used to | |
| 152 | - efficiently store changes between file revisions. | |
| 153 | - * The [./delta_encoder_algorithm.wiki | encoder algorithm] used to | |
| 154 | - efficiently generate deltas. | |
| 155 | - * The [./sync.wiki | synchronization protocol]. | |
| 88 | +<h3>Latest Release: 2.10 (2019-10-04)</h3> | |
| 89 | + | |
| 90 | + * [/uv/download.html|Download] | |
| 91 | + * [./changes.wiki#v2_10|Change Summary] | |
| 92 | + | |
| 93 | +<hr> | |
| 94 | +<h3>Quick Start</h3> | |
| 95 | + | |
| 96 | + 1. [/uv/download.html|Download] or install using a package manager or | |
| 97 | + [./build.wiki|compile from sources]. | |
| 98 | + 2. <tt>fossil init</tt> <i>new-repository</i> | |
| 99 | + 3. <tt>fossil open</tt> <i>new-repository</i> | |
| 100 | + 4. <tt>fossil add</tt> <i>files-or-directories</i> | |
| 101 | + 5. <tt>fossil commit -m</tt> "<i>commit message</i>" | |
| 102 | + 6. <tt>fossil ui</tt> | |
| 103 | + 7. Repeat steps 4, 5, and 6, in any order, as necessary. | |
| 104 | + See the [./quickstart.wiki|Quick Start Guide] for more detail. | |
| 156 | 105 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -5,18 +5,18 @@ | |
| 5 | <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'> |
| 6 | <ul> |
| 7 | <li> [/uv/download.html | Download] |
| 8 | <li> [./quickstart.wiki | Quick Start] |
| 9 | <li> [./build.wiki | Install] |
| 10 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 11 | <li> [./faq.wiki | FAQ] |
| 12 | <li> [./changes.wiki | Change Log] |
| 13 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 14 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 15 | <li> [./hints.wiki | Tip & Hints] |
| 16 | <li> [./permutedindex.html | Documentation Index] |
| 17 | <li> [https://fossil-scm.org/forum | Forum ] |
| 18 | </ul> |
| 19 | <img src="fossil3.gif" align="center"> |
| 20 | </div> |
| 21 | |
| 22 | <p>Fossil is a simple, high-reliability, distributed software configuration |
| @@ -83,73 +83,22 @@ | |
| 83 | the repository are consistent prior to each commit. |
| 84 | |
| 85 | 8. <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 86 | |
| 87 | <hr> |
| 88 | <h3>Links For Fossil Users:</h3> |
| 89 | |
| 90 | * [./permutedindex.html | Documentation index] with [/search?c=d | full text search]. |
| 91 | * [./reviews.wiki | Testimonials] from satisfied Fossil users and |
| 92 | [./quotes.wiki | Quotes] about Fossil and other DVCSes. |
| 93 | * [./faq.wiki | Frequently Asked Questions] |
| 94 | * The [./concepts.wiki | concepts] behind Fossil. |
| 95 | [./whyusefossil.wiki#definitions | Another viewpoint]. |
| 96 | * [./quickstart.wiki | Quick Start] guide to using Fossil. |
| 97 | * [./qandc.wiki | Questions & Criticisms] directed at Fossil. |
| 98 | * [./build.wiki | Compiling and Installing] |
| 99 | * Fossil supports [./embeddeddoc.wiki | embedded documentation] |
| 100 | that is versioned along with project source code. |
| 101 | * Fossil uses an [./fileformat.wiki | enduring file format] that is |
| 102 | designed to be readable, searchable, and extensible by people |
| 103 | not yet born. |
| 104 | * A tutorial on [./branching.wiki | branching], what it means and how |
| 105 | to do it using Fossil. |
| 106 | * The [./selfcheck.wiki | automatic self-check] mechanism |
| 107 | helps insure project integrity. |
| 108 | * Fossil contains a [./wikitheory.wiki | built-in wiki]. |
| 109 | * An [./event.wiki | Event] is a special kind of wiki page associated |
| 110 | with a point in time rather than a name. |
| 111 | * [./settings.wiki | Settings] control the behaviour of Fossil. |
| 112 | * [./ssl.wiki | Use SSL] to encrypt communication with the server. |
| 113 | * There is a |
| 114 | [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] |
| 115 | (with publicly readable |
| 116 | [http://www.mail-archive.com/[email protected] | archives]) |
| 117 | available for discussing Fossil issues. |
| 118 | * [./stats.wiki | Performance statistics] taken from real-world projects |
| 119 | hosted on Fossil. |
| 120 | * How to [./shunning.wiki | delete content] from a Fossil repository. |
| 121 | * How Fossil does [./password.wiki | password management]. |
| 122 | * On-line [/help | help]. |
| 123 | * Documentation on the |
| 124 | [http://www.sqliteconcepts.org/THManual.pdf | TH1 scripting language], |
| 125 | used to customize [./custom_ticket.wiki | ticketing], and several other |
| 126 | subsystems, including [./customskin.md | theming]. |
| 127 | * List of [./th1.md | TH1 commands provided by Fossil itself] that expose |
| 128 | its key functionality to TH1 scripts. |
| 129 | * List of [./th1-hooks.md | TH1 hooks exposed by Fossil] that enable |
| 130 | customization of commands and web pages. |
| 131 | * A free hosting server for Fossil repositories is available at |
| 132 | [http://chiselapp.com/]. |
| 133 | * How to [./server/ | set up a server] for your repository. |
| 134 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 135 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 136 | * [./inout.wiki | Import and export] from and to Git. |
| 137 | * [./fossil-v-git.wiki | Fossil versus Git]. |
| 138 | * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] |
| 139 | (contributed by Gilles Ganault on 2013-01-08). |
| 140 | * [./antibot.wiki | How Fossil defends against abuse by spiders and bots]. |
| 141 | |
| 142 | <h3>Links For Fossil Developers:</h3> |
| 143 | |
| 144 | * [./contribute.wiki | Contributing] code or documentation to the |
| 145 | Fossil project. |
| 146 | * [./theory1.wiki | Thoughts On The Design Of Fossil]. |
| 147 | * [./pop.wiki | Principles Of Operation] |
| 148 | * [./tech_overview.wiki | A Technical Overview Of Fossil]. |
| 149 | * The [./fileformat.wiki | file format] used by every content |
| 150 | file stored in the repository. |
| 151 | * The [./delta_format.wiki | format of deltas] used to |
| 152 | efficiently store changes between file revisions. |
| 153 | * The [./delta_encoder_algorithm.wiki | encoder algorithm] used to |
| 154 | efficiently generate deltas. |
| 155 | * The [./sync.wiki | synchronization protocol]. |
| 156 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -5,18 +5,18 @@ | |
| 5 | <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'> |
| 6 | <ul> |
| 7 | <li> [/uv/download.html | Download] |
| 8 | <li> [./quickstart.wiki | Quick Start] |
| 9 | <li> [./build.wiki | Install] |
| 10 | <li> [https://fossil-scm.org/forum | Support/Forum ] |
| 11 | <li> [./hints.wiki | Tips & Hints] |
| 12 | <li> [./changes.wiki | Change Log] |
| 13 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 14 | <li> [./userlinks.wiki | User inks] |
| 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 17 | <li> [./permutedindex.html | Documentation Index] |
| 18 | </ul> |
| 19 | <img src="fossil3.gif" align="center"> |
| 20 | </div> |
| 21 | |
| 22 | <p>Fossil is a simple, high-reliability, distributed software configuration |
| @@ -83,73 +83,22 @@ | |
| 83 | the repository are consistent prior to each commit. |
| 84 | |
| 85 | 8. <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 86 | |
| 87 | <hr> |
| 88 | <h3>Latest Release: 2.10 (2019-10-04)</h3> |
| 89 | |
| 90 | * [/uv/download.html|Download] |
| 91 | * [./changes.wiki#v2_10|Change Summary] |
| 92 | |
| 93 | <hr> |
| 94 | <h3>Quick Start</h3> |
| 95 | |
| 96 | 1. [/uv/download.html|Download] or install using a package manager or |
| 97 | [./build.wiki|compile from sources]. |
| 98 | 2. <tt>fossil init</tt> <i>new-repository</i> |
| 99 | 3. <tt>fossil open</tt> <i>new-repository</i> |
| 100 | 4. <tt>fossil add</tt> <i>files-or-directories</i> |
| 101 | 5. <tt>fossil commit -m</tt> "<i>commit message</i>" |
| 102 | 6. <tt>fossil ui</tt> |
| 103 | 7. Repeat steps 4, 5, and 6, in any order, as necessary. |
| 104 | See the [./quickstart.wiki|Quick Start Guide] for more detail. |
| 105 |
+38
-16
| --- www/mirrorlimitations.md | ||
| +++ www/mirrorlimitations.md | ||
| @@ -9,52 +9,74 @@ | ||
| 9 | 9 | Fossil is not included in an export to Git. |
| 10 | 10 | |
| 11 | 11 | ## (1) Wiki, Tickets, Technotes, Forum |
| 12 | 12 | |
| 13 | 13 | Git only supports version control. The additional features of Fossil such |
| 14 | -as Wiki, Tickets, Technotes, and the Forum are not supported in Git and | |
| 14 | +as Wiki, Tickets, Technotes, and the Forum are not supported in Git, | |
| 15 | 15 | so those features are not included in an export. |
| 16 | + | |
| 17 | +Third-party Git based tooling may add some of these features (e.g. | |
| 18 | +GitHub, GitLab) but because their data are not stored in the Git | |
| 19 | +blockchain, there is no single destination for Fossil to convert its | |
| 20 | +equivalent data *to*. For instance, Fossil tickets do not become GitHub | |
| 21 | +issues, because that is a proprietary feature of GitHub separate from | |
| 22 | +Git proper, stored outside the blockchain on the GitHub servers. | |
| 23 | + | |
| 24 | +You can also see the problem in its inverse case: you do not get a copy | |
| 25 | +of your GitHub issues when cloning the Git repository. You *do* get the | |
| 26 | +Fossil tickets, wiki, forum posts, etc. when cloning a remote Fossil | |
| 27 | +repo. | |
| 16 | 28 | |
| 17 | 29 | ## (2) Cherrypick Merges |
| 18 | 30 | |
| 19 | -The Git client supports cherrypick merges but does not remember them. | |
| 20 | -In other words, Git does not record a history of cherrypick merges | |
| 21 | -in its blockchain. | |
| 22 | - | |
| 23 | -Fossil tracks cherrypick merges in its blockchain and display cherrypicks | |
| 24 | -(as dashed lines) in its timeline ([example](/timeline?c=0a9f12ce6655b7a5)). | |
| 25 | -But history information of cherrypicks cannot be exported to Git because | |
| 26 | -there is no way to represent it in the Git. | |
| 31 | +The Git client supports cherrypick merges but does not record the | |
| 32 | +cherrypick parent(s). | |
| 33 | + | |
| 34 | +Fossil tracks cherrypick merges in its blockchain and displays | |
| 35 | +cherrypicks in its timeline. (As an example, the dashed lines | |
| 36 | +[here](/timeline?c=0a9f12ce6655b7a5) are cherrypicks.) Because Git does | |
| 37 | +not have a way to represent this same information in its blockchain, the | |
| 38 | +history of Fossil cherrypicks cannot be exported to Git, only their | |
| 39 | +direct effects on the managed file data. | |
| 27 | 40 | |
| 28 | 41 | ## (3) Named Branches |
| 29 | 42 | |
| 30 | 43 | Git has only limited support for named branches. Git identifies the head |
| 31 | 44 | check-in of each branch. Depending on the check-in graph topology, this |
| 32 | 45 | is sufficient to infer the branch for many historical check-ins as well. |
| 33 | 46 | However, complex histories with lots of cross-merging |
| 34 | 47 | can lead to ambiguities. Fossil keeps |
| 35 | -track of historical branch names unambiguously. | |
| 36 | -But the extra details about branch names that Fossil keeps | |
| 48 | +track of historical branch names unambiguously, | |
| 49 | +but the extra details about branch names that Fossil keeps | |
| 37 | 50 | at hand cannot be exported to Git. |
| 38 | 51 | |
| 39 | 52 | ## (4) Non-unique Tags |
| 40 | 53 | |
| 41 | -Git requires tags to be unique. Each tag must refer to exactly one | |
| 54 | +Git requires tags to be unique: each tag must refer to exactly one | |
| 42 | 55 | check-in. Fossil does not have this restriction, and so it is common |
| 43 | 56 | in Fossil to tag multiple check-ins with the same name. For example, |
| 44 | -it is common in Fossil to tag every release check-in with the "release" | |
| 45 | -tag, so that all historical releases can be found all at once. | |
| 46 | -([example](/timeline?t=release)) | |
| 57 | +it is common in Fossil to tag each check-in creating a release both | |
| 58 | +with a unique version tag *and* a common tag like "release" | |
| 59 | +so that all historical releases can be found at once. | |
| 60 | +([Example](/timeline?t=release).) | |
| 47 | 61 | |
| 48 | 62 | Git does not allow this. The "release" tag must refer to just one |
| 49 | 63 | check-in. The work-around is that the non-unique tag in the Git export is |
| 50 | 64 | made to refer to only the most recent check-in with that tag. |
| 51 | 65 | |
| 66 | +This is why the ["release" tag view][ghrtv] in the GitHub mirror of this | |
| 67 | +repository shows only the latest release version; contrast the prior | |
| 68 | +example. Both URLs are asking the repository the same question, but | |
| 69 | +because of Git's relatively impoverished data model, it cannot give the | |
| 70 | +same answer that Fossil does. | |
| 71 | + | |
| 72 | +[ghrtv]: https://github.com/drhsqlite/fossil-mirror/tree/release | |
| 73 | + | |
| 52 | 74 | ## (5) Amendments To Check-ins |
| 53 | 75 | |
| 54 | 76 | Check-ins are immutable in both Fossil and Git. |
| 55 | -However, Fossil has a mechanism by which tags can be added | |
| 77 | +However, Fossil has a mechanism by which tags can be added to | |
| 56 | 78 | its blockchain to provide after-the-fact corrections to prior check-ins. |
| 57 | 79 | |
| 58 | 80 | For example, tags can be added to check-ins that correct typos in the |
| 59 | 81 | check-in comment. The original check-in is immutable and so the |
| 60 | 82 | original comment is preserved in addition to the correction. But |
| 61 | 83 |
| --- www/mirrorlimitations.md | |
| +++ www/mirrorlimitations.md | |
| @@ -9,52 +9,74 @@ | |
| 9 | Fossil is not included in an export to Git. |
| 10 | |
| 11 | ## (1) Wiki, Tickets, Technotes, Forum |
| 12 | |
| 13 | Git only supports version control. The additional features of Fossil such |
| 14 | as Wiki, Tickets, Technotes, and the Forum are not supported in Git and |
| 15 | so those features are not included in an export. |
| 16 | |
| 17 | ## (2) Cherrypick Merges |
| 18 | |
| 19 | The Git client supports cherrypick merges but does not remember them. |
| 20 | In other words, Git does not record a history of cherrypick merges |
| 21 | in its blockchain. |
| 22 | |
| 23 | Fossil tracks cherrypick merges in its blockchain and display cherrypicks |
| 24 | (as dashed lines) in its timeline ([example](/timeline?c=0a9f12ce6655b7a5)). |
| 25 | But history information of cherrypicks cannot be exported to Git because |
| 26 | there is no way to represent it in the Git. |
| 27 | |
| 28 | ## (3) Named Branches |
| 29 | |
| 30 | Git has only limited support for named branches. Git identifies the head |
| 31 | check-in of each branch. Depending on the check-in graph topology, this |
| 32 | is sufficient to infer the branch for many historical check-ins as well. |
| 33 | However, complex histories with lots of cross-merging |
| 34 | can lead to ambiguities. Fossil keeps |
| 35 | track of historical branch names unambiguously. |
| 36 | But the extra details about branch names that Fossil keeps |
| 37 | at hand cannot be exported to Git. |
| 38 | |
| 39 | ## (4) Non-unique Tags |
| 40 | |
| 41 | Git requires tags to be unique. Each tag must refer to exactly one |
| 42 | check-in. Fossil does not have this restriction, and so it is common |
| 43 | in Fossil to tag multiple check-ins with the same name. For example, |
| 44 | it is common in Fossil to tag every release check-in with the "release" |
| 45 | tag, so that all historical releases can be found all at once. |
| 46 | ([example](/timeline?t=release)) |
| 47 | |
| 48 | Git does not allow this. The "release" tag must refer to just one |
| 49 | check-in. The work-around is that the non-unique tag in the Git export is |
| 50 | made to refer to only the most recent check-in with that tag. |
| 51 | |
| 52 | ## (5) Amendments To Check-ins |
| 53 | |
| 54 | Check-ins are immutable in both Fossil and Git. |
| 55 | However, Fossil has a mechanism by which tags can be added |
| 56 | its blockchain to provide after-the-fact corrections to prior check-ins. |
| 57 | |
| 58 | For example, tags can be added to check-ins that correct typos in the |
| 59 | check-in comment. The original check-in is immutable and so the |
| 60 | original comment is preserved in addition to the correction. But |
| 61 |
| --- www/mirrorlimitations.md | |
| +++ www/mirrorlimitations.md | |
| @@ -9,52 +9,74 @@ | |
| 9 | Fossil is not included in an export to Git. |
| 10 | |
| 11 | ## (1) Wiki, Tickets, Technotes, Forum |
| 12 | |
| 13 | Git only supports version control. The additional features of Fossil such |
| 14 | as Wiki, Tickets, Technotes, and the Forum are not supported in Git, |
| 15 | so those features are not included in an export. |
| 16 | |
| 17 | Third-party Git based tooling may add some of these features (e.g. |
| 18 | GitHub, GitLab) but because their data are not stored in the Git |
| 19 | blockchain, there is no single destination for Fossil to convert its |
| 20 | equivalent data *to*. For instance, Fossil tickets do not become GitHub |
| 21 | issues, because that is a proprietary feature of GitHub separate from |
| 22 | Git proper, stored outside the blockchain on the GitHub servers. |
| 23 | |
| 24 | You can also see the problem in its inverse case: you do not get a copy |
| 25 | of your GitHub issues when cloning the Git repository. You *do* get the |
| 26 | Fossil tickets, wiki, forum posts, etc. when cloning a remote Fossil |
| 27 | repo. |
| 28 | |
| 29 | ## (2) Cherrypick Merges |
| 30 | |
| 31 | The Git client supports cherrypick merges but does not record the |
| 32 | cherrypick parent(s). |
| 33 | |
| 34 | Fossil tracks cherrypick merges in its blockchain and displays |
| 35 | cherrypicks in its timeline. (As an example, the dashed lines |
| 36 | [here](/timeline?c=0a9f12ce6655b7a5) are cherrypicks.) Because Git does |
| 37 | not have a way to represent this same information in its blockchain, the |
| 38 | history of Fossil cherrypicks cannot be exported to Git, only their |
| 39 | direct effects on the managed file data. |
| 40 | |
| 41 | ## (3) Named Branches |
| 42 | |
| 43 | Git has only limited support for named branches. Git identifies the head |
| 44 | check-in of each branch. Depending on the check-in graph topology, this |
| 45 | is sufficient to infer the branch for many historical check-ins as well. |
| 46 | However, complex histories with lots of cross-merging |
| 47 | can lead to ambiguities. Fossil keeps |
| 48 | track of historical branch names unambiguously, |
| 49 | but the extra details about branch names that Fossil keeps |
| 50 | at hand cannot be exported to Git. |
| 51 | |
| 52 | ## (4) Non-unique Tags |
| 53 | |
| 54 | Git requires tags to be unique: each tag must refer to exactly one |
| 55 | check-in. Fossil does not have this restriction, and so it is common |
| 56 | in Fossil to tag multiple check-ins with the same name. For example, |
| 57 | it is common in Fossil to tag each check-in creating a release both |
| 58 | with a unique version tag *and* a common tag like "release" |
| 59 | so that all historical releases can be found at once. |
| 60 | ([Example](/timeline?t=release).) |
| 61 | |
| 62 | Git does not allow this. The "release" tag must refer to just one |
| 63 | check-in. The work-around is that the non-unique tag in the Git export is |
| 64 | made to refer to only the most recent check-in with that tag. |
| 65 | |
| 66 | This is why the ["release" tag view][ghrtv] in the GitHub mirror of this |
| 67 | repository shows only the latest release version; contrast the prior |
| 68 | example. Both URLs are asking the repository the same question, but |
| 69 | because of Git's relatively impoverished data model, it cannot give the |
| 70 | same answer that Fossil does. |
| 71 | |
| 72 | [ghrtv]: https://github.com/drhsqlite/fossil-mirror/tree/release |
| 73 | |
| 74 | ## (5) Amendments To Check-ins |
| 75 | |
| 76 | Check-ins are immutable in both Fossil and Git. |
| 77 | However, Fossil has a mechanism by which tags can be added to |
| 78 | its blockchain to provide after-the-fact corrections to prior check-ins. |
| 79 | |
| 80 | For example, tags can be added to check-ins that correct typos in the |
| 81 | check-in comment. The original check-in is immutable and so the |
| 82 | original comment is preserved in addition to the correction. But |
| 83 |
+6
-3
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -49,16 +49,18 @@ | ||
| 49 | 49 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 50 | 50 | fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} |
| 51 | 51 | fossil_prompt.wiki {Fossilized Bash Prompt} |
| 52 | 52 | fossil-v-git.wiki {Fossil Versus Git} |
| 53 | 53 | globs.md {File Name Glob Patterns} |
| 54 | + gitusers.md {Hints For Users With Git Experience} | |
| 54 | 55 | grep.md {Fossil grep vs POSIX grep} |
| 55 | 56 | hacker-howto.wiki {Hacker How-To} |
| 56 | 57 | hacker-howto.wiki {Fossil Developers Guide} |
| 57 | 58 | hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256} |
| 58 | 59 | /help {Lists of Commands and Webpages} |
| 59 | 60 | hints.wiki {Fossil Tips And Usage Hints} |
| 61 | + history.md {The Purpose And History Of Fossil} | |
| 60 | 62 | index.wiki {Home Page} |
| 61 | 63 | inout.wiki {Import And Export To And From Git} |
| 62 | 64 | image-format-vs-repo-size.md {Image Format vs Fossil Repo Size} |
| 63 | 65 | javascript.md {Use of JavaScript in Fossil} |
| 64 | 66 | makefile.wiki {The Fossil Build Process} |
| @@ -135,17 +137,18 @@ | ||
| 135 | 137 | </form> |
| 136 | 138 | </center> |
| 137 | 139 | <h2>Primary Documents:</h2> |
| 138 | 140 | <ul> |
| 139 | 141 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 140 | -<li> <a href='faq.wiki'>FAQ</a> | |
| 142 | +<li> <a href='history.md'>Purpose and History of Fossil</a> | |
| 141 | 143 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 142 | 144 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 145 | +<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> | |
| 146 | +<li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> | |
| 147 | +<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> | |
| 143 | 148 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 144 | 149 | book</a> |
| 145 | -<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> | |
| 146 | -<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> | |
| 147 | 150 | </ul> |
| 148 | 151 | <a name="pindex"></a> |
| 149 | 152 | <h2>Permuted Index:</h2> |
| 150 | 153 | <ul>} |
| 151 | 154 | foreach entry $permindex { |
| 152 | 155 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -49,16 +49,18 @@ | |
| 49 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 50 | fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} |
| 51 | fossil_prompt.wiki {Fossilized Bash Prompt} |
| 52 | fossil-v-git.wiki {Fossil Versus Git} |
| 53 | globs.md {File Name Glob Patterns} |
| 54 | grep.md {Fossil grep vs POSIX grep} |
| 55 | hacker-howto.wiki {Hacker How-To} |
| 56 | hacker-howto.wiki {Fossil Developers Guide} |
| 57 | hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256} |
| 58 | /help {Lists of Commands and Webpages} |
| 59 | hints.wiki {Fossil Tips And Usage Hints} |
| 60 | index.wiki {Home Page} |
| 61 | inout.wiki {Import And Export To And From Git} |
| 62 | image-format-vs-repo-size.md {Image Format vs Fossil Repo Size} |
| 63 | javascript.md {Use of JavaScript in Fossil} |
| 64 | makefile.wiki {The Fossil Build Process} |
| @@ -135,17 +137,18 @@ | |
| 135 | </form> |
| 136 | </center> |
| 137 | <h2>Primary Documents:</h2> |
| 138 | <ul> |
| 139 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 140 | <li> <a href='faq.wiki'>FAQ</a> |
| 141 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 142 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 143 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 144 | book</a> |
| 145 | <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> |
| 146 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 147 | </ul> |
| 148 | <a name="pindex"></a> |
| 149 | <h2>Permuted Index:</h2> |
| 150 | <ul>} |
| 151 | foreach entry $permindex { |
| 152 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -49,16 +49,18 @@ | |
| 49 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 50 | fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} |
| 51 | fossil_prompt.wiki {Fossilized Bash Prompt} |
| 52 | fossil-v-git.wiki {Fossil Versus Git} |
| 53 | globs.md {File Name Glob Patterns} |
| 54 | gitusers.md {Hints For Users With Git Experience} |
| 55 | grep.md {Fossil grep vs POSIX grep} |
| 56 | hacker-howto.wiki {Hacker How-To} |
| 57 | hacker-howto.wiki {Fossil Developers Guide} |
| 58 | hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256} |
| 59 | /help {Lists of Commands and Webpages} |
| 60 | hints.wiki {Fossil Tips And Usage Hints} |
| 61 | history.md {The Purpose And History Of Fossil} |
| 62 | index.wiki {Home Page} |
| 63 | inout.wiki {Import And Export To And From Git} |
| 64 | image-format-vs-repo-size.md {Image Format vs Fossil Repo Size} |
| 65 | javascript.md {Use of JavaScript in Fossil} |
| 66 | makefile.wiki {The Fossil Build Process} |
| @@ -135,17 +137,18 @@ | |
| 137 | </form> |
| 138 | </center> |
| 139 | <h2>Primary Documents:</h2> |
| 140 | <ul> |
| 141 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 142 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 143 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 144 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 145 | <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> |
| 146 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 147 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 148 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 149 | book</a> |
| 150 | </ul> |
| 151 | <a name="pindex"></a> |
| 152 | <h2>Permuted Index:</h2> |
| 153 | <ul>} |
| 154 | foreach entry $permindex { |
| 155 |
+11
-3
| --- www/permutedindex.html | ||
| +++ www/permutedindex.html | ||
| @@ -7,17 +7,18 @@ | ||
| 7 | 7 | </form> |
| 8 | 8 | </center> |
| 9 | 9 | <h2>Primary Documents:</h2> |
| 10 | 10 | <ul> |
| 11 | 11 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 12 | -<li> <a href='faq.wiki'>FAQ</a> | |
| 12 | +<li> <a href='history.md'>Purpose and History of Fossil</a> | |
| 13 | 13 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 14 | 14 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 15 | +<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> | |
| 16 | +<li> <a href='userlinks.wiki'>Key Docs for Fossil Users</a> | |
| 17 | +<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> | |
| 15 | 18 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 16 | 19 | book</a> |
| 17 | -<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> | |
| 18 | -<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> | |
| 19 | 20 | </ul> |
| 20 | 21 | <a name="pindex"></a> |
| 21 | 22 | <h2>Permuted Index:</h2> |
| 22 | 23 | <ul> |
| 23 | 24 | <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Up and Running in</a></li> |
| @@ -104,10 +105,11 @@ | ||
| 104 | 105 | <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm — Fossil Delta</a></li> |
| 105 | 106 | <li><a href="encryptedrepos.wiki">Encrypted Repositories — How To Use</a></li> |
| 106 | 107 | <li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li> |
| 107 | 108 | <li><a href="event.wiki"><b>Events</b></a></li> |
| 108 | 109 | <li><a href="webpage-ex.md">Examples — Webpage</a></li> |
| 110 | +<li><a href="gitusers.md">Experience — Hints For Users With Git</a></li> | |
| 109 | 111 | <li><a href="inout.wiki">Export To And From Git — Import And</a></li> |
| 110 | 112 | <li><a href="fossil-from-msvc.wiki">Express 2010 IDE — Integrating Fossil in the Microsoft</a></li> |
| 111 | 113 | <li><a href="serverext.wiki">Extensions — CGI Server</a></li> |
| 112 | 114 | <li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts — Adding</a></li> |
| 113 | 115 | <li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> |
| @@ -141,10 +143,11 @@ | ||
| 141 | 143 | <li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li> |
| 142 | 144 | <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li> |
| 143 | 145 | <li><a href="quotes.wiki">General — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> |
| 144 | 146 | <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> |
| 145 | 147 | <li><a href="inout.wiki">Git — Import And Export To And From</a></li> |
| 148 | +<li><a href="gitusers.md">Git Experience — Hints For Users With</a></li> | |
| 146 | 149 | <li><a href="mirrorlimitations.md">Git Mirrors — Limitations On</a></li> |
| 147 | 150 | <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> |
| 148 | 151 | <li><a href="mirrortogithub.md">GitHub — How To Mirror A Fossil Repository On</a></li> |
| 149 | 152 | <li><a href="globs.md">Glob Patterns — File Name</a></li> |
| 150 | 153 | <li><a href="env-opts.md">Global Options — Environment Variables and</a></li> |
| @@ -157,10 +160,12 @@ | ||
| 157 | 160 | <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li> |
| 158 | 161 | <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li> |
| 159 | 162 | <li><a href="rebaseharm.md">Harmful — Rebase Considered</a></li> |
| 160 | 163 | <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li> |
| 161 | 164 | <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> |
| 165 | +<li><a href="gitusers.md"><b>Hints For Users With Git Experience</b></a></li> | |
| 166 | +<li><a href="history.md">History Of Fossil — The Purpose And</a></li> | |
| 162 | 167 | <li><a href="index.wiki"><b>Home Page</b></a></li> |
| 163 | 168 | <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> |
| 164 | 169 | <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li> |
| 165 | 170 | <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li> |
| 166 | 171 | <li><a href="server/"><b>How To Configure A Fossil Server</b></a></li> |
| @@ -221,10 +226,11 @@ | ||
| 221 | 226 | <li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> |
| 222 | 227 | <li><a href="childprojects.wiki">Projects — Child</a></li> |
| 223 | 228 | <li><a href="fossil_prompt.wiki">Prompt — Fossilized Bash</a></li> |
| 224 | 229 | <li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> |
| 225 | 230 | <li><a href="tls-nginx.md"><b>Proxying Fossil via HTTPS with nginx</b></a></li> |
| 231 | +<li><a href="history.md">Purpose And History Of Fossil — The</a></li> | |
| 226 | 232 | <li><a href="faq.wiki">Questions — Frequently Asked</a></li> |
| 227 | 233 | <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li> |
| 228 | 234 | <li><a href="quickstart.wiki">Quick Start Guide — Fossil</a></li> |
| 229 | 235 | <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li> |
| 230 | 236 | <li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li> |
| @@ -279,10 +285,11 @@ | ||
| 279 | 285 | <li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li> |
| 280 | 286 | <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li> |
| 281 | 287 | <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li> |
| 282 | 288 | <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li> |
| 283 | 289 | <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li> |
| 290 | +<li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li> | |
| 284 | 291 | <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li> |
| 285 | 292 | <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li> |
| 286 | 293 | <li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li> |
| 287 | 294 | <li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li> |
| 288 | 295 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| @@ -298,10 +305,11 @@ | ||
| 298 | 305 | <li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li> |
| 299 | 306 | <li><a href="fiveminutes.wiki">User — Up and Running in 5 Minutes as a Single</a></li> |
| 300 | 307 | <li><a href="caps/">User Capabilities — Administering</a></li> |
| 301 | 308 | <li><a href="caps/ref.html"><b>User Capability Reference</b></a></li> |
| 302 | 309 | <li><a href="caps/admin-v-setup.md">Users — Differences Between Setup and Admin</a></li> |
| 310 | +<li><a href="gitusers.md">Users With Git Experience — Hints For</a></li> | |
| 303 | 311 | <li><a href="serverext.wiki">Using CGI Scripts — Adding Extensions To A Fossil Server</a></li> |
| 304 | 312 | <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li> |
| 305 | 313 | <li><a href="env-opts.md">Variables and Global Options — Environment</a></li> |
| 306 | 314 | <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> |
| 307 | 315 | <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> |
| 308 | 316 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -7,17 +7,18 @@ | |
| 7 | </form> |
| 8 | </center> |
| 9 | <h2>Primary Documents:</h2> |
| 10 | <ul> |
| 11 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 12 | <li> <a href='faq.wiki'>FAQ</a> |
| 13 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 14 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 15 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 16 | book</a> |
| 17 | <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> |
| 18 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 19 | </ul> |
| 20 | <a name="pindex"></a> |
| 21 | <h2>Permuted Index:</h2> |
| 22 | <ul> |
| 23 | <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Up and Running in</a></li> |
| @@ -104,10 +105,11 @@ | |
| 104 | <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm — Fossil Delta</a></li> |
| 105 | <li><a href="encryptedrepos.wiki">Encrypted Repositories — How To Use</a></li> |
| 106 | <li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li> |
| 107 | <li><a href="event.wiki"><b>Events</b></a></li> |
| 108 | <li><a href="webpage-ex.md">Examples — Webpage</a></li> |
| 109 | <li><a href="inout.wiki">Export To And From Git — Import And</a></li> |
| 110 | <li><a href="fossil-from-msvc.wiki">Express 2010 IDE — Integrating Fossil in the Microsoft</a></li> |
| 111 | <li><a href="serverext.wiki">Extensions — CGI Server</a></li> |
| 112 | <li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts — Adding</a></li> |
| 113 | <li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> |
| @@ -141,10 +143,11 @@ | |
| 141 | <li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li> |
| 142 | <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li> |
| 143 | <li><a href="quotes.wiki">General — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> |
| 144 | <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> |
| 145 | <li><a href="inout.wiki">Git — Import And Export To And From</a></li> |
| 146 | <li><a href="mirrorlimitations.md">Git Mirrors — Limitations On</a></li> |
| 147 | <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> |
| 148 | <li><a href="mirrortogithub.md">GitHub — How To Mirror A Fossil Repository On</a></li> |
| 149 | <li><a href="globs.md">Glob Patterns — File Name</a></li> |
| 150 | <li><a href="env-opts.md">Global Options — Environment Variables and</a></li> |
| @@ -157,10 +160,12 @@ | |
| 157 | <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li> |
| 158 | <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li> |
| 159 | <li><a href="rebaseharm.md">Harmful — Rebase Considered</a></li> |
| 160 | <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li> |
| 161 | <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> |
| 162 | <li><a href="index.wiki"><b>Home Page</b></a></li> |
| 163 | <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> |
| 164 | <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li> |
| 165 | <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li> |
| 166 | <li><a href="server/"><b>How To Configure A Fossil Server</b></a></li> |
| @@ -221,10 +226,11 @@ | |
| 221 | <li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> |
| 222 | <li><a href="childprojects.wiki">Projects — Child</a></li> |
| 223 | <li><a href="fossil_prompt.wiki">Prompt — Fossilized Bash</a></li> |
| 224 | <li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> |
| 225 | <li><a href="tls-nginx.md"><b>Proxying Fossil via HTTPS with nginx</b></a></li> |
| 226 | <li><a href="faq.wiki">Questions — Frequently Asked</a></li> |
| 227 | <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li> |
| 228 | <li><a href="quickstart.wiki">Quick Start Guide — Fossil</a></li> |
| 229 | <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li> |
| 230 | <li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li> |
| @@ -279,10 +285,11 @@ | |
| 279 | <li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li> |
| 280 | <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li> |
| 281 | <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li> |
| 282 | <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li> |
| 283 | <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li> |
| 284 | <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li> |
| 285 | <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li> |
| 286 | <li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li> |
| 287 | <li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li> |
| 288 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| @@ -298,10 +305,11 @@ | |
| 298 | <li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li> |
| 299 | <li><a href="fiveminutes.wiki">User — Up and Running in 5 Minutes as a Single</a></li> |
| 300 | <li><a href="caps/">User Capabilities — Administering</a></li> |
| 301 | <li><a href="caps/ref.html"><b>User Capability Reference</b></a></li> |
| 302 | <li><a href="caps/admin-v-setup.md">Users — Differences Between Setup and Admin</a></li> |
| 303 | <li><a href="serverext.wiki">Using CGI Scripts — Adding Extensions To A Fossil Server</a></li> |
| 304 | <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li> |
| 305 | <li><a href="env-opts.md">Variables and Global Options — Environment</a></li> |
| 306 | <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> |
| 307 | <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> |
| 308 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -7,17 +7,18 @@ | |
| 7 | </form> |
| 8 | </center> |
| 9 | <h2>Primary Documents:</h2> |
| 10 | <ul> |
| 11 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 12 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 13 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 14 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 15 | <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> |
| 16 | <li> <a href='userlinks.wiki'>Key Docs for Fossil Users</a> |
| 17 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 18 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 19 | book</a> |
| 20 | </ul> |
| 21 | <a name="pindex"></a> |
| 22 | <h2>Permuted Index:</h2> |
| 23 | <ul> |
| 24 | <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Up and Running in</a></li> |
| @@ -104,10 +105,11 @@ | |
| 105 | <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm — Fossil Delta</a></li> |
| 106 | <li><a href="encryptedrepos.wiki">Encrypted Repositories — How To Use</a></li> |
| 107 | <li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li> |
| 108 | <li><a href="event.wiki"><b>Events</b></a></li> |
| 109 | <li><a href="webpage-ex.md">Examples — Webpage</a></li> |
| 110 | <li><a href="gitusers.md">Experience — Hints For Users With Git</a></li> |
| 111 | <li><a href="inout.wiki">Export To And From Git — Import And</a></li> |
| 112 | <li><a href="fossil-from-msvc.wiki">Express 2010 IDE — Integrating Fossil in the Microsoft</a></li> |
| 113 | <li><a href="serverext.wiki">Extensions — CGI Server</a></li> |
| 114 | <li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts — Adding</a></li> |
| 115 | <li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> |
| @@ -141,10 +143,11 @@ | |
| 143 | <li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li> |
| 144 | <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li> |
| 145 | <li><a href="quotes.wiki">General — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> |
| 146 | <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> |
| 147 | <li><a href="inout.wiki">Git — Import And Export To And From</a></li> |
| 148 | <li><a href="gitusers.md">Git Experience — Hints For Users With</a></li> |
| 149 | <li><a href="mirrorlimitations.md">Git Mirrors — Limitations On</a></li> |
| 150 | <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> |
| 151 | <li><a href="mirrortogithub.md">GitHub — How To Mirror A Fossil Repository On</a></li> |
| 152 | <li><a href="globs.md">Glob Patterns — File Name</a></li> |
| 153 | <li><a href="env-opts.md">Global Options — Environment Variables and</a></li> |
| @@ -157,10 +160,12 @@ | |
| 160 | <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li> |
| 161 | <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li> |
| 162 | <li><a href="rebaseharm.md">Harmful — Rebase Considered</a></li> |
| 163 | <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li> |
| 164 | <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> |
| 165 | <li><a href="gitusers.md"><b>Hints For Users With Git Experience</b></a></li> |
| 166 | <li><a href="history.md">History Of Fossil — The Purpose And</a></li> |
| 167 | <li><a href="index.wiki"><b>Home Page</b></a></li> |
| 168 | <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> |
| 169 | <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li> |
| 170 | <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li> |
| 171 | <li><a href="server/"><b>How To Configure A Fossil Server</b></a></li> |
| @@ -221,10 +226,11 @@ | |
| 226 | <li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> |
| 227 | <li><a href="childprojects.wiki">Projects — Child</a></li> |
| 228 | <li><a href="fossil_prompt.wiki">Prompt — Fossilized Bash</a></li> |
| 229 | <li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> |
| 230 | <li><a href="tls-nginx.md"><b>Proxying Fossil via HTTPS with nginx</b></a></li> |
| 231 | <li><a href="history.md">Purpose And History Of Fossil — The</a></li> |
| 232 | <li><a href="faq.wiki">Questions — Frequently Asked</a></li> |
| 233 | <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li> |
| 234 | <li><a href="quickstart.wiki">Quick Start Guide — Fossil</a></li> |
| 235 | <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li> |
| 236 | <li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li> |
| @@ -279,10 +285,11 @@ | |
| 285 | <li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li> |
| 286 | <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li> |
| 287 | <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li> |
| 288 | <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li> |
| 289 | <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li> |
| 290 | <li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li> |
| 291 | <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li> |
| 292 | <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li> |
| 293 | <li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li> |
| 294 | <li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li> |
| 295 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| @@ -298,10 +305,11 @@ | |
| 305 | <li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li> |
| 306 | <li><a href="fiveminutes.wiki">User — Up and Running in 5 Minutes as a Single</a></li> |
| 307 | <li><a href="caps/">User Capabilities — Administering</a></li> |
| 308 | <li><a href="caps/ref.html"><b>User Capability Reference</b></a></li> |
| 309 | <li><a href="caps/admin-v-setup.md">Users — Differences Between Setup and Admin</a></li> |
| 310 | <li><a href="gitusers.md">Users With Git Experience — Hints For</a></li> |
| 311 | <li><a href="serverext.wiki">Using CGI Scripts — Adding Extensions To A Fossil Server</a></li> |
| 312 | <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li> |
| 313 | <li><a href="env-opts.md">Variables and Global Options — Environment</a></li> |
| 314 | <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> |
| 315 | <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> |
| 316 |
+15
-2
| --- www/private.wiki | ||
| +++ www/private.wiki | ||
| @@ -31,14 +31,27 @@ | ||
| 31 | 31 | fossil update trunk |
| 32 | 32 | fossil merge private |
| 33 | 33 | fossil commit |
| 34 | 34 | </pre></blockquote> |
| 35 | 35 | |
| 36 | -The private branch remains private. (There is no way to convert a private | |
| 37 | -branch into a public branch.) But all of the changes associated with | |
| 36 | +The private branch remains private, but all of the changes associated with | |
| 38 | 37 | the private branch are now folded into the public branch and are hence |
| 39 | 38 | visible to other users of the project. |
| 39 | + | |
| 40 | +A private branch created with Fossil version 1.30 or newer can also be | |
| 41 | +converted into a public branch using the <code>fossil publish</code> | |
| 42 | +command. However, there is no way to convert a private branch created with | |
| 43 | +older versions of Fossil into a public branch. | |
| 44 | + | |
| 45 | +The <code>--integrate</code> option of <code>fossil merge</code> (to close | |
| 46 | +the merged branch when committing) is ignored for a private branch -- or the | |
| 47 | +check-in manifest of the resulting merge child would include a | |
| 48 | +<code>+close</code> tag referring to the leaf check-in on the private branch, | |
| 49 | +and generate a missing artifact reference on repository clones without that | |
| 50 | +private branch. It's still possible to close the leaf of the private branch | |
| 51 | +(after committing the merge child) with the <code>fossil amend --close</code> | |
| 52 | +command. | |
| 40 | 53 | |
| 41 | 54 | <h2>Syncing Private Branches</h2> |
| 42 | 55 | |
| 43 | 56 | A private branch normally stays on the one repository where it was |
| 44 | 57 | originally created. But sometimes you want to share private branches |
| 45 | 58 |
| --- www/private.wiki | |
| +++ www/private.wiki | |
| @@ -31,14 +31,27 @@ | |
| 31 | fossil update trunk |
| 32 | fossil merge private |
| 33 | fossil commit |
| 34 | </pre></blockquote> |
| 35 | |
| 36 | The private branch remains private. (There is no way to convert a private |
| 37 | branch into a public branch.) But all of the changes associated with |
| 38 | the private branch are now folded into the public branch and are hence |
| 39 | visible to other users of the project. |
| 40 | |
| 41 | <h2>Syncing Private Branches</h2> |
| 42 | |
| 43 | A private branch normally stays on the one repository where it was |
| 44 | originally created. But sometimes you want to share private branches |
| 45 |
| --- www/private.wiki | |
| +++ www/private.wiki | |
| @@ -31,14 +31,27 @@ | |
| 31 | fossil update trunk |
| 32 | fossil merge private |
| 33 | fossil commit |
| 34 | </pre></blockquote> |
| 35 | |
| 36 | The private branch remains private, but all of the changes associated with |
| 37 | the private branch are now folded into the public branch and are hence |
| 38 | visible to other users of the project. |
| 39 | |
| 40 | A private branch created with Fossil version 1.30 or newer can also be |
| 41 | converted into a public branch using the <code>fossil publish</code> |
| 42 | command. However, there is no way to convert a private branch created with |
| 43 | older versions of Fossil into a public branch. |
| 44 | |
| 45 | The <code>--integrate</code> option of <code>fossil merge</code> (to close |
| 46 | the merged branch when committing) is ignored for a private branch -- or the |
| 47 | check-in manifest of the resulting merge child would include a |
| 48 | <code>+close</code> tag referring to the leaf check-in on the private branch, |
| 49 | and generate a missing artifact reference on repository clones without that |
| 50 | private branch. It's still possible to close the leaf of the private branch |
| 51 | (after committing the merge child) with the <code>fossil amend --close</code> |
| 52 | command. |
| 53 | |
| 54 | <h2>Syncing Private Branches</h2> |
| 55 | |
| 56 | A private branch normally stays on the one repository where it was |
| 57 | originally created. But sometimes you want to share private branches |
| 58 |
+4
-4
| --- www/qandc.wiki | ||
| +++ www/qandc.wiki | ||
| @@ -1,13 +1,13 @@ | ||
| 1 | 1 | <title>Questions And Criticisms</title> |
| 2 | 2 | <nowiki> |
| 3 | 3 | <h1 align="center">Questions And Criticisms</h1> |
| 4 | 4 | |
| 5 | -<p>This page is a collection of real questions and criticisms that have been | |
| 6 | -raised against fossil together with responses from the program's author.</p> | |
| 7 | - | |
| 8 | -<p>Note: See also the <a href="faq.wiki">Frequently Asked Questions</a>.</p> | |
| 5 | +<p>This page is a collection of real questions and criticisms that were | |
| 6 | +raised against Fossil early in its history (circa 2008). | |
| 7 | +This page is old and has not been kept up-to-date. See the | |
| 8 | +</nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>.</p> | |
| 9 | 9 | |
| 10 | 10 | <b>Fossil sounds like a lot of reinvention of the wheel. |
| 11 | 11 | Why create your own DVCS when you could have reused mercurial?</b> |
| 12 | 12 | |
| 13 | 13 | <blockquote> |
| 14 | 14 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -1,13 +1,13 @@ | |
| 1 | <title>Questions And Criticisms</title> |
| 2 | <nowiki> |
| 3 | <h1 align="center">Questions And Criticisms</h1> |
| 4 | |
| 5 | <p>This page is a collection of real questions and criticisms that have been |
| 6 | raised against fossil together with responses from the program's author.</p> |
| 7 | |
| 8 | <p>Note: See also the <a href="faq.wiki">Frequently Asked Questions</a>.</p> |
| 9 | |
| 10 | <b>Fossil sounds like a lot of reinvention of the wheel. |
| 11 | Why create your own DVCS when you could have reused mercurial?</b> |
| 12 | |
| 13 | <blockquote> |
| 14 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -1,13 +1,13 @@ | |
| 1 | <title>Questions And Criticisms</title> |
| 2 | <nowiki> |
| 3 | <h1 align="center">Questions And Criticisms</h1> |
| 4 | |
| 5 | <p>This page is a collection of real questions and criticisms that were |
| 6 | raised against Fossil early in its history (circa 2008). |
| 7 | This page is old and has not been kept up-to-date. See the |
| 8 | </nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>.</p> |
| 9 | |
| 10 | <b>Fossil sounds like a lot of reinvention of the wheel. |
| 11 | Why create your own DVCS when you could have reused mercurial?</b> |
| 12 | |
| 13 | <blockquote> |
| 14 |
+12
-10
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -1,19 +1,19 @@ | ||
| 1 | 1 | <title>Fossil Quick Start Guide</title> |
| 2 | 2 | <h1 align="center">Fossil Quick Start</h1> |
| 3 | 3 | |
| 4 | -<p>This is a guide to get you started using fossil quickly | |
| 4 | +<p>This is a guide to help you get started using Fossil quickly | |
| 5 | 5 | and painlessly.</p> |
| 6 | 6 | |
| 7 | 7 | <h2 id="install">Installing</h2> |
| 8 | 8 | |
| 9 | 9 | <p>Fossil is a single self-contained C program. You need to |
| 10 | 10 | either download a |
| 11 | 11 | <a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled |
| 12 | 12 | binary</a> |
| 13 | 13 | or <a href="build.wiki">compile it yourself</a> from sources. |
| 14 | - Install fossil by putting the fossil binary | |
| 14 | + Install Fossil by putting the fossil binary | |
| 15 | 15 | someplace on your $PATH.</p> |
| 16 | 16 | |
| 17 | 17 | <a name="fslclone"></a> |
| 18 | 18 | <h2>General Work Flow</h2> |
| 19 | 19 | |
| @@ -391,13 +391,15 @@ | ||
| 391 | 391 | |
| 392 | 392 | <blockquote> |
| 393 | 393 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> |
| 394 | 394 | </blockquote> |
| 395 | 395 | |
| 396 | -<h2>More Hints</h2> | |
| 397 | - | |
| 398 | - <p>A [/help | complete list of commands] is available, as is the | |
| 399 | - [./hints.wiki|helpful hints] document. See the | |
| 400 | - [./permutedindex.html#pindex|permuted index] for additional | |
| 401 | - documentation. | |
| 402 | - | |
| 403 | - <p>Explore and have fun!</p> | |
| 396 | +<h2 id="links">Other Resources</h2> | |
| 397 | + | |
| 398 | + <ul> | |
| 399 | + <li> <a href="./gitusers.md">Hints for users with prior Git experience</a> | |
| 400 | + <li> <a href="./whyusefossil.wiki">Benefits Of Version Control</a> | |
| 401 | + <li> <a href="./history.md">The Purpose And History of Fossil</a> | |
| 402 | + <li> <a href="./branching.wiki">Branching, Forking, Merge, and Taggings</a> | |
| 403 | + <li> <a href="./hints.wiki">Tips and Usage Hints</a> | |
| 404 | + <li> <a href="./permutedindex.html">Comprehensive documentation index</a> | |
| 405 | + </ul> | |
| 404 | 406 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -1,19 +1,19 @@ | |
| 1 | <title>Fossil Quick Start Guide</title> |
| 2 | <h1 align="center">Fossil Quick Start</h1> |
| 3 | |
| 4 | <p>This is a guide to get you started using fossil quickly |
| 5 | and painlessly.</p> |
| 6 | |
| 7 | <h2 id="install">Installing</h2> |
| 8 | |
| 9 | <p>Fossil is a single self-contained C program. You need to |
| 10 | either download a |
| 11 | <a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled |
| 12 | binary</a> |
| 13 | or <a href="build.wiki">compile it yourself</a> from sources. |
| 14 | Install fossil by putting the fossil binary |
| 15 | someplace on your $PATH.</p> |
| 16 | |
| 17 | <a name="fslclone"></a> |
| 18 | <h2>General Work Flow</h2> |
| 19 | |
| @@ -391,13 +391,15 @@ | |
| 391 | |
| 392 | <blockquote> |
| 393 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> |
| 394 | </blockquote> |
| 395 | |
| 396 | <h2>More Hints</h2> |
| 397 | |
| 398 | <p>A [/help | complete list of commands] is available, as is the |
| 399 | [./hints.wiki|helpful hints] document. See the |
| 400 | [./permutedindex.html#pindex|permuted index] for additional |
| 401 | documentation. |
| 402 | |
| 403 | <p>Explore and have fun!</p> |
| 404 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -1,19 +1,19 @@ | |
| 1 | <title>Fossil Quick Start Guide</title> |
| 2 | <h1 align="center">Fossil Quick Start</h1> |
| 3 | |
| 4 | <p>This is a guide to help you get started using Fossil quickly |
| 5 | and painlessly.</p> |
| 6 | |
| 7 | <h2 id="install">Installing</h2> |
| 8 | |
| 9 | <p>Fossil is a single self-contained C program. You need to |
| 10 | either download a |
| 11 | <a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled |
| 12 | binary</a> |
| 13 | or <a href="build.wiki">compile it yourself</a> from sources. |
| 14 | Install Fossil by putting the fossil binary |
| 15 | someplace on your $PATH.</p> |
| 16 | |
| 17 | <a name="fslclone"></a> |
| 18 | <h2>General Work Flow</h2> |
| 19 | |
| @@ -391,13 +391,15 @@ | |
| 391 | |
| 392 | <blockquote> |
| 393 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> |
| 394 | </blockquote> |
| 395 | |
| 396 | <h2 id="links">Other Resources</h2> |
| 397 | |
| 398 | <ul> |
| 399 | <li> <a href="./gitusers.md">Hints for users with prior Git experience</a> |
| 400 | <li> <a href="./whyusefossil.wiki">Benefits Of Version Control</a> |
| 401 | <li> <a href="./history.md">The Purpose And History of Fossil</a> |
| 402 | <li> <a href="./branching.wiki">Branching, Forking, Merge, and Taggings</a> |
| 403 | <li> <a href="./hints.wiki">Tips and Usage Hints</a> |
| 404 | <li> <a href="./permutedindex.html">Comprehensive documentation index</a> |
| 405 | </ul> |
| 406 |
+4
-4
| --- www/rebaseharm.md | ||
| +++ www/rebaseharm.md | ||
| @@ -317,14 +317,14 @@ | ||
| 317 | 317 | that blames the merged check-in as the source of the problem they’re |
| 318 | 318 | chasing down; they then have to manually work out which of the 10 steps |
| 319 | 319 | the original developer took to create it to find the source of the |
| 320 | 320 | actual problem. |
| 321 | 321 | |
| 322 | -Fossil pushes all 11 check-ins to the parent repository by default, so | |
| 323 | -that someone doing that bisect sees the complete check-in history, so | |
| 324 | -the bisect will point them at the single original check-in that caused | |
| 325 | -the problem. | |
| 322 | +An equivalent push in Fossil will send all 11 check-ins to the parent | |
| 323 | +repository so that a later investigator doing the same sort of bisect | |
| 324 | +sees the complete check-in history. That bisect will point the | |
| 325 | +investigator at the single original check-in that caused the problem. | |
| 326 | 326 | |
| 327 | 327 | ### <a name="comments"></a>7.3 Multiple check-ins require multiple check-in comments |
| 328 | 328 | |
| 329 | 329 | The more comments you have from a given developer on a given body of |
| 330 | 330 | code, the more concise documentation you have of that developer's |
| 331 | 331 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -317,14 +317,14 @@ | |
| 317 | that blames the merged check-in as the source of the problem they’re |
| 318 | chasing down; they then have to manually work out which of the 10 steps |
| 319 | the original developer took to create it to find the source of the |
| 320 | actual problem. |
| 321 | |
| 322 | Fossil pushes all 11 check-ins to the parent repository by default, so |
| 323 | that someone doing that bisect sees the complete check-in history, so |
| 324 | the bisect will point them at the single original check-in that caused |
| 325 | the problem. |
| 326 | |
| 327 | ### <a name="comments"></a>7.3 Multiple check-ins require multiple check-in comments |
| 328 | |
| 329 | The more comments you have from a given developer on a given body of |
| 330 | code, the more concise documentation you have of that developer's |
| 331 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -317,14 +317,14 @@ | |
| 317 | that blames the merged check-in as the source of the problem they’re |
| 318 | chasing down; they then have to manually work out which of the 10 steps |
| 319 | the original developer took to create it to find the source of the |
| 320 | actual problem. |
| 321 | |
| 322 | An equivalent push in Fossil will send all 11 check-ins to the parent |
| 323 | repository so that a later investigator doing the same sort of bisect |
| 324 | sees the complete check-in history. That bisect will point the |
| 325 | investigator at the single original check-in that caused the problem. |
| 326 | |
| 327 | ### <a name="comments"></a>7.3 Multiple check-ins require multiple check-in comments |
| 328 | |
| 329 | The more comments you have from a given developer on a given body of |
| 330 | code, the more concise documentation you have of that developer's |
| 331 |
+26
-13
| --- www/ssl.wiki | ||
| +++ www/ssl.wiki | ||
| @@ -104,25 +104,36 @@ | ||
| 104 | 104 | |
| 105 | 105 | |
| 106 | 106 | <h3 id="certs">Certificates</h3> |
| 107 | 107 | |
| 108 | 108 | To verify the identify of a server, TLS uses |
| 109 | -[https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates]. | |
| 109 | +[https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates], a | |
| 110 | +scheme that depends on a trust hierarchy of so-called | |
| 111 | +[https://en.wikipedia.org/wiki/Certificate_authority | Certificate | |
| 112 | +Authorities]. The tree of trust relationships ultimately ends in the | |
| 113 | +CA roots, which are considered the ultimate arbiters of who to trust in | |
| 114 | +this scheme. | |
| 115 | + | |
| 116 | +The question then is, what CA roots does Fossil trust? | |
| 110 | 117 | |
| 111 | -If you are using a self-signed certificate, you'll be asked if you want | |
| 118 | +If you are using a self-signed certificate, Fossil will initially not | |
| 119 | +know that it can trust your certificate, so you'll be asked if you want | |
| 112 | 120 | to accept the certificate the first time you communicate with the |
| 113 | 121 | server. Verify the certificate fingerprint is correct, then answer |
| 114 | -"always" to remember your decision. | |
| 122 | +"always" if you want Fossil to remember your decision. | |
| 115 | 123 | |
| 116 | 124 | If you are cloning from or syncing to Fossil servers that use a |
| 117 | -certificate signed by a | |
| 118 | -[https://en.wikipedia.org/wiki/Certificate_authority|certificate | |
| 119 | -authority] (CA), Fossil needs to know which CAs you trust to sign those | |
| 120 | -certificates. Fossil relies on the OpenSSL library to have some way to | |
| 121 | -check a trusted list of CA signing keys. | |
| 125 | +certificate signed by a well-known CA or one of its delegates, Fossil | |
| 126 | +still has to know which CA roots to trust. When this fails, you get a | |
| 127 | +big long error message that starts with this text: | |
| 128 | + | |
| 129 | +<pre> | |
| 130 | + SSL verification failed: unable to get local issuer certificate | |
| 131 | +</pre> | |
| 122 | 132 | |
| 123 | -There are two common ways this fails: | |
| 133 | +Fossil relies on the OpenSSL library to have some way to check a trusted | |
| 134 | +list of CA signing keys. There are two common ways this fails: | |
| 124 | 135 | |
| 125 | 136 | # <p>The OpenSSL library Fossil is linked to doesn't have a CA |
| 126 | 137 | signing key set at all, so that it initially trusts no certificates |
| 127 | 138 | at all.</p> |
| 128 | 139 | # <p>The OpenSSL library does have a CA cert set, but your Fossil server's |
| @@ -137,11 +148,13 @@ | ||
| 137 | 148 | fossil set --global ssl-ca-location /path/to/local-ca.pem |
| 138 | 149 | </pre> |
| 139 | 150 | |
| 140 | 151 | The use of <tt>--global</tt> with this option is common, since you may |
| 141 | 152 | have multiple reposotories served under certificates signed by that same |
| 142 | -CA. | |
| 153 | +CA. However, if you have a mix of publicly-signed and locally-signed | |
| 154 | +certificates, you might want to drop the <tt>--global</tt> flag and set | |
| 155 | +this option on a per-repository basis instead. | |
| 143 | 156 | |
| 144 | 157 | A common way to run into the broader first problem is that you're on |
| 145 | 158 | FreeBSD, which does not install a CA certificate set by default, even as |
| 146 | 159 | a dependency of the OpenSSL library. If you're using a certificate |
| 147 | 160 | signed by one of the major public CAs, you can solve this by installing |
| @@ -155,15 +168,15 @@ | ||
| 155 | 168 | certificate set, but it's not in a format that OpenSSL understands how |
| 156 | 169 | to use. Rather than try to find a way to convert the data format, you |
| 157 | 170 | may find it acceptable to use the same Mozilla NSS cert set. I do not |
| 158 | 171 | know of a way to easily get this from Mozilla themselves, but I did find |
| 159 | 172 | a [https://curl.haxx.se/docs/caextract.html|third party source] for the |
| 160 | -<tt>cacert.pem</tt> file. Install it somewhere on your system, then | |
| 161 | -point Fossil at it like so: | |
| 173 | +<tt>cacert.pem</tt> file. I suggest placing the file into your Windows | |
| 174 | +user home directory so that you can then point Fossil at it like so: | |
| 162 | 175 | |
| 163 | 176 | <pre> |
| 164 | - fossil set --global ssl-ca-location /path/to/cacert.pem | |
| 177 | + fossil set --global ssl-ca-location %userprofile%\cacert.pem | |
| 165 | 178 | </pre> |
| 166 | 179 | |
| 167 | 180 | This can also happen if you've linked Fossil to a version of OpenSSL |
| 168 | 181 | [#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can |
| 169 | 182 | work in that case, too. |
| 170 | 183 |
| --- www/ssl.wiki | |
| +++ www/ssl.wiki | |
| @@ -104,25 +104,36 @@ | |
| 104 | |
| 105 | |
| 106 | <h3 id="certs">Certificates</h3> |
| 107 | |
| 108 | To verify the identify of a server, TLS uses |
| 109 | [https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates]. |
| 110 | |
| 111 | If you are using a self-signed certificate, you'll be asked if you want |
| 112 | to accept the certificate the first time you communicate with the |
| 113 | server. Verify the certificate fingerprint is correct, then answer |
| 114 | "always" to remember your decision. |
| 115 | |
| 116 | If you are cloning from or syncing to Fossil servers that use a |
| 117 | certificate signed by a |
| 118 | [https://en.wikipedia.org/wiki/Certificate_authority|certificate |
| 119 | authority] (CA), Fossil needs to know which CAs you trust to sign those |
| 120 | certificates. Fossil relies on the OpenSSL library to have some way to |
| 121 | check a trusted list of CA signing keys. |
| 122 | |
| 123 | There are two common ways this fails: |
| 124 | |
| 125 | # <p>The OpenSSL library Fossil is linked to doesn't have a CA |
| 126 | signing key set at all, so that it initially trusts no certificates |
| 127 | at all.</p> |
| 128 | # <p>The OpenSSL library does have a CA cert set, but your Fossil server's |
| @@ -137,11 +148,13 @@ | |
| 137 | fossil set --global ssl-ca-location /path/to/local-ca.pem |
| 138 | </pre> |
| 139 | |
| 140 | The use of <tt>--global</tt> with this option is common, since you may |
| 141 | have multiple reposotories served under certificates signed by that same |
| 142 | CA. |
| 143 | |
| 144 | A common way to run into the broader first problem is that you're on |
| 145 | FreeBSD, which does not install a CA certificate set by default, even as |
| 146 | a dependency of the OpenSSL library. If you're using a certificate |
| 147 | signed by one of the major public CAs, you can solve this by installing |
| @@ -155,15 +168,15 @@ | |
| 155 | certificate set, but it's not in a format that OpenSSL understands how |
| 156 | to use. Rather than try to find a way to convert the data format, you |
| 157 | may find it acceptable to use the same Mozilla NSS cert set. I do not |
| 158 | know of a way to easily get this from Mozilla themselves, but I did find |
| 159 | a [https://curl.haxx.se/docs/caextract.html|third party source] for the |
| 160 | <tt>cacert.pem</tt> file. Install it somewhere on your system, then |
| 161 | point Fossil at it like so: |
| 162 | |
| 163 | <pre> |
| 164 | fossil set --global ssl-ca-location /path/to/cacert.pem |
| 165 | </pre> |
| 166 | |
| 167 | This can also happen if you've linked Fossil to a version of OpenSSL |
| 168 | [#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can |
| 169 | work in that case, too. |
| 170 |
| --- www/ssl.wiki | |
| +++ www/ssl.wiki | |
| @@ -104,25 +104,36 @@ | |
| 104 | |
| 105 | |
| 106 | <h3 id="certs">Certificates</h3> |
| 107 | |
| 108 | To verify the identify of a server, TLS uses |
| 109 | [https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates], a |
| 110 | scheme that depends on a trust hierarchy of so-called |
| 111 | [https://en.wikipedia.org/wiki/Certificate_authority | Certificate |
| 112 | Authorities]. The tree of trust relationships ultimately ends in the |
| 113 | CA roots, which are considered the ultimate arbiters of who to trust in |
| 114 | this scheme. |
| 115 | |
| 116 | The question then is, what CA roots does Fossil trust? |
| 117 | |
| 118 | If you are using a self-signed certificate, Fossil will initially not |
| 119 | know that it can trust your certificate, so you'll be asked if you want |
| 120 | to accept the certificate the first time you communicate with the |
| 121 | server. Verify the certificate fingerprint is correct, then answer |
| 122 | "always" if you want Fossil to remember your decision. |
| 123 | |
| 124 | If you are cloning from or syncing to Fossil servers that use a |
| 125 | certificate signed by a well-known CA or one of its delegates, Fossil |
| 126 | still has to know which CA roots to trust. When this fails, you get a |
| 127 | big long error message that starts with this text: |
| 128 | |
| 129 | <pre> |
| 130 | SSL verification failed: unable to get local issuer certificate |
| 131 | </pre> |
| 132 | |
| 133 | Fossil relies on the OpenSSL library to have some way to check a trusted |
| 134 | list of CA signing keys. There are two common ways this fails: |
| 135 | |
| 136 | # <p>The OpenSSL library Fossil is linked to doesn't have a CA |
| 137 | signing key set at all, so that it initially trusts no certificates |
| 138 | at all.</p> |
| 139 | # <p>The OpenSSL library does have a CA cert set, but your Fossil server's |
| @@ -137,11 +148,13 @@ | |
| 148 | fossil set --global ssl-ca-location /path/to/local-ca.pem |
| 149 | </pre> |
| 150 | |
| 151 | The use of <tt>--global</tt> with this option is common, since you may |
| 152 | have multiple reposotories served under certificates signed by that same |
| 153 | CA. However, if you have a mix of publicly-signed and locally-signed |
| 154 | certificates, you might want to drop the <tt>--global</tt> flag and set |
| 155 | this option on a per-repository basis instead. |
| 156 | |
| 157 | A common way to run into the broader first problem is that you're on |
| 158 | FreeBSD, which does not install a CA certificate set by default, even as |
| 159 | a dependency of the OpenSSL library. If you're using a certificate |
| 160 | signed by one of the major public CAs, you can solve this by installing |
| @@ -155,15 +168,15 @@ | |
| 168 | certificate set, but it's not in a format that OpenSSL understands how |
| 169 | to use. Rather than try to find a way to convert the data format, you |
| 170 | may find it acceptable to use the same Mozilla NSS cert set. I do not |
| 171 | know of a way to easily get this from Mozilla themselves, but I did find |
| 172 | a [https://curl.haxx.se/docs/caextract.html|third party source] for the |
| 173 | <tt>cacert.pem</tt> file. I suggest placing the file into your Windows |
| 174 | user home directory so that you can then point Fossil at it like so: |
| 175 | |
| 176 | <pre> |
| 177 | fossil set --global ssl-ca-location %userprofile%\cacert.pem |
| 178 | </pre> |
| 179 | |
| 180 | This can also happen if you've linked Fossil to a version of OpenSSL |
| 181 | [#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can |
| 182 | work in that case, too. |
| 183 |
+7
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -421,10 +421,13 @@ | ||
| 421 | 421 | using a gimme card in either the reply or in the next message.</p> |
| 422 | 422 | |
| 423 | 423 | <p>If the second argument exists and is "1", then the artifact |
| 424 | 424 | identified by the first argument is private on the sender and should |
| 425 | 425 | be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. |
| 426 | + | |
| 427 | +<p>The name "igot" comes from the English slang expression "I got" meaning | |
| 428 | +"I have". | |
| 426 | 429 | |
| 427 | 430 | <h4>3.6.1 Unversioned Igot Cards</h4> |
| 428 | 431 | |
| 429 | 432 | <p>Zero or more "uvigot" cards are sent from server to client when |
| 430 | 433 | synchronizing unversioned content. The format of a uvigot card is |
| @@ -469,10 +472,14 @@ | ||
| 469 | 472 | <p>The argument to the gimme card is the ID of the artifact that |
| 470 | 473 | the sender wants. The receiver will typically respond to a |
| 471 | 474 | gimme card by sending a file card in its reply or in the next |
| 472 | 475 | message.</p> |
| 473 | 476 | |
| 477 | +<p>The "gimme" name means "give me". The imperative "give me" is | |
| 478 | +pronounced as if it were a single word "gimme" in some dialects of | |
| 479 | +English (including the dialect spoken by the original author of Fossil). | |
| 480 | + | |
| 474 | 481 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 475 | 482 | |
| 476 | 483 | <p>Sync synchronizing unversioned content, the client may send "uvgimme" |
| 477 | 484 | cards to the server. A uvgimme card requests that the server send |
| 478 | 485 | unversioned content to the client. The format of a uvgimme card is |
| 479 | 486 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -421,10 +421,13 @@ | |
| 421 | using a gimme card in either the reply or in the next message.</p> |
| 422 | |
| 423 | <p>If the second argument exists and is "1", then the artifact |
| 424 | identified by the first argument is private on the sender and should |
| 425 | be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. |
| 426 | |
| 427 | <h4>3.6.1 Unversioned Igot Cards</h4> |
| 428 | |
| 429 | <p>Zero or more "uvigot" cards are sent from server to client when |
| 430 | synchronizing unversioned content. The format of a uvigot card is |
| @@ -469,10 +472,14 @@ | |
| 469 | <p>The argument to the gimme card is the ID of the artifact that |
| 470 | the sender wants. The receiver will typically respond to a |
| 471 | gimme card by sending a file card in its reply or in the next |
| 472 | message.</p> |
| 473 | |
| 474 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 475 | |
| 476 | <p>Sync synchronizing unversioned content, the client may send "uvgimme" |
| 477 | cards to the server. A uvgimme card requests that the server send |
| 478 | unversioned content to the client. The format of a uvgimme card is |
| 479 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -421,10 +421,13 @@ | |
| 421 | using a gimme card in either the reply or in the next message.</p> |
| 422 | |
| 423 | <p>If the second argument exists and is "1", then the artifact |
| 424 | identified by the first argument is private on the sender and should |
| 425 | be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. |
| 426 | |
| 427 | <p>The name "igot" comes from the English slang expression "I got" meaning |
| 428 | "I have". |
| 429 | |
| 430 | <h4>3.6.1 Unversioned Igot Cards</h4> |
| 431 | |
| 432 | <p>Zero or more "uvigot" cards are sent from server to client when |
| 433 | synchronizing unversioned content. The format of a uvigot card is |
| @@ -469,10 +472,14 @@ | |
| 472 | <p>The argument to the gimme card is the ID of the artifact that |
| 473 | the sender wants. The receiver will typically respond to a |
| 474 | gimme card by sending a file card in its reply or in the next |
| 475 | message.</p> |
| 476 | |
| 477 | <p>The "gimme" name means "give me". The imperative "give me" is |
| 478 | pronounced as if it were a single word "gimme" in some dialects of |
| 479 | English (including the dialect spoken by the original author of Fossil). |
| 480 | |
| 481 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 482 | |
| 483 | <p>Sync synchronizing unversioned content, the client may send "uvgimme" |
| 484 | cards to the server. A uvgimme card requests that the server send |
| 485 | unversioned content to the client. The format of a uvgimme card is |
| 486 |
+51
-10
| --- www/tech_overview.wiki | ||
| +++ www/tech_overview.wiki | ||
| @@ -68,11 +68,12 @@ | ||
| 68 | 68 | database files are used by Fossil, with detailed discussion following. |
| 69 | 69 | |
| 70 | 70 | <table border="1" width="80%" cellpadding="0" align="center"> |
| 71 | 71 | <tr> |
| 72 | 72 | <td width="33%" valign="top"> |
| 73 | -<h3 align="center">Configuration Database<br>"~/.fossil"</h3> | |
| 73 | +<h3 align="center">Configuration Database<br>"~/.fossil" or<br> | |
| 74 | +"~/.config/fossil.db"</h3> | |
| 74 | 75 | <ul> |
| 75 | 76 | <li>Global [/help/settings |settings] |
| 76 | 77 | <li>List of active repositories used by the [/help/all | all] command |
| 77 | 78 | </ul> |
| 78 | 79 | </td> |
| @@ -87,11 +88,11 @@ | ||
| 87 | 88 | <li>Metadata about the global state to facilitate rapid |
| 88 | 89 | queries |
| 89 | 90 | </ul> |
| 90 | 91 | </td> |
| 91 | 92 | <td width="33%" valign="top"> |
| 92 | -<h3 align="center">Checkout Database<br>"_FOSSIL_"</h3> | |
| 93 | +<h3 align="center">Checkout Database<br>"_FOSSIL_" or ".fslckout"</h3> | |
| 93 | 94 | <ul> |
| 94 | 95 | <li>The repository database used by this checkout |
| 95 | 96 | <li>The version currently checked out |
| 96 | 97 | <li>Other versions [/help/merge | merged] in but not |
| 97 | 98 | yet [/help/commit | committed] |
| @@ -124,18 +125,58 @@ | ||
| 124 | 125 | |
| 125 | 126 | The configuration database also maintains a list of repositories. This |
| 126 | 127 | list is used by the [/help/all | fossil all] command in order to run various |
| 127 | 128 | operations such as "sync" or "rebuild" on all repositories managed by a user. |
| 128 | 129 | |
| 129 | -On Unix systems, the configuration database is named ".fossil" and is | |
| 130 | -located in the user's home directory. On Windows, the configuration | |
| 131 | -database is named "_fossil" (using an underscore as the first character | |
| 132 | -instead of a dot) and is located in the directory specified by the | |
| 133 | -LOCALAPPDATA, APPDATA, or HOMEPATH environment variables, in that order. | |
| 134 | - | |
| 135 | -You can override this default location by defining the environment | |
| 136 | -variable FOSSIL_HOME pointing to an appropriate (writable) directory. | |
| 130 | +<a name='configloc'></a> | |
| 131 | +<h4>2.1.1 Location Of The Configuration Database</h4> | |
| 132 | + | |
| 133 | +On Unix systems, the configuration database is named by the following | |
| 134 | +algorithm: | |
| 135 | + | |
| 136 | +<blockquote><table border="0"> | |
| 137 | +<tr><td>1. if environment variable FOSSIL_HOME exists | |
| 138 | +<td> → <td>$FOSSIL_HOME/.fossil | |
| 139 | +<tr><td>2. if file ~/.fossil exists<td> →<td>~/.fossil | |
| 140 | +<tr><td>3. if environment variable XDG_CONFIG_HOME exists | |
| 141 | + <td> →<td>$XDG_CONFIG_HOME/fossil.db | |
| 142 | +<tr><td>4. if the directory ~/.config exists | |
| 143 | + <td> →<td>~/.config/fossil.db | |
| 144 | +<tr><td>5. Otherwise<td> →<td>~/.fossil | |
| 145 | +</table></blockquote> | |
| 146 | + | |
| 147 | +Another way of thinking of this algorithm is the following: | |
| 148 | + | |
| 149 | + * Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined | |
| 150 | + * Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems | |
| 151 | + if the ~/.fossil file does not already exist | |
| 152 | + * Otherwise, use the traditional unix name of "~/.fossil" | |
| 153 | + | |
| 154 | +This algorithm is complex due to the need for historical compatibility. | |
| 155 | +Originally, the database was always just "~/.fossil". Then support | |
| 156 | +for the FOSSIL_HOME environment variable as added. Later, support for the | |
| 157 | +[https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames] | |
| 158 | +was added. Each of these changes needed to continue to support legacy | |
| 159 | +installations. | |
| 160 | + | |
| 161 | +On Windows, the configuration database is the first of the following | |
| 162 | +for which the corresponding environment variables exist: | |
| 163 | + | |
| 164 | + * %FOSSIL_HOME%/_fossil | |
| 165 | + * %LOCALAPPDATA%/_fossil | |
| 166 | + * %APPDATA%/_fossil | |
| 167 | + * %USERPROFILES%/_fossil | |
| 168 | + * %HOMEDRIVE%%HOMEPATH%/_fossil | |
| 169 | + | |
| 170 | +The second case is the one that usually determines the name Note that the | |
| 171 | +FOSSIL_HOME environment variable can always be set to determine the | |
| 172 | +location of the configuration database. Note also that the configuration | |
| 173 | +database file itself is called ".fossil" or "fossil.db" on unix but | |
| 174 | +"_fossil" on windows. | |
| 175 | + | |
| 176 | +The [/help?cmd=info|fossil info] command will show the location of | |
| 177 | +the configuration database on a line that starts with "config-db:". | |
| 137 | 178 | |
| 138 | 179 | <h3>2.2 Repository Databases</h3> |
| 139 | 180 | |
| 140 | 181 | The repository database is the file that is commonly referred to as |
| 141 | 182 | "the repository". This is because the repository database contains, |
| 142 | 183 | |
| 143 | 184 | ADDED www/userlinks.wiki |
| --- www/tech_overview.wiki | |
| +++ www/tech_overview.wiki | |
| @@ -68,11 +68,12 @@ | |
| 68 | database files are used by Fossil, with detailed discussion following. |
| 69 | |
| 70 | <table border="1" width="80%" cellpadding="0" align="center"> |
| 71 | <tr> |
| 72 | <td width="33%" valign="top"> |
| 73 | <h3 align="center">Configuration Database<br>"~/.fossil"</h3> |
| 74 | <ul> |
| 75 | <li>Global [/help/settings |settings] |
| 76 | <li>List of active repositories used by the [/help/all | all] command |
| 77 | </ul> |
| 78 | </td> |
| @@ -87,11 +88,11 @@ | |
| 87 | <li>Metadata about the global state to facilitate rapid |
| 88 | queries |
| 89 | </ul> |
| 90 | </td> |
| 91 | <td width="33%" valign="top"> |
| 92 | <h3 align="center">Checkout Database<br>"_FOSSIL_"</h3> |
| 93 | <ul> |
| 94 | <li>The repository database used by this checkout |
| 95 | <li>The version currently checked out |
| 96 | <li>Other versions [/help/merge | merged] in but not |
| 97 | yet [/help/commit | committed] |
| @@ -124,18 +125,58 @@ | |
| 124 | |
| 125 | The configuration database also maintains a list of repositories. This |
| 126 | list is used by the [/help/all | fossil all] command in order to run various |
| 127 | operations such as "sync" or "rebuild" on all repositories managed by a user. |
| 128 | |
| 129 | On Unix systems, the configuration database is named ".fossil" and is |
| 130 | located in the user's home directory. On Windows, the configuration |
| 131 | database is named "_fossil" (using an underscore as the first character |
| 132 | instead of a dot) and is located in the directory specified by the |
| 133 | LOCALAPPDATA, APPDATA, or HOMEPATH environment variables, in that order. |
| 134 | |
| 135 | You can override this default location by defining the environment |
| 136 | variable FOSSIL_HOME pointing to an appropriate (writable) directory. |
| 137 | |
| 138 | <h3>2.2 Repository Databases</h3> |
| 139 | |
| 140 | The repository database is the file that is commonly referred to as |
| 141 | "the repository". This is because the repository database contains, |
| 142 | |
| 143 | DDED www/userlinks.wiki |
| --- www/tech_overview.wiki | |
| +++ www/tech_overview.wiki | |
| @@ -68,11 +68,12 @@ | |
| 68 | database files are used by Fossil, with detailed discussion following. |
| 69 | |
| 70 | <table border="1" width="80%" cellpadding="0" align="center"> |
| 71 | <tr> |
| 72 | <td width="33%" valign="top"> |
| 73 | <h3 align="center">Configuration Database<br>"~/.fossil" or<br> |
| 74 | "~/.config/fossil.db"</h3> |
| 75 | <ul> |
| 76 | <li>Global [/help/settings |settings] |
| 77 | <li>List of active repositories used by the [/help/all | all] command |
| 78 | </ul> |
| 79 | </td> |
| @@ -87,11 +88,11 @@ | |
| 88 | <li>Metadata about the global state to facilitate rapid |
| 89 | queries |
| 90 | </ul> |
| 91 | </td> |
| 92 | <td width="33%" valign="top"> |
| 93 | <h3 align="center">Checkout Database<br>"_FOSSIL_" or ".fslckout"</h3> |
| 94 | <ul> |
| 95 | <li>The repository database used by this checkout |
| 96 | <li>The version currently checked out |
| 97 | <li>Other versions [/help/merge | merged] in but not |
| 98 | yet [/help/commit | committed] |
| @@ -124,18 +125,58 @@ | |
| 125 | |
| 126 | The configuration database also maintains a list of repositories. This |
| 127 | list is used by the [/help/all | fossil all] command in order to run various |
| 128 | operations such as "sync" or "rebuild" on all repositories managed by a user. |
| 129 | |
| 130 | <a name='configloc'></a> |
| 131 | <h4>2.1.1 Location Of The Configuration Database</h4> |
| 132 | |
| 133 | On Unix systems, the configuration database is named by the following |
| 134 | algorithm: |
| 135 | |
| 136 | <blockquote><table border="0"> |
| 137 | <tr><td>1. if environment variable FOSSIL_HOME exists |
| 138 | <td> → <td>$FOSSIL_HOME/.fossil |
| 139 | <tr><td>2. if file ~/.fossil exists<td> →<td>~/.fossil |
| 140 | <tr><td>3. if environment variable XDG_CONFIG_HOME exists |
| 141 | <td> →<td>$XDG_CONFIG_HOME/fossil.db |
| 142 | <tr><td>4. if the directory ~/.config exists |
| 143 | <td> →<td>~/.config/fossil.db |
| 144 | <tr><td>5. Otherwise<td> →<td>~/.fossil |
| 145 | </table></blockquote> |
| 146 | |
| 147 | Another way of thinking of this algorithm is the following: |
| 148 | |
| 149 | * Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined |
| 150 | * Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems |
| 151 | if the ~/.fossil file does not already exist |
| 152 | * Otherwise, use the traditional unix name of "~/.fossil" |
| 153 | |
| 154 | This algorithm is complex due to the need for historical compatibility. |
| 155 | Originally, the database was always just "~/.fossil". Then support |
| 156 | for the FOSSIL_HOME environment variable as added. Later, support for the |
| 157 | [https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames] |
| 158 | was added. Each of these changes needed to continue to support legacy |
| 159 | installations. |
| 160 | |
| 161 | On Windows, the configuration database is the first of the following |
| 162 | for which the corresponding environment variables exist: |
| 163 | |
| 164 | * %FOSSIL_HOME%/_fossil |
| 165 | * %LOCALAPPDATA%/_fossil |
| 166 | * %APPDATA%/_fossil |
| 167 | * %USERPROFILES%/_fossil |
| 168 | * %HOMEDRIVE%%HOMEPATH%/_fossil |
| 169 | |
| 170 | The second case is the one that usually determines the name Note that the |
| 171 | FOSSIL_HOME environment variable can always be set to determine the |
| 172 | location of the configuration database. Note also that the configuration |
| 173 | database file itself is called ".fossil" or "fossil.db" on unix but |
| 174 | "_fossil" on windows. |
| 175 | |
| 176 | The [/help?cmd=info|fossil info] command will show the location of |
| 177 | the configuration database on a line that starts with "config-db:". |
| 178 | |
| 179 | <h3>2.2 Repository Databases</h3> |
| 180 | |
| 181 | The repository database is the file that is commonly referred to as |
| 182 | "the repository". This is because the repository database contains, |
| 183 | |
| 184 | DDED www/userlinks.wiki |
+1
| --- a/www/userlinks.wiki | ||
| +++ b/www/userlinks.wiki | ||
| @@ -0,0 +1 @@ | ||
| 1 | +<title>Links For Foswhyusefossil.wiki#definitions |
| --- a/www/userlinks.wiki | |
| +++ b/www/userlinks.wiki | |
| @@ -0,0 +1 @@ | |
| --- a/www/userlinks.wiki | |
| +++ b/www/userlinks.wiki | |
| @@ -0,0 +1 @@ | |
| 1 | <title>Links For Foswhyusefossil.wiki#definitions |
+6
-3
| --- www/whyusefossil.wiki | ||
| +++ www/whyusefossil.wiki | ||
| @@ -199,18 +199,19 @@ | ||
| 199 | 199 | might commit different changes against the same check-in. This |
| 200 | 200 | results in one parent node having two or more children. |
| 201 | 201 | <li><p>Command: <b>merge</b> → |
| 202 | 202 | combines the work of multiple check-ins into |
| 203 | 203 | a single check-out. That check-out can then be committed to create |
| 204 | - a new that has two (or more) parents. | |
| 204 | + a new check-in that has two (or more) parents. | |
| 205 | 205 | <ul> |
| 206 | 206 | <li><p>Most check-ins have just one parent, and either zero or |
| 207 | 207 | one child. |
| 208 | 208 | <li><p>When a check-in has two or more parents, one of those parents |
| 209 | - is the "primary parent". All the other parent nodes are "secondary". | |
| 209 | + is the "primary parent". All the other parent nodes are "secondary" | |
| 210 | + or "merge" parents. | |
| 210 | 211 | Conceptually, the primary parent shows the main line of |
| 211 | - development. Content from the secondary parents is added | |
| 212 | + development. Content from the merge parents is added | |
| 212 | 213 | into the main line. |
| 213 | 214 | <li><p>The "direct children" of a check-in X are all children that |
| 214 | 215 | have X as their primary parent. |
| 215 | 216 | <li><p>A check-in node with no direct children is sometimes called |
| 216 | 217 | a "leaf". |
| @@ -221,10 +222,12 @@ | ||
| 221 | 222 | <li><p>Definition: <b>branch</b> → |
| 222 | 223 | a sequence of check-ins that are all linked |
| 223 | 224 | together in the DAG through the primary parent. |
| 224 | 225 | <ul> |
| 225 | 226 | <li><p>Branches are often given names which propagate to direct children. |
| 227 | + The tradition in Fossil is to call the main branch "trunk". In | |
| 228 | + Git, the main branch is usually called "master". | |
| 226 | 229 | <li><p>It is possible to have multiple branches with the same name. |
| 227 | 230 | Fossil has no problem with this, but it can be confusing to |
| 228 | 231 | humans, so best practice is to give each branch a unique name. |
| 229 | 232 | <li><p>The name of a branch can be changed by adding special tags |
| 230 | 233 | to the first check-in of a branch. The name assigned by this |
| 231 | 234 |
| --- www/whyusefossil.wiki | |
| +++ www/whyusefossil.wiki | |
| @@ -199,18 +199,19 @@ | |
| 199 | might commit different changes against the same check-in. This |
| 200 | results in one parent node having two or more children. |
| 201 | <li><p>Command: <b>merge</b> → |
| 202 | combines the work of multiple check-ins into |
| 203 | a single check-out. That check-out can then be committed to create |
| 204 | a new that has two (or more) parents. |
| 205 | <ul> |
| 206 | <li><p>Most check-ins have just one parent, and either zero or |
| 207 | one child. |
| 208 | <li><p>When a check-in has two or more parents, one of those parents |
| 209 | is the "primary parent". All the other parent nodes are "secondary". |
| 210 | Conceptually, the primary parent shows the main line of |
| 211 | development. Content from the secondary parents is added |
| 212 | into the main line. |
| 213 | <li><p>The "direct children" of a check-in X are all children that |
| 214 | have X as their primary parent. |
| 215 | <li><p>A check-in node with no direct children is sometimes called |
| 216 | a "leaf". |
| @@ -221,10 +222,12 @@ | |
| 221 | <li><p>Definition: <b>branch</b> → |
| 222 | a sequence of check-ins that are all linked |
| 223 | together in the DAG through the primary parent. |
| 224 | <ul> |
| 225 | <li><p>Branches are often given names which propagate to direct children. |
| 226 | <li><p>It is possible to have multiple branches with the same name. |
| 227 | Fossil has no problem with this, but it can be confusing to |
| 228 | humans, so best practice is to give each branch a unique name. |
| 229 | <li><p>The name of a branch can be changed by adding special tags |
| 230 | to the first check-in of a branch. The name assigned by this |
| 231 |
| --- www/whyusefossil.wiki | |
| +++ www/whyusefossil.wiki | |
| @@ -199,18 +199,19 @@ | |
| 199 | might commit different changes against the same check-in. This |
| 200 | results in one parent node having two or more children. |
| 201 | <li><p>Command: <b>merge</b> → |
| 202 | combines the work of multiple check-ins into |
| 203 | a single check-out. That check-out can then be committed to create |
| 204 | a new check-in that has two (or more) parents. |
| 205 | <ul> |
| 206 | <li><p>Most check-ins have just one parent, and either zero or |
| 207 | one child. |
| 208 | <li><p>When a check-in has two or more parents, one of those parents |
| 209 | is the "primary parent". All the other parent nodes are "secondary" |
| 210 | or "merge" parents. |
| 211 | Conceptually, the primary parent shows the main line of |
| 212 | development. Content from the merge parents is added |
| 213 | into the main line. |
| 214 | <li><p>The "direct children" of a check-in X are all children that |
| 215 | have X as their primary parent. |
| 216 | <li><p>A check-in node with no direct children is sometimes called |
| 217 | a "leaf". |
| @@ -221,10 +222,12 @@ | |
| 222 | <li><p>Definition: <b>branch</b> → |
| 223 | a sequence of check-ins that are all linked |
| 224 | together in the DAG through the primary parent. |
| 225 | <ul> |
| 226 | <li><p>Branches are often given names which propagate to direct children. |
| 227 | The tradition in Fossil is to call the main branch "trunk". In |
| 228 | Git, the main branch is usually called "master". |
| 229 | <li><p>It is possible to have multiple branches with the same name. |
| 230 | Fossil has no problem with this, but it can be confusing to |
| 231 | humans, so best practice is to give each branch a unique name. |
| 232 | <li><p>The name of a branch can be changed by adding special tags |
| 233 | to the first check-in of a branch. The name assigned by this |
| 234 |
+3
-2
| --- www/wikitheory.wiki | ||
| +++ www/wikitheory.wiki | ||
| @@ -65,21 +65,22 @@ | ||
| 65 | 65 | The comments on check-ins and the text in the descriptions of bug reports |
| 66 | 66 | both use wiki formatting. Exactly the same set of formatting rules apply. |
| 67 | 67 | There is never a need to learn one formatting language for documentation |
| 68 | 68 | and a different markup for bugs or for check-in comments. |
| 69 | 69 | |
| 70 | +<a name="assocwiki"></a> | |
| 70 | 71 | <h2>Auxiliary notes attached to check-ins or branches</h2> |
| 71 | 72 | |
| 72 | 73 | Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" |
| 73 | 74 | or "checkin/<i>HASH</i>" are associated with the corresponding |
| 74 | 75 | branch or check-in. The wiki text appears in an "About" section of |
| 75 | 76 | timelines and info screens. Examples: |
| 76 | 77 | |
| 77 | 78 | * [/timeline?r=graph-test-branch] shows the text of the |
| 78 | - [/wiki?name=branch/graph-test-branch|branch/graph-test-branch] | |
| 79 | + [/wiki?name=branch/graph-test-branch&p|branch/graph-test-branch] | |
| 79 | 80 | wiki page at the top of the timeline |
| 80 | 81 | * [/info/19c60b7fc9e2] shows the text of the |
| 81 | - [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6|checkin/19c60b7fc9e2...] | |
| 82 | + [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...] | |
| 82 | 83 | wiki page in the "About" section. |
| 83 | 84 | |
| 84 | 85 | This special wiki pages are very useful for recording historical |
| 85 | 86 | notes. |
| 86 | 87 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -65,21 +65,22 @@ | |
| 65 | The comments on check-ins and the text in the descriptions of bug reports |
| 66 | both use wiki formatting. Exactly the same set of formatting rules apply. |
| 67 | There is never a need to learn one formatting language for documentation |
| 68 | and a different markup for bugs or for check-in comments. |
| 69 | |
| 70 | <h2>Auxiliary notes attached to check-ins or branches</h2> |
| 71 | |
| 72 | Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" |
| 73 | or "checkin/<i>HASH</i>" are associated with the corresponding |
| 74 | branch or check-in. The wiki text appears in an "About" section of |
| 75 | timelines and info screens. Examples: |
| 76 | |
| 77 | * [/timeline?r=graph-test-branch] shows the text of the |
| 78 | [/wiki?name=branch/graph-test-branch|branch/graph-test-branch] |
| 79 | wiki page at the top of the timeline |
| 80 | * [/info/19c60b7fc9e2] shows the text of the |
| 81 | [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6|checkin/19c60b7fc9e2...] |
| 82 | wiki page in the "About" section. |
| 83 | |
| 84 | This special wiki pages are very useful for recording historical |
| 85 | notes. |
| 86 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -65,21 +65,22 @@ | |
| 65 | The comments on check-ins and the text in the descriptions of bug reports |
| 66 | both use wiki formatting. Exactly the same set of formatting rules apply. |
| 67 | There is never a need to learn one formatting language for documentation |
| 68 | and a different markup for bugs or for check-in comments. |
| 69 | |
| 70 | <a name="assocwiki"></a> |
| 71 | <h2>Auxiliary notes attached to check-ins or branches</h2> |
| 72 | |
| 73 | Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" |
| 74 | or "checkin/<i>HASH</i>" are associated with the corresponding |
| 75 | branch or check-in. The wiki text appears in an "About" section of |
| 76 | timelines and info screens. Examples: |
| 77 | |
| 78 | * [/timeline?r=graph-test-branch] shows the text of the |
| 79 | [/wiki?name=branch/graph-test-branch&p|branch/graph-test-branch] |
| 80 | wiki page at the top of the timeline |
| 81 | * [/info/19c60b7fc9e2] shows the text of the |
| 82 | [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...] |
| 83 | wiki page in the "About" section. |
| 84 | |
| 85 | This special wiki pages are very useful for recording historical |
| 86 | notes. |
| 87 |